Apple Emulator v2.0                                          11th-February-1995
-------------------                                          ------------------

Introduction
------------

The decision to write a new version of the emulator before version 1 was
complete was made when it became difficult to continue 'patching' the RAM and
VIDEO routines to support the extended 80-column card.
Also, I have since developed my DOS overlay routine enough to be able to
satisfactorily use them with the emulator.

Planned Architecture
--------------------

A 16K language card in Slot 0 is always available.
A 64K extended 80-column card in the auxiliary slot is always available when
IIe and IIc ROMs are detected.

Reset
-----

  See the keyboard operation for F12 below.

  Memory is initialised to the following pattern at *power-up*:

  xxx0:FF FF FF FF 00 00 00 00 FF FF FF FF 00 00 00 00

Keyboard
--------

  The keyboard works as a normal IBM PC keyboard except for:

    Left Alt   : Open Apple (or simply Apple) key
    Right Alt  : Solid Apple (or Option) key
    Backspace  : Left Arrow / Delete(checkerboard) *** CONFIGURABLE ***
    F1 -F7     : Peripheral card configuration (parallel port mapping,disk
                                                image file names, etc).
    F8         : unassigned
    F9         : Enter debug mode
    F10        : Configure emulator (keyboard assignments, etc)
                 Save/restore state to/from files
                 Change ROMs
                 Change peripheral slot assignments.
    F11        : Quit emulator (with confirmation)
    F12        : Reset (Reset functions are called on press,emulator operation
                        suspended until release) IIe and IIc require a Ctrl key
                        to be pressed before Reset is recognised.
    Scroll Lock: On:  if BIOS reports no joystick, allows the numeric keypad to
                      function as a joystick.
                 Off: numeric keypad functions as normal.
    Insert     : ignored/Tab *** CONFIGURABLE ***
    Delete     : ignored/Delete(checkerboard) *** CONFIGURABLE ***
    Home       : ignored
    End        : ignored
    Page Up    : ignored
    Page Down  : ignored

  Caps Lock and the Ctrl keys work as normal.

  Although not emulated, the Apple IIGS supports reading the following soft
  switch at $C025, and I thought it might be nice to implement it:

     Bit Set | Meaning
    ---------+---------------------------
        0    | Shift key down
        1    | Control key down
        2    | Caps Lock key down
        3    | Key is repeating
        4    | Keypad key pressed
        5    | Key was sent, not pressed
        6    | Closed-Apple key down
        7    | Open-Apple key down

Video
-----

  Will be able to suspend the Apple emulated video while performing
  configuration/debugging etc. processing, and restore Apple emulated video
  when special processing is complete.

  The following PC video modes are required:

   PC Video Mode       | Apple Video Mode
  ---------------------+----------------------------------------------
   Text      40x 25x16 | 40-column text
                       | LoRes graphics
   Text      80x 25x16 | 80-column text
                       | Split LoRes graphics & 80-column text
                       | Split Double LoRes graphics & 40-column text
                       | Split Double LoRes graphics & 80-column text
   Graphics 320x200x16 | HiRes graphics
                       | Split HiRes graphics & 40-column text
   Graphics 640x200x16 | Double HiRes graphics
                       | Split HiRes graphics & 80-column text
                       | Split Double HiRes graphics & 40-column text
                       | Split Double HiRes graphics & 80-column text

State Saving
------------

  Will be able to save the complete state of the emulator on exit, and to
  automatically restore that state when the emulator is started up again.

Memory
------

#define BYTE unsigned char
#define WORD unsigned int

  The Apple has 64K of addressable memory (0-65535/$0-$FFFF). Various parts of
  the total range can address different physical memory at different time, and
  at some times reading the same location will access different memory from
  writing to it.

Normalised Memory Segments
--------------------------

  Each block of memory will be allocated via farmalloc() and farfree() and will
  have a 'normalised' segment pointer associated with it. A normalised segment
  pointer will allow access to the memory using a full-range (0-65535) offset
  even though the memory itself may be <65536 bytes long.

  For example, memory for the internal IIe ROMs is allocated. The internal ROMs
  occupy memory locations $C100-$FFFF ($3F00 bytes).

  Say that the far pointer returned is 615D:0004. The nearest whole segment
  boundary is 615E, meaning that the ROMs will take memory locations 615E:0000-
  615E:3EFF. Apple memory addresses cannot directly access that memory since
  they would try to access 635E:C100-615E:FFFF. Instead the bottom range of the
  memory must be subtracted from the segment. In this case $C10 must be
  subtracted. The new segment is 554E. Now the Apple can directly access
  554E:C100-554E:FFFF (which is equivalent to 615E:0000-615E:3EFF, do your
  segment arithmetic!).

  Essentially, normalised segments eliminate the need to adjust the offset
  depending on the location of the allocated memory.

Accessibility
-------------

  There will be two types of memory accessing:

  1. Monitored
  2. Straight-through

  Both types of memory access operate through the ReadSegs and WriteSegs
  arrays.

  Monitored
  ---------

  Monitored memory I/O activates soft switches and alters the video display
  as appropriate. This is the default behaviour for all memory I/O. The general
  format of Monitored memory I/O calls are:

    BYTE Read(WORD Addr);
    void Write(WORD Addr,BYTE Data);

  Straight-through
  ----------------

  Straight-through memory I/O alters the memory storage directly without
  activiting special soft switch or video processing. It is typically used for
  special pre- or post-processing of Monitored memory I/O. The general format
  of Straight-through memory I/O calls are:

    BYTE Peek(WORD Addr);
    void Poke(WORD Addr,BYTE Data);

Required Apple Memory Blocks
----------------------------

  In the source code, the far pointer to the allocated memory is given as
  <name>Ptr. The normalised segment is given as <name>Seg

  For example, MainRAM -> void far *MainRAMPtr; & unsigned int MainRAMSeg;

   Description             | Range       | Name
  -------------------------+-------------+------------
   Main Internal ROMs      | $C100-$FFFF | MainIntROM
   Aux. Internal ROMs      | $C100-$FFFF | AuxIntROM
   I/O Card ROM            | $C100-$CFFF | IOCardROM
   Card Expansion ROM (x7) | $C800-$CFFF | CardExpROM
   Main LC RAM Bank 1      | $D000-$FFFF | MainLCBnk1
   Main LC RAM Bank 2      | $D000-$DFFF | MainLCBnk2
   Aux. LC RAM Bank 1      | $D000-$FFFF | AuxLCBnk1
   Aux. LC RAM Bank 2      | $D000-$DFFF | AuxLCBnk2
   Hardware I/O            | $C000-$C0FF | HardwareIO
   Main RAM                | $0200-$BFFF | MainRAM
   Aux. RAM                | $0200-$BFFF | AuxRAM
   Main ZP/Stack           | $0000-$01FF | MainZPS
   Aux. ZP/Stack           | $0000-$01FF | AuxZPS
  -------------------------+-------------+------------
  Total Areas:19

  Total Memory Required=$3F00 + $3F00 + $0F00 + $0800x7 + $3000 + $1000 +
                        $3000 + $1000 + $0100 + $BE00   + $BE00 + $0200 + $0200
                       =$2C600 = 181760 bytes

  Notes
  =====

  Main Internal ROMs
  ------------------

  On the ][+ these range from $D000-$FFFF only. On the IIe and IIc they range
  from $C100-$FFFF.

  Auxiliary Internal ROMs
  -----------------------

  These ROMs exist only in the IIc. On the IIe, they map to the Main Internal
  ROMs.

  I/O Card ROM
  ------------

  Unused ROM in $C100-$C7FF should be set to $FF's.

  $C800-$CFFF should be set to $FF's. This range of memory is to be used when
  no expansion ROM is enabled.

  Card Expansion ROM
  ------------------

  The I/O Card Expansion ROM ($C800-$CFFF) can be selected from an array of
  seven memory segments and pointers. Zero pointers indicate no memory exists
  for the I/O card and the default (I/O Card ROM) should be used.

  If a card is in the auxiliary slot, the expansion ROM for that card will be
  in the slot 3 array position, rather than the actual slot 3 expansion ROM.
  This means that any expansion ROM in slot 3 will be inaccessible (which is
  how it should be).

  The overlay files that handle expansion ROM must handle all memory accesses
  and allocation for memory in this range.

Memory Types
------------

  Apple memory can be divided into the following functional types:

  1. $0000-$03FF General-purpose RAM (incl. Zero Page & Stack)
     $0C00-$1FFF
     $6000-$BFFF
  2. $0400-$0BFF Text/Graphics
     $2000-$5FFF
  3. $C000-$C0FF Soft Switches
  4. $C100-$CFFF Card ROM
  5. $D000-$FFFF ROM & Language Card RAM

  General-Purpose RAM (Type 1)
  ----------------------------

  This RAM can be handled by directly reading/writing to the memory specified
  by the ReadSegs and WriteSegs arrays. No special hardware side-effects are
  generated by accessing this memory.

  Text/Graphics RAM (Type 2)
  --------------------------

  When Reading, this type of memory is exactly the same as Type 1 RAM. However,
  when Writing, this type of memory may alter the display, so special video
  routines need to be called. The called video routines will need to examine
  the WriteSegs array to determine which physical video memory is altered.

  For example,

    void VideoWrite(WORD Address,BYTE Data)
    {
      Poke(Address,Data);
      ProcessVideo(Address);
    }

  Soft Switches (Type 3)
  ----------------------

  This memory may not actually exist on a real Apple. The data Read from here
  is generated entirely by hardware, and is not retrieved from RAM. Data
  Written to this memory directly alters various hardware states. The hardware
  affected by this memory can be both Internal amd External (Card I/O).
  Where applicable, the emulator will write an appropriate value just before
  Reading:

    BYTE ReadSoftSwitch(WORD Addr) /* Addr between 0xc000 and 0xc08f */
    {
      void DoSoftSwitchProcessing(Addr&0x00ff);
      /* previous call will Poke appropriate data directly into memory */
      return Peek(Addr);
    }

    BYTE ReadSlotIO(WORD Addr) /* Addr between 0xc090 and 0xc0ff */
    {
      int Slot=(Addr>>4)&7; /* slot now between 1 and 7 */
      Poke(Addr,CallCardOverlay(slot));
      return Peek(Addr);
    }

  Card ROM (Type 4)
  -----------------

  Although it is possible that some peripheral cards may use RAM in this range
  of memory, it is more usually ROM.

  The overlays that handle peripherals handle all memory accesses to this
  range of memory (when slot ROM are switched in).

  ROM & Language Card RAM (Type 5)
  --------------------------------

  This part of memory can be both RAM and ROM, sometimes simultaneously! ROM
  is handled by setting the appropriate entries in the WriteSegs array to zero.
  Otherwise, this is the same as Type 1 RAM.

Memory Paging
-------------

  The Apple memory can be divided into 256 pages of 256 bytes each. The pages
  are numbered from $00 to $FF. In general, the function of the different
  memory areas can be split along page boundaries. It would make sense to
  store what parts of addressable memory map to which physical memory in an
  array of pages:

  unsigned int  ReadSegs[0x100];
  unsigned int WriteSegs[0x100];

  Note that since some parts of memory can read and write to different physical
  memory via the same address, the two arrays are required.

  If any entries in the WriteSegs array in the range 0xd0-0xff are zero, writes
  to those pages are inhibited.

  Also, considering the Memory Types as shown above, the following arrays will
  also be defined:

  BYTE (*ReadHandler)(WORD)[0x100];
  void (*WriteHandler)(WORD,BYTE)[0x100];

  They will contain pointers to functions handling accesses to specific memory
  types:

  1. BYTE ReadRAM(WORD Addr);
  1. void WriteRAM(WORD Addr,BYTE Data);
  2. void WriteVideo(WORD Addr,BYTE Data);
  3. BYTE ReadSoftSwitch(WORD Addr);
  3. void WriteSoftSwitch(WORD Addr,BYTE Data);
  4. BYTE ReadIOCard(WORD Addr);
  4. void WriteIOCard(WORD Addr,BYTE Data);
  5. BYTE ReadLangCard(WORD Addr);
  5. void WriteLangCard(WORD Addr);

Peripheral Cards
----------------

  Peripheral cards are to be handled via DOS overlays. This allows for third-
  party enhancements to the emulator. Cards can theoretically access all the
  Apple's memory. All the ROM code for the peripheral card should be contained
  entirely within the overlay so that one file contains everything required
  to support emulation of the peripheral card.

  Since the peripheral card is given pointers to the current memory segment
  arrays, it may perform as much DMA as it likes when control is passed to it.
  My Mass-Storage peripheral emulation does just that when it handles block
  read/write requests.

  The following overlay functions are required:

* Overlay name:

    char far *Name(void);

      Eg. "Disk ]["

* Overlay type:

    char far *Type(void);

      Returns "Apple Emulator Peripheral"

* Copyright message:

    char far *Copyright(void);

      Eg. "Copyright (c) 1995 Andrew Gregory"

* To perform card Initialisation, as well as communicate important emulator
  environment information:

    unsigned int Initialise(void (*cs)(char,char,unsigned char,unsigned char),
                            unsigned int *rSegs,unsigned int *wSegs);
      Return code is ignored. cs is set to a pointer to an internal emulator
      function that handles status line updating. rSegs and wSegs point to the
      internal emulator ReadSegs and WriteSegs arrays.

* Each card can access five text screen characters on the bottom row of the
  screen for status displays. Can update it's status via a callback function.
  Locations are: columns (slot*5+0) to (slot*5+4) inclusive on screen row 24
  (counting the top row as 0).

  This function is present within the emulator, not the overlay.

    void (*CardStatus)(char Slot,char Pos,unsigned char Char,unsigned char Attr);
      where Slot=1-7,Pos=0-4,Char=0-255,Attr=0-255

* To 'hot-configure':

    void Configure(void);

      Called when the F1-F7 key corresponding to the slot number is pressed.
      The video is restored to the pre-emulator state (usually 80x25x16), and
      changed back to the appropriate emulator mode and display, before and
      after calling this function.

* When the Apple is reset:

    void Reset(void);

* When the device's I/O memory (0xc090-0xc0ff) is accessed:

    BYTE DeviceSelectR(WORD Addr);
    void DeviceSelectW(WORD Addr,BYTE Data);

* When the device's 256 byte slot ROM is accessed:

    BYTE IOSelectR(unsigned int Addr);
    void IOSelectW(unsigned int Addr,BYTE Data);

* When the device's 2048 byte expansion ROM is accessed (the emulator
  determines when a slot's expansion ROM is enabled):

    BYTE IOStrobeR(unsigned int Addr);
    void IOStrobeW(unsigned int Addr,BYTE Data);

* When the emulator is saving it's state:

    void WriteState(int fd);

      fd is an MS-DOS file descriptor. The peripheral should write it's state
      to that file.

* When the emulator is restoring it's state:

    void ReadState(int fd);

      fd is an MS-DOS file descriptor. The peripheral should read it's state
      from the file.

6502 CPU Emulation
------------------

AIM: So long as memory that performs no I/O is accessed, the CPU should be able
     to execute without calling outside functions.

  Memory that may perform I/O on READ
  -----------------------------------
    C000-CFFF

  Memory that may perform I/O on WRITE
  ------------------------------------
    0400-0BFF
    2000-5FFF
    C000-CFFF

  The CPU must have it's own copies of the ReadSegs[] and WriteSegs[] arrays.
  Entries of zero will indicate I/O memory that will require an outside call
  to process correctly.
  The CPU segment tables will need to be updated in parallel with the 'actual'
  segment tables.

  Required Tables
  ---------------

    CPUReadSegs  DW 256 DUP (?)
    CPUWriteSegs DW 256 DUP (?)
    Flag2Stat    DB 4096 DUP (?)  ;80x86 CPU flags -> 6502 status translation
    Stat2Flag    DW 256 DUP (?)   ;6502 status -> 80x86 CPU flags translation

  Register Allocation
  -------------------

  6502 Registers:  A  -  8 bit Accumulator
                   X  -  8 bit Index
                   Y  -  8 bit Index
                   P  -  8 bit Processor Status
                   S  -  8 bit Stack Pointer
                   PC - 16 bit Program Counter
                      &  8 bit data bus
                      & 16 bit address bus
                      ----
                      5x16 bits

  80x86 Registers: *AX/AH,AL - 16/2x8 bit Accumulator
                   *BX/BH,BL - 16/2x8 bit Base Index
                   *CX/CH,CL - 16/2x8 bit Count
                   *DX/DH,DL - 16/2x8 bit Data
                   *SI       - 16 bit Source Index
                   *DI       - 16 bit Destination Index
                   *BP       - 16 bit Base Pointer
                    SP       - 16 bit Stack Pointer
                    SS       - 16 bit Stack Segment
                    DS       - 16 bit Data Segment
                   *ES       - 16 bit Extra Segment
                    CS       - 16 bit Code Segment
                    IP       - 16 bit Instruction Pointer
                    Flags    - 16 bit Flags
                   *=usable

       6502 | 80x86
      ------+-------
        A   | AL
        X   | SI
        Y   | DI
        P   | Flags & (Interrupts Enabled,Break,Decimal Mode)
        S   | AH
        PC  | BP
       Addr | BX
       Data | DL

   Free: CX,DH

  Instruction Execution Operations
  --------------------------------

    1. Memory reading/writing.

  Instruction Execution Stages (unordered)
  ----------------------------

    o Instruction retrieval.
    o Data address determination (get address into BX).
    o Data storage/retrieval ( [BX]->AH / AH->[BX] ).
    o Instruction execution.
    o Clock cycle count.

  Example Implementation of ADC
  -----------------------------

  ADC was chosen because it uses a wide range of the available addressing
  modes, and is one of the two instructions that uses the Decimal mode flag.

    ADC Addressing Modes:

      o Immediate          #
      o Absolute           ?
      o Zero Page          ?
      o Indexed Indirect   (?,Y)
      o Indirect Indexed   (?),X
      o Zero Page Index X  ?,X
      o Absolute Index X   ?,X
      o Absolute Index Y   ?,Y
      o Zero Page Indirect (?)

    Operation:  A <- A + data

    Affects Flags: Negative (Sign), Overflow, Zero, Carry

    Assume:

      o Instruction has been decoded and appropriate routine called.
      o Processor status flags not in 80x86 are named:
          Decimal
          IntrDis
          Break

    General Functions
    -----------------

    MRead    PROC NEAR
             pushf
             push ax bx cx bp si di      ;save regs
             push bx                     ;address to read
             call MntrdRead
             add  sp,2
             mov  dl,al
             pop  di si bp cx bx ax
             popf
             ENDP

    MWrite   PROC NEAR
             pushf
             push ax bx cx bp si di     ;save regs
             xor  dh,dh
             push dx                    ;byte to write
             push bx                    ;address to write
             call MntrdWrite
             add  sp,4
             pop  di si bp cx bx ax
             popf
             ENDP

    General Macros
    --------------

      ReadByte MACRO ;get byte at address BX into DL, trashes DX,ES
               LOCAL @drd,@skipdrd
               push bx
               mov  bl,bh
               xor  bh,bh
               shl  bx,1                   ;create word-sized index
               mov  bx,CPUReadSegs[bx]
               cmp  bx,0
               je   @drd                   ;direct read
               pop  bx
               call MRead
               jmp  @skipdrd
      @drd     mov  es,bx                  ;get segment of direct read
               pop  bx
               mov  dl,es:[bx]             ;and get byte
      @skipdrd label near
               ENDM

      WriteByte MACRO ;put byte in DL into address BX, trashes DX,ES
                LOCAL @dwr,@skipdwr
                push bx
                mov  bl,bh
                xor  bh,bh
                shl  bx,1                  ;create word-sized index
                mov  bx,CPUWriteSegs[bx]
                cmp  bx,0
                jne  @dwr                  ;direct write
                pop  bx
                call MWrite
                jmp  @skipdwr
      @dwr      mov  es,bx                 ;get segment of direct write
                pop  bx
                mov  es:[bx],dl            ;and write byte
      @skipdwr  label near
                ENDM

    Addressing Mode Macros (get required address,inc BP)
    ----------------------

    Absolute  MACRO            ;word address at BP
              mov  bx,bp
              ReadByte
              mov  cl,dl
              inc  bx
              ReadByte
              mov  ch,dl
              inc  bx
              mov  bp,bx
              mov  bx,cx
              ENDM

    ZeroPage  MACRO            ;byte address at BP
              mov  bx,bp
              ReadByte
              mov  bl,dl
              inc  bp
              xor  bh,bh
              ENDM

    IdxIdr    MACRO            ;(?,Y) byte address at BP, add Y(DI)
              mov  bx,bp
              ReadByte
              inc  bp
              xor  dh,dh
              add  dx,di
              mov  bx,dx
              ReadByte
              mov  cl,dl
              inc  bx
              ReadByte
              mov  ch,dl
              mov  bx,cx
              ENDM


    ADC Macros
    ----------

    ADC       MACRO            ;do ADC on data in DL
              LOCAL @dodec,@postproc
              cmp  Decimal,0
              jne  @dodec
              ;regular
              adc  al,dl
              jmp  @postproc
              ;decimal
    @dodec    adc  al,dl
              daa
    @postproc label near
              ENDM

