Thursday, 3 November 2016

uxForth: Unexpanded Forth For A Standard VIC-20

I'm the developer of the DIY 8-bit computer FIGnition, but it doesn't mean I'm not interested in other retro computers and as far as it goes, the Commodore VIC-20 is one of the cutest to come out of the 1980 stables.

The VIC-20 was cute, because it had a combination of fun and dumb features. Like: a full quality 65 key keyboard - and only two cursor keys!

Or the ability to support business applications with a floppy disk drive, but only having 23 column text. Or multi-colour graphics (and even a 2-bit per pixel mode that can co-exist with a 1-bit per pixel mode) with a near complete lack of support for bitmapped graphics. Or it's 16Kb of ROM and only 5Kb of RAM (with just 3582bytes free when Basic boots).

So, the fun challenge here is to see how much of a Forth I can squeeze into the Basic, unexpanded VIC-20 given the RAM limitations. I'm pretty confident I can do this, given that a super-tiny Forth subset has been crammed into just 1Kb of 8086 code (itsy Forth). I'm aiming for something that's kinda more usable.

Dude, Where's My RAM?

The first step (and this is the topic of this blog) is to find out how much RAM we can really use. A VIC-20 boots up and proudly displays: 

But it actually has 5Kb, so where has that other 1.5Kb gone? Armed with a detailed VIC-20 memory map we can see that areas of the first 1Kb have been nicked by Basic and the Kernal, which is a set of OS services abstracted from Basic and forms part of the ROM. For our purposes we don't want to use Basic, but we do want to use the Kernal, so we can read the keyboard, display to the screen and input/output between peripherals. For some of this 1Kb it's obvious which is used by Basic, but not all. So, here I decided to use the VIC-20 ROM disassembly. I first worked out that the Kernal starts at the address $E475, or thereabouts by observing that the rest of that code doesn't reference Basic. So, then I looked up all the system variables used by that section of code and found this set of addresses:

01,X 0100+1,X 0200,X 0259,Y 0263,Y 026D,Y 0277-1,X 0277
0277+1,X 0281 0282 0283 0284 0285 0286 0287
0288 0289 028A 028B 028C 028D 028E 028F
0290 0291 0292 0293 0293,Y 0294 0297 0298
0299 029A 029B 029C 029D 029E 029F 02A0
0300,X 0314 0314,Y 0315
(EABF) 0314 IRQ vector
(FED2) 0316 BRK vector
(FEAD) 0318 NMI vector
(F40A) 031A open a logical file
(F34A) 031C close a specified logical file
(F2C7) 031E open channel for input
(F309) 0320 open channel for output
(F3F3) 0322 close input and output channels
(F20E) 0324 input character from channel
(F27A) 0326 output character to channel
(F770) 0328 scan stop key
(F1F5) 032A get character from keyboard queue
(F3EF) 032C close all channels and files
(FED2) 032E user function
(F549) 0330 load
(F685) 0332 save


I also searched the Kernal code to find references to addresses within the Basic part of the ROM and found none, which meant that Basic sits properly on top of the Kernal. So, this tells us what areas of RAM we can use and it's as follows:

Address Range Size Owner Available for Forth?
$000 .. $08F $090 BASIC Yes, it's Page 0 how could we avoid it :-) ?
$090 .. $0FA KERNAL No.
$0FB .. $0FE $004 Nothing Yes, not sure yet what for.
$0100..$013E KERNAL Unlikely (tape error log/correction buffer)
$013F..$01FF $0C1 CPU Yes, for the return stack
$0200..$0259 $05A BASIC Yes, for Forth code
$025A..$029F KERNAL No.
$02A0..$02FF $060 None Free, more Forth code.
$0300..$0313 $013 BASIC Yes.
$0314..$03FF KERNAL No.
$033C..$03FB KERNAL Cassette buffer, maybe, but limited usage.
$03FC..$03FF $004 Free Some Vars?
$1000..$1DFF $E00 BASIC Forth code
$1E00..$1FF9 $1FA VIC (Screen)
$1FFA..$1FFF $006 Free 6b, more vars?

This gives us a total of $F6B (3947) bytes, or 4453 bytes if we can use the screen, or 4140 (4646) bytes if we include the CPU stack, which of course we will.

In the next part we'll make some basic decisions about the uxForth model, this will help us decide how to use all these areas.