Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Porting kegs-universal changes to gsplus. #31

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
179 changes: 179 additions & 0 deletions doc/kegs-universal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
# kegs-universal
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is merely a copy of the kegs-universal README file.


Minimal changes on the kegs apple2-gs emulator to accept and run apple 2, 2+, 2e, 2c, and 2c+ roms.


I was curious to see how difficult it would be to boot the KEGS Apple 2gs emulator
using the ROMs of older Apple 2 models. Since the 2gs hardware was designed to be
as compatible as possible with the earlier models, it likely supports what the
older ROMs need. This reasoning worked quite well for the Apple 2, 2+,
and 2e ROMs. The changes needed to support the Apple 2c and 2c+ ROMs were
much more interesting. This is described below.

## Supported ROMs

The KEGS configuration code (`config.c`) was minimally changed to accept ROM
files of 12KB (for the Apple 2 and 2+), 16KB (for the Apple 2e and some 2c),
32KB (for the Apple 2c and 2c+). It also uses the standard tests to
determine the type of the machine associated with the ROM and
set the variable `g_a2rom_version` with the following values:
* `'2'` for an Apple 2 or Apple 2+ ROM (both 12KB roms,)
* `'e'` for an Apple 2e (both the 16KB original and enhanced roms,)
* `'c'` for an Apple 2c (tested the 16KB rom0 and the 32KB rom4,)
* `'C'` for an Apple 2c+ (the 32KB rom5,)
* `'g'` for an Apple 2gs (both the 128KB and 256KB roms.)

I also added a configuration option to load the disk controller ROM
into the slot 6 rom space. This is useful with the 2, 2+, and 2e ROMs.
The 2c, 2c+, and 2gs ROMs provide this code.

## Apple 2 and 2+

The Apple 2 and 2+ ROMs booted right away.
For convenience I added code in `moremem.c` to map all alpha keys to uppercase
and to map the delete key to backspace. I also noticed and fixed subtle but
important differences over time:
* The 2gs resets the banked memory in a known state at every reset.
The original language card settings were preserved. The reset code
in file `sim65816.c` now selectively initializes registers
depending on the detected rom type.
* The 2gs contains a special circuit that causes the processor to
always fetch interruption vectors in the rom located in page `ff`
instead of page `00`. The corresponding code, located in `sim65816.c`,
now only does this if `g_a2rom_version=='g'`.
* The KEGS emulator did not implement the language card memory protection
and the double reading of the `c08x`. This was fixed in `moremem.c`
for all architectures. A couple weeks later I also had to modify the
cpu simulation code for INC abs,X because several pieces of code depend
on the 6502/65C02/65816 reading the memory location twice to write-enable
the language card ram.

Note that the character generator ROM still shows characters `e0..ff`
as lowercase character. On the real Apple 2 or 2+, these are uppercase
characters.

## Apple 2e

The Apple 2e ROMs also booted right away and even passed the self test.
This is not surprising as the 2gs is designed to be maximally
compatible with the 2e.

## Apple 2c

Getting the Apple 2c to work represented a lot more work.
It turns out that the 2e and the 2c often follow different paths
to remain compatible with the original Apple 2. For instance,
in the 2e or the 2gs, reading `c019` merely tells you whether
we are in the video vertical blanking time window. On the 2c,
this same register tells you whether there is a pending vertical
blanking interrupt and clears the interrupt. All these details
are fixed in `moremem.c`.

### Rombank

The later Apple 2c ROMS contain 32KB of code. The config file
loads the two 16KB segments in the 2gs pages `ff` and `fe`.
Which ROM bank is shown in page `00` then depends
on the RDROM, CXINT, and ROMBANK bits of the `c068` state
register. The KEGS emulator did not support the ROMBANK
bit and I implemented it to use the rom from page `fe`
instead of `ff`. The Apple 2c port `c028` now
switches the active bank by calling the normal
function `set_statereg()` defined in `moremem.c`.

### Slinky

The later Apple 2c also support a memory expansion card
that can be used as a virtual drive. It turns out that
some versions of the 2c ROMs do not boot properly when
the corresponding i/o ports `c0cx` are not implemented.
Simple code in `moremem.c` now uses up to 1MB of RAM
in pages `02` to `12` to implement the memory extension.

### The Apple 2c mouse

Then I decided to resolve the dramatically different ways
in which the 2c and the 2gs handle the mouse. Whereas the 2gs
uses the rather intelligent adb controller to buffer the mouse
moves and report x or y deltas in range 0..64, the 2c interrupts
the 65C02 for every little move. Each access to an Apple 2c
mouse register now calls a routine that pumps the mouse state
from the 2gs adb controller (function `emulate_a2c_mouse()` in
file `moremem.c`). In parallel, two small changes in `adb.c` were
necessary in order to send mouse deltas in smaller increments
and to prevent the 2gs from interrupting the cpu
when the mouse button is pressed without mouse move.
Prodos really dislikes unhandled interrupts.

### Serial ports

I did not try to emulate the ACIA or map it to the 2gs SCC.

### 3.5" drives.

Later version of the Apple 2c also supports 3.5" drives.
However these are not the same as the 2gs 3.5" drives.
The 2c only uses the Unidisk 3.5" that contain
their own 6502 variant clocked at 2MHz, that is
twice faster than the 2c 65C02 cpu. The proper way
to implement this would be to have the emulator
intercept firmware calls on slot 5. This technique
is already used by Kegs to simulate a hard disk
on slot 7. On the other hand, the 2c+ uses the
same drives as the 2gs...

## Apple 2c+

Surprisingly the changes that made the late Apple 2c ROMs
work smoothly allowed KEGS to boot the Apple 2c+ ROM and
even pass the self test.

Then I came with the crazy idea to make the 2gs 3.5" drives
work with the Apple 2c+ ROM. This can work because the
Apple 2c+ uses the same dumb 3.5" drives as the 2gs.
The difficult is that the Apple 2c+ does so using
the special and poorly documented MIG chip and
using a small 2K cache RAM.

The MIG is rumored to be necessary to allow the 1MHz 65C02 to
keep up with the 3.5" drive data transfer rate. The rumor
says that the Apple engineers only kept the MIG on the 2c+ board
because the 4MHz built-in cpu accelerator was a late addition
to the project. The only programming information I found
on the MIG was some
[reverse engineering by "M.G."](http://apple2.guidero.us/doku.php?id=mg_notes:apple_iic:mig_chip)
and the discussion following a
[bug report for the MAME emulator.](https://github.com/mamedev/mame/issues/2775)

Two realization helped me figure out the rest. First, in the available
2c schematics, the MIG RESET line is tied to both the /IOSEL line
and the A14 ROM line which selects the alternate 16KB ROM bank.
In other words, the MIG can only be accessed when the alternate
ROM bank is selected. Whenever the firmware returns to the main ROM bank,
the MIG is reset to its default, 5.25" inch drive-compatible, state.
This is probably why M.G. only saw brief pulses on the drive lines
when triggering the MIG i/o ports. The second realization was
that the Apple 2c+ supports three dumb 3.5" drives: the internal drive
and two external daisy chained drives. The MIG contains a switch
that either directs the IWM DR2 enable to the internal disk
or to the external port. This makes the internal disk a d2
while the external disks are d1 and d2. After figuring out
how the MIG i/o ports control this switch as well as the
3.5SEL and HDSEL lines, I had the tricky problem to remap
the internal d2 and the external d1 onto the two external d1 and
d2 drives implemented by KEGS. This is all implemented
in file `moremem.c`.

## Apple 2gs

The behavior of KEGS with an Apple 2gs ROM is
mostly unchanged with the following three exceptions:
* The memory protection features of the banked language card memory
is now implemented.
* The slot 7 smartport hard disk emulation is now
switched like the ROM of a virtual card inserted in slot 7.
* If the slot 7 code fails to find a bootable disk, the search
for a bootable disk continues with slots 6 and 5.



9 changes: 9 additions & 0 deletions src/.dir-locals.el
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
;;; Directory Local Variables
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This lets emacs uses tabs in the same way as the rest of the kegs codebase.

;;; For more information see (info "(emacs) Directory Variables")

((c-mode
(c-basic-offset . 8))
(nil
(indent-tabs-mode . t)))


29 changes: 20 additions & 9 deletions src/adb.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ extern word32 g_vbl_count;
extern int g_num_lines_prev_superhires640;
extern int g_num_lines_prev_superhires;
extern int g_rom_version;
extern int g_a2rom_version;
extern int g_fast_disk_emul;
extern int g_limit_speed;
extern int g_irq_pending;
Expand Down Expand Up @@ -1257,7 +1258,10 @@ update_mouse(int x, int y, int button_states, int buttons_valid)
if( (g_mouse_ctl_addr == g_mouse_dev_addr) &&
((g_adb_mode & 0x2) == 0)) {
g_adb_mouse_valid_data = 1;
adb_add_mouse_int();
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes the mouse interrupt behavior when using an Apple2c rom. (see comment in code)

if (mouse_moved || g_a2rom_version == 'g')
/* A2C only interrupts on mouse move
and Prodos8 does not like unexplained interrupts */
adb_add_mouse_int();
}
}

Expand All @@ -1267,6 +1271,13 @@ update_mouse(int x, int y, int button_states, int buttons_valid)
int
mouse_read_c024(double dcycs)
{
return mouse_read_c024_clamp(dcycs, 0x3f);
}

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The apple2c mouse i/o ports in moremem.c call this function with clamp=2 in order to obtain detailed mouse position updates instead of aggregated updates in range -0x3f,+0x3f like a 2gs.

int
mouse_read_c024_clamp(double dcycs, int clamp)
{

word32 ret;
word32 tool_start;
int em_active;
Expand Down Expand Up @@ -1294,18 +1305,18 @@ mouse_read_c024(double dcycs)
delta_y = target_y - g_mouse_a2_y;

clamped = 0;
if(delta_x > 0x3f) {
delta_x = 0x3f;
if(delta_x > clamp) {
delta_x = clamp;
clamped = 1;
} else if(delta_x < -0x3f) {
delta_x = -0x3f;
} else if(delta_x < -clamp) {
delta_x = -clamp;
clamped = 1;
}
if(delta_y > 0x3f) {
delta_y = 0x3f;
if(delta_y > clamp) {
delta_y = clamp;
clamped = 1;
} else if(delta_y < -0x3f) {
delta_y = -0x3f;
} else if(delta_y < -clamp) {
delta_y = -clamp;
clamped = 1;
}

Expand Down
Loading