While apparently having too much free time I decided to improve my assembly programming skills. And what would be a better way to prove myself that I can program in a certain language? Write a compiler, of course. Or an assembler in this case.

But studying processor instructions one by one is boring, so I had to trick myself into it. I have decided to write my own emulator to fully grasp the idea - become fully aware of what a processor actually does while running.

Atari 65XE

To make this task feasible I chose a fairly simple, but powerful architecture - the famous MOS 6502. It was used for example in Commodore 64, original Nintendo Entertainment System, and Atari 65XE which I happen to own. Of course, emulating the whole platform would be a huge task, and I’m too lazy for that. I created a very simplified architecture - processor, 64kB of memory. That’s it.

To ease the code development for my new “platform”, I have created the halfassembler. It’s a half-assed parser/converter to transform mnemonics and ascii hex bytes into a binary representing an instruction set. Why the name? Well, a full blown assembler could do so much more than this one, like calculating addresses and detecting addressing modes. This is not the case here. Programmer has to manually specify which variant of the instruction to use (which defines how operands are loaded from the memory or registers). It was created to at least avoid having to write code in raw bytecode, and ended up as not much more than that.

The emulator development started as one-by-one process of defining functions per each instruction, per each addressing modes. That resulted in heavily redundant code very quickly, which I’ve shortened a little bit using macros. A lot of deduplication is yet to be done. However, this was not the goal of this project. During the process I learned (almost) every instruction and addressing mode of 6502.

I have written an example program for the platform, here’s the listing:

# store starting monitor address at zero page address 0x00F0 (word)
LDA_imm     00
STA_abs     F0 00
LDA_imm     10
STA_abs     F1 00

# store counter at 0x00F2
LDA_imm     00
STA_abs     F2 00

# loop1
LDY_imm     00
LDA_imm     FF

# loop2
# get stored address from zp, add Y and under that address (monitor) put 0xFF
STA_ind_y   F0
INY
INY
INY
STA_ind_y   F0
CPY_imm     FF
BNE         F7
## loop2

# increment starting monitor address by 0x0100
LDA_abs     F1 00
CLC
ADC_imm     01
STA_abs     F1 00

# increase and check counter
INC_zp      F2
LDA_zp      F2
CMP_imm     02
BNE         E0
## loop1

INVALID

Oh, right. An INVALID instruction is exactly what you think. The emulator halts whenever encounters invalid instruction. I use this behavior to halt program execution.

Okay, so we have the code. Now it can be assembled using following commands:

# compile halfassembler
$ make halfass
# use halfassembler
$ ./halfass program2.has program2.bin

The resulting file is a simple binary:

$ xxd -p program2.bin
a9008df000a9108df100a9008df200a000a9ff91f0c8c8c891f0c0ffd0f7
adf1001869018df100e6f2a5f2c902d0e0ff

By providing a filename, we can run this binary using our 6502 emulator.

# compile emulator
$ make 6502
# use emulator with compiled program binary
$ ./6502 program2.bin

Now we can see the debug output:

$ ./6502 program2.bin 
initializing CPU registers
initializing CPU flags
initializing PC value from RAM
initializing RAM


███ 6502 emulator [tomaszu 2023] ███

loading program into RAM
opening input file
closing input file
INITIAL STATE   [PC:0000 SP:FF A:00 X:00 Y:00 | CF:0 ZF:0 ID:0 DM:0 BC:0 OVF:0 NF:0] 
[A9] LDA_imm    [PC:0002 SP:FF A:00 X:00 Y:00 | CF:0 ZF:1 ID:0 DM:0 BC:0 OVF:0 NF:0] 
[8D] STA_abs    [PC:0005 SP:FF A:00 X:00 Y:00 | CF:0 ZF:1 ID:0 DM:0 BC:0 OVF:0 NF:0] 
[A9] LDA_imm    [PC:0007 SP:FF A:10 X:00 Y:00 | CF:0 ZF:0 ID:0 DM:0 BC:0 OVF:0 NF:0] 

There is also an option to turn off debug and use an emulated “screen” to visualize memory content. By changing two #define clauses in settings.h file and rebuilding the emulator we can instead appreciate the beautiful 16x32 ascii art memory visualization.

$ ./6502 program2.bin 


░█▒ 6502 emulator [tomaszu 2023] ▒█░

opening input file
closing input file
████████████████████████████████
████████████████████████████████
████████████████████████████████
████████████████████████████████
████████████████████████████████
████████████████████████████████
████████████████████████████████
████████████████████████████████
████████████████████████████████
████████████████████████████████
████████████████████████████████
████████████████████████████████
████████████████████████████████
████████████████████████████████
████████████████████████████████
████████████████████████████████
(...)
░██░██░██░██░██░██░██░██████████
████████████████████████████████
████████████████████████████████
████████████████████████████████
████████████████████████████████
████████████████████████████████
████████████████████████████████
████████████████████████████████
████████████████████████████████
████████████████████████████████
████████████████████████████████
████████████████████████████████
████████████████████████████████
████████████████████████████████
████████████████████████████████
████████████████████████████████
(...)
░██░██░██░██░██░██░██░██░██░██░█
█░██░██░██░██░██░██░██░██░██░██░
██░██░██░██░██░██░██░██░██░██░██
░██░██░██░██░██░██░██░██░██░██░█
█░██░██░██░██░██░██░██░██░██░██░
██░██░██░██░██░██░██░██░██░██░██
░██░██░██░██░██░██░██░██░██░██░█
█░██░██░████████████████████████
████████████████████████████████
████████████████████████████████
████████████████████████████████
████████████████████████████████
████████████████████████████████
████████████████████████████████
████████████████████████████████
████████████████████████████████

For now that is all I wanted from this project. It was incredibly useful in learning what 6502 can do, and also becoming more familiar with assembly programming. There is a possibility that in the future I will go back to it to improve code quality and develop more interesting programs. Keep in mind, the implementation is far from optimal. There are number of 6502 implementations online, even as far as sub-200 lines of code ones. But this thing also gets the job done. Oh, and there are also unit tests written for large part of the emulator :).

If you’re interested in internals, or want to run it, you can get the code from github.

Thank you for stopping by!