Saturday, July 11, 2015

Achievements Unlocked

This has been a fun week of Forth! And today was particularly fruitful.

In the quest to make a native Forth interpreter for the Commodore 128, I've been reviewing quite a bit of documentation from a variety of angles:

Understanding Forth Internals

Coming to grips with how a system works is often like peeling back the layers of an onion. You just keep going until you reach the core, and then you can see how the whole thing is built. Forth is very much like an onion - it is a layering of words made of other words, working back towards a Forth core where the atomic pieces of the language are defined for the platform.

The Systems Guide to Fig Forth by C.H. Ting is a wonderful trip down the Forth rabbit hole. In this document, you learn how Fig Forth (a very popular early Forth) is constructed, from the wires on up. Looking at the word definitions has proven quite fruitful for my own comprehension.

DurexForth is an open-source implementation of Forth for the Commodore 64. The C64 isn't all THAT different than a C128, so checking out how DurexForth works has been quite enlightening. DurexForth differs from Fig Forth in an important respect - Fig Forth uses a word structure called indirect threading, while DurexForth uses direct threading.

Direct threading adds less layers of abstraction to the process when one word calls another word. For more information on this, check out another very helpful resource, the "Moving Forth" series of articles by Bradford J. Rodriguez. Part 1 of this series explains how the threading models work, and has diagrams to drive the points home.

Commodore 128 Assembly, Disk Access, and Memory Mapping

In a previous tweet, I'd indicated I had picked my development tool chain for this effort. One of Murphy's Laws of Combat states "No plan survives the first contact intact". Thus, I've already changed the tools I've using. For an editor, I'm going with one of my favorites, Notepad++. There is a Notepad++ add-on for KickAssembler that gives you syntax highlighting and easy invocation of the assembler. It's pretty slick.

The Commodore 128 has a lot of memory. It's just a bit tricky to use it effectively. For the Forth interpreter, I'd like to have BASIC out of the way, but keep other things like the Kernel and the I/O available. You can do this - it's just necessary to set the memory configuration register ($FF00) with the right value - in my case, $0E. Mapping the Commodore 128 and the Commodore 128 Programmer's Reference Guide are good references for how the RAM mapping works.

Also, I figured out how to do disk access from assembly - in particular, I wanted to load a disk file into RAM at a specified location. Doing this requires you to call several Kernel routines in order:

  • SETBNK tells the C128 what bank of RAM you want to load the file into, and which bank of RAM has the filename.
  • SETLFS tells the C128 the logical file number you want to use, the device you want to use (usually #8 for the disk drive) and whether you want to load the file to a user-specified RAM address.
  • SETNAM give the C128 the filename of the file you want to act on.
  • LOAD loads the file into RAM.
For all these Kernal calls, you need to set the 8502 registers (A, X, Y) ahead of time as parameters. Doing all this, I loaded a file into my selected area of RAM, then went into the Commodore 128 Monitor (a helpful utility built into ROM for doing assembly language stuff) to make sure it was there. It was!

Here's the source code for the program I wrote, using KickAssembler syntax:

// This program loads a copy of itself
// into RAM bank 0 at location $3000.

// Set some constants for kernel calls and memory locations

.const PRIMM = $FF7D
.const SETBNK = $FF68
.const SETLFS = $FFBA
.const SETNAM = $FFBD
.const LOAD = $FFD5
.const RAM_CONF = $FF00

// This section sets up a one-line BASIC program
// in the C128 with the line:
// 10 SYS 7184
// to call the assembly program that starts
// at $1C10 (7184 decimal).

.pc = $1C01 "Basic Loader"
.byte $0C, $1C, $0A, $00, $9E, $20, $37, $31, $38, $34
.byte $00, $00, $00


.pc = $1C10 "Assembly Program"

// Set memory configuration register for RAM at
// $1C10 to $C000, approximately 41K
// Bit 0, I/O range, = 0
// Bit 1, Lower BASIC ROM, = 1
// Bits 2 and 3, Upper BASIC ROM, = 1 1
// Bits 4 and 5, Other ROM, = 0 0
// Bits 6 and 7, RAM block, = 0 0
// TOTAL BYTE = 00001110 = $0E

lda #$0E

// Load a file into memory

lda #$00 // Load file to RAM bank 0
tax // File name RAM bank 0
lda #$01 // Logical file number 1
ldx #$08 // Device #8 (disk drive)
ldy #$00 // Use address as specified in LOAD
lda #$09 // Filename length is 9
ldx #<FILENAME // Low byte of filename address
ldy #>FILENAME // High byte of filename address
lda #$00 // Set function to LOAD
ldx #$00 // Load file contents to
ldy #$30 // address $3000
jsr LOAD
bcc DONE
.text "DISK ERROR."
.byte 0


// Set memory configuration register
// for normal BASIC operation
// Bit 0, I/O range, = 0
// Bit 1, Lower BASIC ROM, = 0
// Bits 2 and 3, Upper BASIC ROM, = 0 0
// Bits 4 and 5, Other ROM, = 0 0
// Bits 6 and 7, RAM block, = 0 0
// TOTAL BYTE = 00000000 = $00

lda #$00

rts // Return to BASIC


.pc = $2000 "Data area"
.text "TEST1.PRG"

The VICE emulator has been very handy for all this.

More next time! Go Forth young man...

No comments:

Post a Comment