Starting on Commodore-64, generally called C64 (MOS 6510 that is a modified version of 6502, will use 65xx here) assembly on Linux. This 8-bit microprocessor is relatively limited and simple, as Z80 microprocessor, and used on several popular platforms, including Commodore PET/VIC20, Apple][, BBC, Oric 1/Atmos/Telestrat, Atari 400/800/5200/XL/Lynx, NES and SNES or PC Engine/TurboGraphix. The power of the most interesing of these computers are in association with their audio, graphics and video co-processors.
This document, is based on the usage of generaly available and FLOSS, xa assembler, xda65 debugger and VICE emulator, on GNU/Linux platforms.
ToC
Tools
65xx assemblers
- XA/XA65
- CC65 also contains a C compiler (with inline assembler) and linker and some other tools
- DASM assembler (documentation)
- SIDdump (documentation) for dumping and analysis SID (audio chip) programs
- asm6, and asm6f, a fork and modified version for NES/Famicom. The last one is used by 6502 assembly programmable minicube64 fantasy console. faasm6f manage some undocumented 65xx instructions (see the README.md) and the Examples, doc & others section below.
Multi-architecture assemblers
- crasm (github) 6800/6801/6803/6502/65C02/Z80
- naken asm (Documentation) assembler/disassembler/simulator for MSP430, dsPIC, ARM, MIPS, 65xx, 68000, 8051/8052, Atmel AVR8, RISC-V, Xtensa, etc, and WebAssembly
- wla_dx (Wzonka-Lad Assembler Deluxe) GB-Z80/Z80/Z80N/6502/65C02/65CE02/65816/68000/6800/6801/6809/8008/8080/HUC6280/SPC-700/SuperFX
Emulators
Examples, doc & others
- The "NMOS 6510 Unintended Opcodes - No More Secrets" book, all the opcodes including undocumented onees for C64's MOS6510 and C128's CSG8502, See also this short references and explanations, that explain shortly with examples some limitations and bugs.
- CodeBase64.org, Wiki source of articles on Commodore 64 programming
- CC65-contrib
- 64-wiki.com
- 6502.org great reference site.
- Easy6502 is an interactive tutorial and tool to learn 6502 assembly based on 6502js. Local copy without malicious site (like Google) anayltics and local copy of 6502js alone, also degooglized.
- Dan's 8 bit Commodore info magazines, etc
- c64gameframework
- sizecoding.org page on 6502
Music trackers
Graphics tools
- (online) Petscii converter, and it's source code for self-hosting
- (online Petsciiator, and it's source code for self-hosting (java)
Reference documentation
Most was found on https://commodore.bombjack.org/
C64 Programmer's Reference Guide, contains BASIC language, graphics, audio, and 65xx assembly programming courses chapters and various mandatory references. These are books published by no more existing company, CBM (Commodore Business Machines) in 1980s, and given with their computers, considered as abandonware.
- English C64 Programmer's Reference Guide, local copy
- Français C64 Programmer's Reference Guide_(FR), local copy
- Pure text (no graphics) English version C64 Programmer's Reference Guide (TXT Version), local copy
- Español C64 Programmers Reference Guide_(SP), local copy
Very useful, everydays references resources on this book. Text edition has now roman digits introduction part and no pictures, so pages search by number in PDF use not the same index.
- "MCS6510 MICROPROCESSOR INSTRUCTION SET - ALPHABETIC SEQUENCE", page 232 (252 in text edition)
- Followed, starting page 234, by an Instruction references, with detail for each one like number of bytes used, flags set and Number of clock cycles for each instruction
- "COMMODORE 64 MEMORY MAP", page 310 (330 in text edition), to access differents to different parts of the system, including graphics, video and audio chips, RAM, Kernal, I/O ports etc.
- "USER CALLABLE KERNAL ROUTINES", page 272 (292 in text edition). Contains a short reference table of the OS "Kernal" available functions, followed by detailed documentation of each functions.
- All the appendices, including APPENDIX C, ''ASCII AND CHR$ CODES'', page 379 (389 in text edition)
Other documentations:
- 6502 opcodes on 6502.org and Tutorials
- Not very good quality, with lot of errors in my point of view ChibiAkumas 6502 platforms tutorials, probably still some interesting tips
Installing
ArchLinux (and derivatives)
sudo pacman -S vice xa
yay -S xda65 cc65
Debian (and derivatives)
apt install vice xa65 cc65
# need to compile yourself dxa65 debugger:
curl -O http://www.floodgap.com/retrotech/xa/dists/dxa65-0.1.5.tar.gz
tar xf dxa65-0.1.5.tar.gz
cd dxa65-0.1.5/
make
sudo cp -a dxa /usr/local/bin/
sudo cp -a dxa.1 /usr/local/man/man1/
I personnaly use [vim](https://www.vim.org)
(pacman -S vim
, some prefers more heavy [neovim](https://neovim.io/)
, [kakoune](http://kakoune.org)
, [helix-editor](https://helix-editor.com)
, or X/Wayland frontend of vim called gvim
, or more simple, limited, matching more old-faschionned "MS-Windows" things [micro](https://micro-editor.github.io/)
or [nano](https://www.nano-editor.org/)
), as editor. Sometime for more complex, multi-files code, I like to use the very light and graphic editor Geany (pacman -S geany-plugins
to install both geany and its plugins).
Reference documentation
Most was found on https://commodore.bombjack.org/
C64 Programmer's Reference Guide, contains BASIC language, graphics, audio, and 65xx assembly programming courses chapters and various mandatory references. These are books published by no more existing company, CBM (Commodore Business Machines) in 1980s, and given with their computers, considered as abandonware.
- English C64 Programmer's Reference Guide, local copy
- Français C64 Programmer's Reference Guide_(FR), local copy
- Pure text (no graphics) English version C64 Programmer's Reference Guide (TXT Version), local copy
- Español C64 Programmers Reference Guide_(SP), local copy
Very useful, everydays references resources on this book. Text edition has now roman digits introduction part and no pictures, so pages search by number in PDF use not the same index.
- "MCS6510 MICROPROCESSOR INSTRUCTION SET - ALPHABETIC SEQUENCE", page 232 (252 in text edition)
- Followed, starting page 234, by an Instruction references, with detail for each one like number of bytes used, flags set and Number of clock cycles for each instruction
- "COMMODORE 64 MEMORY MAP", page 310 (330 in text edition), to access differents to different parts of the system, including graphics, video and audio chips, RAM, Kernal, I/O ports etc.
- "USER CALLABLE KERNAL ROUTINES", page 272 (292 in text edition). Contains a short reference table of the OS "Kernal" available functions, followed by detailed documentation of each functions.
- All the appendices, including APPENDIX C, ''ASCII AND CHR$ CODES'', page 379 (389 in text edition)
Other documentations:
- 6502 opcodes on 6502.org and Tutorials
- Not very good quality, with lot of errors in my point of view ChibiAkumas 6502 platforms tutorials, probably still some interesting tips
65xx Assembly code basics
Following C64 Programmers Guide, and digging around the net for usage of x64, I sadely found only some buggy parts of things that work. Few months ago I made something that worked from available references, on another computer, I don't have access at this time. Need to found them again.
Tips for conversion from decimal to hexadecimal
Type for converting from decimal to hexadecimal, I launch an Lua interpreter console and type:
string.format("%x",65) -- display hexadecimal value of 65 decimal
0x50 -- display decimal value of 50 hexadecimal
On lua < 5.3 (works also with >= 5.3 and needed in loops
print(string.format("%x",65))
print(0x50)
In Bash shell, you an also do this kind of conversions. From bin or hex to dec:
echo $((2#11110100)) # bin to dec
echo $((16#f4)) # hex to dec
The printf function, similar to C and Lua, display in other format that decimal. '\n' (10 or 0x0a) is the usual "new line" code, used on all operating systems but Apple ones ('\r' 0x0d, "carriage return"), and Microsoft DOS/Windows ones ('\r\n') to be sure to wastie memory for ending line and formating here:
printf '%x\n' $((2#11110100)) # bin to hex
printf '%x\n' 244 # dec to hex: f4
printf '%X\n' 244 # dec to hex with uppercase representation: F4
printf '%o\n' 244 # dec to octal (3 bits).
printf '%d\n' $((16#F4)) # hex to dec
Warning, C64 display in uppercase with lowercase ASCII/UTF-8 codes.
Not useful for C64 (but fixed float), but you can also have float representation and keep only n digits. **Warning, it depend on your locale (',' it the for French as example)
printf '%.2f\n' 3.1416 # => 3.14
printf '%.*f\n' 2 3.1416 # => 3.14 number of float * is given as first arg
printf '%.2f\n' 3,1416 # => 3,14 with fr_FR.UTF-8 locale
C64 specific knowledge
- After "C64 Promgrammer's Reference Guide", page 212, part of memory available for user's programs are betweeb $800 and $9FFF (2048-40959). For working, a BASIC header must be placed in $0800 (2048) and the program itself must start at $1000 (4096)
- "Kernal" is the name of the OS functions available on C64
- That was the most cool computer of its time (1980s) due to very good audio Chip called SID, very efficient graphics and video Chip. It is still used in 2020s in demoscene. As Commodore Amiga, there are Cult computer among computer hacking enthusiasts.
- ASCII characters are the CAPITAL variants only, lowercases one are replaced by symbols in default EEPROM.
Some bits of 65xx assembly
- Comments are text following semicolon (
; comment
) - Labels are text preceding coma (
label:
), that can be used as references for jumps - Datas are defined by their type at begining of line dot+type (
.byte $ff
,.word $53ff
,.aasc "text"
, etc) - Hexadecimal value start with dollar (
$FF
) - Octal with ampersand (
&77
) - Binary with a percent (
%11110000
) - Immediate values (values placed after opcode inside program) start with sharp and can be of various representations (
#0 #$ff #&77 #%11001100
) - Chars and strings are double quoted (
"a" "string"
).
Strings can be defined by there decimal, hexadecimal or ascii values in various assemblers, in our case.
.byte 72,69,76,76,79, 32, 87,79,82,76,68,33, 0 ; decimal
.byte $48,$45,$4c,$4c,$4f, $20, $57,$4f,$52,$4c,$44, $21, $0 ; hexadecimal
.aasc "HELLO WORLD!" ; ascii*
.byte 0 ; string ending 0 (as is C, the more convenient)
65xx, z80 or other 8 bits microprocessors of this era are quite limited, no floating point, no multiplication or division, only 3 specialized registers in case of 65xx:
- A (accumulator, for most operations)
- X and Y (mainly used as indexes).
Another essential part of all microporocessors is a register generally called CSR (control status register) and here, just STATUS REGISTER
containing flags, set as result of various operations and used for other operations and conditionnals like conditionnal jumps. C64's MCS6510 contains flags N Z C I D V
. As a start the more essentials for first programs are:
- z (Zero) flag, set if value loaded in A is zero, or if comparison between two values are equal.
- c (carry) flag, if an operation finish by a carry set. Remember to your basic mathematics lesson, when learning addition with serveral digits. 16 + 9 : 9 + 6 = 15 [5 + (carry=1)], 10 + carry = 20, so result is 25. can also be used for bits shift/rotation, depending on processors.
I didn't found detailed description of the Status register
in the manual, but 64-wiki have a dedicated page, for short reference:
- Negative, oVerflow, Break command, Decimak, IRQ (Interruption Request), Zero, Carry.
7 6 5 4 3 2 1 0
N V B D I Z C
There are also two sessential ones il all processors architectures and a last one specific to 65xx but we don't care them at start:
- PC (program counter) for current position.
- SP (stack pointer), mainly used to save registers states when jumping to subroutines, can be managed by Pxx instruction here (push/pull, more usually push/pop in computer sciences).
- 65xx CPU has also an INPUT/OUTPUT port register used for controling various I/O tasks with the computer system.
The system has some other control register related to various microcontrollers on the board (audio, graphics, peripherals, etc)), they are descibed in their dedicated sections of the manual and are out of topic in this introduction.
The helloworld.s
whole source code (C64 helloworld.s downloadable here), more long description below:
Hello World
I commented instructions as much as necessary, refer to a 6502 assembly book (see above), c64 reference guide contains a 6502 assembly course, or the numerous guide on the net to better understanding it. 65xx world isn't RISC-V world, so not fully standardised by community, each assembler has his specify beside 65xx assembly code itself, you need to have separate tutorial for each one sadely.
CHROUT = $ffd2 ; Kernal function to print a character to OUTPUT channel, page 272,278
; usable memory for programs is between $800 and $9fff (2048-40959), after "C64 Promgrammer's Reference Guide", page 212
; C64 BASIC header, mandatory
.byte $01, $08; Load header into $0801
*=$0801
.byte $0c, $08, $0a, $00, $9e, $20
.byte $34, $30, $39, $36, $00, $00
.byte $00
; datas
hello: ; "hello world!" string preceded by clear screen (147/$93) and followed by 0
.byte 147 ; clear screen character, avoid to write more instructions
.aasc "HELLO WORLD!" ; ascii text
.byte 0 ; end of string, not really needed in this example as:
; aligned code
.dsb $1000 - * ; Pad with zeroes from current position to $1000
;*=$1000 ; this doesn't work, prog should be between $1000 and $9fff (2048-40959)
ldx #0 ; set x to 0
loop:
lda hello,x ; load character hello + x
beq end ; if 0 then ends (lda set z flag if loaded value is zero)
jsr CHROOT ; print character KERNAL routine
inx ; increment x
jmp loop ; loop
end:
rts ; return from subroutine, or here, end program
Detailed description
Definitions that are preprocessed by assembler don't take memory in final program, as they are used exclusively by the assembler. That's the case of defined CHROOT address $FFD2, that is the standard CHROOT Kernal routine. It is possible to use directly address values in code, but using this kind of constant labels, help to make the code more readable.
CHROUT = $ffd2 ; Kernal function to print a character to OUTPUT channel, page 272,278
!!Warning, This kind of definition is compatible with xa65 assembler, but could be different on some others. Look at your assembler documentation!!
As program start at $1000 and the little BASIC header at $800, we have some place to keep datas that would be nil else. There are far more place after, but this will make the program less compact, would be needed for bigger ones. So we place datas before the program instructions.
First we load value 0 to the X register (ldx = load X)
ldx #0
Then we start the main loop and place a label to be able to jump to it, it will be replaced with it's equivalent address in jump instructions in machine code
loop:
Then we load in the accumulator (lda = load A) the content of address labeled by hello:
+ index register X (here hello + 0) in the accumulator.
lda hello,x ; load character hello + x
We prefixed the "HELLO WORD!" string by the code 147 ($97) here. As shown in page 380 (APPENDIX C) of the manual, this is the {clear}
(screen) character when sent to the Kernal print function.
hello: ; "hello world!" string preceded by clear screen (147/$93) and followed by 0
.byte 147 ; clear screen character, avoid to write more instructions
.aasc "HELLO WORLD!" ; ascii text
.byte 0 ; end of string, not really needed in this example as:
As you see, we also suffixed the string by a 0. This is usual in C language strings and optimal in assembly; If the content of the value loaded in accumulator by lda is 0, then the flag Z (zero) will be set (ref: LDA, page 245, 263 in TXT ed.) to 1 else to 0 (details, page 226, 244 in TXT ed.), so, the next instruction:
We test if beq
(be "equal"). This instruction is true if the flag Z is set to 1, as, if a difference between two value is not equal to zero, they are obviously not the same. If the value is zero, we already have finished to read the string, we can go to end:
labeled instruction address, else we continue at next instruction.
beq end ; if 0 then ends (lda set z flag if zero)
Here we jsr
(Jump at SubRoutine) at CHROUT, previously definied as hexadecimal address $FFD2
, where Kernal subroutine CHROUT
to print a char is (page 272, 290 in TXT ed, long description page 278(296)). This will actually print the current value in accumulator at cursor address on screen, and then return to next instruction in this program.
jsr CHROUT ; print character KERNAL routine
We now, inx
(increment the X
register of 1), to place it to the next char in the reference string
inx ; increment x
Then we inconditionnaly jmp
(jump) to the address of instructions labeled by loop:
to read the next char in string and continue
jmp loop ; loop
This part is only reached if the previous beq
instruction condition is true, so we finish the program by rts
(return froom subroutine) and comeback to instruction following the PC
address before calling this program.
end:
rts ; return from subroutine, or here, end program
Assembling and executing
Assembling for the C64:
xa -o helloworld.prg helloworld.s
You can download here the helloworld.prg
Launching:
vice hellowold.prg
Disassembling for analysis or debugging
Here the optional -a dump
, allow to see an hexidecimal representation of datas and opcodes to better understand how codemachine is related to source assembly
dxa65 -a dump helloworld.prg
Example of the main program part, as shown by this disassembler (this is preceded by headers, our text datas and lots of zero):
1000 l1000:
1000 a2 00 ldx #$00
1002 l1002:
1002 bd 0e 08 lda l80e,x
1005 f0 07 beq l100e
1007 l1007:
1007 20 d2 ff jsr $ffd2
100a e8 inx
100b 4c 02 10 jmp l1002
100e l100e:
100e 60 rts
To have mode detailed option of this debugger, look at the man page (called dxa, not dxa65):
man dxa
Hope this detailed instructions are detailed enough and help you. You can reach me on Mastodon if needed.