I have to recommend writing an interpreter (or a compiler and runtime system) as a great way to learn a whole bunch of practical programming. It's especially interesting if the language you implement is a mismatch for the underlying system. The Z80 is an 8/16 bit processor, and I hacked my TRS-80 to install a full 64K of RAM. I decided on a tagged pointer architecture with 8-bit tags and 16-bit pointers. This gave me a 24-bit word size.
The other unusual thing I did was with the number system. I didn't want to implement floating-point, but I also didn't want integers only, so I used fixed-point arithmetic with six digits on each side of the decimal point. I didn't want to convert from binary fractions to decimal fractions, so I did the math in BCD.
One of the weird instructions on the Z80 was one that rotated by 4-bits the contents of the A register and the contents of a memory location. I never found a use for it until I did the BCD math and then it was obvious. It rotates a single BCD digit out of a packed BCD representation in memory so you can do multi-precision adds and subtracts.
I soon ran out of memory with the assembler. There was no way to assemble separate modules, so I made a table that contained the external entry points for the module and duplicated that across the other modules. The project grew until I had seven separate modules. The development cycle was like this:
- After editing a module, save the assembly source code to a source casette. Be sure to write at least three copies.
- Compile the module to machine code. Dump the code to an object casette. Again, three copies.
- Reboot, load the debugger.
- Attempt to load all the object modules. Sometimes this would work ok, other times it would hang. If it hanged, you had to reboot and start loading again.
- Run some code. When it crashed, debug the machine code. (The source code isn't available at this point, so you have to take notes and try to figure out what was supposed to be happening.)
- Reboot and load the editor.
- Load the source code for the buggy module and try to find the bug. Once you think you have it fixed, go back to the beginning.
- If you need to add a cross-module link, you have to edit the link table. This was kept track of in a notebook and a copy was made to each module. If you changed the link table, you had to update it in all seven modules, so it was usually a good idea to update these in batches.
During these 40 minute cycles I had a lot of time to think while mindlessly shuffling cassette tapes. I eventually decided that this was largely a waste of time and was becoming less fun with every line of code I added. I also thought maybe I should get a job. I wasn't sure I could get one, but I figured that maybe someone would be willing to throw a little part-time work my way. I eventually worked up the courage to call Gerry Sussman and see if he knew of anyone that would find my summer project the least bit interesting.