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.