The C65 ROM: Pick up the thread after 30 years.
I watched the MEGA65 project very early and wondered, if it would ever happen, that this machine would see the light of day. But I got the impression, that this is a promising project and offered my assistance. I was accepted and wrote the User’s Guide chapter on BASIC 10, mainly using information from C128 manuals, preliminary C65 documentation and experimenting with the XEMU emulator from Gábor Lénárt. Soon I found out that the existing ROM images that were available thanks to Bo Zimmerman, were in a preliminary state. There were unimplemented commands and functions, bugs inherited from the BASIC 7 of the Commodore 128, which was obviously used as base for the BASIC 10, and a graphics library that was about 80% completed. Also there were ROM images for different types of hardware, programmed while hardware changes in the development of the C65 happened, like the DMA controller or the floppy controller.
Then there was the day, when I got a MEGA65 prototype loaned from the MEGA65 team and I could really program this machine. Soon I found out, that the existing firmware, that was loaded as ROM image from the SD card, could barely be used beyond using some simple programs and demos. The C64 mode was fine, but I wanted to use the power of the MEGA65. This could be done in machine language, so I wrote my first MEGA65 program, a Z-interpreter for playing stories of the legendary Infocom archive, like Zork, The Hitchhiker’s Guide To The Galaxy, The Leather Goddesses Of Phobos and many more. This worked out very well and I turned my attention back to the incomplete BASIC interpreter. I knew, there was the open ROM project, which promised to deliver an advanced BASIC interpreter, but the time schedule was such, that I didn’t want to wait. So I self assigned the job of finishing the BASIC interpreter to myself. My motivation was not to just finish the interpreter for the C65. I found it more challenging to unleash the power of the MEGA65 and decided to use the new features of the MEGA65, which could in many cases overcome the insufficiency of the C65. The task can be described like:
1) Fix bugs
2) Finish incomplete code
3) Implement planned, but missing commands and features
4) Change the memory map, to take advantage of the RAM capacity of the MEGA65
5) Exchange awkward code, that was necessary due to a bad bank switching design
6) Optimise existing code: faster and shorter alogrithms
7) Use the hardware multiplication and divison unit of the MEGA65
8) Implement convenient features, like the DOS wedge
It would be nearly impossible to work on these tasks without the original source codes as a starting point, but thanks to Bo Zimmerman, who probably hosts the largest arvchive of CBM related technical information, ROM images and software, I could find the necessary source files of Dennis Jarvis, one of the former C65 developers.
Looking at the source, I saw the next obstacle: They were written in an assembler format, that none of the currently used assemblers could understand. Some of the issues were:
1) No different mnemonics for short and long branches, the assembler has to figure out
2) Local label of the form nn$, which can be reused after each normal label
3) Octal constants are prefixed with @, not the usual & as in other assemblers
4) Use of 65GS02 mnemonics, which were not 65C02 compatible and unknown to many assemblers
5) Different form of pseudo-ops for many data declarations
After some research I found out, that the sources were 1991 assembled, using the BSO (Boston Systems Office) assembler, which was a cross assembler, running on a DEC-VAX with the operating system VMS. This assembler (and the hardware) isn’t available anymore.
Fortunately I had written my own 6502/65C02 assembler some years ago, in order to write programs for a variety of 6502 powered retro computer. So I enhanced the assembler, to support all features of the MEGA65 CPU 45GS02, which includes all of the C65 CPU, but added even more instructions and address modes. Then I introduced a compatibilty mode, which could handle all of the particularities of the BSO assembler. (Funny that I had named my assembler BSA many years ago)
Fortunately the old source archives included besides the source files, the list files, that were the result of the assembler run too and these included the object code in form of hex numbers.
So I could test my assembler by assembling the old source files and comparing the result with the listings. This helped to iron out all issues and finally the output of the BSA was bit identical to the object code, extracted from the list files of the BSO from 1991.
After all these preparations I could finally start to continue the development of the BASIC interpreter and the graphics library by using my self built toolchain. So far I did following changes or implementations:
1) Memory usage
C65: bank 0: 0000-1fff : 8K : system and basic internal variables, zero page, stack, buffers, etc.
C65: bank 1: 0000-1fff : 8K : DOS variables and buffers
C65: bank 0: 2000-7fff : 24K : BASIC program text
C65: bank 1: 2000-7fff : 24K : BASIC variables
C65: bank 0: 8000-ffff : 32K : graphics RAM
C65: bank 1: 8000-f7ff: 30K : graphics RAM
C65: bank 1: f800-ffff: 2K : colour/attributes RAM
MEGA65: bank 0: 0000-1fff : 8K : system and basic internal variables, zero page, stack, buffers
MEGA65: bank 1: 0000-1fff : 8K : DOS variables and buffers
MEGA65: bank 0: 2000-ffff : 56K : BASIC program text
MEGA65: bank 1: 2000-f7ff : 54K : BASIC variables
MEGA65: bank 4: 0000-ffff : 64K : graphics RAM
MEGA65: bank 5: 0000-ffff: 64K : graphics RAM
MEGA65: bank 1: f800-ffff: 2K : colour/attributes RAM
The C65 had 24K free for program and variables each
It could use at maximum one 320 x 200 screen with 7 bitplanes (128 colours) or a
640 x 400 screen with 2 bitplanes (4 colours)
The MEGA65 with the new memory map can use 56K for BASIC program and 54K for variables and use 128K for graphics, enough for a hires screen 640 x 400 with 4 bitplanes (16 colours) or two 320 x 200 screens with 8 bitplanes (256) colours.
2) BUG fix
There was a bug in BASIC 10 inherited from the BASIC 7 of the Commodre 128:
Imagine following code snippet:
100 IF A=1 THEN PRINT "A=1": ELSE BEGIN
110 PRINT "A # 1":BEND: PRINT "SEE"
Executed on a C128 or C65 this code will print the text "SEE" only, if the ELSE branch is executed, but will be ignored, if the TRUE branch is taken. So it’s clear, that the PRINT "SEE" should be executed in any case, because it’s outside the IF-THEN-ELSE statement.
This one was easy to fix.
3) Code optimisation
Then there were so many code parts, especially in the graphics library, that were unnecessary long. I could reduce the code size of the graphics library already from 12K to 9K by using loops and tables to replace long linear and repetitive code sequences. The new won space may be used for enhancements.
4) Bank access
The C65 has an awkward memory management. It can combine many different 8K segments from ROM and RAM, but there is no easy way for a program running in a bank, to access data from a different bank. The solution for the C65 was, to use DMA transfer. So even for fetching a single byte from another bank, for example getting the next character from the BASIC source, it was necessary to build a 12 byte long list describing the desired DMA transfer, trigger the DMA controller and then reading the byte. What a tremendous effort to retrieve just one byte.
The MEGA65 has here a brilliant and easy to use solution: An address mode, that uses 4 zero page bytes to construct an effective 28 bit address, which is sufficient to access the complete 256 MB address space of the MEGA65. So you can uses instructions like:
LDA [ZP],Z STA [ZP],Z CMP [ZP],Z etc. to access any address in the MEGA65.
Replacing some of the DMA calling codes with the 32 bit indirect access already increased the execution speed of BASIC by more than 30%.
5) Hardware multiplication and division
The MEGA65 has an easy to use hardware math unit.
A mulitiplier performs a 32 x 32 bit multiplicazion in just 1 cycle.
A division unit performs a 32 / 32 bit divison in 16 cycles.
So far the multiplier is used to speed up the floating point multiplication in using the hardware to multiply the mantissas of both factors. This increased the speed by a factor of more than 2.2.
6) Convience Enhancments
Finally I made my dream from the C64 days come true and make the BASIC more convenient by implementing very small features, which I like nevertheless much.
How often did it happen, that one hits return, while the cursor is on the line with the READY. Prompt and see the ?OUT OF DATA ERROR, because the interpreter tries to read data for the variable Y. Now you can hit RETURN on the READY. line and nothing happens.
Also the DOS wedge, using the shortcuts @ / ← $ for disk command, load, store and directory is implemented now.
The line numbers can now use the full 16 bit range from 0 to 65535.
Lines can be indented with spaces, which are not swallowed by the interpreter.
And there is much more to come.
Finally it’s interesting to read in the old sources.
You find gems like [….] is an asshole (I’ll not repeat the name here).
Or: OH-OH I programmed a recursion, Dr. Ja-Ja warned me about this.
In fact, that is one of the code pieces which have to be fixed.
Nesting BEGIN … BEND compound statements is dangerous and can crash the machine.
Or the funny code, where a 16 x 16 bit multiplication is called, in order to multiply an index with 1.5, which can be done with only 4 simple instructions instead.
So far my text on working on the MEGA65 kernel and BASIC software.
The text became longer, than expected, I hope, I didn’t bore you, but if you read this sentence, you read it probably completely.