Programming the TGL-6502

This post describes how to develop software for my TGL-6502 emulator board. The most recent update to the GitHub repository includes a desktop implementation of the emulator software so you don't need to build the hardware to write and test programs.


This is going to be a very long post unfortunately so I thought a brief executive summary would be useful. My goal in this post is to describe how you can develop and test software targeted at my 6502 emulator - to achieve that I will describe how to;

  1. Compile a version of the TGL-6502 firmware that runs on a desktop computer (Linux or Windows).
  2. How to install a 6502 C compiler and assembler and configure it to generate code suitable for running on the TGL-6502.
  3. Provide a simple support library to use the hardware specific to the TGL-6502.
  4. And finally, give an example program that uses all of the above to generate a working (and hopefully useful) ROM image for the board.

So, if you are interested - please read on.

The Desktop Emulator

Debugging firmware for a microcontroller can be very frustrating especially if you don't have any hardware debugging support. When I started this project one of the first things I did was make a test harness for the emulator code so I could test in on a desktop machine first and have reasonable confidence that it would work on the target CPU. I have cleaned up the code since then and it is now included in the repository.

On a Windows system you can simply use the emu6502 solution file in the firmware directory - this will build a Windows console application that simulates the TGL-6502. The equivalent tool on Linux is called tgl6502 and can be built with the following commands:

cd firmware
make HOST=local

The firmware code is structured so that the hardware emulation (memory access, timers, the IO slot and the serial port) is cleanly separated from the 6502 CPU emulator core. This was done to make it easier to move to a different host processor in the future but had the added advantage of letting me easily build a debug platform to use to test the target code.

Emu6502 on Windows

For the system memory the desktop emulators simply allocate a 128K block to simulate the SPI RAM chip and load a file called 6502.rom as the ROM. The desktop versions can't simulate things like the buttons or LED's of course but they can simulate the interrupts, timers and serial port. This is enough to verify that any ROM image will work before burning it to the EEPROM and running it on the real hardware.

Now that we can test out the ROM code before burning it we need a way to generate the ROM image.

Installing the Toolchain

There are a wide range of 6502 assemblers available (the ASxxxx cross assembler is a good example - it supports the 6502 along with a wide range of other 8 bit processors). My 6502 assembly knowledge is pretty much non-existent however so I was more interested in a C compiler, preferably one that was at least partially ANSI compliant.

TGL-6502 RevB System

The best tool I've found for this is CC65, a full toolchain for 6502 based systems. As well a a C compiler and assembler it also provides a librarian and linker which behave in a very similar fashion to the corresponding GCC tools. Given that it is available for both Windows and Linux makes it the perfect choice.

Installation is relatively straight forward:


For Windows simply download the ZIP file from the Windows Snapshot link at the bottom of this page, unzip it to the directory of your choice and add that directory to your path.

You will also need some additional GNU command line tools (specifically GNU make and it's supporting tools) so you will need to install those as well. There are a number of different installers for these tools available, I prefer the Cygwin implementation. With Cygwin you will have to explicitly select GNU make from the Development Tools section in the installer - it is not selected by default. Once Cygwin is installed you will need to add the bin directory to your path as well.


On Linux (and other Unix systems) the easiest way to install CC65 is to build it from source. Simply use the following commands:

$ git clone
$ cd cc65
$ make
$ sudo make prefix=/usr/local install

This will clone the GitHub repository, build the toolchain and install it to /usr/local.

Note: The instructions above assume your Linux box has the standard development tools and libraries installed (gcc, make, etc). If the build process complains about missing dependencies you will have to resolve them and install the appropriate packages. I have installed it on a number of machines so far and haven't come across any issues so it should just work out of the box.

To test the installation just type the following command and check the version response (this works on both Windows and Linux):

$ cc65 -V
cc65 V2.14 - Git d112322

The TGL-6502 Support Library

Unfortunately there are a few more things required before we can start compiling programs for the emulator. CC65 supports a number of systems 'out of the box' including the Apple II, a range of Commodore machines and others - the TGL-6502 is not one of these so we have to provide some additional files. The key things that are required are:

  1. A library of compiler support functions. These are assesmbly language routines the compiler calls to perform operations that are not natively supported by the 6502 instruction set such as 16 bit multiplication and division.
  2. A linker description file that describes the memory layout of the target system and where various parts of the program should be placed.
  3. The C startup code. This code is used to set up memory, the stack and initialise variables prior to calling your main() function.

I've provided all of these in the 'software/target/mos6502' directory of the repository and they seem to be working well. There are two linker configuration files - rom16.cfg and rom8.cfg for a 16K and 8K ROM image respectively. The 16K ROM configuration (see the source here) starts the code at $C000, reserves 1K of RAM for the program at $0400 and a 1K stack at $0800.

The startup file - romcrt0.s - was copied from one of the existing startup files. It simply copies initialised variables from ROM to RAM, clears the uninitialised variables and then calls the main() function in the C program. If main() ever returns the start up code simply goes into an endless loop.

TGL-6502 RevB Mainboard

The C support library for the TGL-6502 was similarly made by copying parts of an existing implementation. This can be found in the software/target/mos6502/runtime directory and must be built before any other code. Assuming you have followed the steps above to install make and cc65 you can do that just by changing into the directory and typing make. This will generate the file software/target/mos6502/runtime.lib which will be used in the linker step for any ROM code.

The CC65 documentation has a page devoted to adding board support which was very helpful - I didn't discover it until after I had already started the process however so some of the steps I took were a little less efficient. Eventually I will go back and clean up the support library but for now it's working the way I need it to.

Another component that will be needed is the standard C runtime library which provides things like string handling functions and memory management. For the moment I've created what I call 'baselib' which provides the TGL-6502 specific console IO functions and some simple string handling utilities. You will need to compile this as well before compiling any other programs. Simple change into the directory software/target/common/baselib and run make.

MinMon - A Minimal Monitor Program

Now that all the infrastructure is set up we need a program. To help with testing I decided to write a simple monitor program that allows me to inspect and modify any memory location in the system and execute arbitrary code. It is still a work in progress but has enough functionality for basic testing. Here is a sample session using the emulator on Linux:

$ ./tgl6502
Read 16384 bytes from '6502.rom'
MinMon V1.0
$0400: $33 $D2 $00 $00 $20 $D2 $00 $00 $23 $D2 $01 $00 $25 $D2 $01 $00
$0410>W $1000 $01 $02 $03 $04 $05
$0410>R $1000
$1000: $01 $02 $03 $04 $05 $00 $00 $00 $00 $00 $00 $00 $00 $00 $00 $00

The source for the program is in the directory software/target/common/minmon and can be used as a template for you own programs. I've made the Makefile as generic as possible - all you need to do is copy it into a new directory under software/target/common, change the FILENAME variable at the top of the Makefile to what you want the output file to be called and just place your source code in the directory. Running make will compile all the C and assembly files, link them with the runtime library and generate the ROM image.

This article is not the place to go in to a lot of details about how the TGL-6502 emulates the hardware but a simple overview is in order. All of the hardware is provided in a memory mapped block at $FF00, that block is laid out as per the following structure:

/** The memory mapped IO structure
 * The memory at IO_BASE is laid out according to this structure. Note that it
 * must match the structure defined in the emulator firmware.
typedef struct _TGL6502_IO {
  uint8_t     m_version;                 //!< RO: Version byte
  uint8_t     m_ioctl;                   //!< RW: IO control flags
  uint8_t     m_conin;                   //!< RO: Console input
  uint8_t     m_conout;                  //!< RW: Console output
  uint16_t    m_time;                    //!< RW: Current time (seconds since midnight)
  uint16_t    m_kips;                    //!< RO: Instructions per second (1000s)
  uint8_t     m_slot0;                   //!< RW: Expansion slot control
  uint8_t     m_spicmd;                  //!< RW: SPI control byte
  uint8_t     m_spibuf[SPI_BUFFER_SIZE]; //!< RW: SPI transfer buffer
  uint8_t     m_pages[MMU_PAGE_COUNT];   //!< RW: MMU page map
  } TGL6502_IO;

This structure and the supporting constants are defined in the tgl6502.h file. The associated C source file tgl6502.c defines the global variable pIO that points to the memory mapped IO region and implements functions for console input and output which is enough for most simple programs.

I've kept the console interface very simple - writing a byte to pIO->m_conout will cause it to be sent on the serial port. Reading is a little more complex - the last unread input character is available in pIO->m_conin, if no input is available this memory location will read $00. Bit 2 of the pIO->m_ioctl byte is used as a flag for pending input - if the bit is set data is available and a read of pIO->m_conin will return a valid value, if it is clear no data is available. Reading pIO->m_conin will clear the flag.

I've wrapped these up into kbhit(), getch() and putch() functions which can be used to test for available input, read an input character and output a character respectively.

The rest of the MinMon source comprises the command interpreter and string formatting functions. As I mentioned earlier the feature set is very limited (it is essentially the software equivalent of front panel toggle switches) but it is enough to exercise the TGL-6502 hardware for testing purposes.

Building the ROM image is simply a matter of typing make in the source directory, this will generate the minmon.rom file. If you rename the file to 6502.rom and run the emulator it will be loaded and used as the system ROM image.

Next Steps

Being able to compile simple 6502 programs and run them on the desktop under the same CPU core that is implemented on the LPC810 has been a great help in testing - by the time I flash the CPU and load the EEPROM I can be reasonably sure that that any problems I might have are likely to be in the hardware emulation code which really simplifies the debugging process.

There is still a lot of work to do with the 6502 toolchain - at the moment I don't have a C runtime library implementation (the standard C functions for string manipulation, memory allocation and the like) which will be needed for more complex programs. The majority of those functions are system neutral so it should be relatively easy to port one of the existing CC65 implementations across.

Tape Based Software

The biggest limit with the TGL-6502 is the lack of persistent storage and that, unfortunately, cannot be addressed with the current hardware (there just aren't enough pins and memory to implement an SD card interface). There is, however, a fairly large ROM available (128K) which is accessible through bank switching so it can contain multiple programs with a simple menu based launcher to select the appropriate bank to switch in and run.

This is one of the next features I intend to add to MinMon along with the ability to load larger blocks of data into memory using the XMODEM protocol over the serial connection. This combination should minimise the need to flash the EEPROM on a regular basis.

Although this project was started as a bit of fun (and a challenge to see if I could fit it all in 4K of code) it has turned out to be a pretty decent learning system. Developing code for a 6502 is very similar to developing for an embedded system and many of the same challenges and techniques apply, building up the toolchain, linker configurations and runtime libraries provides a good understanding of compiler behaviour which is just as applicable to GCC as it is to CC65; dealing with the constrained resources of an 8 bit system (memory, IO and speed) forces you to think a bit differently about your code to come up with smarter ways to acheive the result you want. I think it would actually make a pretty interesting training tool for an embedded programming course.

TGL-6502 System Front

At any rate, my next goal is to finish off the firmware and get the hardware interface working properly. Stay tuned for further updates.