Introduction

Looks complicated? Read on!

Note

All the code and resource files in this post are available on my GitHub page, because it’s fun to share.

I want to delve more into the Commodore 128. It’s such a nice system. Some of the new and advanced features are not easy to program though. So I decided to create a system, a framework if you will, of re-usable parts once I had figured out the basics.

Macros

I use Kick Assembler, Sublime Text and VICE to develop on my PC.

Using more of Kick Assemblers power was on my to-do list and this is a start. I decided to create easy to use macros that will generate specific object code, or make it easy to insert reusable subroutines into object code.

Setup

First thing to do was to modify the default basic startup macro to use $1c01, not $0801:

.macro BasicUpstart128(address) {  
    .pc = $1c01 "C128 Basic"  
    .word upstartEnd  // link address  
    .word 10          // line num  
    .byte $9e         // sys  
    .text toIntString(address)  
    .byte 0  
upstartEnd:  
    .word 0           // empty link signals the end of the program  
    .pc = $1c0e "Basic End"  
}  

Then, some handy setup macros. For example:

.macro Go80 () {  
	lda MODE  // are we in 80 columns mode?  
	bmi !+  // bit 7 set? then yes  
	jsr SWAPPER // swap mode to 80 columns  
!:  
}

Isn’t that nice? Entering Go80() beats remembering and typing in that code. I can now also use this macro DoEscapeCode('X') which will also switch between 80 and 40 column mode. This is the macro code for that:

.macro DoEscapeCode (code) {  
	lda #code  
	jsr JESCAPE  
}  

And it allows easy access to the other escape codes as well.

VDC

These are one-off calls. They accept parameters and will therefore generate specific code for specific situations, mostly during setup, and they can be added into the code when required. Some code is better added as subroutines though, for example: writing to a VDC register:

.macro WriteVDC () {  
	stx VDCADR  
!:  bit VDCADR  
	bpl !-  
	sta VDCDAT  
}  

This can be added as a subroutine to object code like so:

WRITE_VDC:  
:WriteVDC()  
rts

It can then be called multiple times, like this:

	ldx #26        // modify register 26, colour.  
	lda #010011    // left nybble is BG, right is FG  
	jsr WRITE_VDC  

This will change the background and character colour on the 80 columns display.

Memory Management (Unit)

One of the first things to tackle when using the C128 is the use of the MMU. Especially when mixing Basic and machine code.

Check out this memory map overview. We will get back to it later:

The Commodore 128 memory map. Fun.

This can be quite confusing, so I created a macro which will make selecting the proper bank configuration easier, as the values as used in the BANK command can be used, and some custom configuration when I do so choose to add them:

.macro SetBank(id) {  
.if(id==0) {
	lda #111111  // no roms, RAM 0  
}  
.if(id==1) {  
	lda #%01111111  // no roms, RAM 1  
}  
.if(id==12) {  
	lda #000110  // int.func. ROM, Kernal, IO, RAM 0  
}  
.if(id==14) {  
	lda #000001  // all roms, char ROM, RAM 0  
}  
.if(id==15) {  
	lda #000000  // all roms, RAM 0. default.  
}  
.if(id==99) {  
	lda #001110  // IO, kernal, RAM0. No basic  
}  
	sta MMUCR  
}

So, adding SetBank(15) to my source code will enable the default bank configuration.

First Test

This is the code for a first test I’ve made. It sets up some default stuff, and it then fills the 80 column display with a character, and changes the colour of the display.

#import "c128system.asm"  
#import "c128macros.asm"  
  
:BasicUpstart128(MAIN)  // I like to use : to indicate a macro call.  

MAIN:  
		:SetBank(15)
		:Go80()  
  
		ldx #18                       // register 18 = update address hi  
		lda #$00  
		jsr WRITE_VDC  // write 0  
		inx                           // register 19 = update address lo  
		jsr WRITE_VDC  // write 0  
  
		ldy #0                        // byte counter  
		lda #8                        // page counter  
		sta $fa  

// we write to the data register  
// data will be passed to address defined by 18-19  

		ldx #31
		lda #$66  // we write this char to screen memory  
!:  
		jsr WRITE_VDC  
		iny  
		bne !-                   // byte loop  
		dec $fa  
		bne !-                   // page loop  
  
		ldx #26                  // modify register 26, colour.  
		lda #010011              // left nybble is BG, right is FG  
		jsr WRITE_VDC  
		rts  
  
// assemble sub routines.  
  
WRITE_VDC:  
		:WriteVDC()  
		rts  

READ_VDC:  
		:ReadVDC()  
rts  

That’s it for now. I will keep trying new things and post about them.  At this time, I am not sure what I want to program on the C128, but I have a few ideas…

Go on to Commodore 128 assembly - Part 2.