; xex.inc - easy way to generate an atari 8-bit executable with ca65, ; without dealing with ca65's linker scripts and segments. ; see xex.rst (or xex.html) for full documentation. .macro xex_failtarget target .ifdef target .fatal "must assemble with '-t none'" .endif .endmacro xex_failtarget __APPLE2__ xex_failtarget __APPLE2ENH__ xex_failtarget __ATARI2600__ xex_failtarget __ATARI5200__ xex_failtarget __ATARI__ xex_failtarget __ATARIXL__ xex_failtarget __ATMOS__ xex_failtarget __BBC__ xex_failtarget __C128__ xex_failtarget __C16__ xex_failtarget __C64__ xex_failtarget __CBM__ xex_failtarget __CBM510__ xex_failtarget __CBM610__ xex_failtarget __CX16__ xex_failtarget __GEOS__ xex_failtarget __GEOS_APPLE__ xex_failtarget __GEOS_CBM__ xex_failtarget __LUNIX__ xex_failtarget __LYNX__ xex_failtarget __NES__ xex_failtarget __OSIC1P__ xex_failtarget __PET__ xex_failtarget __PLUS4__ xex_failtarget __SIM6502__ xex_failtarget __SIM65C02__ xex_failtarget __SUPERVISION__ xex_failtarget __VIC20__ .ifndef RUNAD .include "atari.inc" .endif .ifndef xex_verbose xex_verbose=1 .endif .ifndef xex_warnings xex_warnings=1 .endif xex_api_called .set 0 xex_segcount .set 1 xex_old_org .set -1 .macro xex_vprint arg .if xex_verbose .out .concat("xex.inc: ",arg) .endif .endmacro .macro xex_warn arg .if xex_verbose .warning .concat("xex.inc: ",arg) .endif .endmacro .macro xexstart startaddr, endaddr .if xex_api_called = 0 .fatal "xex.inc: don't call xexstart directly, use xex_org." .endif xex_api_called .set 0 .if xex_old_org > -1 xex_endseg .endif .org 0 ; can be anything really... .ifndef xex_ffff_emitted .byte $ff,$ff xex_ffff_emitted=1 xex_vprint .sprintf("starting segment %d at $%04x (with ffff header)", xex_segcount, startaddr) .else xex_vprint .sprintf("starting segment %d at $%04x", xex_segcount, startaddr) .endif .word startaddr .word endaddr-1 .org startaddr ; we don't need a label here really, but define it so it shows up in ; the VICE label file created by -Ln. .ident(.sprintf("xex_startaddr_%d", xex_segcount)): xex_segcount .set xex_segcount + 1 .endmacro .macro xex_org startaddr,limit xex_api_called .set 1 xexstart startaddr,.ident(.sprintf("xex_endaddr_%d", xex_segcount)) xex_old_org .set startaddr .ifblank limit xex_limit .set $10000 ; impossibly high .else xex_limit .set limit .endif .endmacro .macro xex_endseg .local endaddr endaddr = * - 1 .if xex_old_org < 0 xex_warn "xex_endseg called when not in a segment; harmless but redundant." .exitmacro .endif .if endaddr < xex_old_org .fatal .sprintf("cannot create an empty segment (start $%04x, end $%04x)", xex_old_org, endaddr) .endif .ident(.sprintf("xex_endaddr_%d", xex_segcount-1)): xex_vprint .sprintf(" ending segment %d at $%04x, length $%04x", xex_segcount-1, endaddr, endaddr-xex_old_org+1) xex_old_org .set -1 .assert .not (endaddr >= xex_limit), error, .sprintf("xex.inc: segment %d exceeds user-requested limit $%04x, by $%04x bytes", xex_segcount-1, xex_limit, endaddr - xex_limit + 1) xex_limit .set $10000 .endmacro .macro xex_run runaddr xex_org RUNAD .word runaddr xex_endseg xex_vprint .sprintf(" run address: $%04x", runaddr) .ifndef xex_run_addr xex_run_addr .set runaddr .else xex_warn .sprintf("multiple run addresses (previous was $%04x)", xex_run_addr) xex_run_addr .set runaddr .endif .endmacro .macro xex_init initaddr xex_org INITAD .word initaddr xex_endseg xex_vprint .sprintf(" init address: $%04x", initaddr) .endmacro .macro xex_incbin addr, filename, offset, length .local o .ifblank offset o = 0 .else o = offset .endif xex_org addr .ifblank length .incbin filename, o .else .incbin filename, o, length .endif xex_endseg .endmacro ;;; THIS DOESN'T WORK! .if 0 .macro xex_include addr, filename xex_org addr .out .sprintf("before include %s: %04x", filename, *) .include filename .out .sprintf("after include: %04x", *) xex_endseg .endmacro .endif