Monday, January 25, 2021

New Project: 6507 Graphing Calculator

Despite the decision in 2019 to finish up some lingering projects before beginning anything new, I did start working on a new calculator project. One of the members on the 6502 forum organized a contest about a year ago for projects using the 6507 processor, so I decided to make an exception and build this calculator as an entry. Most of my free time in 2020 was spent working on my Robot Game project before making a lot of progress on this project. Rather than a series of progress update posts like my other calculator projects have, this is one long post about the project so far. Some of this information is already available in the 6502 forum post on the project. 

Contest
The contest started in January 2020. The goal is to build something using a 6507 processor, 6532 RIOT, which stands for RAM, IO, and Timer, and a 2KB 2716 EPROM. After some discussion, a larger ROM of up to 8KB was allowed since it would be difficult for most people to program a 2716. The limit seems to apply to the total memory, so I may have accidentally disqualified myself by adding 2KB SRAM to the 8KB ROM. The 6507 and 6532 run at 1MHz and are both NMOS parts, so they consume a lot of current compared to the newer CMOS 65C02. The pair was used together in products like the Atari 2600. The 6507 is a reduced version of the 6502 and has 28 pins instead of 40, so it can only address 8KB of memory rather than the usual 64KB. It's also missing the IRQ and NMI interrupt pins. These limitations make it interesting to work with. The 6532 has 128 bytes of RAM, two IO ports, and a programmable timer, which is a lot of useful stuff to have in one package.

Hardware
The first 2KB of address space is occupied by RAM. Because the 6507 can only address 8KB, the last 4KB of address space is banked for the ROM. The 6532 takes 256 bytes of space leaving 1.75KB in the middle of the memory map that is assigned to ROM. Having a fixed amount of ROM there that isn't banked simplifies handling the banks and initializing the system. Here is the memory map:
  • 0x0000 - 0x07FF: 2KB RAM
  • 0x0800 - 0x08FF: RIOT
  • 0x0900 - 0x0FFF: 1.75KB fixed ROM
  • 0x1000 - 0x1FFF: 4KB banked ROM
The address decoding is done with a an ATF16V8C GAL, which is similar to a CPLD, though less capable. The WinCUPL software for programming these is atrocious and crashes frequently, but setting up the design for the GAL itself was pretty simple. The chips can be programmed just fine with the TL866II+ programmer that I use for EEPROMs. According to the datasheet, the ATF16V8C can draw over 100mA, so I also got some lower power variants to try to see if they save power.

The display is a 128x64 monochrome LCD compatible with the KS0108. This is the first time I've used a display like this and it works pretty well so far. The header pins are on the bottom edge of the screen, so it has to be turned upside down to fit the system I'm building. This means the font data has to be written upside down and backwards, which complicates things a little. It wasn't easy to find a completely free 5x8 font, so I made one myself. Later, I experimented with different styles to see what looks best:


The system would not start reliably on a breadboard, and I eventually tracked the problem down to sagging voltage on the breadboard. The problem was actually the jumper wires used to carry the supply current not the breadboard itself. Soldering everything onto protoboard fixed the startup problems and everything worked reliably after that:


There is still a good amount of work to do even though the hardware is pretty far along. The keyboard needs to be soldered and connected to one of the latches. There are a few possibilities for making the labels for the buttons that still need to be worked out. Another big part still missing is the power supply. The first plan was to run the system on four AA batteries, though this would waste a lot of power using a linear regulator or even a buck regulator since a lot of power would be left in the batteries by the time the voltage drops too low to use with a regulator. Instead, three AAs with a boost regulator will allow me to fully use more of the batteries' charge and also use rechargeables, which wouldn't be possible with the buck regulator. My plan is to use a MAX756 boost regulator, though I still need to set it up and test it. One cool thing about the chip is that it has a low battery indicator, which is something I was planning on trying to implement myself. The power supply also needs a way to be switched on and off by software control, so the keypad can have an on/off key rather than a separate power switch. This is really important so the system has a chance to save it's state before powering itself down. The last piece of the power supply system is a way to put the processor to sleep when it isn't needed. This can be done with the RDY pin. Hopefully, the timer and interrupt system of the RIOT can be combined with the GAL and software control to save energy while the calculator is between key presses.

Software
The interface is built around a Forth-style system with an 8 level stack of 8 byte objects. Unlike most Forths, there are three different types: floats, strings, and hex. The floats use 6 bytes for a 12 digit BCD significand and 2 bytes for the exponent and sign. Strings can take up all 8 bytes. Hex objects hold a 16-bit unsigned integer that can be used for memory addressing and come in two types: smart and raw. The raw hex type just stores a single 16-bit number. The smart hex type holds a base address, offset, and calculated sum of the two. The offset comes from any value added or subtracted to the object. This lets pointers be garbage collected since only the base needs to be updated then the existing offset can be applied to generate the sum. Garbage collection is also not a common feature in most Forths, but is definitely necessary for a Forth-based calculator with only 2KB of RAM to be viable. 

The Forth system is token based to conserve memory. The header of each word describes how many objects of which type the word expects on the stack. The dispatcher can check those objects and prevent stack underflow, which saves a lot of space compared to doing the checks in the word itself. Marking each word and piece of data with a token allows for garbage collection, since every piece of data that is a pointer which may change can be identified and updated when needed. 

So far, the basic Forth system including defining words, tick, and EXEC, and the four basic arithmetic functions for floats and hex objects have been defined. The main functions remaining to be implemented are loops, IF statements, and transcendental functions like sine and logarithms. The firmware is already 6.5KB, so there is very little room to squeeze in a lot of capability. My plan now is to continue writing everything in assembly, even if the firmware overflows the 8KB limit, then rewrite key parts of it in Forth to squeeze it into the available memory.

Floating point
The floating point numbers have a 12 decimal digit significand with guard, round, and sticky digits and rounding to even, which is the same format used by the HP-48 series of calculators. Implementing a floating point package is somewhat complicated and easy to make a mistake on, so a lot of testing is needed to verify the calculations. At first, I included tests in the firmware running in my JavaScript emulator, but this doesn't work well at all for a large file of test data. Next, I ported the emulator to node.js, so I can run the tests in a separate console window. This works by first generating a few million test calculations in Python using the Decimal package configured for the same number of decimal places and rounding style. The calculations are written to a file that comes to about 200MB then directed to the node.js emulator. The firmware running in the emulator reads the file generated by Python and performs calculations on the data there then compares the results to the results in the file and flags any that don't match. This system let me catch several errors in my code that would have been difficult to find otherwise.

Conclusion
Despite the months of work on this project, there is still a lot to do! In the coming months, I'll work on the keypad, power supply, and finishing the firmware, so I can finish the project and submit it to the contest.