{c64asm}
is a c64 6502 assembler written in R.
The syntax for the assembly code closely follows that of TASS.
{c64asm}
includes syntax extensions which allow for easier integration
of pre-computed values and data segments directly from R.
The Commodore 64 (c64) is an 8-bit home computer running on a Motorola 6510 (a variant of the 6502).
The c64 supports 16 colours, 8 hardware sprites and a maximum resolution of 320x200 pixels.
It includes co-processors for sound (SID chip) and video (VIC-II) chip.
6502 machine code:
- 3 general purpose registers: A, X, Y
- No multiplication instruction
General features
- Syntax similar to TASS64 syntax.
- Settable program counter
- e.g.
* = $0801
- e.g.
- Defined variables
- e.g.
border = $d020
- e.g.
- Low/High byte extraction from symbol (similar to TASS)
- e.g.
lda #<routine
will store the low byte of the address of symbolroutine
in theA
register
- e.g.
For integration with R
.rtext
directive to include anR
string as text data.rbyte
directive to include anR
integer vector as bytes{...}
to delimit code to be evaluated at run time to manipulate labels and variables e.g.lda {border + 1}
- R Errors will be thrown for most syntax errors in the assembly code.
- There are still corner cases which will compile into invalid machine
code:
- E.g. if the user orchestrates a
jmp
to a location more than 128 bytes away from the current program counter.
- E.g. if the user orchestrates a
indirect indexed
andindexed indirect
modes using symbolic addresses seems to work, but need more tests.- You may need to force zero page addressing modes with
symbols/variables by explicitly noting that only the low byte should
be used. e.g.
BUFPNT = $A6
is the pointer to the tape i/o bufferlda <BUFPNT
should be used to force zero page addressing mode, otherwiselda BUFPNT
would assume absolute addressing mode.- The actual result would be the same, but zero page addressing takes fewer bytes and takes fewer CPU cycles to execute than absolute.
devtools::install_github('coolbutuseless/c64asm')
The documentation for the package is available online at coolbutuseless.github.io (thanks to pkgdown)
helloworld
- write text to the screenhelloborder
- colour cycling in the border as fast as possiblehelloworld_details
- a look at the intermediate assembly outputs during compilationascii
- Using R-specific.rbyte
directive to put characters from an R variable into the assembly codecustom_character_set
- Building a custom character set in R and passing it into the assemblly code with the.rbyte
directive.symbol-arithmetic
- How to do arithmetic on the program counter and address labels
The code for the vignettes is also available in the prg/
directory of
this repository.
The following c64/6502 ASM code will clear the screen and then write
Hello #rstats!
at the top
asm <- '
*=$0801
.byte $0c, $08, $0a, $00, $9e, $20 ; 10 SYS 2080
.byte $32, $30, $38, $30, $00, $00
.byte $00
*=$0820
lda #$93 ; Clear the screen
jsr $ffd2
ldx #$0 ; initialise the offset pointer into our message
loop lda message,x ; load a character and write it to screen
and #$3f ; Manually place chars on screen
sta $0400,x
inx
cpx #$0e
bne loop
rts
message
.text "Hello #rstats!"
'
prg <- c64asm::compile(asm)
prg
## [1] 01 08 0c 08 0a 00 9e 20 32 30 38 30 00 00 00 00 00 00 00 00 00 00 00 00 00
## [26] 00 00 00 00 00 00 00 00 a9 93 20 d2 ff a2 00 bd 35 08 29 3f 9d 00 04 e8 e0
## [51] 0e d0 f3 60 c8 45 4c 4c 4f 20 23 52 53 54 41 54 53 21
library(c64vice)
c64vice::run_prg(prg)
The compiler makes a few passes through the data to resolve symbol values.
The c64asm::compile()
function is just a wrapper which calls the
following 4 functions
line_tokens <- c64asm::create_line_tokens(asm)
- For each line in the input break it into tokens.
- Filter any rows that contain no instructions
prg_df <- c64asm::create_prg_df(line_tokens)
- Create a data.frame from
line_tokens
- This is the key data structure for the compilation process
- The compilation process is just a matter of manipulating this data.frame and merging with information about the instructions
- Create a data.frame from
prg_df <- c64asm::process_symbols(prg_df)
- Resolve labels to their actual addresses
- Replace any defined variables with their values
prg_df <- c64asm::process_zero_padding(prg_df)
- If there are gaps between blocks of code, insert sequences of zero
bytes 5,
prg <- c6asm::extract_prg_bytes(prg_df)
- Extract the vector of raw bytes which represent the c64 PRG executable
- If there are gaps between blocks of code, insert sequences of zero
bytes 5,
The actual contents of the c64 prg
executable file is the sequence of
values in the hexbytes
column.
![](https://raw.githubusercontent.com/TimTaylor/c64asm/main/man/figures/logo-tass.png)
![](https://raw.githubusercontent.com/TimTaylor/c64asm/main/man/figures/logo-asm.png)
![](https://raw.githubusercontent.com/TimTaylor/c64asm/main/man/figures/logo-vice.png)