An Atari Emulator by Michael Munoz Introduction The Atari emulator program (atari.exe) allows programs written for the Atari 400, and 800 (XL and XE not implemented, yet) to run on an IBM PC compatible. It is fairly easy to write an emulator that partially emulates the Atari, but very difficult to write an emulator that implements all of the Atari functions in real time. The IBM PC running at 33Mhz is still not fast enough to emulate an Atari running at 1.79Mhz. The reason is that the Atari performs its graphics and sound tasks in parallel with the 6502 microprocessor; the IBM PC must perform these same tasks serially (one at a time). The Atari computer really was ahead of its time and the current day IBM PC is still a kludge at best (no bias here). All of the Atari features can be implemented in software but they would take too much INTEL 80x86 cpu time and the emulator would run far too slow. This Atari emulator program is not complete, however I am releasing the source code into the public domain so that others interested in emulating the Atari can gain some useful insight in one approach to emulating the Atari computer. Understanding this emulator may also help in writing software for the Atari. The program has some hitherto undiscovered bugs and is certainly not polished; feel free to improve upon this one, or write a much better one. All of the source code used to generate this program is included. The source code is not well documented and for this I apologize; I simply ran out of time. (For reference, this program took me about six weeks to write.) As I write this, I am in the process of moving; if this document looks like it was written in a hurry - that's because it was. I originally wrote the Atari emulator in Borland C++; I thought I could hand code some critical routines in assembler and achieve a reasonable emulator speed. As it turns out (not a big surprise) the 6502 opcode emulation is the time critical part of the software. So I decided to write the entire program in assembler. (I have included the C++ version in a file called emu.zip. The C++ version has the text mode working only and the keyboard software is flaky, but it is worth a look since this version was a test bed for the assembler version.) My experience is in hardware design, but I know that you can greatly speed up development time (hardware or software) if you can use someone else's working design. So I started scanning the public domain for a similar program, i.e. an emulator for the Commodore or the Apple. I looked at a number of emulators for other computers; many of the programs were good, but most were unmaintainable (i.e. I could not modify them for my purpose without a lot of work). Then I stumbled upon Randy W. Spurlock's excellent Apple IIc emulator. I never actually ran the Apple IIc emulator so I don't know if the product itself is any good, but the source code is wonderful. (You may see references to the Apple emulator in the source code.) Randy emulated the 65c02 opcodes, which is not a problem since the 65c02 is downward compatible with the 6502. The 65c02 emulator is in a file named "emulate.asm". I made some changes to this file to accommodate the special Atari requirements (I may have actually slowed his code down a bit). I also kept the keyboard routine, "keyboard.asm", and modified the keyboard tables (and functions) to match the Atari keyboard. I kept a few of the functions from some of the other Apple emulator software like the interrupt vector management routines in "int.asm", the clear memory routine from "memory.asm", the cpu reset routine in "reset.asm" and a few of the data tables in "data.asm", The rest of the code is unique to the Atari emulator. The Atari emulator currently implements all ANTIC/CITA graphics modes1 in "antic.asm", and the player graphics in "gita.asm". (I did not implement the missiles- it seemed pointless since I did not implement collision detection.) Also, I steal the SIOV vector and call the my_siov() routine in "sio.asm" to handle the disk drive. The joystick is supported using the number pad in the keyboard emulator, "keyboard.asm". It would not be hard to make a cable connected to the IBM parallel port and use real Atari joysticks, but I had intended on using the IBM parallel port to emulate the Atari SIO. The prototype of the IBM parallel port emulation of the Atari SIO is in a file called "siocopy.zip". Using the Atari Emulator You will need a copy of the Atari 400 or 800 OS and BASIC ROMs in a file called "atari.rom". This file should use the standard six byte Atari header telling "atari.exe" where to load the file. The header for the OS ROM file without BASIC is FF FF 00 E0 FF FF, i.e. starts at $E000 and ends at $FFFF; the total file length is 8198 bytes. The header with BASIC is FF FF 00 A0 FF FF, i.e. copy everything from $A000 to $FFFF into a file; the total file length is 24582 bytes. I have included an Atari program, "romcopy.com" that will allow you to copy the Atari Basic and OS ROMs into a file. You will then need to get this file onto the IBM. To do this you will need "mule.exe", the "util.exe" or "sio2pc.exe". The Atari emulator is fairly straight-forward to start, simply run the file "atari.exe". There are no command line options, just type "Atari" then press the "enter" key. The first thing you will see is the familiar blue screen and either "Ready" or "Atari Memo Pad". To load a program from an Atari disk image file, hit the F12 key; a menu will pop up as follows: The current emulator status is: D1: D2: Basic is ON 1- Change the filename for D1: 2- Change the filename for D2: 3- Load Cartridge (not working) 4- Toggle Basic 5- Exit Enter the number of the desired command Type 1 and the "enter" key, and you will be asked to give a filename. Type the name of the Atari disk image file here e.g. "dos25.xfd". The hit 5 to return to the Atari emulator. You will then need to reboot the Atari by hitting the control/F1 key combination. You can now access the disk in the standard manner. You can access the F12 menu at any time; this allows the use of "Flippies". The emulator keys are: F1 - reset Control/F1 - hard reset (reboot) F2 - Option F3 - Select F4 - Start Control/F5 - Exit Atari emulator F12 - Access Menu Num Lock - Light on means the number key pad is in joystick mode. Pad 8 - joystick up Pad 2 - joystick down pad 4 - Joystick left pad 6 - joystick right pad 7 - joystick left/up pad 9 - joystick right/up pad 1 - joystick left/down pad 3 - joystick right/down pad 5 - trigger down/ joystick up (jump) Tab - trigger Alt - trigger hold down (use tab to clear) Control/Page up - screen move up Alt/Page up - increase the interval between video update Control/Page down - screen move down Alt/Page down - decrease the interval between video update Emulator Design Philosophy Timing and Control The timing on the Atari computer is critical, but there is no critical timing for the Atari emulator. The Atari computer has a 6502 microprocessor and several sound and graphics subprocessors that operate in parallel (for the most part). The Atari subprocessor called ANTIC can display graphics on the TV screen while the 6502 is running your program. The ANTIC chip only stops the 6502 when it needs to access the Atari RAM (memory). Whereas, the Atari emulator is written in INTEL 80x86 assembler which performs each 80x86 instruction serially (one instruction at a time). Therefore to emulate the Atari on an IBM PC we have to separate all of the operations performed by the Atari subprocessors into a set of serial events. These events must occur in a certain order to properly emulate the Atari. Determining the order of these events is the single most difficult part of the Atari emulator design. Luckily I have done this work for you. This section describes the timing and interrelationship between the 6502 and Atari subprocessors. The Atari computer graphics are displayed at the same time as the Atari user program is running. If the programmer wants to take control of the graphics he must synchronize the 6502 with the ANTIC graphics subprocessor. There are several methods/registers that we can use to synchronize the 6502 with ANTIC: Display list interrupts (DLIs), cycle counting, wait for horizontal sync (WSYNC ) and the vertical scan register. The Atari Technical Manual describes how to program using these methods, but we need to do the reverse; we must assume the Atari programmer is using these methods and handle each method appropriately. Antic interrupts or stops the microprocessor (6502) at known intervals. This means that we can determine the number of 6502 clock cycles that can occur before the microprocessor is interrupted or stopped. The ANTIC chip is tightly synchronized with the TV timing in that it tries to read the Atari memory and update the graphics at the end of every horizontal TV scan line. A TV scan line (U.S. NTSC) is 63.5 microseconds long and the 6502 clock is 1.79Mhz, therefore we have 63.5uSx1.79Mhz = 114 clock cycles per scan line. Each 6502 instruction (i.e. LDA, STA, etc.) takes anywhere from 2 to 7 clock cycles. There can be 16 to 57 instructions completed in one horizontal scan. Which do we pick 16 or 57 or use some other number like an average? No matter which number we pick, we will eventually find an Atari program that will not display properly. The only way to deal with this problem is to keep track of the number of clock cycles each instruction requires and subtract that number from a counter starting at 114. When the counter is less than or equal to zero, it is time to update the graphics. The ANTIC chip handles the timing and memory access between the 6502 and the ANTIC and GITA chips (Note: I assume the U.S. NTSC television display standard). The TV paints the picture from left to right by scanning the electron beam horizontally across the screen; after it paints one horizontal line, the beam moves down to paint the next horizontal line. Each movement of the electron beam from left to right across the TV screen is called a horizontal scan (or just scan). The TV standard specifies 262 horizontal scan lines (rows of video data) to display and entire TV picture2. There is an ANTIC register called VCOUNT that holds the current horizontal scan line (also called the vertical scan line) number divided by 2, i.e. VCOUNT contains a number from 0 to 130. This number tells us which horizontal TV line has just been painted. The hardware register VCOUNT can be use to synchronize the 6502 with the TV display since we know which TV scan line is currently being painted by reading the VCOUNT register. The emulator needs to keep a counter that simulates the ANTIC horizontal scan line counter. The emulator maintains a variable called Antic_evcount which stands for ANTIC extended vertical scan line counter. This variable holds a number from 0 to 261. This counter is used by the emulator for various software tests like whether the current scan line is visible (not all scan lines are displayed on the TV screen). The hardware register VCOUNT can be used to position graphics on a particular scan line, ie., at a particular vertical position on the TV screen, but how do we position the graphics at a particular horizontal position on the TV screen? When the Atari program writes to the hardware location WSYNC (wait for horizontal sync.) the 6502 is stopped until the start of the horizontal blank interval (the time interval just after the current scan line is painted, but before the next scan line is painted). The horizontal blanking interval is about 28 clock cycles long; which means anywhere from 4 to 14 instructions can execute before the next horizontal scan line appears on the TV. We can make a few changes during the horizontal blanking interval. WSYNC is often used to change the colors from one scan line to the next so that we can display 256 colors at the same time. Now we can also change the colors in the middle of the horizontal line by writing directly to the color registers at a particular instant in time. For example, the following Atari program will display five colorful vertical bars: 10 *=$600 20 START STA WYSNC 30 LDX #5 40 LOOP DEX 50 BNE LOOP 60 LDA #$11 70 STA COLPF2 80 LDA #$22 90 STA COLPF2 100 LDA #$33 110 STA COLPF2 120 LDA #$44 130 STA COLPF2 140 LDA #$55 150 STA COLPF2 160 JMP START If you run this program using the Atari Assembler/Editor cartridge you will notice that the colorful bars have dark green horizontal bands running through them. These horizontal bands are a result of ANTIC reading the character set from memory. During this time the 6502 is disabled so that there is no color update; the last color (in this case dark green) is displayed during this time. I guarantee that this program will not work on any Atari emulator in existence today. The reason I am so confident is that for this program to work, the emulator would have to update the graphics after every pixel (graphics dot) is displayed on the screen. To see why, let's look at this program in more detail. At line 20 we write to address $d40a (WSYNC); this cause ANTIC to stop the 6502 until after the current horizontal line is displayed. The processor restarts during the horizontal blanking interval. Nothing is displayed on the screen at this time (about 28 clock cycles) so we have a delay loop (lines 30 through 50) that waits 27 clock cycles. We write the first color (lines 60 and 70) on the 33rd clock cycle since WSYNC; this clock count correlates to the start of the horizontal scan line painted on the TV. The first color on the screen is $11, dark green. Now these two instructions take 6 clock cycles to complete and ANTIC has some non-zero response time (ANTIC does not change the color on the TV when you write it, rather, there is some delay). The 6502 instruction clock cycles (6) and the ANTIC delay (~11) puts us on the 50 clock cycle since WSYNC. We have changed the color on the TV display before the current horizontal scan line is completely painted. The remaining instructions do the same thing as the two previous instructions except they write different colors to the color register. The only way we could emulate this program is to write one pixel at a time then run a couple of 6502 instructions then write the next pixel to the display. While this would work, it takes a lot of 80x86 code before we can display a pixel (see antic.asm). This would slow the program down to a crawl. Even if you were clever and could write a pixel quickly, you would still have to make a jump or subroutine call the appropriate graphics function after every 6502 instruction (or couple of instructions). I considered the options and decided that there are very few programs (other than demos) that do cycle counting to display graphics. As a compromise I decided to update the graphics after every display list instruction has been completely painted. For example, ANTIC mode 2 (text mode) paints 40 characters on the screen (or 320x8 pixels) so during a video update I completely paint the 40 characters and set the cycle counter (the BP register) to 8*114 (number of scan lines displayed multiplied by the number of cycles per scan line). Now what does all of this have to do with WSYNC? Since I update all of the scan lines for a particular graphics mode at the same time, when a write to WSYNC occurs I have already painted more the one scan line; therefore any changes made to the Atari graphics registers will not take effect until the last scan line of the current graphics mode is painted. If we are displaying a high resolution graphics mode like mode F where we paint only one scan line per display list instruction, then this is not a problem. However, for graphics modes that paint more than one scan line per display list instruction, the display will not be correct when WSYNC is used. For more information on scan lines and graphics modes, see the Atari Technical Reference Manual. The display list is a set of instructions that tell ANTIC how to display the display data. A display list for a standard text screen might look like: *=$7c00 $70 ; Blank 8 scan lines $70 ; Blank 8 scan lines $70 ; Blank 8 scan lines $42 ; ANTIC mode 2 and get graphics data starting at address $7c40 $40 $7C $02 ; ANTIC mode 2, one line of 40 text characters . . ; repeat $02 for each line of text that you want on the screen $02 ; ANTIC mode 2 $41 ; jmp to $7C00 (top of display list) and wait for vertical blank $00 $7C Each line of the display list is an ANTIC instruction or an Atari RAM address. In the previous example, instruction $02 tells ANTIC to display one line (8 scan lines) of 40 text characters. If we had used the instruction $0F, we would display one line (one scan line) of 320 bit graphics. After each ANTIC instruction we can also call a special routine called the display list interrupt (DLI). The DLI allows the Atari programmer to change the color registers or player/missle graphics before the next display list instruction executes. If bit 7 of the display list instruction is set, then a DLI is requested. The DLI occurs after the current display list instruction is executed (all of the scan lines for the particular graphics mode are painted). To emulate the DLI I only need to test the DLI bit of the display list instruction and call the DLI routine in the user's Atari code. The DLI test is performed in "antic.asm" and the 6502 JSR routine is called to jump to the display list interrupt routine (see "hardware.asm"). The variable dli_flag is used to tell Hardware_Update() in "hardware.asm" whether we need to call the DLI routine. Most of the time I spent working on this program was in figuring out how to handle the timing between the 6502 emulator and the graphics. The Apple emulator (from which this code is derived) used an interrupt and updated the screen 60 times per second. If you understood the above discussion, you will also understand why this method would not work very well for the Atari. I did not implement the POKEY chip because the sound is much more difficult to emulate than the graphics. Below is a brief description of how I handle some of the less time critical functions of the Atari computer. The sound chip, POKEY, runs independently with respect to the 6502 and the graphics chips. It seems like it would be straight foreword to use a sound card to emulate POKEY. Pure tones can be emulated this way, but the special effects are more difficult. You would need some type of translation table that converted Atari POKEY commands into the IBM sound card commands. Which means someone would have to listen to the Atari sound and find an equivalent sound on the IBM sound card for every possible sound combination This is a time consuming process that I did not wish to tackle. Pokey also handles the serial input/output communications between the Atari computer and its peripherals. I bypass all of the SIO code and replace the SIOV vector with an IBM routine to read Atari image files from the IBM hard drive. It would also be easy to add code for a printer and modem (if anyone wants to tackle this). When a 6502 'JSR' instruction is encountered, I call a special routine that checks the jump address to see if it is the SIOV address. If it is, I call a subroutine called my_SIOV in "sio.asm". I had planned on replacing all of the Atari OS subroutines with IBM routines, but I ran out of time. I do not emulate the PIA chip at all. The PIA chip handles the joysticks and speaker (on the xl and xe series it also handles the memory page switching). Joystick number 1 is emulated using the number pad on the IBM keyboard (see "keyboard.asm"). Software Structure In this section, I briefly describe the function of the various software modules. But first some miscellaneous information that might help you slog through the code. The 8086 uses 64K pages to access code and data. There are four special registers that are used to access the 8086 memory; these register are called segment registers. The four segment register are CS, DS, ES, SS. Each segment register accesses 64Kbytes of IBM RAM. This emulator accesses the emulated Atari RAM using segment register DS (data segment). The emulator variables are accessed using segment register CS (code segment). The IBM video memory uses segment ES (extra segment). When you look through the code notice that the local variables are accessed using a segment override, e.g. mov al,cs:nmi_flag. Normally the 80x86 uses the DS segment register to access all data, however I have tied up all of the RAM available accessed by DS register to emulate the Atari RAM. Therefore all of my emulator variables are located in the memory accessed by CS. I tell the assembler that the data is accessed using the CS register by prefacing my variable name with cs: (this is a segment override). If you look through the Atari Technical Reference Manual you will notice that the hardware addresses $d000 through $d4ff have different functions depending on whether you are reading the register or writing to the register. To simulate the hardware registers, we need two separate memory locations: one for read the register and one for writing to the register. The Atari only uses the first 32 hardware addresses for each hardware chip (ANTIC, GITA, POKEY and PIA). For example, ANTIC starts at address $d400 and ends at address $d420; this leaves memory locations $d421 through $d4ff available. I map the ANTIC read registers starting at $d400 and the write registers starting at $d420. The Atari hardware registers are read using the true address, e.g., mov di,0d400h mov al,[di] Here I have moved the Atari hardware address for DMACTL ($d400) to the index register DI. Then I read the data at the memory location pointed to by register DI, i.e. the data at memory location 0D400h, into register AL. When I write to a hardware address I use a pointer to memory (Antic_RAM) that is 32 bytes above the hardware location, i.e. I write to address 0D420h to write to the DMACTL register. The variable "Antic_RAM" points to location 0D420h, so that I can write to DMACTL using: mov di,0 mov Antic_RAM[di],al In file Atari.inc I have equates for all of the Atari registers that use the names from the OS, e.g. DMACTL equ 0d400h. I have a second set of names that point to the write locations, e.g. LB_DMACTL equ 0. So that when I want to write to a hardware location I can do: mov di,LB_DMACTL mov Antic_RAM[di],al You will see this type of operation throughout the code. All it means is that I have separated the read and write locations for the Atari hardware. Emulate.asm, the 6502 microprocessor emulator This code was adapted from Randy Spurlock's Apple emulator. The emulator starts with the routine called "emulate". This routine calls the routine Initialize in "atari.asm". Next the emulator loads the program counter with the address in the power up vector $FFFA. Essentially, The 6502 emulator is a series of jumps from one instruction to the next. If you examine the code you will see a table called "Opcode_Table" that contains the addresses for the start of the code for each 6502 instruction. After the instruction has completed, a macro called "fetch" (see macro.inc) is executed which gets the next instruction and uses the instruction opcode to jump indirectly to the start of the next instruction. This leap frogging method is a bit confusing, but it is faster than making subroutine calls. The 80x86 registers CL,CH,DL are used to emulate the 6502 registers A,X, and Y. The register SI is used as the 6502 program counter. The 6502 flags are kept in the DH register. Register BP is used to keep track of the number of 6502 clock cycles. The cycle count determines the number of 6502 instructions that can be executed before a Hardware_Update must be performed. The cycle count is set by the ANTIC graphics mode routines. The Ticks macro subtracts the number of cycles required for the current instruction from the BP register. You really do not need to know how the 6502 emulator code works (unless you want to) to understand the Atari emulator. There are some possible speed improvements for the 6502 emulator. First, note that the emulator updates the flags after every instruction. Only a few 6502 instructions need these flags so it is really a waste of time to update the flags after every instruction. For the N and Z flags you only need to save the last register value that effected these flags. The C and V flags do need to be updated, but a simple RCL and a MOV can be used to save the C flag. Only four instructions effect the V flag, so this can be updated at the end of the instruction that changes it. The I and D flags rarely change so these can be stored in memory. The only time the flags need to be combined into a single byte is for the PHP instruction. I saw this method of keeping track of the flags in at least one 6502 emulator. I do not think that there will be much speed gained overall by doing this sort of thing. Second, the code for each 6502 opcode can be hand optimized to speed things up. Currently, a set of macros is used to implement the 6502 instructions. The macros are too general and not efficient. By hand-coding the routines some instructions can be combined to gain some speed improvement. However, I am not sure that these changes will yield any significant speed improvement. Worst of all, these changes may make the code un-maintainable (difficult for someone else to update). Currently, if you want to change the 6502 emulator code, you alter the macro. If all of the routines are hand coded for speed, each routine has to be updated separately. The more changes you make, the more likely you are to introduce bugs. Atari.asm, the atari.exe startup code Load_System: This routine reads in the file "atari.rom" which holds the Atari operating system. The filename can be changed by modifying the pointer System_ROM located in "data.asm". Initialize: This routine allocates the 64K RAM for the Atari memory and the extra 64K RAM for the XL/XE series. Currently the extra RAM is only used to swap out Atari Basic. The keyboard interrupt is initialized by calling Int_Init (int.asm). The MCGA 320x200x256 graphics mode is set up by calling Video_Init (video.asm). The keyboard LEDs are set to the joystick off mode by calling Set_LED (keyboard.asm). Finally, the 6502 registers are initialized in CPU_Reset (reset.asm). Exit: This routine makes a clean exit from the Atari emulator program, returning control to MSDOS. Write_Memory: Whenever a 6502 instruction wants to write to memory, we need to check the address to make sure that the user is not trying to write to ROM. Also, if the address is a hardware chip, we want to write to the memory address + 32 bytes and take any action required. We do this by jumping to the appropriate write routine using the ROM_Table located near the end of this file. Currently, we can jump to one of three routines: STD_Mem_Write, ROM_Write, and Write_Hardware. This routine can be deleted to speed things up, but you will have to make a few changes to the Atari OS. System_Request: Simulates an Atari Break by setting the break flag in POKMSK and setting the interrupt request flag so that Hardware_Update can set up the interrupt address. System_Access: Sets a flag in response to the user hitting the F12 key so that a call to routine Access is made during Hardware_Update. Access: This routine displays the Atari Emulator status and allows the user to load files and turn on and off Basic. One trick should be noted: the two zeros at the end Stat1 and Stat2 hold the string length for the file names in filenum1 and filenum2. Do not separate or reorder these variables. Rom_Write: Does nothing Memory.asm Clear_Memory: Called by Initialize to clear the Atari memory STD_Mem_Write: Called by Write_Memory to write to the Atari Ram Video.asm COLOR_TABLE: All of the Atari to IBM colors are mapped here. Many of the colors do not match the Atari colors. This is where you change them. Video_Init: Sets up the IBM MCGA 320x200x256 graphics mode by calling Video_MCGA_OPEN and set up the video address in ES. Video_Reset: Restores the video to 80x25 text mode by calling Video_Text_OPEN. Int.asm Int_Init: Replaces the keyboard interrupt routine with INT_KEY. Int_Reset: Restores the old keyboard interrupt vector. Reset.asm CPU_Reset: Resets the 6502 registers Keyboard.asm INT_KEY: This routine checks to see if the key was pushed down (make) or let go (break). The the key stroke is translated to an Atari keycode and a special routine is called if necessary. For example hitting the F1 key calls the Atari_Reset routine. The routines are numerous so just take a look at the source code. Data.asm This file contains various data tables that are used throughout the program. Hardware.asm Hardware_Update: This routine is run every time the BP register goes to zero. The BP register is set by the ANTIC routines and decreased by the 6502 emulator. This routine runs a number of tests to see if we need to call the ANTIC routines. Next Player_Missle routine is called if needed. Then a check for an nmi or interrupt flag is performed. To understand exactly what this routine does, you will have to go through the code line by line. Note that the TV display starts when the variable Antic_evcount is 8. At the end of the display list there is a JVB (jump to top of display list and wait for vertical blank). This instruction sets the Antic_wait flag to 1. If this flag is 1, nothing is displayed until Antic_evcount counts to 8. Also note that Antic_evcount has a maximum count of 262. When Antic_evcount is greater than or equal to 262 it is reset back to zero. The Atari computer can display more than the 200 scan lines that the IBM displays. There are two variables that control what portion of the Atari screen is visible on the IBM display. These variables are sTOP and sBOT. When the program is running, these values can be changed by using Control PageUp or Control PageDown keys. You will also notice that some routines test the current Antic_evcount against the sTOP or sBOT variables. These checks are done to ensure that any display data in excess of 200 scan lines is truncated. Write_Hardware: This routine determines which hardware chip is being addressed and calls the appropriate routine. Jsr_Steal: This routine is call from emulate.asm when the 6502 instruction is a JSR. Currently, I only check to see if the address is the SIOV vector. If it is then I call my own routine, my_siov. You could replace all of the Atari OS subroutines with IBM routines and eliminate the need for the Atari ROMs. while speeding up the Atari emulator. Do_Nothing: This is a dummy routine to account for the holes in the Atari hardware address space. Pokey_Update: Writes to the POKEY write memory location, reset the interrupt status bits and resets the SKSTAT register. Pia_Update: Currently only writes to the PIA write memory location. Nothing is done with the values written there. Antic.asm The routines here emulate each of the ANTIC graphics modes 2 through F. (I also wanted to implement the GITA graphics modes, but I simply forgot how GITA worked.) There are two additional ANTIC instruction: Antic_Blank and Antic_Jmp. In order to understand how the code that emulate ANTIC modes 2 through F works, you need to be very familiar with each of the ANTIC modes. Once you understand the graphics modes, you should be able to go through Antic_mode2() through Antic_modeF() line by line and figure out what is going on. I discuss the code for the ANTIC modes in general terms. If you need to know exactly what is going on you will have to go through each of the individual routines. Antic_Blank: The Antic chip can place from 1 to 8 blank lines on the screen. It seems trivial to emulate a blank line function, however there are some difficulties. First, the TV has 262 scan line of which anywhere from 200 to 240 are visible depending on the TV. The IBM leaves little choice in the screen dimension; the closest "standard" IBM video mode to the Atari is the 320x200 pixel mode with 256 colors. So while the Atari can paint 240 horizontal lines on the screen, the emulator can only paint 200. This is not a real problem because most Atari programs only use 200 lines anyway for the actual display. The problem is that many programs use the blank lines to be sure that the next line painted onto the screen will be visible on the TV. So here is the problem: since the first line we paint in the IBM display will be visible, we have no need for these blank lines. If we paint these blank lines anyway, we lose part of our display. If we do not paint these blank lines then the whole screen may be moved up. I solve the problem in two ways. First, I have two variables sTOP and sBOT that define the top and bottom of the screen with respect to the current vertical scan line count (Antic_evcount). (Note that sBOT-sTOP = 200 scan lines.) I only paint the blank lines that would be visible on the IBM display. This method will only work when the Atari program expects only 200 lines to be visible on the TV. However, when more than 200 scan lines are non-blank the bottom of the display will be cut off. In "keyboard.asm" I have two routines called ScreenUP() and ScreenDOWN() that change sTOP and sBOT to move the image on the screen up and down. These routines are called when Control Page Up or Control Page Down keys are pressed by the user. This allows the user to decide how the screen should be displayed. Antic_JMP: The Antic JMP instruction has two purposes: (1) to load a new display list address and (2) to jmp to the start of the display list address and wait for vertical blank. The only trick here is with item (2). When bit 6 of the JMP instruction is set, this is the end of the display. I set a flag called Antic_wait that is used by Hardware_Update() so that no other display instructions are called until the Antic_evcount is equal to 8. In addition, I clear the rest of the screen (if we are not already at the bottom of the display). Antic Modes: Here is some general information that is common to all of the ANTIC graphics modes. Variables: instr_count: This value is altered in antic.asm and copied to the bp register in hardware.asm. The 6502 emulated instruction subtract the number of clock cycles per second from the bp register; when the bp register is zero, it is time to run Hardware_Update(). Hsync: This value is the clock cycles per horizontal sync. and is initially set to 128. Hsync is used by the ANTIC routines to compute the number of clock cycles for instr_count. Increasing the value of Hsync results in more 6502 emulated instructions executing before another Hardware_Update() occurs. This value can be increased and decreased while the Atari emulator is running by hitting the ALT Page UP or ALT Page DOWN keys, respectively. sTOP: The top of the IBM screen with respect to Antic_evcount. sBOT: The bottom of the IBM screen with respect to Antic_evcount. Video_Pntr_Cur: This is a pointer to the IBM video RAM that points to the current pixel location. This value is increased each time a pixel is written and reset to zero when the ANTIC instruction jump and wait for vertical sync is issued. Video_Pntr_Old: This value points to the position of the IBM video RAM before the last ANTIC instruction was executed (before the previous scan lines were painted). This variable is used by gita.asm so that the players can be drawn on top of the scan lines just painted. Antic_dlist: This variable points to the display list in the emulated Atari RAM. Antic_pc: This is the ANTIC program counter Antic_evcount: This is the current vertical TV line out of 262 lines. Antic_memscan: This variable points to the current address for the data to be displayed on the emulated Atari screen. Antic_Old_memscan: In the ANTIC modes where we are painting more than one scan line, the display data has to be used more than once. We simply keep a copy so that we can display the same data more than once. Antic_wait: This is a flag that tells Hardware_Update() whether it should call the Antic routines. This flag is set to 1 by the ANTIC instruction jmp and wait for vertical interrupt (Antic_jmp()). Hardware_Update set this value to 0 when Antic_evcount = 8. Antic_sc: The value is set by the antic routines and used by player_missle() in gita.asm. This value is the number of scan lines just painted by ANTIC. Antic_dliflag: This flag is set by the ANTIC routines if Hardware_Update needs to perform a display list interrupt. ch1: This variable is used by the ANTIC text modes to hold the display data. Gita.asm Player_Missile: This routine was written after I had decided not to finish this emulator. I only wrote this routine because I was not sure how to explain the method I was going to use to implement the player/missile graphics. Obviously it is used to display the player/missile graphics. I did not implement the missile graphics because it seemed pointless until I figured out how to implement collision detection. Instead of talking about how the players were implemented, I want to explain my ideas on how to implement Player-Playfield collision detection. Collision detection is a group of registers that tell the program whether or not the players and missile graphics are on top of (or underneath) the playfield colors. It is easy to determine if a player or missile is on top of the playfield. The playfield data has been written to the IBM video RAM before the Player_Missile() routine is called. We simply look at the IBM video memory location where we are going to write the non-zero player/missile pixel and see if it is a non-background color. If it is then we know we have a collision. The tricky bit is figuring out which playfield color register is associated with our non-background color. I decided that the best way is to keep a reverse playfield color table. That is if we write color 134 to color register COLPF1 then we would write the address of COLPF1 into our table at location 134, e.g. mov COLOR_TABLE[134],COLPF1 Now when we read the non-background color from the IBM video RAM, we can get the associated Atari color register by using this table. Once we know the color register, we can set the proper bits in the collision registers. Player-Player collision are fairly straightforword so I won't discuss them. Summary I have not adequately discussed the theory of operation of the Atari emulator. Part of the problem is that the reader needs to be very familiar with programming the Atari or I need to spend a lot of time discussing how the Atari works. Since I am not qualified to write a text on programming the Atari, I have to assume that the reader has some level of competence in Atari programming. Having said that, I hope that I have at least provided some insight into how to write an Atari emulator. _______________________________________________________________________________ 1 I had intended to also do the GITA graphics modes, but I forgot what they were and how they worked. I haven't done any real programming for the Atari since 1984 when I wrote an FFT algorithm that took 8 hours to run a 1024 point FFT and subsequently bought an IBM PC. 2 There is actually 525 horizontal scan lines that are interleaved, but the Atari does not do interleaving so we only care about half of these lines, i.e. 525/2 = 262.5.