1 unstable release
0.1.0 | Jan 29, 2025 |
---|
#463 in Programming languages
139 downloads per month
230KB
3K
SLoC
C64 Assembler
The goal of this crate is to being able to compile C64 assembly directly from Rust.
The reasoning behind it is that in a typical C64 development workflow the programs are generated in a separate step and then being saved to a disk image. For generating disk images there are already crates out there like cmb.
However some projects require more control over the compilation stage and disk construction stage. Having a C64 assembler written in rust can build a bridge and allows custom disk formats and faster iterations during development.
Modules and functions
An crate::Application is organized in crate::Module and crate::Function. Modules can be shared between applications. A module public API is organized in functions. Multiple variations of functions can exists. By swapping out functions in a module a module can be size-optimized or CPU cycles optimized based on the actual needs of the program.
Usage
Building pattern
An application can be build using builder patterns.
use c64_assembler::builder::ApplicationBuilder;
use c64_assembler::builder::ModuleBuilder;
use c64_assembler::builder::InstructionBuilder;
let application = ApplicationBuilder::default()
.name("Set black border")
.include_vic20_defines()
.module(
ModuleBuilder::default()
.name("main")
.instructions(
InstructionBuilder::default()
.add_basic_header()
.label("main_entry_point")
.lda_imm(0x00)
.comment("Load black color")
.sta_addr("VIC20_BORDER_COLOR")
.rts()
.build(),
)
.build(),
)
.build().unwrap();
Validating
Using the crate::validator::Validator to check for consistency.
use c64_assembler::validator::Validator;
let validation_result = application.validate();
assert!(validation_result.is_ok());
Generating dasm source
Using the crate::generator::DasmGenerator a dasm compatible assembly source can be generated.
use c64_assembler::generator::Generator;
use c64_assembler::generator::DasmGenerator;
let source = DasmGenerator::default().generate(application).unwrap();
println!("{}", source);
Would output
; --- Application: SET BLACK BORDER ---
; NOTE: This file is generated, do not modify
processor 6502
VIC20_BORDER_COLOR = $D020
org $0800
; --- Module begin: MAIN ---
byte $00, $0C, $08 ; New basic line
; 10 SYS 2062
byte $0A, $00, $9E, $20, $32, $30, $36, $32
byte $00, $00, $00 ; End basic program
main_entry_point:
lda #$00
sta VIC20_BORDER_COLOR
rts
; --- Module end: MAIN ---
Generating .PRG byte stream
Using the crate::generator::ProgramGenerator to generate the byte stream. The byte stream includes the loading address.
use c64_assembler::generator::{Generator, ProgramGenerator, print_hexdump};
let bytes = ProgramGenerator::default().generate(application).unwrap();
print_hexdump(&bytes);
0000: 00 08 00 0C 08 0A 00 9E 20 32 30 36 32 00 00 00
0010: A9 00 8D 20 D0 60
Using macros (work in progress)
To reduce the boilerplating macros can be used. This is still under development. Expect less stability, error messages and some instructions not supported.
use c64_assembler_macro::application;
let application = application!(
name="Set black border"
include_vic20_defines
module!(
name="main"
instructions!(
include_basic_header
main_entry_point:
"Load black color into accumulator"
lda #$00
sta VIC20_BORDER_COLOR
rts
)
)
).unwrap();