Adding a serial port to my 6502 computer

In my last blog post, I wrote about the 8-bit computer which I’ve been building, using an existing design by Ben Eater. The I/O capabilities of the original design are rather limited, so one of the first enhancements I’m making is to add a serial port.

Hardware

The main chip I am adding is the WDC 65C51N ACIA, which is a modern version of the MOS 6551. Versions of this chip have been on the market for around 40 years, and lots of classic computer designs use some version of it for serial output. I bought this one new, and the date code indicates that it was manufactured 11 years ago, so it’s a fair guess that they are not selling as fast as they used to.

I also needed a 1.8432 MHz oscillator, which I used to clock both the computer and the UART.

Lastly, I used a USB/UART module to interface with a modern computer. This module hosts a FT232RL chip, and the pins on this one are DTR, RX, TX, VCC, CTS and GND.

Address decoding

The 65C02 CPU in my computer uses memory-mapped I/O, so I needed to fit this new I/O chip into the memory map before I could start using it.

The original design uses a single-chip solution for address decoding, where the select lines for the ROM, RAM, and a 65C22 VIA chip are connected via 3 NAND gates.

This leaves an unused space between address 4000 and address 5FFF.

Address Maps to
8000-FFFF ROM
6000-7FFF I/O – 65C22 VIA
4000-5FFF Not decoded
0000-3FFF RAM

The 65C51 ACIA has two chip select inputs: one active-high and one active-low, much the same as the 65C22 VIA. All I needed to do was invert A13, and there was an unused NAND gate in the existing design which I could use for it.

This places the 65C51 in the unused address space. It’s not exactly efficient to assign 8KB of address space to a device which needs 4 bytes, but it does work.

Address Maps to
8000-FFFF ROM
6000-7FFF I/O – 65C22 VIA.
4000-5FFF UART – 65C51 ACIA
0000-3FFF RAM

While editing this blog post, I also re-read Garth Wilson’s address decoding guide for the 6502, which shows some alternative schemes for achieving this.

Wiring

I’m using the following wiring between the 65C51 and the USB/UART module.

Note: The the two clock inputs are connected to the 1.8432 MHz oscillator, which is not shown correctly here.

Software

This particular revision of the 6551 has some hardware bugs, though they are well-documented and can be worked around in software. Most of the excellent example code online is aimed at older (less buggy) revisions.

After four attempts, I was able to write an assembly-language program which could produce some output. The information on this 6502.org thread, and this 6502.org comment were the most accurate for my hardware setup.

I’m using this code to set up the ACIA for 8-N-1 communication at 19,200 bytes per second, with no interrupts.

ACIA_RX = $4000
ACIA_TX = $4000
ACIA_STATUS = $4001
ACIA_COMMAND = $4002
ACIA_CONTROL = $4003

reset:
    ; ... other stuff
    ; ACIA setup
    lda #$00
    sta ACIA_STATUS
    lda #$0b
    sta ACIA_COMMAND
    lda #$1f
    sta ACIA_CONTROL
    ; ... other stuff

To send, I needed to add a delay between bytes, since the hardware bug prevents the transmit bit in the status register from operating correctly. I found some code with nested loops, but it only worked after increasing the delay far beyond what should have been necessary. An alternative work-around is to generate a timed interrupt from the 65C22 VIA, which I’m hoping to try later.

; print A register to ACIA
; Based on http://forum.6502.org/viewtopic.php?f=4&t=2543&start=30#p29795
print_char_acia:
  pha
  lda ACIA_STATUS
  pla
  sta ACIA_TX
  jsr delay_6551
  rts

delay_6551:
    phy
    phx
delay_loop:
  ldy #6 ; inflated from numbers in original code.
minidly:
  ldx #$68
delay_1:
  dex
  bne delay_1
  dey
  bne minidly
  plx
  ply
delay_done:
  rts

I am using this routine to receive characters. It will block until the next character is received, and I will most likely need to replace this with something interrupt-driven once I start to add more complex programs.

; hang until we have a character, return it via A register.
recv_char_acia:
  lda ACIA_STATUS
  and #$08
  beq recv_char_acia
  lda ACIA_RX
  rts

I ended up with a program which prints “Hello” to both the LCD and serial port when the computer resets, then accepts text input. Any characters received over serial are then printed back to the terminal, and also to the LCD.

Mistakes were made

Since this is a learning project, I’m keeping a log of mistakes that I’m making along the way. Today’s lesson is to check everything, because it’s very difficult to debug multiple problems at once. When I ran my first test program, there were four faults.

  • I interfaced the USB/UART module to the 65C51 by matching up pin names, which does not work – RX on one side of the serial connection should go to TX on the other.
  • I incorrectly calculated the memory map, so the test program was writing to address 8000 while the 65C51 was mapped to address 4000.
  • I based my code on examples which do not work on this chip revision, because of the hardware bug noted above.
  • I also miscalculated the baud rate, so even if I didn’t have the other faults, my settings for minicom would not have worked.

Wrap-up

It’s very straightforward to modify Ben Eater’s 6502 computer design by adding a 65C51 ACIA. This upgrade will allow me to write (or port) software which uses text I/O. I’m planning a few more changes to this design before I port anything too serious though.

This is also the first time I’ve included (pieces of) schematics in a blog post. I’m drawing these with KiCad, and using a slightly modified version of Nicholas Parks Young’s 6502 KiCad library, which has saved me a bit of time.

Building a 6502 computer

I’ve been using 6502 assembly for some hobby projects recently, but only testing in an emulator. It’s about time to target some real hardware, so for the past few weeks I’ve been following Ben Eater’s 6502 computer tutorial.

I am a complete beginner when it comes to electronics, so I spent a bit of time making useless circuits to toggle LED’s, then jumped right in to building the simplest possible circuit exercise a 65c02 CPU, known as a NOP generator. I used a 555 timer and an inverter to get a 3Hz clock, which is quite a bit slower than my desktop PC.

I then extended the circuit to run NOP instructions from a ROM. I generated a ROM filled with the 6502 NOP instruction by printing a character to a file, then concatenating the file to itself times to fill the ROM.

printf '\xEA' > rom-original.bin
dd if=rom-original.bin of=rom-original.bin bs=1 seek=1 count=32768

I am using an open-source tool called minipro with a TL866II+ programmer to burn the ROM.

$ minipro -p AT28C256 -w rom-original.bin
Found TL866II+ 04.2.86 (0x256)
Warning: Firmware is out of date.
  Expected  04.2.123 (0x27b)
  Found     04.2.86 (0x256)
Erasing... 0.02Sec OK
Protect off...OK
Writing Code...  7.44Sec  OK
Reading Code...  0.77Sec  OK
Verification OK
Protect on...OK

Next, I tried to extend the circuit to blink some LED’s based on a programmed sequence. When I ran the program, the 65c22 I/O chip warmed up, and my row of LED lights did not blink. It turns out that I had mixed up the meaning of VCC and VSS, and applied a reverse voltage to the chip. I found a post from somebody else who had made the same mistake, and corrected it before the chip was damaged.

This program worked well initially, but the computer would sometimes crash when running at this slow speed, so I started running it with a 1MHz or 1.8MHz oscillator instead. I now know that this is because I had plugged the 65c02 and 65c22 clock inputs into the 555 timer output, when I should have been running it through the inverter first. The rising-edge and falling-edge times of the 555 timer are apparently not fast enough to clock these chips reliably.

The next step was to add an LCD and some RAM. My first attempt did not work, and it took me a few hours of troubleshooting to rule out any hardware problems. In the end it was a simple programming error, where I had used a jmp instruction instead of jsr in my test program.

So it’s not much, but it works! Based on some of the problems that I had while building this, it was definitely a good idea to start with a known-good design on a breadboard.

I’ve got a few ideas (and components) for extending this computer already, and I’m hoping to learn a thing or two about hardware and software along the way.

IntelliJ plugin for 6502 assembly language

I’ve been writing some 6502 assembly code in the past year, and have found the developer experience for this language to be lacking some modern conveniences.

In my last blog post, I described the development tools that I used to implement a simple NES game on Linux. I used text editor to write the code, and it couldn’t do much more than syntax highlighting.

I write most of my other code in IntelliJ, so I decided to take a look at what would be required to write a plugin for 6502 assembly support. I managed to together something that mostly works, which I published to the JetBrains Marketplace last weekend.

You can find it by searching “6502 Assembly” in most JetBrains IDE’s.

Features

Firstly, it supports syntax highlighting. I limited the scope of the plugin to ca65 assembler syntax only, since it’s the assembler that I know best.

You can navigate to any label with Ctrl+Click. This will not yet work for other types of symbols, such as constants, macros, and imports.

If the plugin sees a jump or branch statement, and can figure out where the jump goes, then it will show a gutter icon which navigates to the target.

You can find the usages of a label.

In ca65, you can use nested scopes. The plugin shows code folding buttons to collapse these scopes.

You can navigate to a symbol by name using the “Go To Symbol” action.

The plugin allows for commenting and un-commenting blocks of code, though there is no formatter, so indentation sometimes still has to be fixed manually.

It also allows you to rename a label and its usages, which is a great time-saver.

Limitations

One limitation is that the plugin does not fully parse expressions, so it will not detect errors from mis-matched brackets.

Secondly, the plugin does not understand the project structure. This means that if you re-use the same label name in different places in your project, the “Rename” and “Find Usages” function will match all of them at once, because it is not smart enough to follow imports and apply scope rules.

Future improvements

A lot of things can be done with an IDE plugin, but I’m planning to use this initial implementation for a while before attempting to add any big-ticket features.

A reasonable goal might be to have “Hello World” project skeletons for common 6502 systems, and to support launching with some common emulators. I would like to be able to set breakpoints and debug 6502 programs in an IDE, but external debugging interfaces are not commonly available for retro emulators, so this is probably not a reasonable goal.

The code is on GitHub under an MIT license. If you are using this plugin and would like to help improve it, then pull requests are welcome.

Building my first NES game: A retrospective

Last year, I spent a fair amount of time learning how to make games for the Nintendo Entertainment System. The homebrew scene for this system is very much alive, and I was quite proud that I was able to make a simple, working video game which runs on a NES emulator.

So, I present to you: 8-bit Table Tennis.

This blog post just a few notes about the tools and resources that I used for NES development on Linux, plus a few things that I learned along the way.

Development tools

The CPU in the NES is a derivative of the once-ubiquitous MOS 6502, and games for it are mostly written in 6502 assembly. I already do a fair amount of programming, but quickly found that my usual editors, compilers and debuggers were useless for this platform.

The main resources I used were:

To build my code, I used the cc65 toolchain, which includes a cross-assembler. I did most of my testing in the Nestopia emulator, and most of my editing in Gedit. All three of these are available in the official Debian repositories.

Gedit setup

Out of the box, Gedit can’t syntax highlight 6502 assmebly. I found a language spec file for it on the the 6502.org forums, and edited it slightly before using it.

At the time of writing, new language specs can be installed on Debian like this:

sudo cp asm6502.lang /usr/share/gtksourceview-4/language-specs/asm6502.lang && sudo chmod 0644 /usr/share/gtksourceview-4/language-specs/asm6502.lang

It possible to add a console and git support to with gedit, but this is still a long way from a full IDE.

Geany

Geany is a programming text editor, and is also available in the official Debian repositories.

I wasn’t able to get it to recognise 6502 assembly, but it does have good x86 assembly (NASM) support, which is similar enough for it to allow navigation through the source code using labels.

Geany can also set up projects with build scripts (such as a Makefile), which could allow for quicker testing.

FCEUX

FCEUX is a NES emulator which has an in-built debugger, and it runs well under WINE. I was also able to build and run it natively on Linux, but the debugger is only present in the Windows build.

I also briefly experimented with using the FCEUX Lua interface to pause/resume the emulator over a TCP socket for debugging, since that interface is available in the Linux build. I could only pause the emulator between frames, so I decided to abandon this.

Graphics

I created the graphics in the GNU Image Manipulation Program, and manually broke the title page down into tiles.

I also wrote a custom program to convert a 4-colour PNG file into the native NES CHR graphics format. I run this conversion as part of the build process, so that the graphics files can be stored in a modern format.

Things I learned

Programming for the NES is quite simple once the project is up and running, but even simple tasks can be a real hassle. I spent more time than I expected on mundane tasks such as collision detection, and certainly did a poor job of implementing game physics. The 6502 CPU has no built-in way to multiply, divide, or perform floating point operations. The version used for the NES additionally lacks any way to perform a binary-to-decimal conversion, which would have been very useful for displaying the player scores! In hindsight, I would have been able to improve the physics by pre-computing some lookup tables.

On the other hand, I expected it to be difficult to work within 2KiB of RAM, but due to the simplicity of the game, I only used around 25% of it, and had plenty of CHR ROM space leftover as well.

It took me three weekends to make this project, without having ever written a line of 6502 assembly before. I think that development would be a lot faster if I were able to set breakpoints from my code editor, so I will definitely be looking at other debugging emulators before attempting my next 6502 assembly project.

Conclusion

8-bit Table Tennis is available as an iNES ROM on GitHub.

I have only been running it with an emulator, so if any readers of this blog own a flash cartridge for the NES (such as an Everdrive), then please let me know if it works on the real hardware!