Commander X16 Bank switching in Assembly and Intro to x16emu debug

Bank Switching in assembly

In the Commander X16 the memory range from $a000 to $bfff, 8k of ram, will be banked. This will allow the computer to access up to 2048k (2Mb)

I am planning on creating a game using assembly and loading all of the graphics/sprites/text in the initial load and then switch banks to access them. Looking at the forum I couldn’t find any posts about bank switching and very little on using the emulator debugger. So I decided to do a guide after I figured out something useful.

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.

I did some reading of the Commander X16 Programmers Reference guide and found the reference to banked memory. The manual did direct me to the I/O Programming section but it didn’t help me much. This listed $9F61 as the byte to adjust banking 0-255. The X16 will have a minimum of 512k so that is banks 0-63. Personally, I hope they just ship all of them with 2048k, banks 64-255. One less configuration option to make it easier for the programmers.

The below code is my initial config before my main loop. I have created the string which I am going to copy into the banks. I’ve declared VIA1 at $9F61. I’ve declared a few kernal functions that I will use. I’ve declared three variables and allocated memory locations for them. bank_selection will track which memory bank is currently selected. message_position will track which character in the string I am currently accessing. message_in_bank is the memory location I will write to. Then I initialised two of the variables. The bank_selection variable is set to 1 as it appears there is some code already in 00 $a000. (I found overwriting the contents of 00a000 did strange things and I didn’t sort it out until I had worked out how to use the debugger down below). The message_position variable is set to 0 for the first character.

;-----------------------------------------------------------------------------
.segment "DATA"
; here is where you put raw data / binary / sprites / text blobs / etc
message: .asciiz "this is a string which we will put in to the different banks of $a000"

;-----------------------------------------------------------------------------
.segment "CODE"

; constants
VIA1 = $9F61

; give kernal functions names
CHROUT = $ffd2 ; CHROUT outputs a character (C64 Kernal API)
CHRIN = $FFCF ; CHRIN read from default input

; variable locations
bank_selection = $0070
message_position = $0071
message_in_bank = $A000

; variables set
lda #1
sta bank_selection
; I am starting at bank 1 because it appears there is something written in bank 0 at $a000-$a041

lda #0
sta message_position

You may have noticed in the above code there is no basic launcher code. Previous to this project I had been setting my memory start location to $0801 and including a ‘basic loader’ string. $0c,$08,$0a…. etc and then my actual assembly code starting at $0810. For this project, I had been doing a sys2061 to start it while I was working on it. One time I accidentally typed run, and it ran! That wasn’t supposed to happen! I had a look at my prg in a hex editor. Sure enough, I saw 9E, 20, 32,30,36….. It appears that ca65 and its linker create a little basic loader.

The below code is my main loop. I am checking to see which bank is currently selected and if less then 20 we will run the code. If bank 20 is currently selected will we branch out to a function which will end the program. I then set the bank selection. I put the message position counter into register x. Then I put a character from the message, register x (register x is the offset in the message string) in to register a. I do a quick check to make sure the character is not null. If it is null then we have reached the end of the string and we branch out to a function which will change banks and reset counters. Then we store the current character into message_in_bank variable with the same offset. Then we increment the message_position so next time it will read the next character. Then we loop back to the beginning and go again.

;-----------------------------------------------------------------------------
.proc main
    lda bank_selection
    cmp #20 ;lets only do x banks
    bcs wait_user ; if it equals x then quit
    
    ; set RAM bank
    lda bank_selection
    sta VIA1

    ; get location of next character from our screen message
    ldx message_position
    lda message,x
    cmp #0 ; CoMPare accumulator with value 0 which is null terminator
    beq update_counter 
    ; output it to bank
    sta message_in_bank,x
    ; increase out message position
    inc message_position

    ; loop back to start
    jmp main
.endproc

The below code is my two support functions. update_counter resets the message_position variable back to 0 so the main loop will start to read from the first character again. I also increase the bank selection so I can write to the next memory bank. Then I jump back to the main loop. wait_user just prints out a single character to the screen then waits for the user to press return/enter then it ends.

.proc update_counter
    ; lets go back to the first character in message
    lda #0
    sta message_position
    ; increase the next bank
    inc bank_selection
    jmp main ; lets jmp back to the mail loop with updated counters
.endproc

.proc wait_user
    ; send a * out to the screen so we know its finish
    lda #42
    jsr CHROUT
    jsr	CHRIN ; Read input until Enter/Return is pressed
    rts ; Return to caller
.endproc

Intro to x16emu debug

When writing up this code I realised I wasn’t going to get any feedback if it was successful. I needed a way to see what was happening in memory. I was getting strange problems. In the past, I’ve had debuggers, breakpoints and been able to step each line of my code etc. I didn’t know how I was going to do that with X16emu. I did find a post on the forum by codewar65 which reminded me to read the actual documentation. In the x16emu readme.md there was a section on the debugger! I changed my visual studio code run task to include the “-debug” and started it up. Pressing F12 to break into the debugger.

x16emu -debug , Pressing F12 to start the debugger

Now to learn how to use it better! I started to use F10 to try and step ‘over’ routines and F11 to step over each instruction being executed. Happy! Then I wanted to see what was in memory. codewar65 had mentioned that page up and page down would let you browse memory. So I paged down to A000 and noticed it had FF:A000.

x16emu -debug, Showing $A000 bank FF.

I suspected that FF:A000 meant I was looking at bank FF. This proved frustrating until I was re-reading readme.md for x16emu. I could type m %x were %x was the address. Originally my code started at bank 00. I tried it m 00a000. There was some code there already, not the blank 00 00 00 I expected. Then I stepped each instruction and I would get strange results. So I quickly decided to avoid 00a000 and changed my starting bank to 1. Compiled, ran and moved to 01a000 and there it was, the string had been written to memory.

x16emu -debug. Showing $A000 bank 1.

I then looked at a few different banks. 02:A000, 03:A000… 13:A000 my string was in all of them. 14:A000 my string wasn’t which made sense. I had stopped the main loop when the bank was decimal 20 which is hex 14.

With the free plan on WordPress, I can’t upload text files or zip files. So I’ve turned it into a pdf for people who want all of the code in a single document to copy and paste from.

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

3 thoughts on “Commander X16 Bank switching in Assembly and Intro to x16emu debug

  1. Niedowidek

    Great post. Do you use any add-ons to Visual Studio that help working with assembly language? I tried VS once, didn’t like it, but I heard experience is better with well chosen plugins.

    Like

    Reply
    1. justinbaldock Post author

      Hi Niedowidek, I am currently only using the ca65 Macro Assembler Language Support plug-in which works ok for me. Would I like a more integrated experience for the CX-16, Yes, but this is working for me.

      Like

      Reply

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