6502 computer – from breadboard to PCB

I’ve recently been working on building a 6502-based computer on breadboards as a learning project. After making a few revisions, and porting some software to run on it, I was confident that my computer worked well enough to connect up permanently on a circuit board.

This blog post about the process that I went through to convert my working breadboard prototype to a PCB. As somebody who is not trained in electronics, this involved some fresh challenges for me.

Schematic capture

As a first step, I needed to draw up a proper schematic in a CAD tool. I’ve already been learning to use KiCad’s Eeschema for schematic capture, so I went ahead and drew up the whole thing.

I decided to put all of the components on one crowded page, just because I haven’t learned to manage multiple pages yet.

Mostly, this was just replicating what I’d already built, but I also needed to:

  • create a symbol for the DS-1813, which I could not find in the libraries I’m using
  • decide on a pinout for expansion ports
  • add a jumper block, so that the two interrupt lines (NMI and IRQ) can be disconnected from the on-board chips

I avoided changing the scope of the board to include important features (storage, audio), and instead added a header with all of the important buses and signals. My goal is to have a stable base system to work with, and I can always do a second board once I’ve figured out how these should work.

I did not use KiCad’s inbuilt BOM plugins, but instead manually wrote a parts list for future reference. The only components which I needed to order were sockets and pin headers, everything else was being lifted from the prototype.

Footprint assignment

Once the Eeschema electrical rules check was passing, I moved on to selecting footprints for everything. I spent quite a bit of time checking the datasheets for my planned components against the KiCad library to choose footprints that would fit.

I could not find anything which matched the footprint of the mini SPDT switches that I’m using, so I made my own.

I had not yet chosen a ZIF socket for the EEPROM, so I selected a footprint for a standard socket instead. This was a risky move, because I had to guess how much space to leave when laying out the board.

PCB design

I had already selected a board manufacturer, so I set up my track widths and via sizes according to their capabilities.

Next, I imported the netlist and placed the components. I attempted to fit everything in 10cm x 10cm, 2-layer board to save on manufacturing costs, but it was very crowded. I was worried about having enough space for traces, fitting an (unmeasured) ZIF socket, and adding some mounting holes, so I spaced it out to fit on a 10cm x 11.6cm board instead.

I also added footprints for M3-sized mounting holes, and edge cuts with rounded corners.

It took me 4 attempts to successfully route all of the traces. The result breaks every PCB layout best practice which I had read about, but I was happy just to have everything connected by that point.

In hindsight, I could have made this task easier by splitting up the expansion header, and building an accurate footprint for a ZIF socket. I also could have placed the decoupling capacitors closer to the IC power pins, and avoided interrupting the ground plane.

The silkscreen setup was more time-consuming than expected. I wanted to make the board as self-documenting as possible, so that I would not need to open the design files on a computer when writing code and adding hardware peripherals. I labelled every switch, button, light and IC, as well as every expansion header pin (74 of them!). I also added some simple assembly hints such as resistor values, and +/- signs to indicate the polarity of the lone electrolytic capacitor.

The 3D render view in KiCad shows how the finished board would look.

Components did not display on the board at first, but there is a menu option to download the 3D models. After setting this up, and setting the solder mask colour to black, I was able to render the board properly. This looks very similar to the assembled board, with just a few parts missing.

Manufacturing

I only recently discovered that low-volume PCB manufacturing is accessible to hobbyists. Ordering the boards was really easy. I exported gerber and drill files according to their instructions, loaded them into a zip file, and uploaded them to a web portal.

I am glad that an online gerber viewer was available, because it allowed me to spot an error which I had not noticed in KiCad, where I had two pins labelled PA1.

The default settings are reasonable, but I chose black solder mask, a lead-free surface finish, and chose the option to exclude the manufacturer’s order number from the board.

The gerber viewer also has a tab which shows checks against their manufacturing rules.

I placed the order, and eagerly checked each day as the boards progressed through the manufacturing steps. From placing the order to getting the boards on my desk, the whole process took 7 days.

Assembly & test

Once all of the sockets arrived in the mail, I started assembling.

The standard advice I’ve read for through-hole assembly is to solder low-profile components first. Instead, I added enough components to light up the power LED.

I know that the power LED dims if anything is drawing too much current (eg. a short circuit). By getting this working first, I will know I’ve made a mistake (and can switch off the board quickly) if it doesn’t light up later.

I don’t think I’ve ever soldered more than a few pins or wires at a time, and this board had 323 pins to solder.

After a about 2 hours, I had a fully-populated board. It’s smaller and looks better than the breadboards, but it was time to move all the chips across to see if it actually worked.

Much to my surprise, the computer booted up to EhBASIC on the first attempt.

Wrap-up

This has been quite a fun project. It has proven to me that open source tools are perfectly sufficient for this type of hardware development. I’m also very happy to have reached a point where can call this project “done”, since I have a permanent home-built computer.

My plans for learning about low-level computing are not done yet, of course. I’ve kept everything at a low 1.8 MHz, so that I can continue to prototype hardware peripherals on a breadboard. I’ve also left a switch to select which half of ROM to boot from, so that I don’t brick my computer every time I make an error in an assembly language program.

I’ve uploaded the full parts list, design files, and firmware to GitHub at mike42/6502-computer.

Porting BASIC to my 6502 computer

I’ve recently been building my own 6502-based computer. After a lot of work on the hardware side, I decided that it’s time to move past “Hello World” programs, and port a BASIC interpreter.

Background

This computer is architecturally similar to a 1980’s home computer, where BASIC was a widely-used interpreted language. Porting an existing interpreter will be a fast way to get a command-prompt, and it will also add the ability to load arbitrary software, by typing out a program.

I heard about an interpreter called EhBASIC (“Enhanced BASIC”) from on this YouTube video by Chris Bird. It’s source-available (free for non-commercial use only), and seems to be well-regarded by 6502 enthusiasts.

EhBASIC was written by the late Lee Davidson. I based my port on the version v2.22, hosted on Klaus Dormann’s GitHub. I used Hans Otten’s mirror of Lee’s website as a reference, plus the EhBASIC section of the 6502.org forum.

Porting process

EhBASIC was a breeze to port.

I started with the ‘patched’ code from Klaus2m5/6502_EhBASIC_V2.22, which I placed in a local git repository, so that I could track the changes.

I first changed some of the syntax so that the code would assemble with ca65, since that is the assembler I’m using for everything else. There are other ca65 ports around, though they do not include the same patches.

The --feature labels_without_colons setting in ca65 was useful here, since the original source does not include colons after label names. The output was 10.5KiB of machine code, which will fit in the 16KiB of ROM space which I have available in my home-built computer.

I also confirmed that the code did not depend on using undocumented 6502 CPU opcodes, which would have been incompatible with my newer-generation 65C02 CPU.

Next, I dropped in the correct routines to read and write characters. I already had something similar from an earlier blog post. I needed to make some small changes to use the carry bit (SEC / CLC opcodes) to indicate whether a character had been received, and to set up the 6551 ACIA on startup. I programmed the code to an EEPROM, and it worked the first time.

All of my changes against the original code, including the Makefile and ca65 config file are in this commit. The memory addresses line up with the decoding scheme described here.

Wrap-up

BASIC is an interesting language, if only for historical reasons. Classic BASIC feels very clunky by modern standards, but the user experience is not that different to the modern Python REPL. Or at least, it is more similar than you might expect given how far computers have advanced.

I’m running EhBASIC on an 8-bit CPU, where the main alternative is plain 6502 assembly language. BASIC has allowed me to hit the ground running, and gives me access to floating point maths and user-loadable programs, on a computer that does not have an operating system or removable storage.

My home-built computer has a switch for selecting between two ROM’s. My plan is to write my own code in assembly language, but keep this port of EhBASIC in the secondary ROM, so that the computer can always boot to something that works.

Upgrades and improvements to my 6502 computer

For the past few weeks, I’ve been building a 6502-based retro computer, based originally on a tutorial and design by Ben Eater. My first major change was to add a serial port, so that I can write programs which accept text as input.

This blog post is a list of things which I’ve changed since, to try to make a computer which is a bit more suited to my intended use. My aim at the moment is to lock in a simple base system, so that I can use it as permanent platform for 6502 assembly and hardware experiments.

Re-visiting address decoding

The computer has a 32 KiB RAM chip, and a 32 KiB ROM chip. Due to the way that the glue logic was set up, only 16 KiB of the RAM can be used, but the full ROM space is available.

I wanted to flip this around, since I’m planning to run most programs from RAM, with only a loader or interpreter in the ROM. The idea would be to add a switch to select between the upper and lower half of ROM, map the full RAM in, and leave a large space available for I/O and other experiments.

I planned out a coarse, 2-chip address decoder to achieve this.

The resulting memory map is:

Address Maps to
C000-FFFF ROM – 16 KiB
A000-BFFF Not decoded – 8 KiB
8000-9FFF I/O – 8 KiB
0000-7FFF RAM

This is partly based on the info in Garth Wilson’s address decoding primer, and also Daryl Richtor’s SBC-2 computer.

I re-wired every chip select, modified my test program to use the new memory map, and also added a switch to select between the two halves of the ROM.

Implementing power-on reset

In Ben Eater’s 6502 computer, the reset line is connected to +5v through a resistor, and grounded when a momentary switch is pressed. At power-on, I needed to press the reset button before the computer would do anything useful.

I removed the pull-up resistor from the reset line, and added a DS1813-5+ supervisory IC instead. This holds the reset line low for 150ms at power-on, or when reset is pressed. This suggestion was from the 6502.org forums.

There was no KiCad symbol for the DS1813-5 in the default library, so I needed to create one.

Interrupt lines

I found that I had made an error when connecting the interrupt lines.

The 6502 has two interrupt inputs: IRQ and NMI. On my 6502 computer, the interrupt output of the 65C22S is connected to the IRQ input of the CPU, while the interrupt output of the 65C51S is connected to the NMI input of the CPU.

The 65C51N uses an open-drain IRQ output, so this configuration left the CPU’s NMI input floating most of the time, which caused many spurious interrupts. This was easily fixed by connecting the NMI line to +5v through a 3.3k resistor.

I also implemented some tips from Garth Wilson’s 6502 primer, and connected RDY and BE to +5v through 3.3k resistors, where they had previously been connected to +5v directly.

Power

Up until this point, I had been powering my computer with a breadboard power supply. I’m hoping to build something permanent, so I designed a circuit to power it through a DC barrel jack.

This takes a DC voltage, and steps it down to 5v through a voltage regulator. The other components are a power switch, plus a diode to avoid damaging anything if I connect a plug with the wrong polarity.

This computer does not draw much current, so it’s not necessary to add a heat-sink on the voltage regulator.

Getting my wires crossed

My next change was to remove the LCD screen. This had been useful for debugging, but I would prefer to free up breadboard space and I/O connections for other parts of this project.

I did this in four steps, testing after each one. First I moved the LCD off to its own breadboard, then updated the computer’s test program to stop using the LCD, ran the computer without the LCD connected, and finally re-located the UART chip into the newly-vacant space.

After the last step, the computer started producing the wrong text (eg. “H” became “D”). I had mixed up two bits in the data bus, and by some luck the UART setup code was not affected. The orange and red wires on the left of this photo are the ones which were swapped.

I was able narrow down the source of the problem quickly, since I was breaking everything down into small steps, which were easy to individually verify.

Next steps

For all this effort, not much has changed. I still just have an 8-bit computer which says “Hello” when it starts. The next part of this project will involve porting across some non-trivial software.

On the hardware side, I’ve made a lot of progress learning how to use KiCad. As long as everything works, my plan is to migrate this design from breadboard to a PCB in the near future.

A first look at programmable logic

I’ve recently been learning a bit about how computers work on a low-level by building a 6502-based retro computer from scratch. I’ve noticed that plenty of retro computer designs use simple programmable logic devices, including GAL’s, PAL’s and PLA’s.

I decided to take a closer look at these, since they may be a useful part of my toolkit. The particular part which grabbed my attention was the ATF22V10 programmable logic device (PLD).

This ticks a few important boxes for my current projects:

  • Available in DIP packaging, so it is suitable for use on a breadboard.
  • Operates at 5 volts.
  • Currently in production.
  • Has a relatively high pin count (slightly more than the ATF16V8).
  • Can be programmed using a TL866II+ programmer, which I already use for EEPROM programming.

These devices have been around for many decades, and I found mixed information online about the software and hardware requirements for programming these chips. A lot more has been written about the (discontinued) Lattice 22V10, which has similar capabilities, but a different programming process.

Setting up a programming environment

The logic functions for programming the ATF22V10 are specified using a human-readable hardware description language, which needs to be compiled into a device-specific “fuse map” for programming the device.

The only real choice for the ATF22V10 seems to be an obscure language called CUPL, which can be processed by Atmel/Microchip’s proprietary compiler, WinCUPL. This is distributed as a freeware Windows binary.

I do all of my other software development on Linux, so I installed WinCUPL in a WINE environment. On Ubuntu 20.04, WINE is installed with the following command:

sudo apt-get install wine

For later parts of the process, I found that I also needed winetricks and cabextract:

sudo apt-get install cabextract
wget https://raw.githubusercontent.com/Winetricks/winetricks/master/src/winetricks
chmod +x winetricks

The install was simple:

wine awincupl.exe

I was then launching the program with the following command.

$ wine  ~/.wine/drive_c/Wincupl/WinCupl/Wincupl.exe

On first start-up, it prompted for registration info. This is a carry-over from before this tool was freeware, and the download page lists the correct key as 60008009.

I got persistent errors about missing DLL modules.

I initially thought that this was a library registration or 32/64-bit compatibility issue, though I eventually found that I needed to install mfc40 via winetrics:

$ ./winetricks mfc40

After this installer ran, I was able to launch WinCUPL. The editor was using a a proportional font, though the dialog indicated that it was attempting to use Courier New, which is monospace.

This is fixed by installing the Microsoft fonts via the ttf-mscorefonts-installer package.

sudo apt-get --reinstall install ttf-mscorefonts-installer

At this point I had a working editor. If it was not clear yet that this is ancient technology, this screen capture shows the I/O decoding example which is bundled with WinCUPL, which was apparently written in 1984.

Writing a test program

WinCUPL ships with a folder of example programs, and some documentation which includes a reference manual and tutorial. Most introductory information online seems to be from old electrical or computer engineering courses.

I read through:

The closest thing to a “Hello World” program for one of these chips is the GATES.PLD example, which shows how to apply some basic logic operations. It was written for the ATF16V8, so I looked up my own device to find its mnemonic, g22v10:

After swapping around the pins, I ended up with this program for testing the ATF22V10:

Name     MyTest;
PartNo   00;
Date     6/07/2021;
Revision 01;
Designer Mike;
Company  None;
Assembly None;
Location None;
Device   g22v10;

Pin 1 = a;
Pin 2 = b;

Pin 14 = inva;
Pin 15 = invb;
Pin 16 = and;
Pin 17 = nand;
Pin 18 = or;
Pin 19 = nor;
Pin 20 = xor;
Pin 21 = xnor;

inva = !a;
invb = !b;
and = a & b;
nand = !(a & b);
or = a # b;
nor = !(a # b);
xor = a $ b;
xnor = !(a $ b);

To get a JED file from this, I navigated to compile options, and disabled the “Simulate” and “Absolute” options. This skips the simulation step, which otherwise blocks compilation.

I did not find the simulator very intuitive, but I did return to it later after watching this recent tutorial. The devices can only be reprogrammed 100 times, so for anything complex, I’ll definitely be checking outputs in the simulator rather than relying on trial-and-error.

Programming the chip

I use TL866II+ programmer with the minipro open source programming software for other chips, and I found mixed information online about whether this would work for the ATF22V10.

In my case, it worked, but only after a few attempts. My first mistake was to read the chip instead of writing it.

$ minipro -p ATF22V10CQZ -r MyTest.jed
Found TL866II+ 04.2.86 (0x256)
Warning: Firmware is out of date.
  Expected  04.2.123 (0x27b)
  Found     04.2.86 (0x256)
Reading device...  0.54Sec  OK

When I plugged this into my test circuit (shown later), the blank chip warmed up, and produced erratic output.

After re-generating the JED file, I programmed the chip again, but got some warnings and verification errors. The chip still produced heat and erratic output in my test circuit.

$ minipro -p ATF22V10CQZ -w MyTest.jed
Found TL866II+ 04.2.86 (0x256)
Warning: Firmware is out of date.
  Expected  04.2.123 (0x27b)
  Found     04.2.86 (0x256)

VPP=12V
Warning! JED file doesn't match the selected device!

Declared fuse checksum: 0x7005 Calculated: 0x7005 ... OK
Declared file checksum: 0x6DDD Calculated: 0x6DDD ... OK
JED file parsed OK

Use -P to skip write protect

Erasing... 0.82Sec OK
Writing jedec file...  4.97Sec  OK
Reading device...  0.54Sec  OK
Writing lock bit... 0.26Sec OK
Verification failed at address 0x0006: File=0x00, Device=0x01

Reading back the device produced a completely different JED file (different length, different contents).

Detour: Firmware update

I started digging into the warnings, and decided to attempt a firmware update. The best instructions I could find for this were from this GitLab issue.

The short version is that once you have a firmware file from the hardware vendor, they can be applied by minipro.

$ minipro -F updateII.dat
Found TL866II+ 04.2.86 (0x256)
Warning: Firmware is out of date.
  Expected  04.2.123 (0x27b)
  Found     04.2.86 (0x256)
updateII.dat contains firmware version 4.2.126 (newer)

Do you want to continue with firmware update? y/n:y
Switching to bootloader... failed!

I got this error, probably because the device was restarted. On my setup, I am passing the USB device through to a VM, and needed to press a button to redirect it again.

$ minipro -F updateII.dat 
Found TL866II+ in bootloader mode!
updateII.dat contains firmware version 4.2.126 (newer)

Do you want to continue with firmware update? y/n:y
Erasing... OK
Reflashing... 100%
Resetting device... OK
Reflash... OK

Programming the chip again

With the updated firmware, I made a third attempt to write the chip, using the same JED file as before.

$ minipro -p ATF22V10CQZ -w MyTest.jed
Found TL866II+ 04.2.126 (0x27e)
Warning: Firmware is newer than expected.
  Expected  04.2.123 (0x27b)
  Found     04.2.126 (0x27e)

VPP=12V
Warning! JED file doesn't match the selected device!

Declared fuse checksum: 0x7005 Calculated: 0x7005 ... OK
Declared file checksum: 0x6DDD Calculated: 0x6DDD ... OK
JED file parsed OK

Use -P to skip write protect

Erasing... 0.33Sec OK
Writing jedec file...  5.04Sec  OK
Reading device...  0.41Sec  OK
Writing lock bit... 0.35Sec OK
Verification failed at address 0x16C6: File=0x01, Device=0x00

Reading the device returned a JED file with all 0’s, and there are still some warnings, but the circuit appeared to work correctly.

Alternatives

The firmware update seemed to fix any problems for me, but I have read plenty of accounts of tricky issues when attempting to program the ATF22V10 with the TL866II+ programmer.

An alternative programmer might be the Wellon VP-290 (mentioned here). On the software side, using the ATF22V10 really seems to require WinCUPL, though it does have a command-line.

There is plenty of software designed for the discontinued Lattice 22V10 GAL, though my best guess is that these are not compatible with the Atmel chips I’m using here. These include GALasm, which has a license which prohibits commercial use (enough for me to steer clear), plus am open source re-implementation called galette.

Wrap-up

It’s been interesting to take a look at the lowest-possible level of the software stack, even just to produce a “Hello World” program.

I am not using PLD’s in my current project, but it’s good to know that I could program one if I needed to. The main use for me would be as an alternative to discrete logic chips, so that I can keep parts count, layout space, and propagation delays under control. For prototyping, it will also be helpful to be able to replicate old circuits which use similar components, or to simulate logic chips which aren’t in my inventory.

One thing that I learned is that a fuse is not the same as a gate. The device I used has thousands of fuses (programmable connections) which are capable of configuring the inputs to a few hundred gates. If the temperature of my first two programming attempts is anything to go by, it seems to be perfectly possible to configure the fuses to produce a short circuit.

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!

Building a tiny Linux gaming PC

I recently put together a small form factor PC to use with my TV, with the aim of building a Linux-based, console-like gaming setup. This mostly went to plan, and is certainly a big upgrade from my previous hardware, which was based on a retired desktop computer.

I am writing a bit about it today, partly because it’s been an interesting project, but also to show a working setup for anybody who is attempting something similar.

Quick reference

The build is based around these components (PC part picker list here):

  • In Win Chopin case
  • AMD Ryzen 5 3400G CPU
  • Gigabyte B450 I AORUS PRO Motherboard
  • Noctua NH-L9a-AM4 Cooler
  • Corsair Vengeance LPX 16 GB (2x8GB) DDR4-3200 RAM
  • Samsung 970 Evo 1 TB

Peripherals:

  • Sony DualShock 4 controller – Works over Bluetooth and can be used as a touch-pad.
  • 8BitDo SF30 Pro controller – Also works over Bluetooth, and can emulate an Xbox controller.
  • Logitech K400R wireless keyboard/mouse

Hardware setup

This is the smallest PC I’ve built with desktop parts, and there is not a lot of spare space in the case. I first put the everything together on my desk confirm that it would POST, then disassembled the case to make some modifications.

Three screws hold in the power supply, two torx screws hold the aluminium shell to the chassis, and two Phillips-head screws and some tabs hold the plastic front panel cover to the chassis.

In that last image, the motherboard almost fills the case, and a power supply has to fit in there as well.

My plan was to route the power cables around the back of the case rather than leaving them in the main cavity. This involved cutting out a square near one of the drive trays for all of the power cables to exit, and another for the ATX power connector to connect to the motherboard. I also removed a metal tab from the power supply, since I had cut out the metal that it was supposed to be screwed to.

There was an existing hole which I could use for the CPU power connector, which was already the correct size.

I then swapped the power supply fan for a Noctua A4x10 PWM so that I could control the fan speed from software. I do not recommend this, since it is unnecessary, and opening a power supply is an electrocution hazard.

Next, I got all of the cables into place, since there is no space to do this after the motherboard is installed

Once the power cables were in the right place, I added the motherboard, then connected the front panel I/O, feeding all of the excess cables to the space behind the case. This is a very tight fit, and I accidentally bent the case and squashed some cables before finally getting the plastic front panel to attach.

After finally lining everything up, I checked that the case closed, and re-attached the shell.

Lastly, I was able to re-fit one of the two drive trays, in case that is ever needed in future.

BIOS setup

This build uses integrated graphics, so the system memory is also being used as video memory. This means that RAM speed is more important than usual.

I enabled XMP to run the RAM at its rated 3200 MHz speed. This is higher than the highest officially supported speed of 2933 MHz for this CPU, but for me that was not an issue.

I set the built-in motherboard LED’s to blue, to match the default colour on the Sony DualShock 4 controller. This also meant that I would not need to get any RGB software working on Linux.

Some online sources suggest disabling the AMD Cool & Quiet function to get extra gaming performance, but I consider this advice to be out-dated, unless you’re overclocking. Instead, I am leaving this setting enabled, then automatically setting the CPU to ‘performance’ mode while gaming via software.

Once I got everything working, I disabled everything that I wasn’t using, and enabled Ultra Fast Boot. This makes the system start faster, but also removes the ability to re-configure the BIOS, because the keyboard will not work. The settings can be reset via a jumper on the motherboard if I need to get back into it.

Software setup

I installed Ubuntu 20.04 LTS. I would suggest sticking with the Long Term Support version of Ubuntu if you are using it for gaming, since Steam is available in the package manager. Bluetooth, Wi-Fi, sound and 3D-accelerated graphics all worked out of the box.

Configuring Ubuntu

These settings make Ubuntu act less like a desktop, and more like a Home Theater PC.

First I enabled automatic login, then cleared the password for the login keyring. This avoids needing to use a keyboard on startup, or getting prompted for a password when web browsers attempt to access the keyring.

Under power settings, I set “Blank Screen” to “Never”, and Automatic Suspend to “Off”.

Under screen lock settings, I set “Blank Screen Delay” to “Never”, and disabled “Automatic Screen Lock”.

Next, I set the theme to dark mode, the display output to 1920×1080 60Hz, and the audio output device to HDMI. The display is 4K, but compatibility will be challenging enough without adding scaling issues in there, so I’m using a lower resolution for now.

Lastly, I set up a tool called psensor to start on boot, so that I could check temperatures.

Installing applications

Linux gaming has advanced a fair bit recently, and since it is a large topic, I won’t try to cover in too much detail here. I’m connecting this computer up to a TV, so this type of setup is only suitable for games with controller support. Still, this includes:

  • Native Linux games.
  • Windows games running via a compatibility layer (WINE/DXVK or Proton).
  • Games for other systems played through an emulator.

An extensive catalogue is available through the Linux version of Steam, which I installed via the Ubuntu package manager. I could have set Steam to launch on startup and call it a day, but there are two other launchers which I installed alongside it:

  • Lutris (from the lutris-team/lutris PPA).
  • Retroarch (from the libretro/testing PPA)

Next, I installed WINE (32 bit and 64 bit), winetricks, and gamemode from the Ubuntu package manager, then Proton GE from GitHub. Proton GE is a widely-used Proton fork, which integrates fixes from upstream WINE. This gave me several different run-times for Windows applications, and simply switching between them or adding some environment variables has been sufficient to work around every compatibility issue that I’ve encountered so-far.

Lastly, I installed Kodi from the team-xbmc/ppa PPA.

Game testing

For this section, I’m listing out a few games from my library that I’ve tested. As a quick reminder, this is all running on integrated graphics, on Linux.

I was mainly checking that I could get a (subjectively) playable frame-rate, and that I could get two controllers to work in local multiplayer where available.

Steam

GRIP combat racing runs via Proton. It is a fast-paced racing game, has local split-screen multiplayer, and runs very smoothly at 720p.

Untitled Goose Game also runs via Proton. It has recently added a 2-player mode, and runs at 1080p with no hiccups.

The Tomb Raider reboot is available native for Linux. With low settings, it runs fine at 1080p, with the benchmark indicating a 99 FPS average.

Shadow of the Tomb Raider, also available native for Linux, is a real challenge for this integrated GPU. I am running it at 1080p, lowest settings, though the benchmark comes back with just a 43 FPS average.

Lutris

Lutris has an Epic Games store installer, which runs the store-front under WINE. I decided to try it out, though it is easily the most buggy software mentioned in this blog post.

The only the only game I tested from there was Rocket League. Native Linux support was recently dropped from this title on Steam, but the Windows version from Epic runs just fine on integrated graphics, and has local multiplayer. I run it at 1080p.

I also tested the original Crysis by manually setting up a WINE prefix and adding it to Lutris. I run this at 720p, medium settings. After a few loops, the benchmark indicates that this averages 112 FPS, so I could probably increase the quality or resolution.

I also tested Crysis 2 via WINE, which had some concerns about my graphics card. I also run it at 720p, using the ‘Gamer’ profile. The benchmark indicates that this averages 60 FPS.

Super Tux Kart is an open source racing game, which can be installed via Lutris. This is a native Linux build, and runs just fine at 1080p. It has controller support and local multiplayer.

Wrap-up

This blog post touches on quite a few topics, so I’ve left out a lot of the details. If you’ve read this far, though, then it’s time to talk about down-sides. Some things did not work as planned.

  • Reading/controlling fan speeds did not work from Linux on this Gigabyte motherboard. This has worked so consistently for me on other hardware, that I did not even think to check for compatibility here. This means I can only set fan speeds in the BIOS.
  • The power supply is noisier than I expected under load. This is due to coil whine, not fan noise.
  • When using the 8BitDo SF30 controller over Bluetooth, RetroArch would pause for 10 or 15 seconds at a time. I tracked this down to the fact that I am connecting it as an Xbox controller, and RetroArch was attempting (and failing) to check its battery level. Connecting the controller after a core has launched avoids the problem.
  • I had hoped to run everything under Wayland, but the Epic Games store had terrible graphical glitches. Everything else on this page (Steam, Lutris, Retroarch) worked on Wayland, and this is apparently due to the way that this app uses OpenGL.

This setup works very well for me, and I am glad to be able to show that it’s possible to do some basic gaming without Microsoft Windows or a dedicated GPU. Still, there are a lot of trade-offs that come from this form-factor and platform, and that’s not for everybody.

If you’re using Linux or a Ryzen APU for gaming, or have tried building in the InWin Chopin case, then please feel free to leave a comment below. I would be interested to know about anything you’ve done differently, and how it worked out.

How to auto-scale the display in GNOME Boxes

I recently installed a virtual machine in GNOME Boxes, and the display was stuck at 1024×768.

The type of display used here is called SPICE, and it includes a channel for auto-scaling. The guest simply needs the agent to be installed.

In this case, I’m running a Debian guest, which means that I must have forgotten to install the spice-vdagent package.

# apt-get install spice-vdagent
Reading package lists... Done
Building dependency tree       
Reading state information... Done
The following NEW packages will be installed:
  spice-vdagent
0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded.
Need to get 47.6 kB of archives.
After this operation, 174 kB of additional disk space will be used.
Get:1 http://deb.debian.org/debian buster/main amd64 spice-vdagent amd64 0.18.0-1 [47.6 kB]
Fetched 47.6 kB in 0s (99.9 kB/s)  
Selecting previously unselected package spice-vdagent.
(Reading database ... 132855 files and directories currently installed.)
Preparing to unpack .../spice-vdagent_0.18.0-1_amd64.deb ...
Unpacking spice-vdagent (0.18.0-1) ...
Setting up spice-vdagent (0.18.0-1) ...
Created symlink /etc/systemd/system/sockets.target.wants/spice-vdagentd.socket → /lib/systemd/system/spice-vdagentd.socket.
[spice-vdagentd.conf:2] Line references path below legacy directory /var/run/, updating /var/run/spice-vdagentd → /run/spice-vdagentd; please update the tmpfiles.d/ drop-in file accordingly.
Processing triggers for man-db (2.8.5-2) ...
Processing triggers for systemd (241-5) ...

The easiest way to ensure that everything is running correctly is to reboot, since the agent will start on boot, and this also forces a new log-in, and a new connection to the display.

# reboot

Result

Assuming that you are otherwise on the default settings, the display in the guest VM will now automatically adjust as you resize the window.

If the “Share Clipboard” setting is enabled for the virtual machine, then spice-vdagent will also enable you to copy & paste text between the host and guest.