Porting the emulator to a new platform -------------------------------------- The machine specific graphics / user interface sections are all kept in separate files. Currently there are four files :- 1. atari_x11.c File supporting X Windows 2. atari_svgalib.c File supporting Linux with SVGALIB 3. atari_curses.c File supporting System V Curses (not BSD Curses) 4. atari_amiga.c File supporting CBM Amiga 1200 Only one of these files should be linked in at any one time. Each file contains the following functions:- Atari_Initialise() Initialises the graphical environment, create screen/window, allocate colours etc. Atari_Exit() Tidies system up just before emulator finishes. Delete all allocated resources (screens, windows etc.) Atari_PreUpdate () Called just prior to the screen refresh. In the SVGALIB and Amiga version it is used to initialise pointers to the start of screen memory. Atari_PostUpdate () Called just after the screen has been finished. In the X11 version it is used to copy the offscreen image onto the main window. Atari_ScanLine () Called for each scanline to be displayed. Atari_Keyboard () You might find the ways this is implemented a bit strange but I wanted to increase the chances of it working on non-ascii keyboards (not that I think its very likely). Atari_Joystick () Returns value of joystick and trigger. Joystick position is in bits 0-3 and trigger is in bit 4. Return 0x1f if the joystick is not available. Atari_Paddle () Returns value of paddle and trigger. Paddle position is in bits 0-7 and trigger is in bit 8. Return 0x100 if the paddle is not available. Implementation Overview (Very Brief) ------------------------------------ +-----------------+----------------+ | monitor program | O.S. Support | | +--------+------+ | | | 6502 CPU | | | |Emulation Layer| | +--------+---------------+---------+ | RAM/ROM and Memory Mapped H/W | +----------------------------------+ 1. Monitor Program (monitor.c) Development debugging tool. Allows emulated system to be interogated. 2. O.S. Support (atari.c, atari_sio.c, atari_h_device.c) Support routines that allow the Emulated Operating system to talk to the host machines. These are implemented using a pseudo 6502 escape instruction. Support for other operating systems could be built into this section 3. 6502 CPU Emulator Layer (cpu.c) Access memory by calling routines within hardware layer. Not the fastest way of doing this, but it produces a compact flexible system that should be able to emulate other platforms. 4. Hardware Layer (atari_custom.c) RAM / ROM / DEVICES and memory mapped custom chips. Again support for other hardware systems could be inserted into this section. Link Hardware and Operating System to CPU ========================================= This emulator has been designed to cope with multiple 6502 based computers. In order to add support for another computer you need to implement a hardware emulation layer and link this into the 6502 CPU module. e.g. The Atari 800 is linked in by defining five routines :- UBYTE Atari800_GetByte (UWORD addr); Returns the byte at the memory address. UWORD Atari800_GetWord (UWORD addr); Returns the word at the memory address. void Atari800_PutByte (UWORD addr, UBYTE byte); Stores the byte at the memory address. void Atari800_Hardware (void); Called after every instruction to emulate the machines hardware. void Atari800_Escape (UBYTE code); Called when the CPU module encounters the special emulators additional ESC instruction. The opcode is 0xff and it takes a single immediate operand that contains a user defined value. This instruction is used to patch the operating system. Each of these sections are linked to the CPU module at runtime by assigning the addresses of the functions to the respective CPU pointer functions. #include "cpu.h" XGetByte = Atari800_GetByte; XPutByte = Atari800_PutByte; Hardware = Atari800_Hardware; Escape = Atari800_Escape; The CPU module will now call the Atari800 specific function for all memory accesses. e.g. Switching between multiple systems. Set up and Patch required O.S. here switch (machine) { case Atari800 : XGetByte = Atari800_GetByte; XPutByte = Atari800_PutByte; Hardware = Atari800_Hardware; Escape = Atari800_Escape; break; case BBC : XGetByte = BBC_GetByte; XPutByte = BBC_PutByte; Hardware = BBC_Hardware; Escape = BBC_Escape; break; case CBM64 : XGetByte = CBM64_GetByte; XPutByte = CBM64_PutByte; Hardware = CBM64_Hardware; Escape = CBM64_Escape; break; } Memory Types ------------ For the Atari 800 hardware, 64K of memory is allocated together with 64K of memory attribute space. Every byte of emulated memory has an associated attribute. Valid attributes are ROM, RAM and HARDWARE. Writes to addresses with the ROM attribute are blocked by PutByte. Reads and writes to addresses with the HARDWARE attribute are handled as special cases by both PutByte and GetByte. The RAM attribute allows both reads and writes to the specified address. e.g. UBYTE memory[65536]; UBYTE attribute[65536]; UBYTE Atari800_GetByte (UWORD addr) { UBYTE byte; if (attribute[addr] == HARDWARE) { switch (addr) { case HARDWARE_ADDRESS_1 : case HARDWARE_ADDRESS_2 : case HARDWARE_ADDRESS_x : byte = Special Code; } } else { byte = memory[addr]; } return byte; } Screen Generation ----------------- Each scanline is processed separatly and composes of 384 pixels - enough for a wide playfield. There are several independent graphic elements may which overlap :- 1. The normal playfield (3 bits) - each pixel may be one of :- a. Playfield 0 b. Playfield 1 c. Playfield 2 d. Playfield 3 e. Background 2. Player 0 (1 bit) 3. Player 1 (1 bit) 4. Player 2 (1 bit) 5. Player 3 (1 bit) 6. Missile 0 (1 bit) 7. Missile 1 (1 bit) 8. Missile 2 (1 bit) 9. Missile 3 (1 bit) Provision must be made for collision detection. To do this each of the 384 pixels is represented using a 16 pixel that contains all the above information. The 16 bit pixel is processed according to the contents of PRIOR in order to determine the final pixel colour. f e d c b a 9 8 7 6 5 4 3 2 1 0 --------------------------------------------------------------- M3 M2 M1 M0 P3 P2 P1 P0 --- --- --- --- PF3 PF2 PF1 PF0 Example: Collision detection if (P0) { P0PF |= Pixel & 0x0f P0PL |= (Pixel >> 8) & 0x0f } if (M0) { M0PF |= Pixel & 0x0f M0PL |= (Pixel >> 8) &0x0f } The GTIA modes can be generated directly from the 384 pixel buffer. Unanswered Questions -------------------- 1. In collision detection, does a player collide with itself ? i.e. Does P0PL bit 0 always contain a 0 or a 1. Does P1PL bit 1 always contain a 0 or a 1. Does P2PL bit 2 always contain a 0 or a 1. Does P3PL bit 3 always contain a 0 or a 1.