Montag, 17. November 2008

Back to the roots: 6510 assembler, the hard way.


Today i did something really useless and oldschool which gave me such great pleasure that i decided to share it with you. 
As most of you don't know yet, i recently had a flash of nostalgia in which i bought a Commodore C64 on ebay. Originally intended to play some of the great old games as they were meant to be played (i.e. with long loading times and on a big screen TV) i found myself entering the only true first program there can ever be:

10 PRINT "HELLO, WORLD";
20 GOTO 10

(Hint: If you're not from the 80s, just grab one of the available C64 emulators and try it out if you can't guess what it does).

Giggeling about my achievement i decided to take this one step further: Away from BASIC (there, i said it) towards one of the manliest computer languages ever invented: Machine code. Fortunatly a 600 page programming book was included in my C64 package wich has a chapter on assembler programming and even a table with all the mnemoics of the 6510.

It took me a little time to remember, that there isn't actually a 'real' editor as taken for granted on todays computers inlcuded in the C64 OS. What you get is a BASIC interpreter wich is also used for typical OS tasks. On the plus side the boot time is only about 2 seconds, try that with your OS...

So what basically needs to be done is write a basic program that puts the machine code into its place in memory and call it. That shouldn't be too hard if only the machine code was already there.

What program to write first after all that time? I decided not to be too ambicious and do something really, really basic. In this case: Changing the background color of the screen. This has two advantages:
  1. It's really easy to do. You have just to put one byte into the right place.
  2. You get immediate feedback if your programm works correct.
 From the programming book i learned that to make white the background color you have to put a 1 into memory location $d021 hexadecimal (that's 53281 decimal). In BASIC you would do this with the command

POKE 53281,1

To do the same in machine code you have to do 2 step:
  1. Load the value '1' into one of the CPU's registers.
  2. Store that register into the given memory location.
Well, i wanted to do it the hard way, but not the nuts way, so i wrote the program in 6510 assembler first. Here it is:

LDA #$01
STA $D021
RTS

As you might have expected the first command loads a 1 into the CPU's A register (the so called Akkumulator) and the second one stores A's value to memory location $D021. The last line returns from the program which is the sane thing to do since you don't know what comes in the next memory location and it might very well crash your C64 which is not so good if you haven't saved your code yet. 

This was easy. However, in this form it can't be put into memory. It has to be machine code, read: Bits and bytes. Fortunatly the this was easy to translate with with a mnemonic table from the book: LDA equals $A9, STA equals $8D and RTS equals $60. I remembered that the 6510 stores 16 bit adresses with the lower byte first, so the address $d021 becomes $21 $d0 in memory. Put all that together gives me the machine code:






$A9 $01LDA #$01
$8D $21 $D0STA $D021
60RTS

Almost done. Only one tiny problem still: To use the POKE command those byte values have to be in decimal form. I did not get this far to let this task be done by a pocket calculator or worse! 
Here's how it goes: Take the upper half of the byte (first character) and multiply it by 16 and add the value of the lower half (second character) where A equla 10, B equals 11 etc.
Why does it work? Because the lower half represent 4 bits and with those you can represent the 16 values from 0 to 15. So the upper half gives you, how many times you have the 'full' 16 values and the lower half gives you the rest.








$A9=10 * 16 + 9=169
$01=0 * 16 + 1=1
$8d=8 * 16 + 13=141
$21=2 * 16 + 1=33
$D0=13 * 16 + 0=208
$60=6 * 16 + 0=96

There it is, our machine code sequence! The only thing left is, to poke the values into the memory and execute them. This will be done be a very easy and dumb (yes, i already heard about loops, don't tell me) BASIC program:

10 POKE 3072, 169
20 POKE 3073, 1
30 POKE 3074, 141
40 POKE 3075, 33
50 POKE 3076, 208
60 POKE 3077, 96
70 SYS 3072

That's it. The last line calls the machine code routine we poked into the memory starting at memory address 3072. Ready to go...

Wait! It's always a good idea to save anything machine code related first. Actually save any work you do regularly. But i'm sure i don't have to tell this to a PC user...

SAVE "MYASM",8

So, now we're ready to go.

RUN