Commodore 64 to Commander X16 converter | Dansbo Tech Blog

Tech Ramblings

Commodore 64 to Commander X16 converter

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.


Post a Comment

Your email address will not be published.

Time limit is exhausted. Please reload CAPTCHA.