The largest portion of this project consisted of programming. Besides the C programs which run on the Atmel and perform the actual emulation, we wrote test suites to interface to the external SRAM chip and send binary data on the serial port from a PC to the Atmel using the xmodem protocol 1, set up a cross-assembler to create 6502 object files, and wrote some 6502 assembly test code. This section will outline the details of each of these elements.
1 We also wrote a Java program which can send a file over a comm port, but it was never tested or used once we started meddling with the xmodem protocol. That file is available here for download.
Download the 6502 Emulation sources
To emulate the 6502, we had to write code that would perform each 6502 instruction on the Atmel. Originally, when we had planned to include video output, we were going to need fast translation of the instructions. In order to achieve this, we created a mapping to an array of structures containing function pointers to the Atmel code for each 6502 instruction. The values were hashed based on the opcode of the 6502 instructions. The structures also contained the byte length of the instructions, which vary by instruction. To simplify the code for each instruction, we used a number of macros. Most of these are for accessing the external SRAM, but there are also ones defined for ADC and SBC instructions that handle decimal mode.
The 6502 has 5 registers, and we put them in registers 4-8 on the Atmel. We also made extensive use of a union that allows us to create 16-bit value and access the upper and lower 8 bits independently. The program memory and stack are both character arrays in the Atmel's SRAM. Because of the limited amount of SRAM available on-chip, programs can be no longer than 512 bytes. The 6502's memory is on the external SRAM chip, which is described in further detail below.
During initialization, we setup the ports as follows: port A -> external SRAM bits A0-A7, port B -> external SRAM bits A8-A12,CE,OE,WE, port C -> external SRAM bits D0-D7, port D -> UART (bits 0,1). More details on the UART communication can be found below in the section on the Xmodem protocol. After the 6502 registers, stack, emulation flags, Xmodem protocol, and function mapping are initialized, the program drops into an infinite loop which allows programs to be loaded and executed. It listens on port D for single-character control commands coming via the USART from Hyperterm.
Using Hyperterm on a PC, you can enter commands to control the emulator. To load a .o65 object file (6502 assembled code), type 'l', and then from the menu choose Transfer->Send File... In the dialog that opens, select the file, choose the Xmodem protocol, and click Send. In a few seconds, the transfer should begin. Once it completes, the program should be loaded, and you can now type 'e' to begin running it. The program execution loop is actually a task that is run every 1ms, on the Atmel's timer0 overflow interrupt. Various debugging output will appear in Hyperterm to show the PC, what instructions is being executed and the values of the registers. Currently we send the following output to Hyperterm: program counter, instruction opcode,and the values of all five 6502 registers. Program execution will continue until a RTS (return from subroutine) instruction is encountered, at which point the execution cycle terminates and the emulator returns to its listen mode. You can now run the program again or load a new program.
Download the 84256A SRAM Chip Testing sources
The 84256A is a 32K external SRAM chip that we used as the data memory of the 6502 CPU. The chip has an access time of 70ns, and is controlled by three bits: chip enable, write enable, and output enable. These three control bits are inverted, meaning that they are off when they are high. By sending out the address and the data on the appropriate ports and setting write enable to low, 8-bit data can be stored to memory by the Atmel. Similarly, setting the address bits to the appropriate value and setting the output enable bit low, 8 bits of data can be read by the Atmel. We found that we could leave the chip enable bit high, and reading and writing would still have the same effect. The pin assignments for this interface are shown the Hardware section of this site.
In order to test the memory chip, we wrote data bytes to various memory locations and then attempted to read this data back into the Atmel. The binary representation of the data read back was displayed on the LEDs of the STK-500 board.
Download the Xmodem Protocol Testing source
Designing the Xmodem receiver on the Atmel was another obstacle that we needed to overcome. When the Atmel detects that data has been received on the USART, it checks to see if it is the character 'l' for load. Once this character has been received, the Xmodem protocol begins, and the receiver (the Atmel chip) sends a NAK over the USART to initiate file transfer. Packets are then sent over the channel. Error checking is done by using the checksum, and the data is stored in the program array, once it is confirmed that there are no errors.
The configuration of the Xmodem protocol packet is as follows:
SOH (start of header) byte [1 byte]
packet number [1 byte]
1's complement of packet number [1 byte]
packet data [128 bytes]
checksum [1 byte]
In the instance that a packet contains less than 128 bytes, the Xmodem protocol pads the remaining bytes in the packet with ^Z characters (which are SUB, or substitute, characters in ASCII). Once all of the data packets have been received, the transmitter sends two consecutive EOT (end of transmission) bytes, and after the receiver responds to these packets with a NAK followed by an ACK, both sides close the connection.
Download the 6502 Assembly test programs
We used the 6502
Cross-Assembler xa65 to assemble 6502 code from a Unix environment. The
program sources we used were test1.a65, test2.a65, bubblesort.a65, and fastmult10.a65. The cross-assembler can be compiled with the
make command using
gcc. The bubblesort and test2 code did not work as well as we had hoped, but test1 and fastmult10 executed correctly.