Here I want to provide an example of a very very simple program for the Commodore 64 and what is needed in order to convert that program to run on the Commander X16
First the small progam that uses direct memory access to write to the screen.
*=$0BB8 ; 3000 decimal, address where program should be loaded to.
; Program writes an 'A' at top left and bottom right of screen and
; sets it's color
main:
lda #01 ; Screen code of 'A'
sta $0400 ; Write to upper left corner
sta $07E7 ; Write to lower right corner
lda #13 ; Light Green color
sta $D800 ; Top left character
lda #5 ; Green color
sta $DBE7 ; Lower right character
rts
The result should look something like this:
Here is a hexdump of the compiled program:
b8 0b a9 01 8d 00 04 8d e7 07 a9 0d 8d 00 d8 a9
05 8d e7 db 60
Let me try and clean this up a bit to figure out what all these codes mean:
b8 0b ; Address in mem where program is stored
a9 01 ; lda #01
8d 00 04 ; sta $0400
8d e7 07 ; sta $07E7
a9 0d ; lda #13
8d 00 d8 ; sta $D800
a9 05 ; lda #5
8d e7 db ; sta $DBE7
60 ; rts
Now that all the opcodes have been lined up and we have some nice comments, it is very easy to see that the hex numbers are just the small program we wrote. Remember that the 6502/6510 is little-endian which means that it writes the low byte first.
Now if the above program was run directly on the Commander X16, it would definitely not do what was intended. $400-$7FF in CX16 is just memory (called golden memory) so writing to it will not display anything on the screen. $0800-$9EFF is also just memory so writing to these addresses is not going to display anything on screen. Fortunately it is not going to break anything either unless we have other programs loaded at those addresses.
Our program is very simple, but we still need to change it in order to make it run on the Commander X16. In this example, we know that every time something is stored to an address from $400-$7FF it is the same as plotting a character to the screen and every time something is stored to an address from $D800-$DBFF it is the same as setting a foreground color for a character on screen.
Next issue is that the Commodore 64 default resolution is 40×25.
The Commander X16 default resolution is 80×60, but can be set to 40×30
For ease of use, we will just set the CX16 to run in 40×30 mode and ignore the bottom 5 lines of the screen.
In order to calculate the coordinates where characters or colorcodes are stored, we first convert our hexadecimal numbers to decimal. This time we start from the bottom:
- $DBE7 = 56295 ; address being written to
- $D800 = 55296 ; base address
Then we subtract the base address from the address we write to.
- 56295-55296 = 999
To calculate the Y coordinate, we simply divide by the number of columns and disregard anything after the comma.
- 999 / 40 = 24.975 = 24
So the Y coordinate is 24. To calculate the X coordinate, we subtract Y coordinate multiplied with columns (40)
- 999 – (24*40) = 39
So the final set of coordinates is X=39, Y=24, but we already knew that 😉
Let us try with the other set of numbers.
- $0400 = 1024
- $07E7 = 2023
- 2023-1024 = 999
- 999 / 40 = 24.975 = 24
- 999 – (24*40) = 39
In this case the character set and color values in Commander X16’s VERA are the same as the Commodote 64 so we can write the character and colors without any conversion. That means that all we really need to do is:
- Ensure that LOAD address works in Commander X16
- “Catch” all the writes that are going to Commodore 64 screen/color RAM
- Set up VERA to point to correct VRAM address
- Store the value to VERA
I will try to write some pseudo code to give an example on how the C64 program would be converted to X16.
Read the 2 first bytes of program to know where converted program should be stored
Set screen mode to 40x30
Read file in a loop
If opcode is LDA Then
Execute the opcode
Push A to stack
Endif
If opcode is STA Then
If address is between $0400 & $07FF
Calculate the screen coordinate
Write screen coordinate to VERA
Pull A from stack
Write A to VERA
Endif
If address is between $D800 & $DBFF
Calculate the screen coordinate
Write screen coordinate to VERA
Pull A from stack
Combine background color with A
Write A to VERA
Endif
Endif
If opcode is RTS Then
Execute the opcode
Endif
Until end of file is reached
Above code is extremely simplified and only takes the 3 opcodes that are actually used in our c64 test program into account (LDA, STA & RTS).
Everytime LDA is encountered, we need to store the value read to the stack as we do not yet know what will happen with the value.
Let us say that after LDA we encounter ASL, then we need to do a PLA to get the value from stack, do the ASL and then do a PHA to save the value onto the stack again.
When we encounter STA, we need to figure out where we are storing to. If it is in Screen- or color-memory, we need to calculate the screen coordinates and write them to vera.
If it is screen-memory, we can then PLA and STA to VERAs dataport.
If it is color-memory we need to PLA and combine it with the existing background color before writing it back to VERA dataport.
The generated code would look something like this:
*=$0BB8 ; 3000 decimal
;Program writes an 'A' at top left and bottom right of screen and sets it's color
main:
lda #0 ; Set screen to 40x30
jsr $FF5F
lda #00 ; Write 0 to high addr
sta $9F22
lda #00 ; Write 0 to middle addr
sta $9F21
lda #00 ; double 0
clc
asl
sta $9F20 ; Write it to low addr
lda #01 ; Screen code of 'A'
sta $9F23 ; Write upper left corner
lda #00 ; write 0 to high addr
sta $9F22
lda #24 ; write 24 to middle addr
sta $9F21
lda #39 ; double 39
clc
asl
sta $9F20 ; write it to low addr
lda #01 ; Screen code of 'A'
sta $9F23
lda #00 ; Write 0 to high addr
sta $9F22
lda #00 ; Write 0 to middle addr
sta $9F21
lda #00 ; double 0
clc
asl
inc ; increment for color addr
sta $9F20 ; Write it to low addr
lda $9F23
and #$F0
ora #13 ; Light Green color
sta $9F23
lda #00 ; write 0 to high addr
sta $9F22
lda #24 ; write 24 to middle addr
sta $9F21
lda #39 ; double 39
clc
asl
inc ; increment for color addr
sta $9F20 ; write it to low addr
lda $9F23
and #$F0
ora #5 ; Green color
sta $9F23
rts
On the Commander X16, the result would end up looking like this:
The above is not very sophisticated code and a lot could be done to improve the quality, but there are a lot of things that needs to be taken into consideration when converting from C64 to CX16. Our program only works directly with the screen- and color-ram, but programs can easily be manipulating things like KERNAL jump-vectors
It may be possible to write a program that runs on a normal PC that can convert Commodore 64 programs to Commander X16 programs, but as far as I can see it would be an almost impossible task to create a program that runs on the Commander X16 and converts Commodore 64 programs on the fly.