Commander X16 Introduction to graphic put char function using assembly.

The Commander X16 has a nice graphic library to help you get started. At the time of writing the Programmers Reference Guide is still early in its development and I found it a little difficult. 

It took me several days to get the graph_put_char function to put anything on the screen so I thought I should write a guide to help others.

The code listed in this guide was created for the ca65 cross-compiler. I used Visual Studio Code as my text editor with git version control. For use on r36 of the X16 emulator.

Initially, I spent time reading the Graphics section of the Programmers Reference Guide. It all seemed straight forward but I wasn’t getting visual results. I kept on coming back to it and then posted for some help on the FB page and the forum. With a bit of help seeing working code I was able to figure it out.

Before we go much further I just want to show the X16 screen co-ordinate method. The top left-hand corner is 0,0. The odd point which caught me out is for text, the anchor point is the bottom left-hand corner. So if you position a character at y=0 the text will be off-screen.

X16 Screen co-ordinates and character anchor point

The below code is for configuring variables and setting everything up. I created a zero page variable which is going to point to the string variable that my function will then display to the screen. I have created a ‘kernal.inc’ file which has several system constants to make the code easier to read. I then created 3 string variables with some test string data.

;-----------------------------------------------------------------------------
.segment "ZEROPAGE"
; here is where you put any zero page variables.
; These are all items located in the $0000-$00ff memory space
; On the X-16 Users c can use $0000-$007F. Kernal and Basic zero page is $0080-$00FF
pointerToString = $22
pointerToStringLow = $22
pointerToStringHigh = $23
;-----------------------------------------------------------------------------
.segment "STARTUP" ; This segment contains the startup code which initializes the C software stack and the libraries
.include "kernal.inc"
;-----------------------------------------------------------------------------
.segment "DATA"
; here is where you put raw data / binary / sprites / text blobs / etc
; variables
; strings
textString1: .asciiz "this is string 1"
textString2: .asciiz "this is string 2"
textString3: .asciiz "this is string 3"

In the below code I create part of my main function. To make my code easier to read I created the kernal.inc file. This file has several constants declared. Eg SCREEN_MODE_320x200x256C = $80, or COLOUR_RED = $02. This hopefully makes my code easier to read. In my kernal.inc file I also added constants for all of the kernal functions. Instead of looking up the jump addresses each time I can use the kernal function names to call them. Eg SCREEN_SET_MODE = $FF5F. The first step in my main function was setting the screen mode to the 320×200 graphics mode. Eventually, all of the kernal graphics functions will respect the window clipping setting, currently, only 2 functions use it, graph_put_char and graph_clear. Looking at the documentation for GRAPH_SET_WINDOW we can see that r0 will be the origin x co-ordinate and r1 will be the origin y coordinate. I set them both to zero. I had been experiencing strange results and I think that was because of non-zero/high values in the r0H and r1H. So I now make sure to zero them fully. We also see that r2 will be the width and r3 will be the height of the clipping window. I set those to 319 and 199 respectfully. Then I jumped to the subroutine. Now I wanted to set the graphic function colours. GRAPH_SET_COLORS takes 3 parameters. .a is the stroke colour, .x is the fill colour and .y is the background colour. For this example, I am going to use red colour for the pen/stroke colour. 

;-----------------------------------------------------------------------------
.segment "CODE"
jmp main
.proc main
    ; set the screen mode using a kernal function
    lda #SCREEN_MODE_320x200x256C
    jsr SCREEN_SET_MODE; set the graphic window size
    ; set r0(start-x) and r1(start-y) to zero
    stz r0L ; STore Zero in to location
    stz r0H
    stz r1L
    stz r1H
    ; set r2 (width)
    lda #<319
    sta r2L
    lda #>319
    sta r2H
    ; set r3 (height)
    lda #<199
    sta r3L
    lda #>199
    sta r3H
    ; use the kernal function
    jsr GRAPH_SET_WINDOW
    ; set the graphic colours
    lda #COLOUR_RED
    ldx #COLOUR_PURPLE
    ldy #COLOUR_BLUE
    ; use the kernal function
    jsr GRAPH_SET_COLORS

The basic configuration has been done, now time to set which string we want to display and where. I created a local label @drawString1: to help me break up the main function and make it easier to read. Once again r0 and r1 will be our x and y starting co-ordinates. Then I needed to figure out how to be able to change which string will be displayed to screen. I had used pointers in C before so I thought it should be simple enough. And it is. I load the low address byte into .a and then store it in my pointer low variable. Then load the high address byte into .a and store it in my pointer high variable.

@drawString1:
    ; set start x/y position of text
    lda #7 ; 8 pixels for both x and y, remember we count from 0, 0-7
    sta r0L ; STore Accumulator to location
    sta r1L
    stz r1H ; STore Zero in to location
    stz r2H; point to the string
    lda #<textString1 ; get the low address byte from memory 
    sta pointerToStringLow ; 
    lda #>textString1 ; get the high address byte from memory
    sta pointerToStringHigh
    ; now call my function to display string
    jsr write_text

My write_text function is below. I wrote a short description so I know what it does. I’m slowly learning assembly and I’ve noticed a few times my registers values changing. I figured I would try and prevent my code from stomping on registers. In this code, we can see I’ve pushed the .y on to the stack. Once I’ve stored .y I reset it to 0 as I will use it as a counter. I then store it in variable r11. I create a local label @loop: and the code in here will be used to read in the string and exit the loop when all of the string has been read. Looking at GRAPH_PUT_CHAR documentation we can see that r0 and r1 are our x and y coordinates. We also see that .a is our single-byte character. I did have a problem for a while with only the first character being displayed. It was only when I used the debugger and watched all the registers that I noticed that .y when from being 00 to A0 and I knew that wasn’t right. So to me, it appears that GRAPH_PUT_CHAR changes the value of .y. So I put some extra code to load .y from r11, increase it, resave it to r11 then jump back to the start of the loop. Once the loop has finished I pull the original value of .y off the stack and return to the main function.

.proc write_text
    ; requires r0 and r1 set for x and y
    ; requires pointer be set to point to string to write to graphics
    ; will preserve .y
    phy ; PusH Y on to stack
    ; set counter
    ldy #0
    sty r11
@loop:
    ldy r11 
    lda (pointerToString),y ; go to pointed to memory address and read offset .y in to .a
    beq @end ; Branch if EQual, if zero flag is set then branch to @end
    jsr GRAPH_PUT_CHAR ; looks just graph_put_char stomps on y
    ; load the .y counter, increase it and store it
    ldy r11
    iny
    sty r11
    jmp @loop
@end:
    ply ; PuLl Y back off stack
    rts ; ReTurn from Subroutine
.endproc

I then finished off the main function with a little local label @loop: and jmp @loop to create an infinite loop.

@loop:
    jmp @loop
.endproc

To download the source files for this guide visit my Github repository.

I would encourage any feedback or comments to improve this guide.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s