C64 Development: Getting Started

Hello World
My brother gave me a refurbished C64 for christmas. I wanted to try out writing some assembly code for it. I have previously played around with C64 assembly, but only on an emulator.

The C64 has a MOS Technology 6510 processor that runs at 1 MHz. It is a modified version of the popular MOS 6502. The Ricoh 2A03 in the NES is also based on a 6502.

One of the simplest C64 programs flashes the background: loop:	inc $d020 ; increment BG color jmp loop A few lines can be added to check for the space key, to be able to stop the program: loop:	inc $d020 ; increment BG color

lda #$7f ; check for space pressed sta $dc00 lda $dc01 and #$10 bne loop This is all great, but I have one problem: I don't have an assembler for my C64, so how do I actually run this?

C64 Development on PC
An easy way to develop for C64 is writing the code in a your favorite editor, and using DASM to cross assemble. Add these lines at the top of the program: processor 6502 org      $1000 ; program base address And assemble: dasm tiny.asm -otiny.prg And run it with the WinVICE emulator: WinVICE\x64 tiny.prg VICE loads the program automatically, and we only need to jump to the memory location $1000 from BASIC: SYS 4096 It could not be easier. For fast development, this is clearly superior to working on the actual hardware. But of course, there's nothing like the real thing, and I want to run this on my real C64. Here is where it gets a bit tricky. 

Embedding Machine Code in BASIC
Here is some great advice from the past, which I'm going to completely ignore:

''If you wish to write machine language programs, it is strongly suggested that you purchase an assembler of some sort. Without an assembler you will probably have to "POKE" the machine language program into memory, which is totally unadvisable.''

- Commodore 64 Programmer's Reference Guide, Copyright (C) 1982 by Commodore Business Machines, Inc. All rights reserved.

Idea: Poking Machine Code to Memory
If you want to transfer an assembled program to a physical C64, one option is to POKE it directly into memory and running it from there. For example, the following assembly code: inc $d020 is assembled to machine code, listed in hexadecimal: ee 20  d0 or in decimal: 238 32 208 We can POKE this directly into memory from BASIC: poke 4096,238 poke 4097,32 poke 4098,208 and then jump directly to address 4096 to execute it: sys 4096

Using this approach, we can now formulate a plan for running assembly code from BASIC:
 * 1) write assembly code program on PC
 * 2) assemble to machine code using DASM
 * 3) list machine code as a list of decimal integers
 * 4) write a BASIC program that POKEs the integers into memory
 * 5) run the machine code from memory

General BASIC Poking Program
Machine code can be embedded as data in a BASIC program that POKEs it into memory and calls SYS to run it. The following program has the machine code version of the above program embedded as data, and POKEs it into address 4096: 10 bs=4096 20 read b 30 if b=-1 then sys(4096) 40 poke bs,b 50 bs=bs+1 60 goto 20 100 data 238,32,208 9999 data -1 The program repeatedly copies the embedded data (in this case, our three numbers) to memory starting from address 4096. The data ends when the value -1 is reached, after which the program jumps directly to address 4096 to execute the machine code. The cool thing about it, though, is that it can be used with any machine code program.

Let's Try It Out!
Let's try converting this assembler program to BASIC: loop:	inc $d020 ; increment BG color

lda #$7f ; check for space pressed sta $dc00 lda $dc01 and #$10 bne loop If we save the above listed program as test.asm, we can assemble it with DASM: dasm test.asm -otest.prg The output is 17 bytes of machine code: hexdump -C test.prg 00000000 00 10 ee 20 d0 a9 7f 8d  00 dc ad 01 dc 29 10 d0 00000010  f1 If we rearrange it, it's a bit more recognizable. Note that all addresses are Little Endian, meaning that the least significant byte appears before the most significant byte: 00 10     ; Location header ($1000, little endian) ee 20 d0  ; inc $d020 - increment at address $d020 (flash background) a9 7f     ; lda #$7f  - load accumulator (immediate addressing) 8d 00 dc  ; sta $dc00 - store accumulator in address $dc00 ad 01 dc  ; lda $dc01 - load accumulator (the opcode is different for absolute addressing) 29 10     ; and #$10  - and accumulator with $10 d0 f1     ; bne PC - 15 (8 bit representation of -15 is 0xF1) This ruby program outputs the bytes in decimal form: File.binread("test.prg").bytes.each{ |b| print " %d" % b} ; puts Output: 0 16 238 32 208 169 127 141 0 220 173 1 220 41 16 208 241 This can be embedded as data in BASIC. We skip the two bytes of location header, as the location is implied by the BASIC program: 100 data 238,32,208,169,127,141,0,220 110 data 173,1,220,41,16,208,241 So the full BASIC program with our embedded machine code becomes: 10 bs=4096 20 read b 30 if b=-1 then sys(4096) 40 poke bs,b 50 bs=bs+1 60 goto 20 100 data 238,32,208,169,127,141,0,220 110 data 173,1,220,41,16,208,241 9999 data -1 When the program is run, we get a sweet flashing background! Yay!

Auto Generated BASIC with Embedded Machine Code
I wrote a little Ruby script to automatically generate the BASIC program with an embedded machine code program: ruby prg2bas.rb test.prg >test.bas Here is the Ruby source: d = File.binread(ARGV[0],10000,2).bytes s = "10 bs=4096 20 read b 30 if b=-1 then sys(4096) 40 poke bs,b 50 bs=bs+1 60 goto 20" x = 0 d.each { |b| if (x % 8) == 0 s += "\n#{x/8*10+100} data " else s += "," end s += "%d" % b   x+=1 } puts s+"\n9999 data -1"