pax_global_header00006660000000000000000000000064133377712540014526gustar00rootroot0000000000000052 comment=429f508c2b19e1abeafef195d273b9b82c1fda00 acme-crossassembler-0.96.4/000077500000000000000000000000001333777125400155605ustar00rootroot00000000000000acme-crossassembler-0.96.4/.gitignore000066400000000000000000000002671333777125400175550ustar00rootroot00000000000000### Vim # Swap [._]*.s[a-v][a-z] [._]*.sw[a-p] [._]s[a-v][a-z] [._]sw[a-p] # Session Session.vim # Temporary .netrwhist *~ # Auto-generated tag files tags # Persistent undo [._]*.un~ acme-crossassembler-0.96.4/ACME_Lib/000077500000000000000000000000001333777125400170535ustar00rootroot00000000000000acme-crossassembler-0.96.4/ACME_Lib/6502/000077500000000000000000000000001333777125400174475ustar00rootroot00000000000000acme-crossassembler-0.96.4/ACME_Lib/6502/opcodes.a000066400000000000000000000101061333777125400212430ustar00rootroot00000000000000;ACME 0.94.4 !ifdef lib_6502_opcodes_a !eof lib_6502_opcodes_a = 1 ; if your self-modifying code not only changes its arguments, but even its opcodes, use these constants. ; unused slots in this table indicate "illegal" instructions. ; addressing mode modifiers: ; for implied addressing (BRK/CLC/NOP/TAX/...) and branches ; _A for "accumulator addressing" (ASL/ROL/LSR/ROR without argument) ; (...using "_A" instead of nothing so user does not forget to specify addressing mode) ; _IMM 8-bit constant (immediate addressing) ; _8 8-bit address (zero page addressing) ; _8X, _8Y 8-bit address with X/Y indexing ; _8XI, _8IY 8-bit address, indirect: TLA ($12, x) and TLA ($12), y ; _16 16-bit address (absolute addressing) ; _16X, _16Y 16-bit address with X/Y indexing ; _16I 16-bit address, indirect: JMP ($1234) ;pattern %......00 pattern %......01 pattern %......10 pattern %......11 (all illegal) opcode_BRK = 0x00: opcode_ORA_8XI = 0x01 opcode_ORA_8 = 0x05: opcode_ASL_8 = 0x06 opcode_PHP = 0x08: opcode_ORA_IMM = 0x09: opcode_ASL_A = 0x0a opcode_ORA_16 = 0x0d: opcode_ASL_16 = 0x0e opcode_BPL = 0x10: opcode_ORA_8IY = 0x11 opcode_ORA_8X = 0x15: opcode_ASL_8X = 0x16 opcode_CLC = 0x18: opcode_ORA_16Y = 0x19 opcode_ORA_16X = 0x1d: opcode_ASL_16X = 0x1e opcode_JSR_16 = 0x20: opcode_AND_8XI = 0x21 opcode_BIT_8 = 0x24: opcode_AND_8 = 0x25: opcode_ROL_8 = 0x26 opcode_PLP = 0x28: opcode_AND_IMM = 0x29: opcode_ROL_A = 0x2a opcode_BIT_16 = 0x2c: opcode_AND_16 = 0x2d: opcode_ROL_16 = 0x2e opcode_BMI = 0x30: opcode_AND_8IY = 0x31 opcode_AND_8X = 0x35: opcode_ROL_8X = 0x36 opcode_SEC = 0x38: opcode_AND_16Y = 0x39 opcode_AND_16X = 0x3d: opcode_ROL_16X = 0x3e opcode_RTI = 0x40: opcode_EOR_8XI = 0x41 opcode_EOR_8 = 0x45: opcode_LSR_8 = 0x46 opcode_PHA = 0x48: opcode_EOR_IMM = 0x49: opcode_LSR_A = 0x4a opcode_JMP_16 = 0x4c: opcode_EOR_16 = 0x4d: opcode_LSR_16 = 0x4e opcode_BVC = 0x50: opcode_EOR_8IY = 0x51 opcode_EOR_8X = 0x55: opcode_LSR_8X = 0x56 opcode_CLI = 0x58: opcode_EOR_16Y = 0x59 opcode_EOR_16X = 0x5d: opcode_LSR_16X = 0x5e opcode_RTS = 0x60: opcode_ADC_8XI = 0x61 opcode_ADC_8 = 0x65: opcode_ROR_8 = 0x66 opcode_PLA = 0x68: opcode_ADC_IMM = 0x69: opcode_ROR_A = 0x6a opcode_JMP_16I = 0x6c: opcode_ADC_16 = 0x6d: opcode_ROR_16 = 0x6e opcode_BVS = 0x70: opcode_ADC_8IY = 0x71 opcode_ADC_8X = 0x75: opcode_ROR_8X = 0x76 opcode_SEI = 0x78: opcode_ADC_16Y = 0x79 opcode_ADC_16X = 0x7d: opcode_ROR_16X = 0x7e opcode_STA_8XI = 0x81 opcode_STY_8 = 0x84: opcode_STA_8 = 0x85: opcode_STX_8 = 0x86 opcode_DEY = 0x88: opcode_TXA = 0x8a opcode_STY_16 = 0x8c: opcode_STA_16 = 0x8d: opcode_STX_16 = 0x8e opcode_BCC = 0x90: opcode_STA_8IY = 0x91 opcode_STY_8X = 0x94: opcode_STA_8X = 0x95: opcode_STX_8Y = 0x96 opcode_TYA = 0x98: opcode_STA_16Y = 0x99: opcode_TXS = 0x9a opcode_STA_16X = 0x9d opcode_LDY_IMM = 0xa0: opcode_LDA_8XI = 0xa1: opcode_LDX_IMM = 0xa2 opcode_LDY_8 = 0xa4: opcode_LDA_8 = 0xa5: opcode_LDX_8 = 0xa6 opcode_TAY = 0xa8: opcode_LDA_IMM = 0xa9: opcode_TAX = 0xaa opcode_LDY_16 = 0xac: opcode_LDA_16 = 0xad: opcode_LDX_16 = 0xae opcode_BCS = 0xb0: opcode_LDA_8IY = 0xb1 opcode_LDY_8X = 0xb4: opcode_LDA_8X = 0xb5: opcode_LDX_8Y = 0xb6 opcode_CLV = 0xb8: opcode_LDA_16Y = 0xb9: opcode_TSX = 0xba opcode_LDY_16X = 0xbc: opcode_LDA_16X = 0xbd: opcode_LDX_16Y = 0xbe opcode_CPY_IMM = 0xc0: opcode_CMP_8XI = 0xc1 opcode_CPY_8 = 0xc4: opcode_CMP_8 = 0xc5: opcode_DEC_8 = 0xc6 opcode_INY = 0xc8: opcode_CMP_IMM = 0xc9: opcode_DEX = 0xca opcode_CPY_16 = 0xcc: opcode_CMP_16 = 0xcd: opcode_DEC_16 = 0xce opcode_BNE = 0xd0: opcode_CMP_8IY = 0xd1 opcode_CMP_8X = 0xd5: opcode_DEC_8X = 0xd6 opcode_CLD = 0xd8: opcode_CMP_16Y = 0xd9 opcode_CMP_16X = 0xdd: opcode_DEC_16X = 0xde opcode_CPX_IMM = 0xe0: opcode_SBC_8XI = 0xe1 opcode_CPX_8 = 0xe4: opcode_SBC_8 = 0xe5: opcode_INC_8 = 0xe6 opcode_INX = 0xe8: opcode_SBC_IMM = 0xe9: opcode_NOP = 0xea opcode_CPX_16 = 0xec: opcode_SBC_16 = 0xed: opcode_INC_16 = 0xee opcode_BEQ = 0xf0: opcode_SBC_8IY = 0xf1 opcode_SBC_8X = 0xf5: opcode_INC_8X = 0xf6 opcode_SED = 0xf8: opcode_SBC_16Y = 0xf9 opcode_SBC_16X = 0xfd: opcode_INC_16X = 0xfe acme-crossassembler-0.96.4/ACME_Lib/6502/std.a000066400000000000000000000013071333777125400204040ustar00rootroot00000000000000;ACME 0.95 !ifdef lib_6502_std_a !eof lib_6502_std_a = 1 ; labels and macros for plain 6502 processor !address { cpu_nmi = $fffa cpu_reset = $fffc cpu_irq = $fffe } ; skip byte !macro bit8 { !byte $24 ; opcode of BIT $.. command } ; skip word !macro bit16 { !byte $2c ; opcode of BIT $.... command } ; increase 16-bit counter !macro inc16 .t { inc .t bne + inc .t + 1 + } ; far branches !macro bcc .t { bcs + jmp .t + } !macro bcs .t { bcc + jmp .t + } !macro beq .t { bne + jmp .t + } !macro bne .t { beq + jmp .t + } !macro bmi .t { bpl + jmp .t + } !macro bpl .t { bmi + jmp .t + } !macro bvc .t { bvs + jmp .t + } !macro bvs .t { bvc + jmp .t + } acme-crossassembler-0.96.4/ACME_Lib/65816/000077500000000000000000000000001333777125400175445ustar00rootroot00000000000000acme-crossassembler-0.96.4/ACME_Lib/65816/std.a000066400000000000000000000021251333777125400205000ustar00rootroot00000000000000;ACME 0.96.3 !ifdef lib_65816_std_a !eof lib_65816_std_a = 2 ; Labels and macros for Western Digital's 65c816 processor !address { cpu_e_cop = $fff4 cpu_e_abort = $fff8 cpu_e_nmi = $fffa cpu_e_reset = $fffc cpu_e_irq = $fffe cpu_n_cop = $ffe4 cpu_n_brk = $ffe6 cpu_n_abort = $ffe8 cpu_n_nmi = $ffea ; no reset vector for native mode because reset always enters emulation mode cpu_n_irq = $ffee } !macro cpu_emu {; switch to emulation mode sec xce } !macro cpu_native {; switch to native mode clc xce } !macro a8 {; switch A to 8 bit sep #%..#..... !as } !macro a16 {; switch A to 16 bit rep #%..#..... !al } !macro i8 {; switch X/Y to 8 bit sep #%...#.... !rs } !macro i16 {; switch X/Y to 16 bit rep #%...#.... !rl } !macro ai8 {; switch A/X/Y to 8 bit sep #%..##.... !as !rs } !macro ai16 {; switch A/X/Y to 16 bit rep #%..##.... !al !rl } !macro a8i16 {; switch A to 8, X/Y to 16 bit +a8 +i16 } !macro a16i8 {; switch A to 16, X/Y to 8 bit +a16 +i8 } !macro inc24 .t {; increase 24-bit counter inc .t bne + inc .t + 1 bne + inc .t + 2 + } acme-crossassembler-0.96.4/ACME_Lib/apple ii/000077500000000000000000000000001333777125400205365ustar00rootroot00000000000000acme-crossassembler-0.96.4/ACME_Lib/apple ii/convtab.bin000066400000000000000000000004001333777125400226560ustar00rootroot00000000000000  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~acme-crossassembler-0.96.4/ACME_Lib/cbm/000077500000000000000000000000001333777125400176145ustar00rootroot00000000000000acme-crossassembler-0.96.4/ACME_Lib/cbm/baserror.a000066400000000000000000000027371333777125400216060ustar00rootroot00000000000000;ACME 0.95.1 !ifdef lib_cbm_baserror_a !eof lib_cbm_baserror_a = 1 ; i/o error 0 (BREAK) is mapped to basic error 30 ; codes 1 through 9 are the same for i/o and basic errors: baserror_TOO_MANY_FILES = 1 baserror_FILE_OPEN = 2 baserror_FILE_NOT_OPEN = 3 baserror_FILE_NOT_FOUND = 4 baserror_DEVICE_NOT_PRESENT = 5 baserror_NOT_INPUT_FILE = 6 baserror_NOT_OUTPUT_FILE = 7 baserror_MISSING_FILE_NAME = 8 baserror_ILLEGAL_DEVICE_NUMBER = 9 ; from here on basic errors only: baserror_NEXT_WITHOUT_FOR = 10 baserror_SYNTAX = 11 baserror_RETURN_WITHOUT_GOSUB = 12 baserror_OUT_OF_DATA = 13 baserror_ILLEGAL_QUANTITY = 14 baserror_OVERFLOW = 15 baserror_OUT_OF_MEMORY = 16 baserror_UNDEFD_STATEMENT = 17 baserror_BAD_SUBSCRIPT = 18 baserror_REDIMD_ARRAY = 19 baserror_DIVISION_BY_ZERO = 20 baserror_ILLEGAL_DIRECT = 21 baserror_TYPE_MISMATCH = 22 baserror_STRING_TOO_LONG = 23 baserror_FILE_DATA = 24 baserror_FORMULA_TOO_COMPLEX = 25 baserror_CANT_CONTINUE = 26 baserror_UNDEFINED_FUNCTION = 27 baserror_VERIFY = 28 baserror_LOAD = 29 baserror_BREAK = 30 ; basic 3.5 and higher: baserror_CANT_RESUME = 31 baserror_LOOP_NOT_FOUND = 32 baserror_LOOP_WITHOUT_DO = 33 baserror_DIRECT_MODE_ONLY = 34 baserror_NO_GRAPHICS_AREA = 35 baserror_BAD_DISK = 36 ; basic 7 and higher: baserror_BEND_NOT_FOUND = 37 baserror_LINE_NUMBER_TOO_LARGE = 38 ; for RENUMBER baserror_UNRESOLVED_REFERENCE = 39 ; for RENUMBER baserror_UNIMPLEMENTED_COMMAND = 40 ; for OFF and QUIT baserror_FILE_READ = 41 acme-crossassembler-0.96.4/ACME_Lib/cbm/basic1.a000066400000000000000000000111551333777125400211230ustar00rootroot00000000000000;ACME 0.94.4 !ifdef lib_cbm_basic1_a !eof lib_cbm_basic1_a = 1 ; token values token_END = $80 token_FOR = $81 token_NEXT = $82 token_DATA = $83 token_INPUT_ = $84 ; INPUT# token_INPUT = $85 token_DIM = $86 token_READ = $87 token_LET = $88 token_GOTO = $89 token_RUN = $8a token_IF = $8b token_RESTORE = $8c token_GOSUB = $8d token_RETURN = $8e token_REM = $8f token_STOP = $90 token_ON = $91 token_WAIT = $92 token_LOAD = $93 token_SAVE = $94 token_VERIFY = $95 token_DEF = $96 token_POKE = $97 token_PRINT_ = $98 ; PRINT# token_PRINT = $99 token_CONT = $9a token_LIST = $9b token_CLR = $9c token_CMD = $9d token_SYS = $9e token_OPEN = $9f token_CLOSE = $a0 token_GET = $a1 token_NEW = $a2 token_TAB = $a3 ; the token already includes '(' token_TO = $a4 token_FN = $a5 token_SPC = $a6 ; the token already includes '(' token_THEN = $a7 token_NOT = $a8 token_STEP = $a9 token_ADD = $aa ; '+' token_SUBTRACT = $ab ; '-' token_MULTIPLY = $ac ; '*' token_DIVIDE = $ad ; '/' token_POWEROF = $ae ; '^' token_AND = $af token_OR = $b0 token_GREATER = $b1 ; '>' token_EQUAL = $b2 ; '=' token_LESS = $b3 ; '<' token_SGN = $b4 token_INT = $b5 token_ABS = $b6 token_USR = $b7 token_FRE = $b8 token_POS = $b9 token_SQR = $ba token_RND = $bb token_LOG = $bc token_EXP = $bd token_COS = $be token_SIN = $bf token_TAN = $c0 token_ATN = $c1 token_PEEK = $c2 token_LEN = $c3 token_STR_ = $c4 ; STR$ token_VAL = $c5 token_ASC = $c6 token_CHR_ = $c7 ; CHR$ token_LEFT_ = $c8 ; LEFT$ token_RIGHT_ = $c9 ; RIGHT$ token_MID_ = $ca ; MID$ token_PI = $ff ; greek letter pi ; Macros for inserting BASIC commands. Note that "#" and "$" characters in ; BASIC keywords have been converted to "_" in the macro names. ; *All* function macros already include the '(' character. !macro b_END {!by token_END} !macro b_FOR {!by token_FOR} !macro b_NEXT {!by token_NEXT} !macro b_DATA {!by token_DATA} !macro b_INPUT_ {!by token_INPUT_} ; INPUT# !macro b_INPUT {!by token_INPUT} !macro b_DIM {!by token_DIM} !macro b_READ {!by token_READ} !macro b_LET {!by token_LET} !macro b_GOTO {!by token_GOTO} !macro b_RUN {!by token_RUN} !macro b_IF {!by token_IF} !macro b_RESTORE {!by token_RESTORE} !macro b_GOSUB {!by token_GOSUB} !macro b_RETURN {!by token_RETURN} !macro b_REM {!by token_REM} !macro b_STOP {!by token_STOP} !macro b_ON {!by token_ON} !macro b_WAIT {!by token_WAIT} !macro b_LOAD {!by token_LOAD} !macro b_SAVE {!by token_SAVE} !macro b_VERIFY {!by token_VERIFY} ; As "DEF" cannot be used without "FN", here is a macro called "b_DEFFN" ; instead of one called "b_DEF": !macro b_DEFFN {!by token_DEF, token_FN} ; DEFFN !macro b_POKE {!by token_POKE} !macro b_PRINT_ {!by token_PRINT_} ; PRINT# !macro b_PRINT {!by token_PRINT} !macro b_CONT {!by token_CONT} !macro b_LIST {!by token_LIST} !macro b_CLR {!by token_CLR} !macro b_CMD {!by token_CMD} !macro b_SYS {!by token_SYS} !macro b_OPEN {!by token_OPEN} !macro b_CLOSE {!by token_CLOSE} !macro b_GET {!by token_GET} !macro b_NEW {!by token_NEW} !macro b_TAB {!by token_TAB} ; TAB( the token already includes '(' !macro b_TO {!by token_TO} !macro b_FN {!by token_FN} !macro b_SPC {!by token_SPC} ; SPC( the token already includes '(' !macro b_THEN {!by token_THEN} !macro b_NOT {!by token_NOT} !macro b_STEP {!by token_STEP} !macro b_ADD {!by token_ADD} ; + !macro b_SUBTRACT {!by token_SUBTRACT} ; - !macro b_MULTIPLY {!by token_MULTIPLY} ; * !macro b_DIVIDE {!by token_DIVIDE} ; / !macro b_POWEROF {!by token_POWEROF} ; ^ !macro b_AND {!by token_AND} !macro b_OR {!by token_OR} !macro b_GREATER {!by token_GREATER} ; > !macro b_EQUAL {!by token_EQUAL} ; = !macro b_LESS {!by token_LESS} ; < !macro b_SGN {!by token_SGN, $28} ; SGN( !macro b_INT {!by token_INT, $28} ; INT( !macro b_ABS {!by token_ABS, $28} ; ABS( !macro b_USR {!by token_USR, $28} ; USR( !macro b_FRE {!by token_FRE, $28} ; FRE( !macro b_POS {!by token_POS, $28} ; POS( !macro b_SQR {!by token_SQR, $28} ; SQR( !macro b_RND {!by token_RND, $28} ; RND( !macro b_LOG {!by token_LOG, $28} ; LOG( !macro b_EXP {!by token_EXP, $28} ; EXP( !macro b_COS {!by token_COS, $28} ; COS( !macro b_SIN {!by token_SIN, $28} ; SIN( !macro b_TAN {!by token_TAN, $28} ; TAN( !macro b_ATN {!by token_ATN, $28} ; ATN( !macro b_PEEK {!by token_PEEK, $28} ; PEEK( !macro b_LEN {!by token_LEN, $28} ; LEN( !macro b_STR_ {!by token_STR_, $28} ; STR$( !macro b_VAL {!by token_VAL, $28} ; VAL( !macro b_ASC {!by token_ASC, $28} ; ASC( !macro b_CHR_ {!by token_CHR_, $28} ; CHR$( !macro b_LEFT_ {!by token_LEFT_, $28} ; LEFT$( !macro b_RIGHT_ {!by token_RIGHT_, $28} ; RIGHT$( !macro b_MID_ {!by token_MID_, $28} ; MID$( !macro b_PI {!by token_PI} ; greek letter pi acme-crossassembler-0.96.4/ACME_Lib/cbm/basic10.a000066400000000000000000000040401333777125400211760ustar00rootroot00000000000000;ACME 0.94.4 !ifdef lib_cbm_basic10_a !eof lib_cbm_basic10_a = 1 !source ; from 0x80 to 0xff, 0xce 0x02 to 0xce 0x0a, 0xfe 0x02 to 0xfe 0x26 ; token values token_PASTE = $e3 ; was called GSHAPE in basic v7 token_CUT = $e4 ; was called SSHAPE in basic v7 token_LINE = $e5 ; was called DRAW in basic v7 token_DIR = $ee ; was called DIRECTORY in basic v7 ; Macros for inserting BASIC commands !macro b_PASTE {!by token_PASTE} ; aka GSHAPE !macro b_CUT {!by token_CUT} ; aka SSHAPE !macro b_LINE {!by token_LINE} ; aka DRAW !macro b_DIR {!by token_DIR} ; aka DIRECTORY ; STASH/FETCH/SWAP are all decoded to DMA: !macro b_DMA {!by $fe, $1f} ; extended token $fe $20 isn't used ($20 is ' ') ;!macro b_DMA {!by $fe, $21} ; extended token $fe $22 isn't used ($20 is '"') ;!macro b_DMA {!by $fe, $23} ; new instructions: !macro b_TYPE {!by $fe, $27} ; display sequential disk file !macro b_BVERIFY {!by $fe, $28} !macro b_ECTORY {!by $fe, $29} ; no-op in case someone types "DIRECTORY"? !macro b_ERASE {!by $fe, $2a} ; delete file !macro b_FIND {!by $fe, $2b} ; search in basic program !macro b_CHANGE {!by $fe, $2c} ; edit program !macro b_SET {!by $fe, $2d} ; set system parameter !macro b_SCREEN {!by $fe, $2e} !macro b_POLYGON {!by $fe, $2f} !macro b_ELLIPSE {!by $fe, $30} !macro b_VIEWPORT {!by $fe, $31} ; unimplemented? !macro b_GCOPY {!by $fe, $32} ; copy graphics !macro b_PEN {!by $fe, $33} ; set pen color !macro b_PALETTE {!by $fe, $34} ; set palette color !macro b_DMODE {!by $fe, $35} ; set draw mode !macro b_DPAT {!by $fe, $36} ; set draw pattern !macro b_PIC {!by $fe, $37} !macro b_GENLOCK {!by $fe, $38} !macro b_FOREGROUND {!by $fe, $39} ; set foreground color ; extended token $fe $3a isn't used ($3a is ':') !macro b_BACKGROUND {!by $fe, $3b} ; set background color !macro b_BORDER {!by $fe, $3c} ; set border color !macro b_HIGHLIGHT {!by $fe, $3d} ; set highlight color !macro b_MOUSE {!by $fe, $3e} ; set mouse parameters !macro b_RMOUSE {!by $fe, $3f} ; read mouse position !macro b_DISK {!by $fe, $40} ; send disc command acme-crossassembler-0.96.4/ACME_Lib/cbm/basic2.a000066400000000000000000000003151333777125400211200ustar00rootroot00000000000000;ACME 0.94.4 !ifdef lib_cbm_basic2_a !eof lib_cbm_basic2_a = 1 !source ; from 0x80 to 0xca ; token values token_GO = $cb ; Macros for inserting BASIC commands !macro b_GO {!by token_GO} acme-crossassembler-0.96.4/ACME_Lib/cbm/basic3.5.a000066400000000000000000000067621333777125400213000ustar00rootroot00000000000000;ACME 0.94.4 !ifdef lib_cbm_basic3_5_a !eof lib_cbm_basic3_5_a = 1 !source ; from 0x80 to $cb ; token values token_RGR = $cc token_RCLR = $cd ; if this file gets included via "cbm/basic7.a" or "cbm/basic10.a", do not define RLUM ; (because v7 and higher use $ce as prefix byte for additional functions): !ifndef lib_cbm_basic7_a { !ifndef lib_cbm_basic10_a { token_RLUM = $ce } } token_JOY = $cf token_RDOT = $d0 token_DEC = $d1 token_HEX_ = $d2 ; HEX$ token_ERR_ = $d3 ; ERR$ token_INSTR = $d4 token_ELSE = $d5 token_RESUME = $d6 token_TRAP = $d7 token_TRON = $d8 token_TROFF = $d9 token_SOUND = $da token_VOL = $db token_AUTO = $dc token_PUDEF = $dd token_GRAPHIC = $de token_PAINT = $df token_CHAR = $e0 token_BOX = $e1 token_CIRCLE = $e2 ; if this file gets included via "cbm/basic10.a", do not define GSHAPE/SSHAPE/DRAW ; (because in v10, they are called PASTE/CUT/LINE): !ifndef lib_cbm_basic10_a { token_GSHAPE = $e3 token_SSHAPE = $e4 token_DRAW = $e5 } token_LOCATE = $e6 token_COLOR = $e7 token_SCNCLR = $e8 token_SCALE = $e9 token_HELP = $ea token_DO = $eb token_LOOP = $ec token_EXIT = $ed ; if this file gets included via "cbm/basic10.a", do not define DIRECTORY ; (because in v10, it is called DIR): !ifndef lib_cbm_basic10_a { token_DIRECTORY = $ee } token_DSAVE = $ef token_DLOAD = $f0 token_HEADER = $f1 token_SCRATCH = $f2 token_COLLECT = $f3 token_COPY = $f4 token_RENAME = $f5 token_BACKUP = $f6 token_DELETE = $f7 token_RENUMBER = $f8 token_KEY = $f9 token_MONITOR = $fa token_USING = $fb token_UNTIL = $fc token_WHILE = $fd ; Macros for inserting BASIC commands. Note that "#" and "$" characters in ; BASIC keywords have been converted to "_" in the macro names. ; *All* function macros already include the '(' character. !macro b_RGR {!by token_RGR, $28} ; RGR( !macro b_RCLR {!by token_RCLR, $28} ; RCLR( !macro b_RLUM {!by token_RLUM, $28} ; RLUM( !macro b_JOY {!by token_JOY, $28} ; JOY( !macro b_RDOT {!by token_RDOT, $28} ; RDOT( !macro b_DEC {!by token_DEC, $28} ; DEC( !macro b_HEX_ {!by token_HEX_, $28} ; HEX$( !macro b_ERR_ {!by token_ERR_, $28} ; ERR$( !macro b_INSTR {!by token_INSTR, $28} ; INSTR( !macro b_ELSE {!by token_ELSE} !macro b_RESUME {!by token_RESUME} !macro b_TRAP {!by token_TRAP} !macro b_TRON {!by token_TRON} !macro b_TROFF {!by token_TROFF} !macro b_SOUND {!by token_SOUND} !macro b_VOL {!by token_VOL} !macro b_AUTO {!by token_AUTO} !macro b_PUDEF {!by token_PUDEF} !macro b_GRAPHIC {!by token_GRAPHIC} !macro b_PAINT {!by token_PAINT} !macro b_CHAR {!by token_CHAR} !macro b_BOX {!by token_BOX} !macro b_CIRCLE {!by token_CIRCLE} !macro b_GSHAPE {!by token_GSHAPE} !macro b_SSHAPE {!by token_SSHAPE} !macro b_DRAW {!by token_DRAW} !macro b_LOCATE {!by token_LOCATE} !macro b_COLOR {!by token_COLOR} !macro b_SCNCLR {!by token_SCNCLR} !macro b_SCALE {!by token_SCALE} !macro b_HELP {!by token_HELP} !macro b_DO {!by token_DO} !macro b_LOOP {!by token_LOOP} !macro b_EXIT {!by token_EXIT} !macro b_DIRECTORY {!by token_DIRECTORY} !macro b_DSAVE {!by token_DSAVE} !macro b_DLOAD {!by token_DLOAD} !macro b_HEADER {!by token_HEADER} !macro b_SCRATCH {!by token_SCRATCH} !macro b_COLLECT {!by token_COLLECT} !macro b_COPY {!by token_COPY} !macro b_RENAME {!by token_RENAME} !macro b_BACKUP {!by token_BACKUP} !macro b_DELETE {!by token_DELETE} !macro b_RENUMBER {!by token_RENUMBER} !macro b_KEY {!by token_KEY} !macro b_MONITOR {!by token_MONITOR} !macro b_USING {!by token_USING} !macro b_UNTIL {!by token_UNTIL} !macro b_WHILE {!by token_WHILE} acme-crossassembler-0.96.4/ACME_Lib/cbm/basic4.a000066400000000000000000000020631333777125400211240ustar00rootroot00000000000000;ACME 0.94.4 !ifdef lib_cbm_basic4_a !eof lib_cbm_basic4_a = 1 !source ; from 0x80 to $cb ; CAUTION - these tokens are different to the ones in BASIC 3.5, BASIC 7 and BASIC 10! ; token values token_CONCAT = $cc token_DOPEN = $cd token_DCLOSE = $ce token_RECORD = $cf token_HEADER = $d0 token_COLLECT = $d1 token_BACKUP = $d2 token_COPY = $d3 token_APPEND = $d4 token_DSAVE = $d5 token_DLOAD = $d6 token_CATALOG = $d7 token_RENAME = $d8 token_SCRATCH = $d9 token_DIRECTORY = $da ; Macros for inserting BASIC commands: !macro b_CONCAT {!by token_CONCAT} !macro b_DOPEN {!by token_DOPEN} !macro b_DCLOSE {!by token_DCLOSE} !macro b_RECORD {!by token_RECORD} !macro b_HEADER {!by token_HEADER} !macro b_COLLECT {!by token_COLLECT} !macro b_BACKUP {!by token_BACKUP} !macro b_COPY {!by token_COPY} !macro b_APPEND {!by token_APPEND} !macro b_DSAVE {!by token_DSAVE} !macro b_DLOAD {!by token_DLOAD} !macro b_CATALOG {!by token_CATALOG} !macro b_RENAME {!by token_RENAME} !macro b_SCRATCH {!by token_SCRATCH} !macro b_DIRECTORY {!by token_DIRECTORY} acme-crossassembler-0.96.4/ACME_Lib/cbm/basic7.a000066400000000000000000000046641333777125400211400ustar00rootroot00000000000000;ACME 0.94.4 !ifdef lib_cbm_basic7_a !eof lib_cbm_basic7_a = 1 ; Macros for inserting BASIC commands. Note that "#" and "$" characters in ; BASIC keywords have been converted to "_" in the macro names. ; *All* function macros already include the '(' character. !source ; from 0x80 to $fd ; extended (16-bit) tokens, $ce range (replacing RLUM function): ; extended token $ce $00 isn't used ; extended token $ce $01 isn't used !macro b_POT {!by $ce, $02:!pet '('} !macro b_BUMP {!by $ce, $03:!pet '('} ; if this file gets included via "cbm/basic10.a", do not define PEN ; (because in v10, there is an instruction called PEN): !ifndef lib_cbm_basic10_a { !macro b_PEN {!by $ce, $04:!pet '('} } !macro b_RSPPOS {!by $ce, $05:!pet '('} !macro b_RSPRITE {!by $ce, $06:!pet '('} !macro b_RSPCOLOR {!by $ce, $07:!pet '('} !macro b_XOR {!by $ce, $08:!pet '('} !macro b_RWINDOW {!by $ce, $09:!pet '('} !macro b_POINTER {!by $ce, $0a:!pet '('} ; extended (16-bit) tokens, $fe range: ; extended token $fe $00 isn't used ; extended token $fe $01 isn't used !macro b_BANK {!by $fe, $02} !macro b_FILTER {!by $fe, $03} !macro b_PLAY {!by $fe, $04} !macro b_TEMPO {!by $fe, $05} !macro b_MOVSPR {!by $fe, $06} !macro b_SPRITE {!by $fe, $07} !macro b_SPRCOLOR {!by $fe, $08} !macro b_RREG {!by $fe, $09} !macro b_ENVELOPE {!by $fe, $0a} !macro b_SLEEP {!by $fe, $0b} !macro b_CATALOG {!by $fe, $0c} !macro b_DOPEN {!by $fe, $0d} !macro b_APPEND {!by $fe, $0e} !macro b_DCLOSE {!by $fe, $0f} !macro b_BSAVE {!by $fe, $10} !macro b_BLOAD {!by $fe, $11} !macro b_RECORD {!by $fe, $12} !macro b_CONCAT {!by $fe, $13} !macro b_DVERIFY {!by $fe, $14} !macro b_DCLEAR {!by $fe, $15} !macro b_SPRSAV {!by $fe, $16} !macro b_COLLISION {!by $fe, $17} !macro b_BEGIN {!by $fe, $18} !macro b_BEND {!by $fe, $19} !macro b_WINDOW {!by $fe, $1a} !macro b_BOOT {!by $fe, $1b} !macro b_WIDTH {!by $fe, $1c} !macro b_SPRDEF {!by $fe, $1d} !macro b_QUIT {!by $fe, $1e} ; "unimplemented command" ; if this file gets included via "cbm/basic10.a", do not define STASH/FETCH/SWAP ; (because in v10, they all get decoded to DMA): !ifndef lib_cbm_basic10_a { !macro b_STASH {!by $fe, $1f} ; extended token $fe $20 isn't used ($20 is ' ') !macro b_FETCH {!by $fe, $21} ; extended token $fe $22 isn't used ($22 is '"') !macro b_SWAP {!by $fe, $23} } !macro b_OFF {!by $fe, $24} ; "unimplemented command" !macro b_FAST {!by $fe, $25} !macro b_SLOW {!by $fe, $26} acme-crossassembler-0.96.4/ACME_Lib/cbm/c128/000077500000000000000000000000001333777125400202715ustar00rootroot00000000000000acme-crossassembler-0.96.4/ACME_Lib/cbm/c128/basic.a000066400000000000000000000001401333777125400215070ustar00rootroot00000000000000;ACME 0.94.4 !ifdef lib_cbm_c128_basic_a !eof lib_cbm_c128_basic_a = 1 !source acme-crossassembler-0.96.4/ACME_Lib/cbm/c128/kernal.a000066400000000000000000000015721333777125400217140ustar00rootroot00000000000000;ACME 0.95 !ifdef lib_cbm_c128_kernal_a !eof lib_cbm_c128_kernal_a = 1 ; Taken from the web. ; Sorry, I can't give credit because I don't have the URL anymore. !address { k_spin_spout = $ff47 k_close_all = $ff4a k_c64mode = $ff4d k_dma_call = $ff50 k_boot_call = $ff53 k_phoenix = $ff56 k_lkupla = $ff59 ; find channel with file number A k_lkupsa = $ff5c ; find channel with secondary address Y k_swapper = $ff5f k_dlchr = $ff62 k_pfkey = $ff65 k_setbnk = $ff68 k_getcfg = $ff6b k_jsrfar = $ff6e k_jmpfar = $ff71 k_indfet = $ff74 k_indsta = $ff77 k_indcmp = $ff7a k_primm = $ff7d k_release_number = $ff80 } !source ; $ff81-$fff5 is backward compatible to older machines ; $fff6/$fff7 are unused (ff ff) !address { k_indirect128mode = $fff8 ; indirect vector, without JMP command! } ; $fffa through $ffff are cpu hardware vectors (see <6502/std.a>) acme-crossassembler-0.96.4/ACME_Lib/cbm/c128/kernel.a000066400000000000000000000001261333777125400217120ustar00rootroot00000000000000;ACME 0.95.1 !warn "Please use instead" !src acme-crossassembler-0.96.4/ACME_Lib/cbm/c128/mmu.a000066400000000000000000000164221333777125400212360ustar00rootroot00000000000000;ACME 0.95 !ifdef lib_cbm_c128_mmu_a !eof lib_cbm_c128_mmu_a = 2 ; Memory Management Unit (MMU) 8722 ; registers in i/o area (i/o needs to be enabled to access these): !address { ; configuration register mmu_cr_d500 = $d500 ; same as "mmu_cr" at $ff00. Use "mmu_cr" instead, as that is *always* available. ; preconfiguration registers (internal format just like mmu_cr) mmu_pcr_a = $d501 ; c128 kernel default is $3f (BANK 0) mmu_pcr_b = $d502 ; c128 kernel default is $7f (BANK 1) mmu_pcr_c = $d503 ; c128 kernel default is $01 (BANK 14) mmu_pcr_d = $d504 ; c128 kernel default is $41 (all system roms, with ram 1) } ; contents of cr and all four pcr: mmu_CR_RAMBANK_MASK = %##...... ; this controls which RAM bank is used in areas where RAM is enabled ;mmu_CR_RAMBANK_0 = %........ mmu_CR_RAMBANK_1 = %.#...... mmu_CR_RAMBANK_2 = %#....... ; on an unmodified c128, there is no ram bank 2 (0 will be used instead) mmu_CR_RAMBANK_3 = %##...... ; on an unmodified c128, there is no ram bank 3 (1 will be used instead) mmu_CR_HIGH_MASK = %..##.... ; this controls the "high area" (c000..ffff), but i/o (d000..dfff) is separate ;mmu_CR_HIGH_SYSROM = %........ ; editor, charset (or i/o, see below), kernel mmu_CR_HIGH_INTFUNCROM = %...#.... mmu_CR_HIGH_EXTFUNCROM = %..#..... mmu_CR_HIGH_RAM = %..##.... mmu_CR_MID_MASK = %....##.. ; this controls the "middle area" (8000..bfff) ;mmu_CR_MID_SYSROM = %........ ; this is the upper half of basic mmu_CR_MID_INTFUNCROM = %.....#.. mmu_CR_MID_EXTFUNCROM = %....#... mmu_CR_MID_RAM = %....##.. mmu_CR_LOW_MASK = %......#. ; this controls the "low area" (4000..7fff) ;mmu_CR_LOW_SYSROM = %........ ; this is the lower half of basic mmu_CR_LOW_RAM = %......#. mmu_CR_IO_MASK = %.......# ; this controls i/o space (d000..dfff) ;mmu_CR_IO_ON = %........ mmu_CR_IO_OFF = %.......# ; if i/o is off, contents depend on "high area" ; configuration register values used by C128 firmware (lookup table at $f7f0, see end of file): mmu_CR_BANK0 = $3f ; full 64 KiB ram bank 0 mmu_CR_BANK1 = $7f ; full 64 KiB ram bank 1 mmu_CR_BANK2 = $bf ; full 64 KiB ram bank 2 mmu_CR_BANK3 = $ff ; full 64 KiB ram bank 3 mmu_CR_BANK4 = $16 ; 32 KiB bank 0; 32 KiB IFROM with i/o overlay mmu_CR_BANK5 = $56 ; 32 KiB bank 1; 32 KiB IFROM with i/o overlay mmu_CR_BANK6 = $96 ; 32 KiB bank 2; 32 KiB IFROM with i/o overlay mmu_CR_BANK7 = $d6 ; 32 KiB bank 3; 32 KiB IFROM with i/o overlay mmu_CR_BANK8 = $2a ; 32 KiB bank 0; 32 KiB EFROM with i/o overlay mmu_CR_BANK9 = $6a ; 32 KiB bank 1; 32 KiB EFROM with i/o overlay mmu_CR_BANK10 = $aa ; 32 KiB bank 2; 32 KiB EFROM with i/o overlay mmu_CR_BANK11 = $ea ; 32 KiB bank 3; 32 KiB EFROM with i/o overlay mmu_CR_BANK12 = $06 ; 32 KiB bank 0; 16 KiB IFROM; 16 KiB kernel with i/o overlay mmu_CR_BANK13 = $0a ; 32 KiB bank 0; 16 KiB EFROM; 16 KiB kernel with i/o overlay mmu_CR_BANK14 = $01 ; 16 KiB bank 0; 32 KiB basic; 16 KiB kernel with font overlay mmu_CR_BANK15 = $00 ; 16 KiB bank 0; 32 KiB basic; 16 KiB kernel with i/o overlay ; An unmodified C128 does not have a "ram bank 2" or "ram bank 3". ; Whenever one of these is activated, ram banks 0 and 1 will be used instead. ; IFROM means internal function ROM (socket U36) ; EFROM means external function ROM (socket in a REU, for example) !address { ; mode configuration register mmu_mcr = $d505 } ; contents: mmu_MCR_40COLUMNS = %#....... ; (pin 48) 40/80 key: 0 means pressed, 1 means released (writable! if cleared, will always read as 0!) mmu_MCR_C64MODE = %.#...... ; (pin 47) setting this bit makes the MMU disappear from the memory map :) mmu_MCR_EXROM = %..#..... ; (pin 46) if zero on boot, system will enter c64 mode (writable!) mmu_MCR_GAME = %...#.... ; (pin 45) if zero on boot, system will enter c64 mode (writable!) mmu_MCR_FSDIR_OUTPUT = %....#... ; (pin 44) direction of fast serial bus mmu_MCR_UNUSED = %.....##. ; always set mmu_MCR_8502MODE = %.......# ; (pin 43 inverted) setting this to zero switches to Z80 cpu !address { ; ram configuration register mmu_rcr = $d506 } ; contents: mmu_RCR_VICBANK_MASK = %##...... ; this controls which RAM bank is "seen" by VIC ;mmu_RCR_VICBANK_0 = %........ mmu_RCR_VICBANK_1 = %.#...... mmu_RCR_VICBANK_2 = %#....... ; on an unmodified c128, there is no ram bank 2 (0 will be used instead) mmu_RCR_VICBANK_3 = %##...... ; on an unmodified c128, there is no ram bank 3 (1 will be used instead) mmu_RCR_RAMBLOCK_MASK = %..##.... ; on an unmodified c128, these bits are irrelevant (they select 256 KiB of 1 MiB of memory) ;mmu_RCR_RAMBLOCK_0 = %........ mmu_RCR_RAMBLOCK_1 = %...#.... ; on an unmodified c128, there is only ram block 0 mmu_RCR_RAMBLOCK_2 = %..#..... ; on an unmodified c128, there is only ram block 0 mmu_RCR_RAMBLOCK_3 = %..##.... ; on an unmodified c128, there is only ram block 0 mmu_RCR_SHARE_MASK = %....##.. ;mmu_RCR_SHARE_NONE = %........ mmu_RCR_SHARE_BOTTOM = %.....#.. ; system default mmu_RCR_SHARE_TOP = %....#... mmu_RCR_SHARESIZE_MASK = %......## ;mmu_RCR_SHARESIZE_1K = %........ ; system default mmu_RCR_SHARESIZE_4K = %.......# mmu_RCR_SHARESIZE_8K = %......#. mmu_RCR_SHARESIZE_16K = %......## !address { ; page pointers for zero page and stack: ; write to "bank" register will be latched (reading gives old value) ; until "page" register is written to as well. mmu_zp_page = $d507 ; address bits a8..a15, default $00 mmu_zp_bank = $d508 ; address bits a16..a19, default $0 (on an unmodified c128, only bit0 is meaningful) mmu_stack_page = $d509 ; address bits a8..a15, default $01 mmu_stack_bank = $d50a ; address bits a16..a19, default $0 (on an unmodified c128, only bit0 is meaningful) } mmu_PxH_UNUSED = %####.... ; always set mmu_PxH_RAMBLOCK_MASK = %....##.. ;mmu_PxH_RAMBLOCK_0 = %........ mmu_PxH_RAMBLOCK_1 = %.....#.. ; on an unmodified c128, there is only ram block 0 mmu_PxH_RAMBLOCK_2 = %....#... ; on an unmodified c128, there is only ram block 0 mmu_PxH_RAMBLOCK_3 = %....##.. ; on an unmodified c128, there is only ram block 0 mmu_PxH_RAMBANK_MASK = %......## ;mmu_PxH_RAMBANK_0 = %........ mmu_PxH_RAMBANK_1 = %.......# mmu_PxH_RAMBANK_2 = %......#. ; on an unmodified c128, there is no ram bank 2 (0 will be used instead) mmu_PxH_RAMBANK_3 = %......## ; on an unmodified c128, there is no ram bank 3 (1 will be used instead) !address { ; version register mmu_vr = $d50b ; read-only, value is $20 } mmu_VR_BANKS_MASK = %####.... ; 2 ram banks mmu_VR_VERSION_MASK = %....#### ; mmu version 0 ; reading addresses up until $d5ff returns $ff ; these registers are always available (in *every* memory configuration) unless C64 mode is entered: !address { ; configuration register mmu_cr = $ff00 ; always use this instead of $d500 ; load configuration registers: ; a read access will return the value of the corresponding preconfiguration register ; any write access will copy the value of the corresponding preconfiguration register to mmu_cr mmu_lcr_a = $ff01 ; c128 kernel default is $3f (BANK 0) mmu_lcr_b = $ff02 ; c128 kernel default is $7f (BANK 1) mmu_lcr_c = $ff03 ; c128 kernel default is $01 (BANK 14) mmu_lcr_d = $ff04 ; c128 kernel default is $41 (all system roms, with ram 1) ; the c128 ROMs contain a look-up table to convert bank numbers to their ; corresponding configuration register values: ; romc_* needs "high rom area" enabled ($c000..$ffff) romc_bank_to_cr_table = $f7f0 ; 3f 7f bf ff 16 56 96 d6 2a 6a aa ea 06 0a 01 00 } acme-crossassembler-0.96.4/ACME_Lib/cbm/c128/petscii.a000066400000000000000000000027521333777125400221010ustar00rootroot00000000000000;ACME 0.94.4 !ifdef lib_cbm_c128_petscii_a !eof lib_cbm_c128_petscii_a = 1 !source ; color codes ; vic vdc petscii_BLACK = 144: petscii_DBLACK = 144 petscii_WHITE = 5: petscii_LWHITE = 5 petscii_RED = 28: petscii_DRED = 28 petscii_CYAN = 159: petscii_LCYAN = 159 petscii_PURPLE = 156: petscii_LMAGENTA = 156 petscii_GREEN = 30: petscii_DGREEN = 30 petscii_BLUE = 31: petscii_DBLUE = 31 petscii_YELLOW = 158: petscii_LYELLOW = 158 petscii_ORANGE = 129: petscii_DMAGENTA = 129 petscii_BROWN = 149: petscii_DYELLOW = 149 petscii_LRED = 150 petscii_GRAY1 = 151: petscii_DCYAN = 151 petscii_GRAY2 = 152: petscii_LBLACK = 152: petscii_DGRAY = 152 petscii_LGREEN = 153 petscii_LBLUE = 154 petscii_GRAY3 = 155: petscii_DWHITE = 155: petscii_LGRAY = 155 ; switching character set petscii_LOCK = 11 ; forbid CBM-shift (C64 uses 8) petscii_UNLOCK = 12 ; allow CBM-shift (C64 uses 9) petscii_LOWERCASE = 14 ; switch to lowercase/uppercase character set petscii_UPPERCASE = 142 ; switch to uppercase/graphics character set ; function keys petscii_F1 = 133: petscii_F2 = 137 petscii_F3 = 134: petscii_F4 = 138 petscii_F5 = 135: petscii_F6 = 139 petscii_F7 = 136: petscii_F8 = 140 ; C128-specific stuff petscii_UNDERLINEON = 2 petscii_UNDERLINEOFF = 130 petscii_FLASHON = 15 petscii_FLASHOFF = 143 petscii_BELL = 7 petscii_TAB = 9 petscii_SETTAB = 24 petscii_LINEFEED = 10 petscii_ESCAPE = 27 petscii_SHIFTSTOP = 131 petscii_HELP = 132 acme-crossassembler-0.96.4/ACME_Lib/cbm/c128/vdc.a000066400000000000000000000177321333777125400212210ustar00rootroot00000000000000;ACME 0.95 !ifdef lib_cbm_c128_vdc_a !eof lib_cbm_c128_vdc_a = 1 ; there are three different versions of the C128's Video Display Controller: ; v0: VDC 8563 R7A in C128 and C128-D ; v1: VDC 8563 R8/R9 in C128 and C128-D ; v2: VDC 8568 in C128-DCR ; 8563 and 8568 have different pinouts, so do not try to use one of them on a ; board intended for the other! ; hardware differences: ; the 8563 uses external circuitry to convert RGBIHV to monochrome/luminance. ; the 8568 does a part of this internally and therefore needs fewer external ; components. ; the 8568 has an additional register (r37) to change sync polarities. ; software differences: ; the horizontal scroll bits of r25 differ between v0 and v1/v2: in v0, set these ; bits to zero. in v1/v2, set these bits to the same value as bits4-7 of r22. ; the additional register r37 can be ignored (as the C128 kernal does). ; access macros: !macro vdc_lda { - bit vdc_state bpl - lda vdc_data } !macro vdc_sta { - bit vdc_state bpl - sta vdc_data } !macro vdc_ldx { - bit vdc_state bpl - ldx vdc_data } !macro vdc_stx { - bit vdc_state bpl - stx vdc_data } !macro vdc_ldy { - bit vdc_state bpl - ldy vdc_data } !macro vdc_sty { - bit vdc_state bpl - sty vdc_data } ; color codes: ; These are the colors officially available on the C128 - the same names as if ; using a C64's VIC, but different codes of course. ; color name RGBI VIC equivalent vdccolor_BLACK = %.... ; 0 vdccolor_WHITE = %#### ; 15 vdccolor_RED = %#... ; 8 vdccolor_CYAN = %.### ; 7 vdccolor_PURPLE = %#.## ; 11 vdccolor_GREEN = %.#.. ; 4 vdccolor_BLUE = %..#. ; 2 vdccolor_YELLOW = %##.# ; 13 vdccolor_ORANGE = %#.#. ; 10 (on VDC, this is in fact a dark shade of purple) vdccolor_BROWN = %##.. ; 12 vdccolor_LRED = %#..# ; 9 vdccolor_GRAY1 = %.##. ; 6 (on VDC, this is in fact a dark shade of cyan) vdccolor_GRAY2 = %...# ; 1 (this is almost, but not quite, entirely black) vdccolor_LGREEN = %.#.# ; 5 vdccolor_LBLUE = %..## ; 3 vdccolor_GRAY3 = %###. ; 14 ; The following alternative names are much easier to remember when you're used ; to writing programs for the VDC: There are eight main colors, and each one is ; available in a light and a dark shade - even black and white! ; primary colors RGBI code vdccolor_DRED = %#... vdccolor_DGREEN = %.#.. vdccolor_DBLUE = %..#. vdccolor_LRED = %#..# vdccolor_LGREEN = %.#.# vdccolor_LBLUE = %..## ; secondary colors RGBI code vdccolor_LCYAN = %.### vdccolor_LMAGENTA = %#.## vdccolor_LYELLOW = %##.# vdccolor_DCYAN = %.##. vdccolor_DMAGENTA = %#.#. vdccolor_DYELLOW = %##.. ; black & white RGBI code vdccolor_DBLACK = %.... vdccolor_LBLACK = %...# vdccolor_LWHITE = %#### vdccolor_DWHITE = %###. ; if you don't like the concept of shaded black/white, then use these: ; gray level RGBI code vdccolor_BLACK = %.... ; "dark black" => "black" vdccolor_DGRAY = %...# ; "light black" => "dark grey" vdccolor_LGRAY = %###. ; "dark white" => "light grey" vdccolor_WHITE = %#### ; "light white" => "white" ; attribute flags (2rufRGBI) vdcattr_2ND = %#....... ; second character set vdcattr_REVS = %.#...... ; reverse mode vdcattr_UL = %..#..... ; underline vdcattr_FLASH = %...#.... ; flash vdcattr_R = %....#... ; red vdcattr_G = %.....#.. ; green vdcattr_B = %......#. ; blue vdcattr_I = %.......# ; intensity !address { ; direct registers vdc_state = $d600 ; READING this location yields status flags, see below vdc_reg = $d600 ; WRITING this location selects an indirect register vdc_data = $d601 ; data of selected indirect register } ; status flags in vdc_state: vdcstate_READY = %#....... ; RAM access is finished vdcstate_LIGHTPEN = %.#...... ; light pen has been activated vdcstate_IN_BORDER = %..#..... ; electron beam is in upper or lower border vdcstate_VERSIONMASK = %...##### ; vdc version (0, 1 or 2) ; indirect registers (default value, see $e179 in C128 kernal) vdcr_htotal = $00 ; 126 (127 for PAL, depends on kernal version) characters per line, minus one vdcr_columns = $01 ; 80 characters per line, actually displayed vdcr_hdisp = $01 vdcr_hsync_pos = $02 ; 102 character position to send horizontal sync in vdcr_syncwidth = $03 ; $49 4b vertical, 4b horizontal vdcr_vtotal = $04 ; 32 (39 or 38 for PAL, depends on kernal version) character lines per screen, minus one vdcr_vadjust = $05 ; 0 additional scan lines per screen (to fix timings) vdcr_lines = $06 ; 25 character lines per screen, actually displayed vdcr_vdisp = $06 vdcr_vsync_pos = $07 ; 29 (32 for PAL) character line to send vertical sync in vdcr_interlace = $08 ; 0 interlace mode (0=2=std, 1=jitter, 3=interlace) vdcr_charheight_total = $09 ; 7 5b total, minus one vdcr_crsr_start = $0a ; $20 2b mode, 5b scanline vdcr_CRSRSTART_MODE_MASK = %.##..... ; vdcr_CRSRSTART_MODE_FIXED = %........ ; fixed cursor vdcr_CRSRSTART_MODE_OFF = %..#..... ; invisible vdcr_CRSRSTART_MODE_FAST = %.#...... ; flashing with 1/16 of refresh freq vdcr_CRSRSTART_MODE_SLOW = %.##..... ; flashing with 1/32 of refresh freq vdcr_crsr_end = $0b ; 7 5b scanline vdcr_display_hi = $0c ; 0 RAM address of display buffer vdcr_display_lo = $0d ; 0 vdcr_crsr_hi = $0e ; 0 RAM address of cursor vdcr_crsr_lo = $0f ; 0 vdcr_lp_y = $10 ; -- y position of light pen (lines, plus 1) vdcr_lp_x = $11 ; -- x position if light pen (characters, plus 8) vdcr_ram_hi = $12 ; -- RAM address of register $1f vdcr_ram_lo = $13 ; -- vdcr_attr_hi = $14 ; $08 RAM address of attribute buffer vdcr_attr_lo = $15 ; $00 vdcr_charwidth = $16 ; $78 4b total minus one, 4b displayed minus one vdcr_charheight_disp = $17 ; 8 5b displayed, minus one vdcr_control_v = $18 ; $20 vertical scroll and much other stuff vdcr_CONTROLV_BLOCKMODE_MASK = %#....... ; vdcr_CONTROLV_BLOCKMODE_WRITE = %........ vdcr_CONTROLV_BLOCKMODE_COPY = %#....... vdcr_CONTROLV_REVERSESCREEN = %.#...... vdcr_CONTROLV_FLASHFREQ_MASK = %..#..... ; vdcr_CONTROLV_FLASHFREQ_FAST = %........ vdcr_CONTROLV_FLASHFREQ_SLOW = %..#..... vdcr_CONTROLV_UPSCROLL_MASK = %...##### vdcr_control_h = $19 ; $47 ($40 for vdc version 0) horizontal scroll and much other stuff vdcr_CONTROLH_MODE_MASK = %#....... ; vdcr_CONTROLH_MODE_TEXT = %........ vdcr_CONTROLH_MODE_BITMAP = %#....... vdcr_CONTROLH_ATTRIBUTES_MASK = %.#...... ; vdcr_CONTROLH_ATTRIBUTES_OFF = %........ vdcr_CONTROLH_ATTRIBUTES_ON = %.#...... vdcr_CONTROLH_SEMIGRAPHICS = %..#..... vdcr_CONTROLH_PIXELWIDTH_MASK = %...#.... ; vdcr_CONTROLH_PIXELWIDTH_NARROW = %........ vdcr_CONTROLH_PIXELWIDTH_WIDE = %...#.... vdcr_CONTROLH_LEFTSCROLL_MASK = %....#### vdcr_color = $1a ; $f0 4b foreground, 4b background vdcr_COLOR_FOREGROUND_MASK = %####.... ; only used if attributes are off vdcr_COLOR_BACKGROUND_MASK = %....#### vdcr_row_inc = $1b ; 0 address increment per row vdcr_charset = $1c ; $20 3b charset pointer, 1b RAM type, 4b unused vdcr_CHARSET_ADRESS_MASK = %###..... vdcr_CHARSET_RAMTYPE_MASK = %...#.... ; vdcr_CHARSET_RAMTYPE_16K = %........ vdcr_CHARSET_RAMTYPE_64K = %...#.... vdcr_underline = $1d ; 7 5b scanline vdcr_cycles = $1e ; -- number of write- or copy-cycles. 0 means 256. vdcr_access = $1f ; -- RAM content of address r$12/13 vdcr_source_hi = $20 ; -- RAM address of cycle start vdcr_source_lo = $21 ; -- vdcr_enable_start = $22 ; 125 column to enable display in vdcr_enable_end = $23 ; 100 column to disable display in vdcr_dram_refresh = $24 ; 5 RAM refresh rate (lower 4 bits) vdcr_sync_polarity = $25 ; -- only in VDC 8568 vdcr_HSYNC_POLARITY = %#....... vdcr_VSYNC_POLARITY = %.#...... !address { ; the c128 ROMs contain look-up tables to convert vic color values to vdc color ; values and vice-versa: ; rom4_* needs "low rom area" enabled ($4000..$7fff) ; rom8_* needs "middle rom area" enabled ($8000..$bfff) ; romc_* needs "high rom area" enabled ($c000..$ffff) rom4_vic_to_vdc_color_table = $6a4c ; 00 0f 08 07 0b 04 02 0d 0a 0c 09 06 01 05 03 0e rom8_vdc_to_vic_color_table = $81f3 ; 00 0c 06 0e 05 0d 0b 03 02 0a 08 04 09 07 0f 01 romc_vic_to_vdc_color_table = $ce5c ; 00 0f 08 07 0b 04 02 0d 0a 0c 09 06 01 05 03 0e } acme-crossassembler-0.96.4/ACME_Lib/cbm/c128/vic.a000066400000000000000000000012061333777125400212130ustar00rootroot00000000000000;ACME 0.95 !ifdef lib_cbm_c128_vic_a !eof lib_cbm_c128_vic_a = 1 !source ; registers 0..2e !address { ; registers only present in the C128 variant of this chip: vic_keyboard = $d02f vic_clock = $d030 ; the c128 ROMs contain two copies of a look-up table to convert vic color ; values to their corresponding petscii color codes: ; rom4_* needs "low rom area" enabled ($4000..$7fff) ; romc_* needs "high rom area" enabled ($c000..$ffff) rom4_vic_to_petscii_color_table = $76b5 ; 90 05 1c 9f 9c 1e 1f 9e 81 95 96 97 98 99 9a 9b romc_vic_to_petscii_color_table = $ce4c ; 90 05 1c 9f 9c 1e 1f 9e 81 95 96 97 98 99 9a 9b } acme-crossassembler-0.96.4/ACME_Lib/cbm/c128/zeropage.a000066400000000000000000000264101333777125400222520ustar00rootroot00000000000000;ACME 0.95 !ifdef lib_cbm_c128_zeropage_a !eof lib_cbm_c128_zeropage_a = 1 ; These labels and their values are given in the C128 manual. ; Label names were converted to lower case and prefixed by "z_", however. ; The following labels appear twice, only their first appearance is active: ; z_verck, given as $0c and $93 ; z_count, given as $0d and $a5 ; z_keysiz, given as $78 and $da ; z_keynum, given as $77 and $dc ; z_xcnt, given as $0110 and $0a80 ; z_hulp, given as $78 and $0aa0 ; z_keynxt, given as $dd and $1152 ; z_xsave , given as $0ab2 and $115d ; z_bitmsk, given as $da and $116d ; z_dejavu, given as $0a02 and $1221 !address { z_d8502 = $00 z_d6510 = $00 z_r8502 = $01 z_r6510 = $01 z_bank = $02 z_pc_hi = $03 z_pc_lo = $04 z_s_reg = $05 z_a_reg = $06 z_x_reg = $07 z_y_reg = $08 z_stkptr = $09 z_integr = $09 z_charac = $09 ; labels from MONITOR z_pcb = $02 z_pch = $03 z_pcl = $04 z_flgs = $05 z_acc = $06 z_xr = $07 z_yr = $08 z_sp = $09 z_endchr = $0a z_trmpos = $0b z_verck = $0c z_count = $0d z_dimflg = $0e z_valtyp = $0f z_intflg = $10 z_garbfl = $11 z_dores = $11 z_subflg = $12 z_inpflg = $13 z_domask = $14 z_tansgn = $14 z_channl = $15 z_poker = $15 z_linnum = $16 z_temppt = $18 z_lastpt = $19 z_tempst = $1b z_index = $24 z_index1 = $24 z_index2 = $26 ; given as $24 in german manual, which is probably wrong z_resho = $28 z_resmoh = $29 z_addend = $2a z_resmo = $2a z_reslo = $2b z_txttab = $2d z_vartab = $2f z_arytab = $31 z_strend = $33 z_fretop = $35 z_frespc = $37 z_max_mem_1 = $39 z_curlin = $3b z_txtptr = $3d z_form = $3f z_fndpnt = $3f z_datlin = $41 z_datptr = $43 z_inpptr = $45 z_varnam = $47 z_fdecpt = $49 z_varpnt = $49 z_lstpnt = $4b z_andmsk = $4b z_forpnt = $4b z_eormsk = $4c z_vartxt = $4d z_opptr = $4d z_opmask = $4f z_grbpnt = $50 z_tempf3 = $50 z_defpnt = $50 z_dscpnt = $52 z_helper = $55 z_jmper = $56 z_oldov = $58 z_tempf1 = $59 z_arypnt = $5a z_highds = $5a z_hightr = $5c z_tempf2 = $5e z_deccnt = $5f z_grbtop = $61 z_dptflg = $61 z_lowtr = $61 z_expsgn = $62 z_fac1 = $63 z_degree = $69 z_sgnflg = $69 z_argexp = $6a z_strng1 = $70 z_arisgn = $70 z_facov = $71 z_strng2 = $72 z_polypt = $72 z_curtol = $72 z_fbufpt = $72 z_autinc = $74 z_mvdflg = $76 z_noze = $77 z_sprnum = $77 z_keynum = $77 z_hulp = $78 z_keysiz = $78 z_syntmp = $79 z_dsdesc = $7a z_tos = $7d z_runmod = $7f z_parsts = $80 z_point = $80 z_parstx = $81 z_oldstk = $82 z_colsel = $83 z_multicolor_1 = $84 z_multicolor_2 = $85 z_foreground = $86 z_scale_x = $87 z_scale_y = $89 z_stopnb = $8b z_grapnt = $8c z_vtemp1 = $8e z_vtemp2 = $8f z_status = $90 z_stkey = $91 z_svxt = $92 ; z_verck = $93 ; already given as $0c ! z_c3po = $94 z_bsour = $95 z_syno = $96 z_xsav = $97 z_ldtnd = $98 z_dfltn = $99 z_dflto = $9a z_prty = $9b z_dpsw = $9c z_msgflg = $9d z_ptr1 = $9e z_t1 = $9e z_ptr2 = $9f z_t2 = $9f z_time = $a0 z_r2d2 = $a3 z_pcntr = $a3 z_bsour1 = $a4 ; z_count = $a5 ; already given as $0d ! z_cntdn = $a5 z_bufpt = $a6 z_inbit = $a7 z_bitci = $a8 z_rinone = $a9 z_ridata = $aa z_riprty = $ab z_sal = $ac z_sah = $ad z_eal = $ae z_eah = $af z_cmpo = $b0 z_tape1 = $b2 z_bitts = $b4 z_nxtbit = $b5 z_rodata = $b6 z_fnlen = $b7 z_la = $b8 z_sa = $b9 z_fa = $ba z_fnadr = $bb z_roprty = $bd z_fsblk = $be z_drive = $bf z_cas1 = $c0 z_stal = $c1 z_stah = $c2 z_memuss = $c3 z_data = $c5 z_ba = $c6 z_fnbank = $c7 z_ribuf = $c8 z_robuf = $ca z_keytab = $cc z_imparm = $ce z_ndx = $d0 z_kyndx = $d1 z_keyidx = $d2 z_shflag = $d3 z_sfdx = $d4 z_lstx = $d5 z_crsw = $d6 z_mode = $d7 z_graphm = $d8 z_charen = $d9 z_sedsal = $da z_sedeal = $dc z_sedt1 = $de z_sedt2 = $df ; z_keysiz = $da ; already given as $78 ! z_keylen = $db ; z_keynum = $dc ; already given as $77 ! z_keynxt = $dd z_keybnk = $de z_keytmp = $df z_bitmsk = $da z_saver = $db z_pnt = $e0 z_user = $e2 z_scbot = $e4 z_sctop = $e5 z_sclf = $e6 z_scrt = $e7 z_lsxp = $e8 z_lstp = $e9 z_indx = $ea z_tblx = $eb z_pntr = $ec z_lines = $ed z_columns = $ee z_datax = $ef z_lstchr = $f0 z_color = $f1 z_tcolor = $f2 z_rvs = $f3 z_qtsw = $f4 z_insrt = $f5 z_insflg = $f6 z_locks = $f7 z_scroll = $f8 z_beeper = $f9 z_lofbuf = $ff z_fbuffr = $0100 z_xcnt = $0110 z_dosf1l = $0111 z_dosds1 = $0112 z_dosf2l = $0113 z_dosds2 = $0114 z_dosf2a = $0115 z_dosofl = $0117 z_dosofh = $0119 z_dosla = $011b z_dosfa = $011c z_dossa = $011d z_dosrcl = $011e z_dosbnk = $011f z_dosdid = $0120 z_didchk = $0122 z_bnr = $0123 z_enr = $0124 z_dolr = $0125 z_flag = $0126 z_swe = $0127 z_usgn = $0128 z_uexp = $0129 z_vn = $012a z_chsn = $012b z_vf = $012c z_nf = $012d z_posp = $012e z_fesp = $012f z_etof = $0130 z_cform = $0131 z_sno = $0132 z_blfd = $0133 z_begfd = $0134 z_lfor = $0135 z_endfd = $0136 z_sysstk = $0137 z_buf = $0200 ; this block is not in the german C128 manual: z_fetch = $02a2 z_fetchvec = $02aa z_stash = $02af z_stavec = $02b9 z_cmpare = $02c8 z_cmpvec = $02c8 z_jsrfar = $02cd z_jmpfar = $02e3 z_esc_fn_vec = $02fc z_bnkvec = $02fe z_ierror = $0300 z_icrnch = $0304 z_iqplop = $0306 z_igone = $0308 z_ieval = $030a z_iesclk = $030c z_iescpr = $030e z_iescex = $0310 z_itime = $0312 z_iirq = $0314 z_ibrk = $0316 z_inmi = $0318 z_iopen = $031a z_iclose = $031c z_ichkin = $031e z_ichkout = $0320 z_iclrch = $0322 z_ibasin = $0324 z_ibasout = $0326 z_istop = $0328 z_igetin = $032a z_iclall = $032c z_exmon = $032e z_iload = $0330 z_isave = $0332 ; this block is not in the german C128 manual: z_ctlvec = $0334 z_shfvec = $0336 z_escvec = $0338 z_keyvec = $033a z_keychk = $033c z_decode = $033e z_decode_shift = $0340 z_decode_cbm = $0342 z_decode_ctrl = $0344 z_decode_alt = $0346 z_decode_caps = $0348 z_keyd = $034a z_tabmap = $0354 z_bitabl = $035e z_lat = $0362 z_fat = $036c z_sat = $0376 z_chrget = $0380 z_chrgot = $0386 z_qnum = $0390 z_ind_sub_ram0 = $039f z_ind_sub_ram1 = $03ab z_indin1_ram1 = $03b7 z_indin2 = $03c0 z_indtxt = $03c9 z_zero = $03d2 z_current_bank = $03d5 z_tmpdes = $03d6 z_fin_bank = $03da z_savsiz = $03db z_bits = $03df z_sprtmp_1 = $03e0 z_sprtmp_2 = $03e1 z_fg_bg = $03e2 z_fg_mc1 = $03e3 z_vicscn = $0400 z_system = $0a00 z_dejavu = $0a02 z_palnts = $0a03 z_init_status = $0a04 z_memstr = $0a05 z_memsiz = $0a07 z_irqtmp = $0a09 z_caston = $0a0b z_kika26 = $0a0c z_stupid = $0a0d z_timout = $0a0e z_enabl = $0a0f z_m51ctr = $0a10 z_m51cdr = $0a11 z_m51ajb = $0a12 z_rsstat = $0a14 z_bitnum = $0a15 z_baudof = $0a16 z_ridbe = $0a18 z_ridbs = $0a19 z_rodbs = $0a1a z_rodbe = $0a1b z_serial = $0a1c z_timer = $0a1d z_xmax = $0a20 z_pause = $0a21 z_rptflg = $0a22 z_kount = $0a23 z_delay = $0a24 z_lstshf = $0a25 z_blnon = $0a26 z_blnsw = $0a27 z_blnct = $0a28 z_gdbln = $0a29 z_gdcol = $0a2a z_curmod = $0a2b z_vm1 = $0a2c z_vm2 = $0a2d z_vm3 = $0a2e z_vm4 = $0a2f z_lintmp = $0a30 z_sav80a = $0a31 z_sav80b = $0a32 z_curcol = $0a33 z_split = $0a34 z_fnadrx = $0a35 z_palcnt = $0a36 z_speed = $0a37 z_sprites = $0a38 z_blanking = $0a39 ; this block is not in the german C128 manual: z_hold_off = $0a3a z_ldtbi_sa = $0a3b z_clr_ea_lo = $0a3c z_clr_ea_hi = $0a3d ; $a40 - $a5f: copy of data at $e0 - $ff of currently inactive screen ; $a60 - $a7f: ? ; z_xcnt = $0a80 ; already given as $0110 ! ; z_hulp = $0aa0 ; already given as $78 ! z_format = $0aaa z_length = $0aab z_msal = $0aac z_sxreg = $0aaf z_syreg = $0ab0 z_wrap = $0ab1 z_xsave = $0ab2 z_direction = $0ab3 z_temps = $0ab4 z_curbnk = $0ac0 z_pat = $0ac1 z_tbuffr = $0b00 z_rs232i = $0c00 z_rs232o = $0d00 z_pkybuf = $1000 z_pkydef = $100a z_dosstr = $1100 z_vwork = $1131 z_xypos = $1131 z_xpos = $1131 z_ypos = $1133 z_xdest = $1135 z_ydest = $1137 z_xyabs = $1139 z_xabs = $1139 z_yabs = $113b z_xysgn = $113d z_xsgn = $113d z_ysgn = $113f z_fct = $1141 z_errval = $1145 z_lesser = $1147 z_greatr = $1148 z_angsgn = $1149 z_sinval = $114a z_cosval = $114c z_angcnt = $114e z_xcircl = $1150 z_ycircl = $1152 z_xradus = $1154 z_yradus = $1156 z_rotang = $1158 z_angbeg = $115c z_angend = $115e z_xrcos = $1160 z_yrsin = $1162 z_xrsin = $1164 z_yrcos = $1166 z_xcentr = $1150 z_ycentr = $1152 z_xdist1 = $1154 z_ydist1 = $1156 z_xdist2 = $1158 z_ydist2 = $115a z_disend = $115c z_colcnt = $115e z_rowcnt = $115f z_strcnt = $1160 z_xcord1 = $1150 z_ycord1 = $1152 z_boxang = $1154 z_xcount = $1156 z_ycount = $1158 z_bxleng = $115a z_xcord2 = $115c z_ycord2 = $115e z_leylen = $1151 ; z_keynxt = $1152 ; already given as $dd ! z_strsz = $1153 z_gettyp = $1154 z_strptr = $1155 z_oldbyt = $1156 z_newbyt = $1157 z_xsize = $1159 z_ysize = $115b ; z_xsave = $115d ; already given as $0ab2 ! z_stradr = $115f z_bitidx = $1161 z_chrpag = $1168 z_bitcnt = $1169 z_scalem = $116a z_width = $116b z_filflg = $116c ; z_bitmsk = $116d ; already given as $da ! z_numcnt = $116e z_trcflg = $116f z_renum_tmp_1 = $1170 z_renum_tmp_2 = $1172 z_t3 = $1174 z_t4 = $1175 z_vtemp3 = $1177 z_vtemp4 = $1178 z_vtemp5 = $1179 z_adray1 = $117a z_adray2 = $117c z_sprite_data = $117e z_vic_save = $11d6 ; this block is not in the german C128 manual: z_upper_lower = $11eb z_upper_graphic = $11ec z_oldlin = $1200 z_oldtxt = $1202 z_puchrs = $1204 z_pufill = $1204 z_pucoma = $1205 z_pudot = $1206 z_pumony = $1207 z_errnum = $1208 z_errlin = $1209 z_trapno = $120b z_tmptrp = $120d z_errtxt = $120e z_text_top = $1210 z_max_mem_0 = $1212 z_tmptxt = $1214 z_tmplin = $1216 z_usrpok = $1218 z_rndx = $121b z_circle_segment = $1220 ; z_dejavu = $1221 ; already given as $0a02 ! z_tempo_rate = $1222 z_voices = $1223 z_ntime = $1229 z_octave = $122b z_sharp = $122c z_pitch = $122d z_voice = $122f z_wave0 = $1230 z_dnote = $1233 z_fltsav = $1234 z_fltflg = $1238 z_nibble = $1239 z_tonnum = $123a z_tonval = $123b z_parcnt = $123e z_atktab = $123f z_sustab = $1249 z_wavtab = $1253 z_pulslw = $125d z_pulshi = $1267 z_filters = $1271 z_int_trip_flag = $1276 z_int_adr_lo = $1279 z_int_adr_hi = $127c z_intval = $127f z_coltyp = $1280 z_sound_voice = $1281 z_sound_time_lo = $1282 z_sound_time_hi = $1285 z_sound_max_lo = $1288 z_sound_max_hi = $128b z_sound_min_lo = $128e z_sound_min_hi = $1291 z_sound_direction = $1294 z_sound_step_lo = $1297 z_sound_step_hi = $129a z_sound_freq_lo = $129d z_sound_freq_hi = $12a0 z_temp_time_lo = $12a3 z_temp_time_hi = $12a4 z_temp_max_lo = $12a5 z_temp_max_hi = $12a6 z_temp_min_lo = $12a7 z_temp_min_hi = $12a8 z_temp_direction = $12a9 z_temp_step_lo = $12aa z_temp_step_hi = $12ab z_temp_freq_lo = $12ac z_temp_freq_hi = $12ad z_temp_pulse_lo = $12ae z_temp_pulse_hi = $12af z_temp_waveform = $12b0 z_pot_temp_1 = $12b1 z_pot_temp_2 = $12b2 z_window_temp = $12b3 z_savram = $12b7 z_defmod = $12fa z_lincnt = $12fb z_sprite_number = $12fc ; this block is not in the german C128 manual: z_irq_wrap_flag = $12fd z_rambot = $1c00 } acme-crossassembler-0.96.4/ACME_Lib/cbm/c64/000077500000000000000000000000001333777125400202105ustar00rootroot00000000000000acme-crossassembler-0.96.4/ACME_Lib/cbm/c64/basic.a000066400000000000000000000001361333777125400214330ustar00rootroot00000000000000;ACME 0.94.4 !ifdef lib_cbm_c64_basic_a !eof lib_cbm_c64_basic_a = 1 !source acme-crossassembler-0.96.4/ACME_Lib/cbm/c64/cia1.a000066400000000000000000000006071333777125400211720ustar00rootroot00000000000000;ACME 0.95 !ifdef lib_cbm_c64_cia1_a !eof lib_cbm_c64_cia1_a = 1 !address { cia1_pra = $dc00 cia1_prb = $dc01 cia1_ddra = $dc02 cia1_ddrb = $dc03 cia1_ta_lo = $dc04 cia1_ta_hi = $dc05 cia1_tb_lo = $dc06 cia1_tb_hi = $dc07 cia1_tod10ths = $dc08 cia1_todsec = $dc09 cia1_todmin = $dc0a cia1_todhr = $dc0b cia1_sdr = $dc0c cia1_icr = $dc0d cia1_cra = $dc0e cia1_crb = $dc0f } acme-crossassembler-0.96.4/ACME_Lib/cbm/c64/cia2.a000066400000000000000000000006071333777125400211730ustar00rootroot00000000000000;ACME 0.95 !ifdef lib_cbm_c64_cia2_a !eof lib_cbm_c64_cia2_a = 1 !address { cia2_pra = $dd00 cia2_prb = $dd01 cia2_ddra = $dd02 cia2_ddrb = $dd03 cia2_ta_lo = $dd04 cia2_ta_hi = $dd05 cia2_tb_lo = $dd06 cia2_tb_hi = $dd07 cia2_tod10ths = $dd08 cia2_todsec = $dd09 cia2_todmin = $dd0a cia2_todhr = $dd0b cia2_sdr = $dd0c cia2_icr = $dd0d cia2_cra = $dd0e cia2_crb = $dd0f } acme-crossassembler-0.96.4/ACME_Lib/cbm/c64/float.a000066400000000000000000000104001333777125400214520ustar00rootroot00000000000000;ACME 0.95 !ifdef lib_cbm_c64_float_a !eof lib_cbm_c64_float_a = 1 ; Here are some definitions to help you call the floating-point functions of the ; C64's BASIC ROM. They work on "float registers", which are actually just ; structures in zero page: !address { fac1_base = $61 ; base address of floating-point accumulator 1 fac2_base = $69 ; base address of floating-point accumulator 2 } ; There is really no need to use these addresses directly when calling the ROM ; functions. You'd only need the addresses when using . !source ; include macro to store floats in six-byte FLPT format !source ; include macro to store floats in five-byte MFLPT format ; convenience macros: ; some float functions need a memory address in A (low) and Y (high) !macro movAY .adr { lda #<.adr ldy #>.adr } ; ...or in X (low) and Y (high) !macro movXY .adr { ldx #<.adr ldy #>.adr } ; other float functions expect or output a value in Y (low) and A (high) !macro movYA .val { ldy #<.val lda #>.val } !macro ldYA .adr { ldy .adr lda .adr + 1 } !macro stYA .adr { sty .adr sta .adr + 1 } ; ...or in X (low) and A (high) !macro ldXA .adr { ldx .adr lda .adr + 1 } !address { ; constants in five-byte "mflpt" format mflpt_pi = $aea8 ; 3.1415926... mflpt_minus32768 = $b1a5 ; -32768 mflpt_1 = $b9bc ; 1 mflpt_half_sqr2 = $b9d6 ; SQR(2) / 2 mflpt_sqr2 = $b9db ; SQR(2) mflpt_minus_point5 = $b9e0 ; -.5 mflpt_log_2 = $b9e5 ; LOG(2) mflpt_10 = $baf9 ; 10 mflpt_99999999 = $bdb3 ; 99 999 999 mflpt_999999999 = $bdb8 ; 999 999 999 mflpt_1000000000 = $bdbd ; 1 000 000 000 mflpt_point5 = $bf11 ; .5, also known as 1 / 2 mflpt_log_2_reciprocal = $bfbf ; 1 / LOG(2) mflpt_half_pi = $e2e0 ; PI / 2 mflpt_double_pi = $e2e5 ; 2 * PI (also see $e309) mflpt_point25 = $e2ea ; .25, also known as 1 / 4 mflpt_2_pi = $e309 ; 2 * PI (also see $e2e5) ; functions - a few points to note: ; fac1/2 might get clobbered even if not mentioned in the function's name, ; because stuff like fac1_times_memAY will load the value from memory ; into fac2 first. ; for subtraction and division, the left operand is in fac2, the right operand in fac1. fac1_print = $aabc ; print string representation of contents of fac1 fac1_to_signedYA = $b1aa ; might throw ILLEGAL QUANTITY fac1_to_signed16 = $b1bf ; might throw ILLEGAL QUANTITY fac1_read_signedYA = $b391 ; convert 16 bit signed int to float fac1_read_unsignedY = $b3a2 ; convert 8 bit unsigned int to float fac1_read_string = $b7b5 ; $22/23 must point to string, A must be string length fac1_to_unsignedYA = $b7f7 ; might throw ILLEGAL QUANTITY (result is also in $14/15) fac1_add_point5 = $b849 ; for rounding, call this before fac1_int fac1_memAY_minus_fac1 = $b850 ; subtract fac1 from mflpt value fac1_fac2_minus_fac1 = $b853 fac1_add_memAY = $b867 ; add mflpt value fac1_add_fac2 = $b86a fac1_log = $b9ea ; LOG() fac1_times_memAY = $ba28 ; multiply by mflpt value fac2_read_memAY = $ba8c ; load mflpt value from memory into fac2 fac2_read_mem_via0x22ptr = $ba90 ; load mflpt value from memory into fac2 fac1_times_10 = $bae2 fac1_divide_by_10 = $bafe ; CAUTION: result is always positive! fac1_divide_memAY_by_fac1 = $bb0f ; divide mflpt value by fac1 value fac1_read_memAY = $bba2 ; load mflpt value from memory into fac1 fac1_read_mem_via0x22ptr = $bba6 ; load mflpt value from memory into fac1 fac1_to_memXY = $bbd4 ; store fac1 to memory as mflpt fac1_read_fac2 = $bbfc ; copy fac2 to fac1 fac2_read_fac1 = $bc0c ; copy fac1 to fac2 fac1_sign_to_A = $bc2b ; $ff, $0, $1 for negative, zero, positive fac1_sgn = $bc39 ; SGN() fac1_abs = $bc58 ; ABS() fac1_compare_to_memAY = $bc5b ; compare to mflpt value in memory fac1_to_signed32 = $bc9b fac1_int = $bccc ; INT() fac1_read_string0 = $bcf3 ; use b7b5 instead; this only works after calling CHRGET fac1_print_unsignedXA = $bdcd fac1_to_string = $bddd ; string is stored at $0100 (address returned in AY) fac1_sqr = $bf71 ; SQR() fac1_fac2_to_the_power_of_memAY = $bf78 fac1_negate = $bfb4 fac1_exp = $bfed ; EXP() ; end of basic rom jumps to start of kernel rom! fac1_rnd = $e097 ; RND() fac1_cos = $e264 ; COS() fac1_sin = $e26b ; SIN() fac1_tan = $e2b4 ; TAN() fac1_atn = $e30e ; ATN() } acme-crossassembler-0.96.4/ACME_Lib/cbm/c64/georam.a000066400000000000000000000027501333777125400216300ustar00rootroot00000000000000;ACME 0.95 !ifdef lib_cbm_c64_georam_a !eof lib_cbm_c64_georam_a = 1 ; This file contains definitions for accessing the "GeoRAM" RAM expansion and ; its clones (BBG, BBU, NeoRAM, ...) ; These units allow access to a single page of memory (256 bytes) visible at ; address $de00 in i/o space. Writing to registers allows to change which ; memory page is visible at that location. !address { ; memory page georam_page = $de00 ; control registers (write-only, these registers can not be read) georam_track = $dffe ; 0..63, i.e. the lower six bits are significant georam_sector = $dfff ; 0..31, i.e. the lower five bits are significant } ; these are the official addresses - actually the registers are accessible ; *everywhere* at $dfxx, but using these locations does not clash with the ; registers of a Commodore REU. ; Upgraded units and clones may have more memory, in those cases the registers ; will have more significant bits. ; I could have called the registers "row" and "column" instead of track and ; sector, but the fact that this device was designed with one six-bit register ; and one five-bit register (instead of one eight-bit register and one ; three-bit register) tells me that this was meant as an easily programmable ; RAM disk: A 1541 disk has 35 tracks with (at most) 21 sectors. Numbers in ; these ranges can be written to the GeoRAM registers without the need to ; shift bits around. ; Knowing this is a handy way of remembering the number of significant bits of ; GeoRAM registers. acme-crossassembler-0.96.4/ACME_Lib/cbm/c64/kernal.a000066400000000000000000000003661333777125400216330ustar00rootroot00000000000000;ACME 0.95.1 !ifdef lib_cbm_c64_kernal_a !eof lib_cbm_c64_kernal_a = 1 !source ; from $ff81 through $fff5 ; $fff6 through $fff9 are developers' initials ("RRBY") ; $fffa through $ffff are cpu hardware vectors (see <6502/std.a>) acme-crossassembler-0.96.4/ACME_Lib/cbm/c64/kernel.a000066400000000000000000000001241333777125400216270ustar00rootroot00000000000000;ACME 0.95.1 !warn "Please use instead" !src acme-crossassembler-0.96.4/ACME_Lib/cbm/c64/memcopy.a000066400000000000000000000017411333777125400220260ustar00rootroot00000000000000;ACME 0.95.7 !ifdef lib_cbm_c64_memcopy_a !eof lib_cbm_c64_memcopy_a = 1 ; this macro inserts code to copy a memory block. ; it calls a function from the basic interpreter, so: ; - BASIC ROM must be banked in ; - the source block must be readable (so no RAM hidden under BASIC, Kernal, or I/O) ; - the target block must be writable (so no RAM hidden under I/O) ; higher addresses are copied first, so: ; - moving data to higher addresses works even if areas overlap ; - moving data to lower addresses only works if areas do not overlap !macro basic_memcopy .src_start, .src_end, .target_start { !address { .z_target_end = $58 .z_src_end = $5a .z_src_start = $5f .fn = $a3bf } lda #<.src_start ldx #>.src_start sta .z_src_start stx .z_src_start + 1 lda #<.src_end ldx #>.src_end sta .z_src_end stx .z_src_end + 1 lda #<(.target_start + .src_end - .src_start) ldx #>(.target_start + .src_end - .src_start) sta .z_target_end stx .z_target_end + 1 jsr .fn } acme-crossassembler-0.96.4/ACME_Lib/cbm/c64/petscii.a000066400000000000000000000015711333777125400220160ustar00rootroot00000000000000;ACME 0.94.4 !ifdef lib_cbm_c64_petscii_a !eof lib_cbm_c64_petscii_a = 1 !source ; color codes petscii_BLACK = 144 petscii_WHITE = 5 petscii_RED = 28 petscii_CYAN = 159 petscii_PURPLE = 156 petscii_GREEN = 30 petscii_BLUE = 31 petscii_YELLOW = 158 petscii_ORANGE = 129 petscii_BROWN = 149 petscii_LRED = 150 petscii_GRAY1 = 151 petscii_GRAY2 = 152 petscii_LGREEN = 153 petscii_LBLUE = 154 petscii_GRAY3 = 155 ; switching character set petscii_LOCK = 8 ; forbid CBM-shift (C128 uses 11) petscii_UNLOCK = 9 ; allow CBM-shift (C128 uses 12) petscii_LOWERCASE = 14 ; switch to lowercase/uppercase character set petscii_UPPERCASE = 142 ; switch to uppercase/graphics character set ; function keys petscii_F1 = 133: petscii_F2 = 137 petscii_F3 = 134: petscii_F4 = 138 petscii_F5 = 135: petscii_F6 = 139 petscii_F7 = 136: petscii_F8 = 140 acme-crossassembler-0.96.4/ACME_Lib/cbm/c64/reu.a000066400000000000000000000041471333777125400211530ustar00rootroot00000000000000;ACME 0.95 !ifdef lib_cbm_c64_reu_a !eof lib_cbm_c64_reu_a = 1 ; This file contains definitions for accessing a RAM Expansion Unit (REU) of ; type 1700, 1764, 1750 and compatible. These units contain a chip called REC ; (RAM Expansion Controller) capable of direct memory access (DMA). ; Standard base address of control registers is $df00 in i/o space. !address { ; status register rec_status = $df00 ; reading will clear IRQ, END and ERROR bits } rec_STATUS_IRQ = %#....... rec_STATUS_END = %.#...... rec_STATUS_ERROR = %..#..... ; for verify command rec_STATUS_TYPE = %...#.... ; chip type (do not use to determine unit size!) rec_STATUS_VERSION = %....#### !address { ; command register rec_command = $df01 } rec_COMMAND_EXECUTE = %#....... ;reserved = %.#...... rec_COMMAND_RELOAD = %..#..... rec_COMMAND_IMMEDIATELY = %...#.... ; do not wait for $ff00 write ;reserved = %....##.. rec_COMMAND_MODE_MASK = %......## ; bit mask for the four modes rec_COMMAND_MODE_STASH = %........ ; computer-to-REU rec_COMMAND_MODE_FETCH = %.......# ; REU-to-computer rec_COMMAND_MODE_SWAP = %......#. ; exchange rec_COMMAND_MODE_VERIFY = %......## ; compare rec_COMMAND_STASH = %#.#..... ; these wait for $ff00 before rec_COMMAND_FETCH = %#.#....# ; starting and then reload values. !address { ; internal address (computer RAM) rec_int_low = $df02 rec_int_high = $df03 ; external address (expansion RAM) rec_ext_low = $df04 rec_ext_high = $df05 rec_ext_bank = $df06 ; A stock 1700 unit has two banks (128 KiB). ; A stock 1764 unit has four banks (256 KiB). ; A stock 1750 unit has eight banks (512 KiB). ; Upgraded units and clones may have more, but the REC chip will always ; "wrap around" after eight banks if crossing bank borders! ; amount of bytes to process rec_amount_low = $df07 rec_amount_high = $df08 ; when to request interrupts rec_irqctrl = $df09 } rec_IRQCTRL_ENABLE = %#....... rec_IRQCTRL_ON_END = %.#...... rec_IRQCTRL_ON_ERROR = %..#..... ; for verify errors !address { ; address control (set to zero for normal operation) rec_addrctrl = $df0a } rec_ADDRCTRL_FIX_INT = %#....... rec_ADDRCTRL_FIX_EXT = %.#...... acme-crossassembler-0.96.4/ACME_Lib/cbm/c64/sid.a000066400000000000000000000037771333777125400211470ustar00rootroot00000000000000;ACME 0.95 !ifdef lib_cbm_c64_sid_a !eof lib_cbm_c64_sid_a = 1 !address { ; write-only registers: ; voice 1: sid_v1_freq_lo = $d400 sid_v1_freq_hi = $d401 sid_v1_width_lo = $d402 sid_v1_width_hi = $d403 sid_v1_control = $d404 ; see below for bits sid_v1_attack_decay = $d405 ; hi-nibble: attack length, low-nibble: decay length sid_v1_sustain_release = $d406 ; hi-nibble: sustain volumne, low-nibble: release length ; voice 2: sid_v2_freq_lo = $d407 sid_v2_freq_hi = $d408 sid_v2_width_lo = $d409 sid_v2_width_hi = $d40a sid_v2_control = $d40b ; see below for bits sid_v2_attack_decay = $d40c ; hi-nibble: attack length, low-nibble: decay length sid_v2_sustain_release = $d40d ; hi-nibble: sustain volumne, low-nibble: release length ; voice 3: sid_v3_freq_lo = $d40e sid_v3_freq_hi = $d40f sid_v3_width_lo = $d410 sid_v3_width_hi = $d411 sid_v3_control = $d412 ; see below for bits sid_v3_attack_decay = $d413 ; hi-nibble: attack length, low-nibble: decay length sid_v3_sustain_release = $d414 ; hi-nibble: sustain volumne, low-nibble: release length } ; voice control bits: sid_VOICECONTROL_NOISE = %#....... sid_VOICECONTROL_RECTANGLE = %.#...... sid_VOICECONTROL_SAWTOOTH = %..#..... sid_VOICECONTROL_TRIANGLE = %...#.... sid_VOICECONTROL_DISABLE_RESET = %....#... ; 1 = disable voice, reset noise generator sid_VOICECONTROL_RINGMODULATION = %.....#.. sid_VOICECONTROL_SYNC = %......#. sid_VOICECONTROL_ON = %.......# ; 0 = release, 1 = attack/sustain/decay !address { ; registers shared by all voices: sid_filter_cutoff_lo = $d415 ; only bits 0/1/2! sid_filter_cutoff_hi = $d416 sid_filter_control = $d417 ; hi-nibble: resonance, lo-nibble: filter ext/v3/v2/v1 sid_filter_volume = $d418 ; hi-nibble: filter mode (disable v3, high, band, low), lo-nibble: volume ; read-only registers: sid_potx = $d419 sid_poty = $d41a sid_v3_waveform_output = $d41b sid_v3_adsr_output = $d41c } ; Do not use the shadow copies of these registers at $d5xx, $d6xx or $d7xx: ; on a C128 they do not exist! acme-crossassembler-0.96.4/ACME_Lib/cbm/c64/vic.a000066400000000000000000000041621333777125400211360ustar00rootroot00000000000000;ACME 0.95 !ifdef lib_cbm_c64_vic_a !eof lib_cbm_c64_vic_a = 1 ; color codes viccolor_BLACK = $0 viccolor_WHITE = $1 viccolor_RED = $2 viccolor_CYAN = $3 viccolor_PURPLE = $4 viccolor_GREEN = $5 viccolor_BLUE = $6 viccolor_YELLOW = $7 viccolor_ORANGE = $8 viccolor_BROWN = $9 viccolor_LRED = $a viccolor_GRAY1 = $b viccolor_GRAY2 = $c viccolor_LGREEN = $d viccolor_LBLUE = $e viccolor_GRAY3 = $f !address { ; register addresses vic_xs0 = $d000 vic_ys0 = $d001 vic_xs1 = $d002 vic_ys1 = $d003 vic_xs2 = $d004 vic_ys2 = $d005 vic_xs3 = $d006 vic_ys3 = $d007 vic_xs4 = $d008 vic_ys4 = $d009 vic_xs5 = $d00a vic_ys5 = $d00b vic_xs6 = $d00c vic_ys6 = $d00d vic_xs7 = $d00e vic_ys7 = $d00f vic_msb_xs = $d010 vic_controlv = $d011 ; vertical control (and much other stuff) vic_line = $d012 ; raster line vic_xlp = $d013 ; light pen coordinates vic_ylp = $d014 vic_sactive = $d015 ; sprites: active vic_controlh = $d016 ; horizontal control (and much other stuff) vic_sdy = $d017 ; sprites: double height vic_ram = $d018 ; RAM pointer vic_irq = $d019 vic_irqmask = $d01a vic_sback = $d01b ; sprites: background mode vic_smc = $d01c ; sprites: multi color mode vic_sdx = $d01d ; sprites: double width vic_ss_collided = $d01e ; sprite-sprite collision detect vic_sd_collided = $d01f ; sprite-data collision detect ; color registers vic_cborder = $d020 ; border color vic_cbg = $d021 ; general background color vic_cbg0 = $d021 vic_cbg1 = $d022 ; background color 1 (for EBC and MC text mode) vic_cbg2 = $d023 ; background color 2 (for EBC and MC text mode) vic_cbg3 = $d024 ; background color 3 (for EBC mode) vic_sc01 = $d025 ; sprite color for MC-bitpattern %01 vic_sc11 = $d026 ; sprite color for MC-bitpattern %11 vic_cs0 = $d027 ; sprite colors vic_cs1 = $d028 vic_cs2 = $d029 vic_cs3 = $d02a vic_cs4 = $d02b vic_cs5 = $d02c vic_cs6 = $d02d vic_cs7 = $d02e } ; See for the C128's two additional registers at $d02f/$d030. ; They are accessible even in C64 mode and $d030 can garble the video output, ; so be careful not to write to it accidentally in a C64 program! acme-crossassembler-0.96.4/ACME_Lib/cbm/c65/000077500000000000000000000000001333777125400202115ustar00rootroot00000000000000acme-crossassembler-0.96.4/ACME_Lib/cbm/c65/basic.a000066400000000000000000000001371333777125400214350ustar00rootroot00000000000000;ACME 0.94.4 !ifdef lib_cbm_c65_basic_a !eof lib_cbm_c65_basic_a = 1 !source acme-crossassembler-0.96.4/ACME_Lib/cbm/flpt.a000066400000000000000000000062231333777125400207260ustar00rootroot00000000000000;ACME 0.94.5 !ifdef lib_cbm_flpt_a !eof lib_cbm_flpt_a = 1 ; CAUTION! The Commodore BASIC interpreter uses two different formats for ; handling floating-point values, so do not confuse them: ; The "float registers" fac1 and fac2 (actually structures in zero page) use a ; six-byte format commonly known as "flpt" (floating point). ; When storing values in variables (or reading values from ROM), a compressed ; five-byte format is used, commonly known as "mflpt" (memory floating point). ; This file contains a macro for writing floating point numbers in the six-byte ; "flpt" format, where the sign bit occupies the sixth byte. ; There are no interpreter functions to use this format, so you will have to ; write your own functions for "copy-mem-to-fac1", "copy-fac2-to-mem" etc. ; Use the macro like this: ; +flpt 3.1415926 ; each use will take up six bytes of memory ; now for the technical stuff (stop reading right now if you value your sanity) ; six-byte layout in memory: ; eeeeeeee 1mmmmmmm mmmmmmmm mmmmmmmm mmmmmmmm sxxxxxxx ; eight bits exponent, 32 bits mantissa with leading '1', sign byte ; exponent byte: ; exponent has a bias of 128 (128 means the decimal point is right before the mantissa's leading digit) ; if exponent is zero, number value is considered to be zero, regardless of mantissa ; exponents 1..128 are for values < 1 ; exponents 129..255 are for values >= 1 ; mantissa: ; mantissa is stored big-endian(!) ; the mantissa's leading digit is always '1' (unless the whole value represents zero) ; sign byte: ; most significant bit is sign: 0 means positive number, 1 means negative number ; the seven lower bits are unused ; so logically, this is equivalent to: ; + .1mmmmmmm mmmmmmmm mmmmmmmm mmmmmmmm * 2^(eeeeeeee - 128) if sign bit is 0 ; - .1mmmmmmm mmmmmmmm mmmmmmmm mmmmmmmm * 2^(eeeeeeee - 128) if sign bit is 1 ; this is ugly, but it gets the job done ; (if it's stupid, but it works, then it's not stupid) !macro flpt .value { !set .float = float(.value) ; make sure to do passes until value is defined !ifndef .float { !by $ff, $ff, $ff, $ff, $ff, $ff ; six place holder bytes } else { ; value is defined, so split up into sign and non-negative value !if .float < 0 { !set .sign = $80 !set .float = -.float } else { !set .sign = $00 } !if .float = 0 { !by 0, 0, 0, 0, 0, 0 ; six zeroes (zero is represented by all bits zero) } else { ; split up into exponent and mantissa !set .exponent = 128 + 32 ; 128 is cbm's bias, 32 is this algo's bias ; if mantissa is too large, shift right and adjust exponent !do while .float >= (2.0 ^ 32.0) { !set .float = .float >> 1 !set .exponent = .exponent + 1 } ; if mantissa is too small, shift left and adjust exponent !do while .float < (2.0 ^ 31.0) { !set .float = .float << 1 !set .exponent = .exponent - 1 } !if .exponent < 1 { !warn "FLPT underflow, using zero instead" !set .float = 0 !set .exponent = 0 !set .sign = 0 } !if .exponent > 255 { !error "FLPT overflow" } !by .exponent !by 255 & int(.float >> 24) !by 255 & int(.float >> 16) !by 255 & int(.float >> 8) !by 255 & int(.float) !by .sign } } } acme-crossassembler-0.96.4/ACME_Lib/cbm/ioerror.a000066400000000000000000000006351333777125400214430ustar00rootroot00000000000000;ACME 0.95.1 !ifdef lib_cbm_ioerror_a !eof lib_cbm_ioerror_a = 1 ; if kernel i/o routine exits with carry set, A holds one of these: ioerror_BREAK = 0 ioerror_TOO_MANY_FILES = 1 ioerror_FILE_OPEN = 2 ioerror_FILE_NOT_OPEN = 3 ioerror_FILE_NOT_FOUND = 4 ioerror_DEVICE_NOT_PRESENT = 5 ioerror_NOT_INPUT_FILE = 6 ioerror_NOT_OUTPUT_FILE = 7 ioerror_MISSING_FILE_NAME = 8 ioerror_ILLEGAL_DEVICE_NUMBER = 9 acme-crossassembler-0.96.4/ACME_Lib/cbm/kernal.a000066400000000000000000000026471333777125400212430ustar00rootroot00000000000000;ACME 0.95 !ifdef lib_cbm_kernal_a !eof lib_cbm_kernal_a = 1 ; Taken from the web. ; Sorry, I can't give credit because I don't have the URL anymore. ; There are alternative names for some calls. !address { k_cint = $ff81 k_ioinit = $ff84 k_ramtas = $ff87 k_restor = $ff8a k_vector = $ff8d k_setmsg = $ff90 k_secnd = $ff93 k_tksa = $ff96 k_memtop = $ff99 k_membot = $ff9c k_key = $ff9f k_settmo = $ffa2 k_iecin = $ffa5:k_acptr = $ffa5 k_iecout = $ffa8:k_ciout = $ffa8 k_untalk = $ffab:k_untlk = $ffab k_unlisten = $ffae:k_unlsn = $ffae k_listen = $ffb1:k_listn = $ffb1 k_talk = $ffb4 k_readss = $ffb7 k_setlfs = $ffba k_setnam = $ffbd ; A is length, X is ptr-low, Y is ptr-high k_open = $ffc0 k_close = $ffc3:k_close_A = $ffc3 k_chkin = $ffc6:k_chkin_X = $ffc6 k_chkout = $ffc9:k_chkout_X = $ffc9:k_ckout = $ffc9 k_clrchn = $ffcc:k_clrch = $ffcc k_chrin = $ffcf:k_basin = $ffcf k_chrout = $ffd2:k_basout = $ffd2:k_bsout = $ffd2 k_load = $ffd5:k_load_AXY = $ffd5 ; A means verify, YYXX is desired load address (if channel == 0), returns end+1 in YYXX k_save = $ffd8:k_save_AXY = $ffd8 ; A is zp address of start ptr(!), YYXX is end address (+1) k_settim = $ffdb k_rdtim = $ffde k_stop = $ffe1 k_getin = $ffe4:k_get = $ffe4 k_clall = $ffe7 k_udtim = $ffea k_scrorg = $ffed k_plot = $fff0:k_plot_CXY = $fff0 ; get/set cursor (to set, clear carry. X/Y are y/x!) k_iobase = $fff3 } acme-crossassembler-0.96.4/ACME_Lib/cbm/kernel.a000066400000000000000000000001141333777125400212320ustar00rootroot00000000000000;ACME 0.95.1 !warn "Please use instead" !src acme-crossassembler-0.96.4/ACME_Lib/cbm/mflpt.a000066400000000000000000000056461333777125400211130ustar00rootroot00000000000000;ACME 0.94.5 !ifdef lib_cbm_mflpt_a !eof lib_cbm_mflpt_a = 1 ; CAUTION! The Commodore BASIC interpreter uses two different formats for ; handling floating-point values, so do not confuse them: ; The "float registers" fac1 and fac2 (actually structures in zero page) use a ; six-byte format commonly known as "flpt" (floating point). ; When storing values in variables (or reading values from ROM), a compressed ; five-byte format is used, commonly known as "mflpt" (memory floating point). ; This file contains a macro for writing floating point numbers in the five-byte ; "mflpt" format, where the sign bit is packed into the mantissa. ; Several interpreter functions use this format (see ). ; Use the macro like this: ; +mflpt 3.1415926 ; each use will take up five bytes of memory ; now for the technical stuff (stop reading right now if you value your sanity) ; five-byte layout in memory: ; eeeeeeee smmmmmmm mmmmmmmm mmmmmmmm mmmmmmmm ; eight bits exponent, 32 bits mantissa with sign bit overlay ; exponent byte: ; exponent has a bias of 128 (128 means the decimal point is right before the mantissa's leading digit) ; if exponent is zero, number value is considered to be zero, regardless of mantissa ; exponents 1..128 are for values < 1 ; exponents 129..255 are for values >= 1 ; mantissa: ; mantissa is stored big-endian(!) ; the mantissa's mandatory leading '1' is replaced by the sign bit ; so logically, this is equivalent to: ; + .1mmmmmmm mmmmmmmm mmmmmmmm mmmmmmmm * 2^(eeeeeeee - 128) if sign bit is 0 ; - .1mmmmmmm mmmmmmmm mmmmmmmm mmmmmmmm * 2^(eeeeeeee - 128) if sign bit is 1 ; this is ugly, but it gets the job done ; (if it's stupid, but it works, then it's not stupid) !macro mflpt .value { !set .float = float(.value) ; make sure to do passes until value is defined !ifndef .float { !by $ff, $ff, $ff, $ff, $ff ; five place holder bytes } else { ; value is defined, so split up into sign and non-negative value !if .float < 0 { !set .sign = $80 !set .float = -.float } else { !set .sign = $00 } !if .float = 0 { !by 0, 0, 0, 0, 0 ; five zeroes (zero is represented by all bits zero) } else { ; split up into exponent and mantissa !set .exponent = 128 + 32 ; 128 is cbm's bias, 32 is this algo's bias ; if mantissa is too large, shift right and adjust exponent !do while .float >= (2.0 ^ 32.0) { !set .float = .float >> 1 !set .exponent = .exponent + 1 } ; if mantissa is too small, shift left and adjust exponent !do while .float < (2.0 ^ 31.0) { !set .float = .float << 1 !set .exponent = .exponent - 1 } !if .exponent < 1 { !warn "MFLPT underflow, using zero instead" !set .float = 0 !set .exponent = 0 !set .sign = 0 } !if .exponent > 255 { !error "MFLPT overflow" } !by .exponent !by (127 & int(.float >> 24)) | .sign !by 255 & int(.float >> 16) !by 255 & int(.float >> 8) !by 255 & int(.float) } } } acme-crossassembler-0.96.4/ACME_Lib/cbm/petscii.a000066400000000000000000000007471333777125400214260ustar00rootroot00000000000000;ACME 0.94.4 !ifdef lib_cbm_petscii_a !eof lib_cbm_petscii_a = 1 ; cursor movements petscii_UP = 145 petscii_DOWN = 17 petscii_LEFT = 157 petscii_RIGHT = 29 ; other control codes petscii_STOP = 3 petscii_CR = 13 ; carriage return petscii_CS = 141 ; carriage return, shifted petscii_REVSON = 18 petscii_REVSOFF = 146 petscii_HOME = 19 petscii_CLEAR = 147 petscii_DEL = 20: petscii_BACKSPACE = 20 petscii_INST = 148: petscii_INSERT = 148 petscii_SHIFTSPACE = 160 acme-crossassembler-0.96.4/contrib/000077500000000000000000000000001333777125400172205ustar00rootroot00000000000000acme-crossassembler-0.96.4/contrib/joe_syntax/000077500000000000000000000000001333777125400214035ustar00rootroot00000000000000acme-crossassembler-0.96.4/contrib/joe_syntax/INSTALL000066400000000000000000000017641333777125400224440ustar00rootroot00000000000000Just to make sure: The syntax file and this help text come with ABSOLUTELY NO WARRANTY! If you destroy your system, don't come whining to me. -------------------- systemwide install -------------------- 1) Copy the syntax file to the correct directory by typing: cp acme.jsf /etc/joe/syntax/ 2) Add the following lines to the "SECOND SECTION" of "/etc/joe/joerc": *.a -indentc 9 -istep 1 -autoindent -syntax acme 3) done! -------------- user install -------------- 1) Copy the syntax file to the correct directory by typing: mkdir -p ~/.joe/syntax cp acme.jsf ~/.joe/syntax/ 2) If you do not have a ~/.joerc file yet, create one by typing: if [ -e ~/.joerc ] ; then echo :include /etc/joe/joerc > ~/.joerc ; fi 3) Add the following lines to the "SECOND SECTION" of your .joerc file: *.a -indentc 9 -istep 1 -autoindent -syntax acme 4) done! --------- testing --------- After installing the syntax file, open the file "color.a" in joe to check whether it works (and to see what it looks like). acme-crossassembler-0.96.4/contrib/joe_syntax/acme.jsf000066400000000000000000000121541333777125400230170ustar00rootroot00000000000000# JOE syntax highlight file for ACME assembly language # new in version 3: added jml, jsl # new in version 4: added !warn, !error, !serious # new in version 5: changed mnemo colors # new in version 6: added !ifndef, !addr # new in version 7: added !symbollist # define colors # # bold inverse blink dim underline # white cyan magenta blue yellow green red black # bg_white bg_cyan bg_magenta bg_blue bg_yellow bg_green bg_red bg_black =Idle =Ident =Anon bold =Bad bold red =Call bold =Comment green =Constant cyan =Keyword bold =Pseudo bold =Mnemo6502 bold yellow =PCMnemo6502 bold red =Mnemo6510 bg_red bold yellow =PCMnemo6510 bg_red bold red =Mnemo65c02 bg_cyan bold yellow =PCMnemo65c02 bg_cyan bold red =Mnemo65816 bg_blue bold yellow =PCMnemo65816 bg_blue bold red :reset Idle * idle noeat " \t" reset :idle Idle * idle ";" line_comment recolor=-1 ":{\n" reset "!.a-zA-Z_-" checkstring recolor=-1 buffer "+" anonf_or_macro recolor=-1 "-" anonb recolor=-1 "0" got_zero recolor=-1 "%" binary recolor=-1 "&" octal recolor=-1 "1-9" decimal recolor=-1 "$" hex recolor=-1 "'" char recolor=-1 "\"" string recolor=-1 # *= "*" # ",:=()><[]*&|!~+\-%^" control recolor=-1 :line_comment Comment * line_comment "\n" reset :call Call * idle noeat "a-zA-Z0-9-" call :anonf_or_macro Anon * idle noeat "+" anonf ".a-zA-Z0-9-" call recolor=-2 :anonf Anon * idle noeat "+" anonf :anonb Anon * idle noeat "-" anonb :got_zero Constant * idle noeat "xX" hex "0-9" decimal noeat :binary Constant * idle noeat "01.#" binary "2-9" badnum noeat :octal Constant * idle noeat "0-7" octal "89" badnum noeat :hex Constant * idle noeat "0-9A-Fa-f" hex :decimal Constant * idle noeat "0-9" decimal :badnum Bad * idle noeat "0-9" badnum :string Constant * string "\"" idle :char Constant * char "'" idle :ident Idle * idle noeat "a-zA-Z0-9_" ident :checkstring Ident * idle noeat istrings "!8" pseudo "!08" pseudo "!by" pseudo "!byte" pseudo "!16" pseudo "!wo" pseudo "!word" pseudo "!24" pseudo "!32" pseudo "!tx" pseudo "!text" pseudo "!raw" pseudo "!pet" pseudo "!scr" pseudo "!scrxor" pseudo "!ct" pseudo "!convtab" pseudo "!fi" pseudo "!fill" pseudo "!zn" pseudo "!zone" pseudo "!sl" pseudo "!symbollist" pseudo "!src" pseudo "!source" pseudo "!bin" pseudo "!binary" pseudo "!eof" pseudo "!endoffile" pseudo "!pseudopc" pseudo "!align" pseudo "!cpu" pseudo "!to" pseudo "!set" pseudo "!macro" pseudo "!if" pseudo "!do" pseudo "!for" pseudo "!ifdef" pseudo "!ifndef" pseudo "!al" pseudo "!as" pseudo "!rl" pseudo "!rs" pseudo "!initmem" pseudo "!warn" pseudo "!error" pseudo "!serious" pseudo "!addr" pseudo "!address" pseudo "ora" mnemo6502 "asl" mnemo6502 "and" mnemo6502 "rol" mnemo6502 "eor" mnemo6502 "lsr" mnemo6502 "adc" mnemo6502 "ror" mnemo6502 "sta" mnemo6502 "stx" mnemo6502 "lda" mnemo6502 "ldx" mnemo6502 "cmp" mnemo6502 "dec" mnemo6502 "sbc" mnemo6502 "inc" mnemo6502 "bit" mnemo6502 "cpx" mnemo6502 "cpy" mnemo6502 "ldy" mnemo6502 "sty" mnemo6502 "php" mnemo6502 "clc" mnemo6502 "plp" mnemo6502 "sec" mnemo6502 "pha" mnemo6502 "cli" mnemo6502 "pla" mnemo6502 "sei" mnemo6502 "dey" mnemo6502 "txa" mnemo6502 "tya" mnemo6502 "txs" mnemo6502 "tay" mnemo6502 "tax" mnemo6502 "clv" mnemo6502 "tsx" mnemo6502 "iny" mnemo6502 "dex" mnemo6502 "cld" mnemo6502 "inx" mnemo6502 "nop" mnemo6502 "sed" mnemo6502 "brk" pcmnemo6502 "jmp" pcmnemo6502 "jsr" pcmnemo6502 "bpl" pcmnemo6502 "bmi" pcmnemo6502 "bvc" pcmnemo6502 "bvs" pcmnemo6502 "bcc" pcmnemo6502 "bcs" pcmnemo6502 "bne" pcmnemo6502 "beq" pcmnemo6502 "rti" pcmnemo6502 "rts" pcmnemo6502 "phy" mnemo65c02 "ply" mnemo65c02 "phx" mnemo65c02 "plx" mnemo65c02 "tsb" mnemo65c02 "trb" mnemo65c02 "stz" mnemo65c02 "bra" pcmnemo65c02 "wai" mnemo65816 "pei" mnemo65816 "per" mnemo65816 "mvp" mnemo65816 "mvn" mnemo65816 "rep" mnemo65816 "sep" mnemo65816 "pea" mnemo65816 "phd" mnemo65816 "tcs" mnemo65816 "pld" mnemo65816 "tsc" mnemo65816 "wdm" mnemo65816 "phk" mnemo65816 "tcd" mnemo65816 "tdc" mnemo65816 "phb" mnemo65816 "txy" mnemo65816 "plb" mnemo65816 "tyx" mnemo65816 "xba" mnemo65816 "xce" mnemo65816 "brl" pcmnemo65816 "cop" pcmnemo65816 "jml" pcmnemo65816 "jsl" pcmnemo65816 "rtl" pcmnemo65816 "stp" pcmnemo65816 "slo" mnemo6510 "rla" mnemo6510 "sre" mnemo6510 "rra" mnemo6510 "sax" mnemo6510 "lax" mnemo6510 "dcp" mnemo6510 "isc" mnemo6510 "anc" mnemo6510 "asr" mnemo6510 "arr" mnemo6510 "sbx" mnemo6510 "dop" mnemo6510 "top" mnemo6510 "lxa" mnemo6510 "jam" pcmnemo6510 "else" keyword "until" keyword "while" keyword done "!a-zA-Z0-9" checkstring # " \t" idle noeat :pseudo Pseudo * idle noeat :mnemo6502 Mnemo6502 * idle noeat :pcmnemo6502 PCMnemo6502 * idle noeat :mnemo65c02 Mnemo65c02 * idle noeat :pcmnemo65c02 PCMnemo65c02 * idle noeat :mnemo65816 Mnemo65816 * idle noeat :pcmnemo65816 PCMnemo65816 * idle noeat :mnemo6510 Mnemo6510 * idle noeat :pcmnemo6510 PCMnemo6510 * idle noeat :keyword Keyword * idle noeat acme-crossassembler-0.96.4/contrib/joe_syntax/color.a000066400000000000000000000014051333777125400226630ustar00rootroot00000000000000;ACME 0.91 ; comments are green !serious "This file is not meant to be assembled." binary1=%00001000 ; label names are grey, constants are cyan binary2=%....#... octal=&0123456789 ; bad constants are bold red decimal=63 hex1=0xcd hex2=$ef !sl "labeldump.l" ; strings are cyan *=$1300 +dings ; macro calls are bold else ; keyword: bold !eof ; pseudo: bold -- ; anonymous labels should be bold (white) ; 6502 mnemonics nop ; normal ones are yellow rts ; PC-changing ones are red ; illegals dop ; most of them are yellow on red jam ; this single one's red on red. Guess why. ; 65c02 extensions stz ; normal ones are yellow on cyan bra ; PC-changing ones (just "BRA") are red ; 65816 extensions xce ; yellow on blue cop ; PC-changing ones are red acme-crossassembler-0.96.4/contrib/toacme/000077500000000000000000000000001333777125400204705ustar00rootroot00000000000000acme-crossassembler-0.96.4/contrib/toacme/docs/000077500000000000000000000000001333777125400214205ustar00rootroot00000000000000acme-crossassembler-0.96.4/contrib/toacme/docs/CHANGES000066400000000000000000000024331333777125400224150ustar00rootroot00000000000000 2015-02-04 Release 0.12 Added "VisAss" converter mode. Removed "umlaut fixing" and just went ahead with UTF-8. Fixed home page (used the ACME sourceforge page). 2012-10-08 Release 0.11 Beautified source, and added space after ',' in addressing modes. 2006-10-04 Release 0.10 Adjusted support for illegals to latest changes in ACME. 2006-03-12 Release 0.9 Improved "Giga Assembler" mode. Thanks to Andreas Paul for his help. Fixed unimportant bug: Attempted to close output file twice. 2005-06-16 Release 0.8 Merged all converters into one program ("toacme"). 2005-06-08 Release 0.7 2005-06-07 Release 0.6 2005-06-02 Release 0.5 Added converters for Flash8-AssBlaster and Giga-Assembler. Added minimal disassembler. 2003-02-18 Release 0.4 beta Now "marked labels" will be converted to global labels, "unmarked labels" will be converted to local labels. Now macro calls are converted correctly. 2003-02-12 Release 0.4 alpha Thanks to Stefan Hübner for sending fixes for AssBlaster macro problems and label scope. 2000-05-09 Release 0.03 Attempt on disassembly mode - quickly aborted :) Illegal opcodes are commented out when generated. 1999-08-17? Release 0.02 (indentation now two TABs instead of one) Added DOS version. 1999-08-15 Release 0.01 (RISCOS version) acme-crossassembler-0.96.4/contrib/toacme/docs/COPYING000066400000000000000000000431271333777125400224620ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. acme-crossassembler-0.96.4/contrib/toacme/docs/INSTALL000066400000000000000000000012731333777125400224540ustar00rootroot00000000000000 ToACME ...source code file converter for ACME Compiling and installing the executable --------------------------------------- Change into the directory "toacme/src" that was created when unpacking the archive and simply type "make". This will compile the sources and produce an executable file. If you have root access: Change into superuser mode using "su" and type "make install" to move the executable to the appropriate directory (system-wide install). If you don't have root access: Type "make userinstall" to move the executable to your "~/bin" directory (user-specific install) Feel free to adjust the Makefile to your specific needs. acme-crossassembler-0.96.4/contrib/toacme/docs/Known problems.txt000066400000000000000000000061041333777125400250620ustar00rootroot00000000000000 VisAss, AssBlaster and Flash8-AssBlaster stuff ---------------------------------------------- The pseudo opcode "\kc" cannot be converted, because ACME does not have a matching pseudo opcode. The converter will insert an explanatory message in the output file. The pseudo opcode "\st" is converted to "!eof". To decide whether this makes sense or not is left as an exercise for the reader. "Marked" AssBlaster labels are converted to global ACME labels. "Unmarked" AssBlaster labels are converted to local ACME labels. If your AssBlaster sources contain marked (!) labels which read like mnemonics ("^lda", "^nop" "^sed" or the like), ACME will interpret the converted versions like instructions. THE CONVERTER DOES NOT HANDLE THIS PROBLEM YET, so you will have to change such label names by hand. If binaries produced by ACME and AssBlaster are the same size, you don't have this problem. AssBlaster allows the characters '[', ']' and '^' to be used in label names. These are converted to uppercase letters, which does not cause any problems - it just looks a bit strange... :) Empty comments (semicolons at end-of-line) are stripped. AssBlaster claims to support the 6510's undocumented ("illegal") instructions. Actually, when I checked, it confused some of the mnemonics ("asr" and "arr"), and for "lax" and "aax" (aka "sax") it actually generated wrong opcodes for some addressing modes. There's a slight problem with macros: Source codes that assemble fine with AssBlaster *might* give "label not defined" errors after ACME conversion. This is because AssBlaster macros can access all labels, while ACME macros can only access global labels (besides their arguments, of course). To fix such errors, you'll have to a) make the macro code reference global labels instead of local ones and b) define those global labels, of course. AssBlaster ACME (unfixed) ACME (fixed by you) \md chk.c1,c2 !macro chk .c1,.c2 { !macro chk .c1,.c2 { lda #c1 lda #.c1 lda #.c1 ldx #c2 ldx #.c2 ldx #.c2 jsr sub jsr .sub jsr sub ;<-FIX \de } } \ma chk.5,7 +chk 5,7 +chk 5,7 rts rts rts sub: .sub .sub rts; later rts; later sub ;<-FIX rts; later Hypra-Ass and Giga-Ass stuff ---------------------------- The following pseudo opcodes cannot be converted, because ACME does not have any matching pseudo opcode. The converter will insert an explanatory message in the output file. .on (uses BASIC line numbers) .go (uses BASIC line numbers) .co (for chained assembly) .dp (format rules) .li (list on printer) .st (stop output) Empty comments (semicolons at end-of-line) are stripped. There's a problem with macros: ACME macro parameters should be *local* labels, but in Hypra-Ass and Giga-Ass, all labels are global. THE CONVERTER DOES NOT HANDLE THIS PROBLEM, so you will have to fix the macros manually: Hypra-Ass ACME (unfixed) ACME (fixed by you) .ma chk (c1,c2) !macro chk c1,c2 { !macro chk .c1,.c2 { lda #c1 lda #c1 lda #.c1 ldx #c2 ldx #c2 ldx #.c2 jsr sub jsr sub jsr sub .rt } } acme-crossassembler-0.96.4/contrib/toacme/docs/README000066400000000000000000000056461333777125400223130ustar00rootroot00000000000000 ToACME ...source code file converter for ACME Copyright --------- ToACME - a source code converter for the ACME crossassembler Copyright (C) 1998-2016 Marco Baye This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Introduction ------------ ToACME is a file converter for the ACME crossassembler. It is meant to be a helpful tool for people switching from using another assembler to using ACME. In such cases, ToACME can be used to convert the source code files to ACME format. Syntax and use -------------- ./toacme FORMAT_ID INPUT_FILE OUTPUT_FILE Calling ToACME without any arguments will show a short message containing copyright information and a list of all known input formats. If called with three arguments, ToACME will interpret the first one as the format ID. It will then try to convert the input file, writing the result to the output file. Please keep in mind that this program cannot cope with *all* features other assemblers may use. So after having converted your sources, don't delete the original ones! Make sure the conversion worked by assembling the new sources using ACME and then comparing the resulting binaries with the ones your previous assembler produced. Known input formats ------------------- Currently, ToACME supports these input file formats: Format ID: source file format quality -------------------------------------------------- object object code files poor hypra C64: Hypra-Assembler ok giga C64: Giga-Assembler ok vis C64: VisAss untested ab3 C64: AssBlaster 3.0 to 3.2 good f8ab C64: Flash8-AssBlaster ok prof C64: Professional Assembler poor (work in progress) Contacting the author --------------------- The newest version can be found at the ACME homepage: http://sourceforge.net/projects/acme-crossass/ If you want to report a bug or make a suggestion, then simply send an email to marco@baye.de Credits ------- Thanks to Stefan Hübner for fixing the AssBlaster macro conversion code. Thanks to Andreas Paul for helping with the Giga-Assembler mode. Thanks to Arndt Dettke for helping with the Hypra-Assembler mode. Thanks to Hoogo for helping with the Professional Assembler mode. acme-crossassembler-0.96.4/contrib/toacme/src/000077500000000000000000000000001333777125400212575ustar00rootroot00000000000000acme-crossassembler-0.96.4/contrib/toacme/src/Makefile000066400000000000000000000025541333777125400227250ustar00rootroot00000000000000CFLAGS = -O3 -Wall -Wstrict-prototypes #LIBS = -lm CC = gcc RM = rm #SRC = PROGS = toacme BINDIR = /usr/local/bin USERBIN = $(HOME)/bin all: $(PROGS) vis.o: config.h acme.h io.h mnemo.h scr2iso.h vis.c ab3.o: config.h ab.h acme.h io.h mnemo.h scr2iso.h ab3.c ab.o: config.h ab.h acme.h io.h scr2iso.h ab.c f8ab.o: config.h ab.h acme.h io.h mnemo.h scr2iso.h f8ab.c giga.o: config.h acme.h gighyp.h io.h mnemo.h pet2iso.h giga.c gighyp.o: config.h acme.h io.h pet2iso.h gighyp.h gighyp.c hypra.o: config.h acme.h gighyp.h io.h pet2iso.h hypra.c obj.o: config.h acme.h io.h mnemo.h obj.c acme.o: config.h acme.h acme.c main.o: config.h version.h main.c mnemo.o: config.h mnemo.c pet2iso.o: config.h pet2iso.h pet2iso.c platform.o: config.h platform.h platform.c prof.o: config.h prof.c scr2iso.o: config.h scr2iso.h scr2iso.c version.o: config.h version.c toacme: vis.o ab.o ab3.o acme.o f8ab.o giga.o gighyp.o hypra.o io.o main.o mnemo.o obj.o pet2iso.o platform.o prof.o scr2iso.o version.o $(CC) $(LIBS) $(CFLAGS) -o toacme vis.o ab.o ab3.o acme.o f8ab.o giga.o gighyp.o hypra.o io.o main.o mnemo.o obj.o pet2iso.o platform.o prof.o scr2iso.o version.o strip toacme clean: -$(RM) -f *.o $(PROGS) *~ core install: all install -d $(BINDIR) install $(PROGS) $(BINDIR) userinstall: all install -d $(USERBIN) install $(PROGS) $(USERBIN) # DO NOT DELETE acme-crossassembler-0.96.4/contrib/toacme/src/Makefile.dos000066400000000000000000000026501333777125400235060ustar00rootroot00000000000000CFLAGS = -Wall -s -Wstrict-prototypes #LIBS = -lm CC = gcc RM = rm #SRC = PROGS = toacme #BINDIR = /usr/local/bin #USERBIN = $(HOME)/bin all: $(PROGS) vis.o: config.h acme.h io.h mnemo.h scr2iso.h vis.c ab3.o: config.h ab.h acme.h io.h mnemo.h scr2iso.h ab3.c ab.o: config.h ab.h acme.h io.h scr2iso.h ab.c f8ab.o: config.h ab.h acme.h io.h mnemo.h scr2iso.h f8ab.c giga.o: config.h acme.h gighyp.h io.h mnemo.h pet2iso.h giga.c gighyp.o: config.h acme.h io.h pet2iso.h gighyp.h gighyp.c hypra.o: config.h acme.h gighyp.h io.h pet2iso.h hypra.c obj.o: config.h acme.h io.h mnemo.h obj.c acme.o: config.h acme.h acme.c main.o: config.h version.h main.c mnemo.o: config.h mnemo.c pet2iso.o: config.h pet2iso.h pet2iso.c platform.o: config.h platform.h platform.c scr2iso.o: config.h scr2iso.h scr2iso.c version.o: config.h version.c toacme: vis.o ab.o ab3.o acme.o f8ab.o giga.o gighyp.o hypra.o io.o main.o mnemo.o obj.o pet2iso.o platform.o scr2iso.o version.o $(CC) $(LIBS) $(CFLAGS) -o toacme.out vis.o ab.o ab3.o acme.o f8ab.o giga.o gighyp.o hypra.o io.o main.o mnemo.o obj.o pet2iso.o platform.o scr2iso.o version.o copy /b \djgpp\bin\pmodstub.exe + toacme.out toacme_p.exe djp toacme.exe djp toacme_p.exe clean: del *.o # -$(RM) -f *.o $(PROGS) *~ core #install: all # install -d $(BINDIR) # install $(PROGS) $(BINDIR) #userinstall: all # install -d $(USERBIN) # install $(PROGS) $(USERBIN) # DO NOT DELETE acme-crossassembler-0.96.4/contrib/toacme/src/Makefile.riscos000066400000000000000000000026241333777125400242240ustar00rootroot00000000000000CFLAGS = -O3 -Wall -Wstrict-prototypes -mthrowback -mlibscl -mno-poke-function-name #LIBS = -lm CC = gcc RM = rm #SRC = PROGS = toacme #BINDIR = /usr/local/bin #USERBIN = $(HOME)/bin all: $(PROGS) vis.o: config.h acme.h io.h mnemo.h scr2iso.h vis.c ab3.o: config.h ab.h acme.h io.h mnemo.h scr2iso.h ab3.c ab.o: config.h ab.h acme.h io.h scr2iso.h ab.c f8ab.o: config.h ab.h acme.h io.h mnemo.h scr2iso.h f8ab.c giga.o: config.h acme.h gighyp.h io.h mnemo.h pet2iso.h giga.c gighyp.o: config.h acme.h io.h pet2iso.h gighyp.h gighyp.c hypra.o: config.h acme.h gighyp.h io.h pet2iso.h hypra.c obj.o: config.h acme.h io.h mnemo.h obj.c acme.o: config.h acme.h acme.c main.o: config.h version.h main.c mnemo.o: config.h mnemo.c pet2iso.o: config.h pet2iso.h pet2iso.c platform.o: config.h platform.h platform.c scr2iso.o: config.h scr2iso.h scr2iso.c version.o: config.h version.c toacme: vis.o ab.o ab3.o acme.o f8ab.o giga.o gighyp.o hypra.o io.o main.o mnemo.o obj.o pet2iso.o platform.o scr2iso.o version.o $(CC) $(LIBS) $(CFLAGS) -o !Unsqueezed vis.o ab.o ab3.o acme.o f8ab.o giga.o gighyp.o hypra.o io.o main.o mnemo.o obj.o pet2iso.o platform.o scr2iso.o version.o Squeeze -f -v !Unsqueezed toacme #clean: # -$(RM) -f *.o $(PROGS) *~ core #install: all # install -d $(BINDIR) # install $(PROGS) $(BINDIR) #userinstall: all # install -d $(USERBIN) # install $(PROGS) $(USERBIN) # DO NOT DELETE acme-crossassembler-0.96.4/contrib/toacme/src/ab.c000066400000000000000000000374771333777125400220270ustar00rootroot00000000000000// ToACME - converts other source codes to ACME format. // Copyright (C) 1999-2015 Marco Baye // Have a look at "main.c" for further info // // stuff needed for "VisAss", "AssBlaster 3.x" and/or "Flash8-AssBlaster" #include #include "ab.h" #include "acme.h" #include "mnemo.h" #include "io.h" #include "scr2iso.h" // comparison: // VisAss AssBlaster 3.x F8-AssBlaster // 00- Mnemonics? // 48- PseudoOps // 80..86 Mnemonics 80..db Mnemonics // 87..95 Illegals // 96..c7 Mnemonics // c8..d4 PseudoOps dc..ec PseudoOps // d5..fe unused ed..fe unused // ff line mark ff line mark ff line mark // Mnemonic table in VisAss/AssBlaster 3.x order const char *visass_ab3_mnemonic_table[] = { NULL, // unused MnemonicCPX, MnemonicCPY, MnemonicLDX, MnemonicLDY, MnemonicSTX, MnemonicSTY, //============================= start of illegals ============================= MnemonicSAX, // broken in VisAss/AB3, see docs (called AAX) MnemonicASR, // broken in VisAss/AB3, see docs MnemonicARR, // broken in VisAss/AB3, see docs MnemonicSBX, // (called AXS) MnemonicDCP, MnemonicDOP, // ACME uses a different opcode MnemonicISC, MnemonicJAM, // ACME uses a different opcode (called KIL) "!error \"See the ToACME docs about the illegal opcode LAR.\";", // broken in VisAss/AB3? see docs MnemonicLAX, // broken in VisAss/AB3, see docs MnemonicRLA, MnemonicRRA, MnemonicSLO, MnemonicSRE, MnemonicTOP, // ACME uses a different opcode //============================== end of illegals ============================== MnemonicADC, MnemonicAND, MnemonicASL, MnemonicBIT, MnemonicBCS, MnemonicBEQ, MnemonicBCC, MnemonicBMI, MnemonicBNE, MnemonicBPL, MnemonicBVS, MnemonicBVC, MnemonicBRK, MnemonicCLC, MnemonicCLD, MnemonicCLI, MnemonicCLV, MnemonicCMP, MnemonicDEC, MnemonicDEX, MnemonicDEY, MnemonicEOR, MnemonicINC, MnemonicINX, MnemonicINY, MnemonicJMP, MnemonicJSR, MnemonicLDA, MnemonicLSR, MnemonicNOP, MnemonicORA, MnemonicPHA, MnemonicPHP, MnemonicPLA, MnemonicPLP, MnemonicROL, MnemonicROR, MnemonicRTI, MnemonicRTS, MnemonicSBC, MnemonicSEC, MnemonicSED, MnemonicSEI, MnemonicSTA, MnemonicTAX, MnemonicTAY, MnemonicTSX, MnemonicTXA, MnemonicTXS, MnemonicTYA, }; // PseudoOpcode table in VisAss/AssBlaster 3.x order const char *visass_ab3_pseudo_opcode_table[] = { NULL, // la NULL because ACME does not need a pseudo opcode for label defs ACME_set_pc, // ba ACME_po_byte, // by ACME_po_fill, // br ACME_po_pet, // tx ACME_po_macro, // md see AB_PSEUDOOFFSET_MACRODEF ACME_endmacro, // me ACME_macro_call, // ma see AB_PSEUDOOFFSET_MACROCALL ACME_po_eof, // st ACME_po_scr, // ts ACME_po_to, // to see AB_PSEUDOOFFSET_OUTFILE ACME_po_word, // wo "; ToACME: Cannot convert \\kc.\n", // kc nothing // "nothing" }; // constants const char nothing[] = "doesnotmatter"; // rename to visass_nopseudoopcode void visass_ab3_illegals(void) { IO_put_string( "; ToACME: Adding pseudo opcode to enable undocumented (\"illegal\") opcodes:\n" "\t!cpu 6510\n" "; ToACME: Support for illegal opcodes is somewhat broken in VisAss/AssBlaster.\n" "; ToACME: Make sure you read the ToACME docs to know what you'll have to\n" "; ToACME: look out for.\n" "; ToACME: Should work: DCP, DOP, ISC, JAM (called KIL in VisAss/AssBlaster),\n" "; ToACME: RLA, RRA, SBX (was called AXS in AssBlaster), SLO, SRE, TOP.\n" "; ToACME: Trouble: ARR, ASR, LAX, SAX (called AAX in VisAss/AssBlaster).\n" ); } // constants // generate error/warning messages const char error_unknown_addressing[] = "Conversion failed: AssBlaster file contains unknown addressing mode.\n"; const char error_unknown_compression[] = "Conversion failed: AssBlaster file contains unknown number compression.\n"; const char warning_unknown_number_format[] = "Warning: AssBlaster file uses unknown number format. Fallback to hexadecimal.\n"; #define SCREENCODE_UPARROW (0x1e) // replacement characters for problematic label names #define AB_LABELSPECIAL_NUL ('O') // AssBlaster uses only lower case #define AB_LABELSPECIAL_LEFT ('L') // characters for labels, so these #define AB_LABELSPECIAL_BACK ('B') // shouldn't cause any clashes. #define AB_LABELSPECIAL_RIGHT ('R') #define AB_LABELSPECIAL_UP ('A') // meaning of input bytes // 0x01-0x1f lower case screen codes (used for label names and comments) #define AB_SPACE 0x20 // 0x20-0x3a special characters #define AB_COMMENT 0x3b // 0x3c-0x40 unused ? // 0x41-0x5f upper case screen codes (used for comments) // 0x60-0x7f unused ? // 0x80-0xec differ between AssBlaster 3.x and Flash8-AssBlaster // 0xed-0xfe unused ? // 0xff end-of-line #define AB_PSEUDOOFFSET_MACRODEF 5 // in AB3 and F8AB #define AB_PSEUDOOFFSET_MACROCALL 7 // indices in PO table #define AB_PSEUDOOFFSET_OUTFILE 10 // are equal // after mnemonic or pseudo opcode, numbers may follow: #define AB_NUMVAL_FLAGBIT 0x80 // indicates packed number // pre- and postfixes for addressing modes // don't care whether argument is 8, 16 or 24 bits wide const char *addressing_modes[][2] = { {"", "" }, // ($00=%.....) implied {" ", "" }, // ($01=%....1) absolute {" ", ", x" }, // ($02=%...1.) absolute,x {" ", ", y" }, // ($03=%...11) absolute,y {" #", "" }, // ($04=%..1..) immediate {NULL, NULL }, // ($05=%..1.1) unused (indirect-y) {NULL, NULL }, // ($06=%..11.) unused (indirect-y) {NULL, NULL }, // ($07=%..111) unused (indirect-y) {" (", "), y" }, // ($08=%.1...) indirect-y {" (", ", x)" }, // ($09=%.1..1) indirect-x {" ", "" }, // ($0a=%.1.1.) relative (=absolute, actually) {" (", ")" }, // ($0b=%.1.11) indirect // above: used by both AB3 and F8AB (except $0a, which is no longer // used by F8AB. But it's indistinguishable from $01 anyway). // FIXME - what does AB3 do with the other unused addressing modes? // I think old AB3 sources may also use mode 0c! // below: used by F8AB only {NULL, NULL }, // ($0c=%.11..) unused (indirect-x) {" [", "]" }, // ($0d=%.11.1) indirect long {NULL, NULL }, // ($0e=%.111.) unused (absolute) {NULL, NULL }, // ($0f=%.1111) unused (absolute-x) {" ", "" }, // ($10=%1....) MVP/MVN in F8AB: arg1.arg2 #define MVP_MVN_ADDRMODE 0x10 {NULL, NULL }, // ($11=%1...1) unused (indirect) {NULL, NULL }, // ($12=%1..1.) unused (indirect long) {" [", "], y" }, // ($13=%1..11) indirect-y long {NULL, NULL }, // ($14=%1.1..) unused (absolute) {" ", ", s" }, // ($15=%1.1.1) stack-relative {" (", ", s), y" }, // ($16=%1.11.) stack-relative-indirect-y // from here on, unused (indirect-y) // addressing mode $10 (for MVP/MVN) is displayed and stored by F8AB // as "arg1.arg2" instead of "arg1,arg2". Therefore the following // constant is used to fix it on-the-fly. }; // variables struct vab *conf; // functions // static void generate_errors(int err_bits) { if (err_bits & AB_ERRBIT_UNKNOWN_ADDRMODE) { fputs(error_unknown_addressing, stderr); fprintf(global_output_stream, "; ToACME: %s", error_unknown_addressing); } if (err_bits & AB_ERRBIT_UNKNOWN_NUMBER_COMPRESSION) { fputs(error_unknown_compression, stderr); fprintf(global_output_stream, "; ToACME: %s", error_unknown_compression); } if (err_bits & AB_ERRBIT_UNKNOWN_NUMBER_FORMAT) { fputs(warning_unknown_number_format, stderr); fprintf(global_output_stream, "; ToACME: %s", warning_unknown_number_format); } } // convert macro/label name character. // AssBlaster allows '^', '[' and ']' in names, so replace these chars. static char conv_name_char(char byte) { byte = SCR2ISO(byte); switch (byte) { case 0x40: return AB_LABELSPECIAL_NUL; case '[': return AB_LABELSPECIAL_LEFT; case '\\': return AB_LABELSPECIAL_BACK; case ']': return AB_LABELSPECIAL_RIGHT; case '^': return AB_LABELSPECIAL_UP; default: return byte; } } // output binary representation of value void AB_output_binary(unsigned long int value) { int mask = 128; if (value > 0xff) AB_output_binary(value >> 8); value &= 0xff; while (mask) { IO_put_byte((value & mask) ? '1' : '0'); mask >>= 1; } } // output hex representation of value void AB_output_hexadecimal(unsigned long int value) { if (value > 0xff) AB_output_hexadecimal(value >> 8); IO_put_low_byte_hex(value); } // convert and send macro/label name (until illegal character comes along) static void pipe_global_name(void) { while ((GotByte < 0x20) || ((GotByte >= '0') && (GotByte <= '9'))) { IO_put_byte(conv_name_char(GotByte)); IO_get_byte(); } } // convert and send label name (until illegal character comes along) // level 1 static void pipe_name(void) { if (conf->flags & VABFLAG_ADD_DOT) { // Dieser kleine Hack macht alle lokalen ABL-Labels // Auch unter ACME lokal. nur mit '^' global markierte // Labels werden auch global übernommen ... if (GotByte == SCREENCODE_UPARROW) IO_get_byte(); // global: ^witharrow => witharrow else IO_put_byte('.'); // local: allothers => .allothers } pipe_global_name(); // this does exactly what is needed } // parse quoted strings static void parse_quoted(void) // now GotByte = unhandled opening quote { IO_put_byte('"'); IO_get_byte(); while ((GotByte != AB_ENDOFLINE) && (GotByte != '"')) { IO_put_byte(SCR2ISO(GotByte)); IO_get_byte(); } IO_put_byte('"'); // closing quote is handled, but EndOfLine must remain unhandled if (GotByte == '"') IO_get_byte(); } // parse label names, quoted strings, operators, literal values etc. // read until AB_ENDOFLINE or AB_COMMENT. Returns error bits. // level 1 // AB uses a full stop character ('.') in some inconvenient places, for example // after macro names (at definitions and calls) and in the MVP/MVN addressing // mode. The kluge variable "dot_replacement" is used to replace the '.' // character with the correct character for ACME. static int parse_unspecified(char dot_replacement) { int err_bits = 0; while ((GotByte != AB_ENDOFLINE) && (GotByte != AB_COMMENT)) { // kluge: replace '.' character with current replacement and // remember not to replace anymore from now on. if (GotByte == '.') { GotByte = dot_replacement; // use replacement dot_replacement = '.'; // in future, keep } if (GotByte & AB_NUMVAL_FLAGBIT) { err_bits |= conf->number_parser(); continue; } if (GotByte < 0x20) { pipe_name(); continue; } if (GotByte == '"') { parse_quoted(); continue; } IO_put_byte(SCR2ISO(GotByte)); IO_get_byte(); } return err_bits; } // parse macro call or start of definition (beware of full stops). // returns error bits. static int parse_macro_stuff(void) // now GotByte = unhandled byte { // I guess local macros are useless, so don't // do the scope fixing as for macro names! pipe_global_name(); return parse_unspecified(SPACE); // output macro arguments } // process mnemonics (real opcodes). returns error bits. // level 1 static int parse_mnemo(int mnemonic_offset) { const char *mnemonic, *pre, *post; int addressing_mode, dot_replacement = '.', err_bits = 0; if (conf->address_mode_count > 2) { addressing_mode = IO_get_byte(); // get addressing mode } else { addressing_mode = 1; // dummy mode for VisAss } IO_get_byte(); // and fetch next (not handled here) mnemonic = conf->mnemonics[mnemonic_offset]; if (mnemonic == NULL) { fputs("Found unused mnemo code in input file.\n", stderr); mnemonic = "!error \"ToACME found unused mnemo code in input file\":"; } fprintf(global_output_stream, "\t\t%s", mnemonic); // determine prefix and postfix of addressing mode if (addressing_mode < conf->address_mode_count) { pre = addressing_modes[addressing_mode][0]; post = addressing_modes[addressing_mode][1]; if (addressing_mode == MVP_MVN_ADDRMODE) dot_replacement = ','; // replace '.' with ',' } else { pre = NULL; post = NULL; } // if addressing mode is invalid, set error bit // output prefix (or space if invalid) if ((pre == NULL) || (post == NULL)) { err_bits |= AB_ERRBIT_UNKNOWN_ADDRMODE; fprintf(stderr, "Found an unknown addressing mode bit pattern ($%x). Please tell my programmer.\n", addressing_mode); } if (pre) IO_put_string(pre); else IO_put_byte(SPACE); err_bits |= parse_unspecified(dot_replacement); // output arg if (post) IO_put_string(post); return err_bits; } // process pseudo opcodes. returns error bits. // level 1 static int parse_pseudo_opcode(int pseudo_offset) { const char *pseudo_opcode; int err_bits = 0; IO_get_byte(); // and fetch next (not handled here) pseudo_opcode = conf->pseudo_opcodes[pseudo_offset]; if (pseudo_opcode != nothing) IO_put_string("\t\t"); else pseudo_opcode = NULL; if (pseudo_opcode) { IO_put_string(pseudo_opcode); } // check for special cases switch (pseudo_offset) { case AB_PSEUDOOFFSET_MACROCALL: // (in ACME: '+') // ACME does not like spaces after the macro call char err_bits |= parse_macro_stuff(); break; case AB_PSEUDOOFFSET_MACRODEF: // macro definition IO_put_byte(SPACE); // but here a space looks good :) err_bits |= parse_macro_stuff(); IO_put_string(" {"); break; case AB_PSEUDOOFFSET_OUTFILE: // outfile selection IO_put_byte(SPACE); // but here a space looks good :) err_bits |= parse_unspecified('.'); // output arg(s) IO_put_string(ACME_cbmformat); break; default: // all other pseudo opcodes if ((pseudo_opcode) && (GotByte != AB_ENDOFLINE) && (GotByte != AB_COMMENT)) IO_put_byte(SPACE); // but here a space looks good :) err_bits |= parse_unspecified('.'); // output pseudo opcode's arg(s) } return err_bits; } // main routine for AssBlaster conversion (works for both AB3 and F8AB). // call with first byte of first line pre-read (in GotByte)! void AB_main(struct vab *client_config) { int err_bits; const char *comment_indent; int length_byte; int handled; conf = client_config; ACME_switch_to_pet(); // convert lines until EndOfFile while (!IO_reached_eof) { err_bits = 0; // no errors yet (in this line) handled = 0; if (conf->flags & VABFLAG_HASLENGTHBYTE) { length_byte = GotByte; IO_get_byte(); } comment_indent = "\t"; if (GotByte >= conf->first_mnemonic && (GotByte < conf->first_mnemonic + conf->mnemonic_count)) { handled = 1; err_bits |= parse_mnemo(GotByte - conf->first_mnemonic); } if (GotByte >= conf->first_pseudo_opcode && (GotByte < conf->first_pseudo_opcode + conf->pseudo_opcode_count)) { handled = 1; err_bits |= parse_pseudo_opcode(GotByte - conf->first_pseudo_opcode); } if (GotByte >= conf->first_unused_byte_value && (GotByte != AB_ENDOFLINE)) { handled = 1; fprintf(global_output_stream, "; ToACME: AssBlaster file used unknown code ($%x). ", GotByte); IO_get_byte(); // fetch next err_bits |= parse_unspecified('.'); // output remainder } if (handled == 0) { switch (GotByte) { case 0: // empty line IO_get_byte(); // skip this byte break; case AB_COMMENT: // early comment comment_indent = ""; break; // ignore now, act later case AB_SPACE: // empty line or late comment comment_indent = "\t\t\t\t"; IO_get_byte(); // skip this space // output whatever found err_bits |= parse_unspecified('.'); break; default: // implied label definition pipe_name(); } } // everything might be followed by a comment, so check if (GotByte == AB_COMMENT) { // skip empty comments by checking next byte if (IO_get_byte() != AB_ENDOFLINE) { // something's there, so pipe until end of line IO_put_string(comment_indent); IO_put_byte(';'); do IO_put_byte(SCR2ISO(GotByte)); while (IO_get_byte() != AB_ENDOFLINE); } } // now check whether line generated any errors if (err_bits) generate_errors(err_bits); // if not at end-of-line, something's fishy if (GotByte != AB_ENDOFLINE) { if (GotByte == '\0') { IO_put_string("; ToACME: found $00 - looks like end-of-file marker."); } else { fputs("Found data instead of end-of-line indicator!?.\n", stderr); IO_put_string("; ToACME: Garbage at end-of-line:"); do IO_put_byte(SCR2ISO(GotByte)); while (IO_get_byte() != AB_ENDOFLINE); } } IO_put_byte('\n'); // read first byte of next line IO_get_byte(); } } acme-crossassembler-0.96.4/contrib/toacme/src/ab.h000066400000000000000000000026561333777125400220230ustar00rootroot00000000000000// ToACME - converts other source codes to ACME format. // Copyright (C) 1999-2006 Marco Baye // Have a look at "main.c" for further info // // stuff needed for "VisAss", "AssBlaster 3.x" and "Flash8-AssBlaster" #ifndef ab_H #define ab_H #include "config.h" // Definition of "Type of VisAss/AssBlaster" structure struct vab { int flags; int (*number_parser) (void); const char **pseudo_opcodes; const char **mnemonics; int address_mode_count; int first_mnemonic; int mnemonic_count; int first_pseudo_opcode; int pseudo_opcode_count; int first_unused_byte_value; int unused_values_count; }; #define VABFLAG_HASLENGTHBYTE (1u << 0) #define VABFLAG_ADD_DOT (1u << 1) // Constants extern const char nothing[]; #define AB_ENDOFLINE 0xff // meaning of internal error word. errors are collected until *after* a line // has been finished so the warning messages don't interfere with the generated // source code. #define AB_ERRBIT_UNKNOWN_ADDRMODE (1u << 0) #define AB_ERRBIT_UNKNOWN_NUMBER_COMPRESSION (1u << 1) // SIZEMASK invalid #define AB_ERRBIT_UNKNOWN_NUMBER_FORMAT (1u << 2) // FORMATMASK invalid extern const char *visass_ab3_mnemonic_table[]; extern const char *visass_ab3_pseudo_opcode_table[]; // Prototypes extern void visass_ab3_illegals(void); extern void AB_output_binary(unsigned long int value); extern void AB_output_hexadecimal(unsigned long int value); extern void AB_main(struct vab *client_config); #endif acme-crossassembler-0.96.4/contrib/toacme/src/ab3.c000066400000000000000000000060171333777125400220740ustar00rootroot00000000000000// ToACME - converts other source codes to ACME format. // Copyright (C) 1999-2015 Marco Baye // Have a look at "main.c" for further info // // AssBlaster 3.x stuff #include #include #include "config.h" #include "ab.h" #include "acme.h" #include "io.h" #include "scr2iso.h" // constants #define AB3_ADDRESSING_MODES 12 // parse AssBlaster's packed number format. returns error bits. //#define AB_NUMVAL_FLAGBIT 0x80 // 10000000 indicates packed number #define AB3_NUMVAL_ADD_1 0x40 // 01000000 #define AB3_NUMVAL_ADD_256 0x20 // 00100000 #define AB3_NUMVAL_FORMATMASK 0x1a // 00011010 #define AB3_NUMVAL__FORMAT_HEX 0x10 // 00010000=16 (oh bob, the base is #define AB3_NUMVAL__FORMAT_DEC 0x0a // 00001010=10 given directly, without #define AB3_NUMVAL__FORMAT_BIN 0x02 // 00000010= 2 any encoding... :)) #define AB3_NUMVAL_SIZEMASK 0x05 // 00000101 #define AB3_NUMVAL__SIZE_0 0x01 // 00000001 #define AB3_NUMVAL__SIZE_1 0x04 // 00000100 #define AB3_NUMVAL__SIZE_2 0x00 // 00000000 static int parse_number(void) // now GotByte = first byte of packed number { int flags = GotByte, err_bits = 0; unsigned long int value = 0, add = 0; // decode value if (flags & AB3_NUMVAL_ADD_1) add += 1; if (flags & AB3_NUMVAL_ADD_256) add += 256; switch (flags & AB3_NUMVAL_SIZEMASK) { case AB3_NUMVAL__SIZE_0: // no bytes follow (0, 1, 256, 257) value = add; break; case AB3_NUMVAL__SIZE_1: // one byte follows (2 to 511) value = add + IO_get_byte(); break; case AB3_NUMVAL__SIZE_2: // two bytes follow (512 to 65535) value = add + IO_get_le16(); break; default: // unknown number compression // remember to generate error err_bits |= AB_ERRBIT_UNKNOWN_NUMBER_COMPRESSION; } // continue parsing on next byte IO_get_byte(); // decode output format switch (flags & AB3_NUMVAL_FORMATMASK) { case AB3_NUMVAL__FORMAT_BIN: IO_put_byte('%'); AB_output_binary(value); break; case AB3_NUMVAL__FORMAT_DEC: fprintf(global_output_stream, "%lu", value); break; case AB3_NUMVAL__FORMAT_HEX: hex_fallback: IO_put_byte('$'); AB_output_hexadecimal(value); break; default: // unknown output format // remember to warn err_bits |= AB_ERRBIT_UNKNOWN_NUMBER_FORMAT; goto hex_fallback; } return err_bits; } // config struct for shared VisAss/AB code struct vab ab3_conf = { VABFLAG_ADD_DOT, parse_number, visass_ab3_pseudo_opcode_table, visass_ab3_mnemonic_table, AB3_ADDRESSING_MODES, // meaning of input bytes (0x80-0xec differ between AB3 and F8AB) 0x80, // first mnemonic 40, // count 0xc8, // first pseudo opcode 13, // count 0xd5, // first unused value 42 // count }; // main void ab3_main(void) { IO_set_input_padding(AB_ENDOFLINE); visass_ab3_illegals(); IO_process_load_address(); // first byte after load address should be AB_ENDOFLINE in AB3 sources if (IO_get_byte() == AB_ENDOFLINE) { IO_get_byte(); // skip it and pre-read first valid byte fputs("Input has AB3 header.\n", stderr); } else { fputs("Input does not have any known AB3 header.\n", stderr); } AB_main(&ab3_conf); } acme-crossassembler-0.96.4/contrib/toacme/src/acme.c000066400000000000000000000024371333777125400223360ustar00rootroot00000000000000// ToACME - converts other source codes to ACME format. // Copyright (C) 1999-2006 Marco Baye // Have a look at "main.c" for further info // // ACME syntax #include "acme.h" #include "io.h" // constants // pseudo opcodes const char ACME_po_to[] = "!to"; const char ACME_cbmformat[] = ", cbm"; const char ACME_po_sl[] = "!sl"; const char ACME_set_pc[] = "*="; const char ACME_po_source[] = "!src"; const char ACME_po_byte[] = "!byte"; const char ACME_po_word[] = "!word"; const char ACME_po_fill[] = "!fill"; const char ACME_po_pet[] = "!pet"; const char ACME_po_scr[] = "!scr"; const char ACME_po_macro[] = "!macro"; const char ACME_endmacro[] = "}; (end of macro definition)\n"; const char ACME_macro_call[] = "+"; const char ACME_po_if[] = "!if"; const char ACME_else[] = "} else {"; const char ACME_endif[] = "}; (end of conditional assembly)\n"; const char ACME_po_eof[] = "!eof"; // pseudo opcodes for 65816 (used by Flash8-AssBlaster) const char ACME_po_al[] = "!al"; const char ACME_po_as[] = "!as"; const char ACME_po_rl[] = "!rl"; const char ACME_po_rs[] = "!rs"; // functions // output pseudo opcode to make ACME use PetSCII encoding void ACME_switch_to_pet(void) { IO_put_string( "; ToACME: Adding pseudo opcode to use PetSCII encoding by default:\n" "!convtab pet\n" ); } acme-crossassembler-0.96.4/contrib/toacme/src/acme.h000066400000000000000000000020141333777125400223320ustar00rootroot00000000000000// ToACME - converts other source codes to ACME format. // Copyright (C) 1999-2006 Marco Baye // Have a look at "main.c" for further info // // ACME syntax #ifndef acme_H #define acme_H // constants // pseudo opcodes and related keywords extern const char ACME_po_to[]; extern const char ACME_cbmformat[]; extern const char ACME_po_sl[]; extern const char ACME_set_pc[]; extern const char ACME_po_source[]; extern const char ACME_po_byte[]; extern const char ACME_po_word[]; extern const char ACME_po_fill[]; extern const char ACME_po_pet[]; extern const char ACME_po_scr[]; extern const char ACME_po_macro[]; extern const char ACME_endmacro[]; extern const char ACME_macro_call[]; extern const char ACME_po_if[]; extern const char ACME_else[]; extern const char ACME_endif[]; extern const char ACME_po_eof[]; // pseudo opcodes for 65816 cpu extern const char ACME_po_al[]; extern const char ACME_po_as[]; extern const char ACME_po_rl[]; extern const char ACME_po_rs[]; // prototypes extern void ACME_switch_to_pet(void); #endif acme-crossassembler-0.96.4/contrib/toacme/src/config.h000066400000000000000000000006231333777125400226760ustar00rootroot00000000000000// ToACME - converts other source codes to ACME format. // Copyright (C) 1999-2006 Marco Baye // Have a look at "main.c" for further info // // Configurable stuff // ...this file gets included by almost all others, even *.h files #ifndef config_H #define config_H // constants #define SPACE 0x20 #define SHIFTSPACE 0xa0 #ifndef FALSE typedef int bool; #define FALSE 0 #define TRUE 1 #endif #endif acme-crossassembler-0.96.4/contrib/toacme/src/f8ab.c000066400000000000000000000175401333777125400222520ustar00rootroot00000000000000// ToACME - converts other source codes to ACME format. // Copyright (C) 1999-2015 Marco Baye // Have a look at "main.c" for further info // // Flash8-AssBlaster stuff #include #include #include "config.h" #include "ab.h" #include "acme.h" #include "mnemo.h" #include "io.h" #include "scr2iso.h" // constants #define F8AB_ADDRESSING_MODES 23 // (FIXME - check back later!) // mnemonic table in Flash8-AssBlaster order (without: JML, WDM) static const char *mnemonic_table[] = { MnemonicADC, // $80 6502 MnemonicAND, // $81 6502 MnemonicASL, // $82 6502 MnemonicBCC, // $83 6502 MnemonicBCS, // $84 6502 MnemonicBEQ, // $85 6502 MnemonicBIT, // $86 6502 MnemonicBMI, // $87 6502 MnemonicBNE, // $88 6502 MnemonicBPL, // $89 6502 MnemonicBRA, // $8a 65c02 MnemonicBRK, // $8b 6502 MnemonicBRL, // $8c 65816 MnemonicBVC, // $8d 6502 MnemonicBVS, // $8e 6502 MnemonicCLC, // $8f 6502 MnemonicCLD, // $90 6502 MnemonicCLI, // $91 6502 MnemonicCLV, // $92 6502 MnemonicCMP, // $93 6502 MnemonicCOP, // $94 65816 MnemonicCPX, // $95 6502 MnemonicCPY, // $96 6502 MnemonicDEC, // $97 F8AB uses DEA as the 65816's "DEC implied" MnemonicDEC, // $98 6502 MnemonicDEX, // $99 6502 MnemonicDEY, // $9a 6502 MnemonicEOR, // $9b 6502 MnemonicINC, // $9c F8AB uses INA as the 65816's "INC implied" MnemonicINC, // $9d 6502 MnemonicINX, // $9e 6502 MnemonicINY, // $9f 6502 // MnemonicJML (65816) seems to be unknown to F8AB ... MnemonicJMP, // $a0 6502 MnemonicJSL, // $a1 65816 ...but it *does* know JSL? Strange. MnemonicJSR, // $a2 6502 MnemonicLDA, // $a3 6502 MnemonicLDX, // $a4 6502 MnemonicLDY, // $a5 6502 MnemonicLSR, // $a6 6502 "+F8AB_BROKEN_MVN", // $a7 65816 F8AB uses non-standard argument "+F8AB_BROKEN_MVP", // $a8 65816 ordering with MVP/MVN MnemonicNOP, // $a9 6502 MnemonicORA, // $aa 6502 MnemonicPEA, // $ab 65816 MnemonicPEI, // $ac 65816 MnemonicPER, // $ad 65816 MnemonicPHA, // $ae 6502 MnemonicPHB, // $af 65816 MnemonicPHD, // $b0 65816 MnemonicPHK, // $b1 65816 MnemonicPHP, // $b2 6502 MnemonicPHX, // $b3 65c02 MnemonicPHY, // $b4 65c02 MnemonicPLA, // $b5 6502 MnemonicPLB, // $b6 65816 MnemonicPLD, // $b7 65816 MnemonicPLP, // $b8 6502 MnemonicPLX, // $b9 65c02 MnemonicPLY, // $ba 65c02 MnemonicREP, // $bb 65816 MnemonicROL, // $bc 6502 MnemonicROR, // $bd 6502 MnemonicRTI, // $be 6502 MnemonicRTL, // $bf 65816 MnemonicRTS, // $c0 6502 MnemonicSBC, // $c1 6502 MnemonicSED, // $c2 6502 strange order - SED before SEC? MnemonicSEC, // $c3 6502 MnemonicSEI, // $c4 6502 MnemonicSEP, // $c5 65816 MnemonicSTA, // $c6 6502 MnemonicSTP, // $c7 65816 MnemonicSTX, // $c8 6502 MnemonicSTY, // $c9 6502 MnemonicSTZ, // $ca 65c02 MnemonicTAX, // $cb 6502 MnemonicTAY, // $cc 6502 MnemonicTCD, // $cd 65816 MnemonicTCS, // $ce 65816 MnemonicTDC, // $cf 65816 MnemonicTRB, // $d0 65c02 MnemonicTSB, // $d1 65c02 MnemonicTSC, // $d2 65816 MnemonicTSX, // $d3 6502 MnemonicTXA, // $d4 6502 MnemonicTXS, // $d5 6502 MnemonicTXY, // $d6 65816 MnemonicTYA, // $d7 6502 MnemonicTYX, // $d8 65816 MnemonicWAI, // $d9 65816 // MnemonicWDM (65816) seems to be unknown to F8AB. MnemonicXBA, // $da 65816 MnemonicXCE, // $db 65816 }; // PseudoOpcode table in Flash8-AssBlaster order static const char *pseudo_opcode_table[] = { NULL, // (la) $dc // NULL because ACME does not need a pseudo opcode for label defs ACME_set_pc, // (ba) $dd ACME_po_byte, // (by) $de ACME_po_fill, // (br) $df ACME_po_pet, // (tx) $e0 ACME_po_macro, // (md) $e1 (see AB_PSEUDOOFFSET_MACRODEF) ACME_endmacro, // (de) $e2 ACME_macro_call, // (ma) $e3 (see AB_PSEUDOOFFSET_MACROCALL) ACME_po_eof, // (st) $e4 // ACME_po_scr is not available in F8AB. Huh?! "; ToACME: Cannot convert \\wa.\n", // (wa) $e5 ACME_po_to, // (on) $e6 (see AB_PSEUDOOFFSET_OUTFILE) ACME_po_word, // (wo) $e7 "; ToACME: Cannot convert \\kc.\n", // (kc) $e8 ACME_po_rl, // (rl) $e9 ACME_po_rs, // (rs) $ea ACME_po_al, // (al) $eb ACME_po_as, // (as) $ec // 0xed-0xfe are unused in F8AB // (FIXME - true? I only checked 0xed) }; // parse AssBlaster's packed number format. returns error bits. //#define AB_NUMVAL_FLAGBIT 0x80 // 10000000 indicates packed number #define F8AB_NUMVAL_ADD_65536 0x40 // 01000000 #define F8AB_NUMVAL_ADD_256 0x20 // 00100000 #define F8AB_NUMVAL_ADD_1 0x10 // 00010000 #define F8AB_NUMVAL_FORMATMASK 0x0c // 00001100 #define F8AB_NUMVAL__FORMAT_BIN 0x00 // 00000000 #define F8AB_NUMVAL__FORMAT_DEC 0x04 // 00000100 #define F8AB_NUMVAL__FORMAT_HEX 0x08 // 00001000 #define F8AB_NUMVAL__FORMAT_ILL 0x0c // 00001100 never used by F8AB #define F8AB_NUMVAL_SIZEMASK 0x03 // 00000011 #define F8AB_NUMVAL__SIZE_0 0x00 // 00000000 #define F8AB_NUMVAL__SIZE_1 0x01 // 00000001 #define F8AB_NUMVAL__SIZE_2 0x02 // 00000010 #define F8AB_NUMVAL__SIZE_3 0x03 // 00000011 static int parse_number(void) // now GotByte = first byte of packed number { int flags = GotByte, err_bits = 0; unsigned long int value = 0, add = 0; // decode value if (flags & F8AB_NUMVAL_ADD_65536) add += 65536; if (flags & F8AB_NUMVAL_ADD_256) add += 256; if (flags & F8AB_NUMVAL_ADD_1) add += 1; switch (flags & F8AB_NUMVAL_SIZEMASK) { case F8AB_NUMVAL__SIZE_0: // no bytes follow (0, 1, 256, 257) value = add; break; case F8AB_NUMVAL__SIZE_1: // one byte follows (2 to 511) value = add + IO_get_byte(); break; case F8AB_NUMVAL__SIZE_2: // two bytes follow (512 to 65535) value = add + IO_get_le16(); break; case F8AB_NUMVAL__SIZE_3: // three bytes follow (anything else) value = add + IO_get_le24(); } // continue parsing on next byte IO_get_byte(); // decode output format switch (flags & F8AB_NUMVAL_FORMATMASK) { case F8AB_NUMVAL__FORMAT_BIN: IO_put_byte('%'); AB_output_binary(value); break; case F8AB_NUMVAL__FORMAT_DEC: fprintf(global_output_stream, "%lu", value); break; case F8AB_NUMVAL__FORMAT_HEX: hex_fallback: IO_put_byte('$'); AB_output_hexadecimal(value); break; default: // unknown output format // remember to warn err_bits |= AB_ERRBIT_UNKNOWN_NUMBER_FORMAT; goto hex_fallback; } return err_bits; } // config struct for shared ab code struct vab f8ab_conf = { VABFLAG_ADD_DOT, parse_number, pseudo_opcode_table, mnemonic_table, F8AB_ADDRESSING_MODES, // meaning of input bytes (0x80-0xec differ between AB3 and F8AB) 0x80, // first mnemonic 92, // count 0xdc, // first pseudo opcode 17, // count 0xed, // first unused value 18 // count }; // main void f8ab_main(void) { const char *header_message; header_message = "Input does not have any known F8AB header.\n"; IO_set_input_padding(AB_ENDOFLINE); IO_put_string( "; ToACME: Adding pseudo opcode to enable 65816 opcodes:\n" "\t!cpu 65816\n" "; ToACME: Adding two macros to fix F8AB's non-standard argument order\n" "; ToACME: concerning MVP/MVN. While the commands are assembled with\n" "; ToACME: the destination bank byte first, the WDC docs say that in\n" "; ToACME: source codes, the source bank byte is given first.\n" "; ToACME: In other words: The macros make sure that assembling this\n" "; ToACME: source with ACME will produce the same binary F8AB produced.\n" "\t!macro F8AB_BROKEN_MVP .dest, .source {mvp .source, .dest}\n" "\t!macro F8AB_BROKEN_MVN .dest, .source {mvn .source, .dest}\n" ); IO_process_load_address(); // most AB files have this format: // load_address_low, load_address_high, AB_ENDOFLINE, actual content // newer versions of F8AB seem to use this: // $ff, $00, $00, $03, AB_ENDOFLINE, actual content if (IO_get_byte() == AB_ENDOFLINE) { IO_get_byte(); // skip it and pre-read first valid byte header_message = "Input has F8AB 1.0 header.\n"; } else { if ((GotByte == 0) && (IO_get_byte() == 3) && (IO_get_byte() == AB_ENDOFLINE)) { IO_get_byte();// skip and pre-read first valid byte header_message = "Input has F8AB 1.2 header.\n"; } } fputs(header_message, stderr); AB_main(&f8ab_conf); } acme-crossassembler-0.96.4/contrib/toacme/src/giga.c000066400000000000000000000115001333777125400223270ustar00rootroot00000000000000// ToACME - converts other source codes to ACME format. // Copyright (C) 1999-2006 Marco Baye // Have a look at "main.c" for further info // // "GigaAss" stuff #include #include "config.h" #include "acme.h" #include "gighyp.h" #include "mnemo.h" #include "io.h" #include "pet2iso.h" // constants // token-to-(pseudo)opcode conversion table (FIXME) const char *giga_token[] = { "FIXME-CALL", // $a0 .CALL ACME_po_macro, // $a1 .MACRO (see MACRO_DEF_TOKEN below) ACME_endmacro, // $a2 .ENDMACRO NULL, // $a3 .GLOBAL (ACME does not need a pseudo NULL, // $a4 .EQUATE opcode for label definitions) // these are indented in the output file ACME_po_byte, // $a5 .BYTE ACME_po_word, // $a6 .WORD ACME_po_fill, // $a7 .DS ACME_po_pet, // $a8 .TEXT (see MACRO_TEXT below) ACME_po_to, // $a9 .OBJECT (see MACRO_OUTFILE below) ACME_set_pc, // $aa .BASE "FIXME-CODE", // $ab .CODE "FIXME-ON", // $ac .ON "FIXME-GOTO", // $ad .GOTO ACME_po_if, // $ae .IF ACME_else, // $af .ELSE ACME_endif, // $b0 .ENDIF ACME_po_sl, // $b1 .SYMBOLS "FIXME-LISTING", // $b2 .LISTING ACME_po_eof, // $b3 .END "FIXME-STOP", // $b4 .STOP "FIXME-PAGE", // $b5 .PAGE "FIXME-NOCODE", // $b6 .NOCODE "FIXME-START", // $b7 .START "FIXME-NOEXP", // $b8 .NOEXP "FIXME-$b9", // $b9 "FIXME-$ba", // $ba "FIXME-$bb", // $bb "FIXME-$bc", // $bc "FIXME-$bd", // $bd "FIXME-$be", // $be "FIXME-$bf", // $bf // these are indented in the output file MnemonicCPX, // $c0 MnemonicCPY, // $c1 MnemonicLDX, // $c2 MnemonicLDY, // $c3 MnemonicCMP, // $c4 MnemonicADC, // $c5 MnemonicAND, // $c6 MnemonicDEC, // $c7 MnemonicEOR, // $c8 MnemonicINC, // $c9 MnemonicLDA, // $ca MnemonicASL, // $cb MnemonicBIT, // $cc MnemonicLSR, // $cd MnemonicORA, // $ce MnemonicROL, // $cf MnemonicROR, // $d0 MnemonicSBC, // $d1 MnemonicSTA, // $d2 MnemonicSTX, // $d3 MnemonicSTY, // $d4 MnemonicJMP, // $d5 MnemonicJSR, // $d6 MnemonicTXA, // $d7 MnemonicTAX, // $d8 MnemonicTYA, // $d9 MnemonicTAY, // $da MnemonicTSX, // $db MnemonicTXS, // $dc MnemonicPHP, // $dd MnemonicPLP, // $de MnemonicPHA, // $df MnemonicPLA, // $e0 MnemonicBRK, // $e1 MnemonicRTI, // $e2 MnemonicRTS, // $e3 MnemonicNOP, // $e4 MnemonicCLC, // $e5 MnemonicSEC, // $e6 MnemonicCLI, // $e7 MnemonicSEI, // $e8 MnemonicCLV, // $e9 MnemonicCLD, // $ea MnemonicSED, // $eb MnemonicDEY, // $ec MnemonicINY, // $ed MnemonicDEX, // $ee MnemonicINX, // $ef MnemonicBPL, // $f0 MnemonicBMI, // $f1 MnemonicBVC, // $f2 MnemonicBVS, // $f3 MnemonicBCC, // $f4 MnemonicBCS, // $f5 MnemonicBNE, // $f6 MnemonicBEQ, // $f7 "FIXME-$f8", // $f8 "FIXME-$f9", // $f9 "FIXME-$fa", // $fa "FIXME-$fb", // $fb "FIXME-$fc", // $fc "FIXME-$fd", // $fd "FIXME-$fe", // $fe "FIXME-$ff", // $ff }; // functions // I don't know whether it's correct, but I had to start somewhere #define FIRST_TOKEN 0xa0 #define MACRO_DEF_TOKEN 0xa1 // ugly kluge to add '{' at end of statement #define MACRO_TEXT 0xa8 // ugly kluge for giga string specialties #define MACRO_OUTFILE 0xa9 // ugly kluge for adding outfile format // process opcode or pseudo opcode (tokenized) static int process_tokenized(void) { const char *token; int flags = 0; if (GotByte < FIRST_TOKEN) { // macro call? IO_put_byte('+'); // add macro call character // fprintf(global_output_stream, "small value:$%x", GotByte); } else { switch (GotByte) { case MACRO_DEF_TOKEN: flags |= FLAG_ADD_LEFT_BRACE; break; case MACRO_TEXT: flags |= FLAG_ADD_ZERO | FLAG_CHANGE_LEFTARROW; break; case MACRO_OUTFILE: flags |= FLAG_ADD_CBM; } flags |= FLAG_INSERT_SPACE; token = giga_token[GotByte - FIRST_TOKEN]; if (token != NULL) IO_put_string(token); IO_get_byte(); } return flags; } // When tokens are known, maybe use the PseudoOpcode function from hypra? // ...for now deleted // [...] // main routine for GigaAss conversion void giga_main(void) { int indent; IO_set_input_padding(0); IO_process_load_address(); ACME_switch_to_pet(); // loop: once for every line in the file while (!IO_reached_eof) { // skip link pointer (if it's zero, report as end marker) if (IO_get_le16() == 0) IO_put_string("; ToACME: Found BASIC end marker.\n"); IO_get_le16(); // skip line number // process line IO_get_byte(); if ((GotByte == SPACE) || (GotByte == ';') || (GotByte == '\0') || (GotByte > 0x7f)) indent = 0; else indent = GigaHypra_label_definition(); // skip spaces while (GotByte == SPACE) IO_get_byte(); // if there is an opcode, process it if ((GotByte != ';') && (GotByte != '\0')) { GigaHypra_indent(indent); GigaHypra_argument(process_tokenized()); } // skip comment, if there is one if (GotByte == ';') GigaHypra_comment(); // end of line IO_put_byte('\n'); } } acme-crossassembler-0.96.4/contrib/toacme/src/gighyp.c000066400000000000000000000062541333777125400227210ustar00rootroot00000000000000// ToACME - converts other source codes to ACME format. // Copyright (C) 1999-2006 Marco Baye // Have a look at "main.c" for further info // // stuff needed for both "Hypra-Ass" and "Giga-Ass" #include "acme.h" #include "gighyp.h" #include "io.h" #include "pet2iso.h" // called with GotByte == ';' void GigaHypra_comment(void) { // check whether anything follows (empty comments => empty lines) if (IO_get_byte()) { IO_put_byte(';'); do IO_put_byte(PET2ISO(GotByte)); while (IO_get_byte()); } } // process operator void GigaHypra_operator(void) // '!' was last read { char middle = PET2ISO(IO_get_byte()); if ((middle != ';') && (middle != '\0')) { if (IO_get_byte() == '!') { switch (middle) { case 'n': IO_put_byte('!'); break; case 'o': IO_put_byte('|'); break; case 'a': IO_put_byte('&'); break; case '=': IO_put_byte('='); break; case '<': IO_put_string(" < "); break; case '>': IO_put_string(" > "); break; default: IO_put_byte('!'); IO_put_byte(middle); IO_put_byte('!'); } IO_get_byte(); } else { IO_put_byte('!'); IO_put_byte(middle); } } else { IO_put_byte('!'); } // exit with unused byte pre-read } // output one or two TABs void GigaHypra_indent(int indent) { if (indent < 8) IO_put_byte('\t'); IO_put_byte('\t'); } // Process opcode and arguments void GigaHypra_argument(int flags) { int paren = 0; // number of open parentheses (to close) // if needed, add separating space between opcode and argument if ((flags & FLAG_INSERT_SPACE) && (GotByte != SPACE) && (GotByte != ';') && (GotByte != '\0')) IO_put_byte(SPACE); // character loop while ((GotByte != ';') && (GotByte != '\0')) { if (GotByte == '!') GigaHypra_operator(); if (GotByte == '"') { // don't parse inside quotes IO_put_byte(GotByte); IO_get_byte(); while ((GotByte != '\0') && (GotByte != '"')) { if ((GotByte == 0x5f) && (flags & FLAG_CHANGE_LEFTARROW)) IO_put_string("\", 13,\""); else IO_put_byte(PET2ISO(GotByte)); IO_get_byte(); } IO_put_byte('"'); if (GotByte == '"') { IO_get_byte(); if ((GotByte == '\0') && (flags & FLAG_ADD_ZERO)) IO_put_string(", 0"); } } else { // most characters go here switch (GotByte) { case '(': if (flags & FLAG_SKIP_OPENING) { flags &= ~FLAG_SKIP_OPENING; flags |= FLAG_SKIP_CLOSING; } else { paren++; IO_put_byte(PET2ISO(GotByte)); } break; case ')': if ((flags & FLAG_SKIP_CLOSING) && (paren == 0)) { flags &= ~FLAG_SKIP_CLOSING; } else { paren--; IO_put_byte(PET2ISO(GotByte)); } break; case SHIFTSPACE: IO_put_byte(SPACE); break; default: IO_put_byte(PET2ISO(GotByte)); } IO_get_byte(); } } if (flags & FLAG_ADD_CBM) IO_put_string(ACME_cbmformat); if (flags & FLAG_ADD_LEFT_BRACE) IO_put_byte('{'); } // convert and send label name. // returns length (for proper indentation). int GigaHypra_label_definition(void) { int count = 0; do { IO_put_byte(PET2ISO(GotByte)); count++; IO_get_byte(); } while ((GotByte != SPACE) && (GotByte != ';') && (GotByte != '\0')); return count; } acme-crossassembler-0.96.4/contrib/toacme/src/gighyp.h000066400000000000000000000020471333777125400227220ustar00rootroot00000000000000// ToACME - converts other source codes to ACME format. // Copyright (C) 1999-2006 Marco Baye // Have a look at "main.c" for further info // // stuff needed for both "Hypra-Ass" and "Giga-Ass" #ifndef gigahypra_H #define gigahypra_H #include "config.h" // Constants #define FLAG_INSERT_SPACE (1u << 0) // insert space before arg #define FLAG_ADD_LEFT_BRACE (1u << 1) // add '{' at end of statement #define FLAG_ADD_CBM (1u << 2) // add file format indicator #define FLAG_ADD_ZERO (1u << 3) // giga string specialty: // open quote at end of line is *normal*. Closed quote: add ",0". #define FLAG_SKIP_OPENING (1u << 4) // strip '(' before args #define FLAG_SKIP_CLOSING (1u << 5) // strip ')' after args #define FLAG_CHANGE_LEFTARROW (1u << 6) // giga string specialty: // '_' (left arrow on C64) is transformed to CR (0x0d). // Prototypes extern void GigaHypra_comment(void); extern void GigaHypra_operator(void); extern void GigaHypra_indent(int indent); extern void GigaHypra_argument(int flags); extern int GigaHypra_label_definition(void); #endif acme-crossassembler-0.96.4/contrib/toacme/src/hypra.c000066400000000000000000000117431333777125400225540ustar00rootroot00000000000000// ToACME - converts other source codes to ACME format. // Copyright (C) 1999-2006 Marco Baye // Have a look at "main.c" for further info // // "HypraAss" stuff #include #include "config.h" #include "acme.h" #include "gighyp.h" #include "io.h" #include "pet2iso.h" // functions // complain about unknown pseudo opcodes static void complain(char a, char b) { IO_put_string("; ToACME: ."); if (a) IO_put_byte(a); if (b) IO_put_byte(b); IO_put_string(" cannot be converted\n"); } // handle ".ba" and ".by" static int process_po_b(char second) { int flags = 0; switch (second) { case 'a': // ".ba" = set base address IO_put_string(ACME_set_pc); break; case 'y': // ".by" = insert bytes IO_put_string(ACME_po_byte); flags |= FLAG_INSERT_SPACE; break; default: complain('b', second); } return flags; } // handle ".ei", ".el", ".en" and ".eq" static int process_po_e(char second) { int flags = 0; switch (second) { case 'i': // ".ei" = endif IO_put_string(ACME_endif); break; case 'l': // ".el" = else IO_put_string(ACME_else); break; case 'n': // ".en" = end IO_put_string(ACME_po_eof); flags |= FLAG_INSERT_SPACE; break; case 'q': // ".eq" = label def break; default: complain('e', second); } return flags; } // handle ".tx" and ".ts" static int process_po_t(char second) { int flags = 0; switch (second) { case 'x': // ".tx" = insert string IO_put_string(ACME_po_pet); flags |= FLAG_INSERT_SPACE; break; case 's': // ".ts" = screen code string IO_put_string(ACME_po_scr); flags |= FLAG_INSERT_SPACE; break; default: complain('t', second); } return flags; } #define ARE(a, b) ((first == a) && (second == b)) // process pseudo opcode static int process_pseudo_opcode(void) // '.' was last read { int first, second; // get first byte. if illegal, complain and exit immediately first = PET2ISO(IO_get_byte()); if ((first == SPACE) || (first == ';') || (first == '\0')) { complain(first, '\0'); return 0; } // get second byte. if illegal, complain and exit immediately second = PET2ISO(IO_get_byte()); if ((second == SPACE) || (second == ';') || (second == '\0')) { complain(first, second); return 0; } IO_get_byte();// pre-read unused byte // check pseudo opcodes (switch/case was actually harder to read) if (first == 'b') { // handle ".ba" and ".by" process_po_b(second); return FLAG_INSERT_SPACE; } if (first == 'e') // handle ".ei", ".el", ".en" and ".eq" return process_po_e(second); if (first == 't') // handle ".tx" and ".ts" return process_po_t(second); if (ARE('.', '.')) { // "..." = macro call IO_put_string(ACME_macro_call); return FLAG_INSERT_SPACE | FLAG_SKIP_OPENING; } if (ARE('m', 'a')) { // ".ma" = macro definition IO_put_string(ACME_po_macro); return FLAG_INSERT_SPACE | FLAG_SKIP_OPENING | FLAG_ADD_LEFT_BRACE; } if (ARE('o', 'b')) { // ".ob" = output to file IO_put_string(ACME_po_to); return FLAG_INSERT_SPACE | FLAG_ADD_CBM; } if (ARE('s', 'y')) { // ".sy" = symbol dump IO_put_string(ACME_po_sl); IO_put_string("\"symboldump.txt\";"); return 0; } if (ARE('i', 'f')) { // ".if" = cond. assembly IO_put_string(ACME_po_if); return FLAG_INSERT_SPACE | FLAG_ADD_LEFT_BRACE; } if (ARE('g', 'l')) // ".gl" = global label def return 0; if (ARE('a', 'p')) // ".ap" = append source IO_put_string(ACME_po_source); else if (ARE('r', 't')) // ".rt" = end of macro def IO_put_string(ACME_endmacro); else if (ARE('w', 'o')) // ".wo" = insert words IO_put_string(ACME_po_word); else complain(first, second); return FLAG_INSERT_SPACE; } // process opcode static void real_opcode(void) // character was last read { IO_put_byte(PET2ISO(GotByte)); IO_get_byte(); if ((GotByte == SPACE) || (GotByte == ';') || (GotByte == '\0')) return; IO_put_byte(PET2ISO(GotByte)); IO_get_byte(); if ((GotByte == SPACE) || (GotByte == ';') || (GotByte == '\0')) return; IO_put_byte(PET2ISO(GotByte)); IO_get_byte(); // exit with unused byte pre-read } // main routine for HypraAss conversion void hypra_main(void) { int indent; IO_set_input_padding(0); IO_process_load_address(); ACME_switch_to_pet(); // loop: once for every line in the file while (!IO_reached_eof) { // skip link pointer (if it's zero, report as end marker) if (IO_get_le16() == 0) IO_put_string("; ToACME: Found BASIC end marker.\n"); IO_get_le16(); // skip line number // process line IO_get_byte(); indent = 0; if ((GotByte != SPACE) && (GotByte != ';') && (GotByte != '\0')) indent = GigaHypra_label_definition(); // skip spaces while (GotByte == SPACE) IO_get_byte(); // if there is an opcode, process it if ((GotByte != ';') && (GotByte != '\0')) { GigaHypra_indent(indent); // branch to relevant routine if (GotByte == '.') { GigaHypra_argument(process_pseudo_opcode()); } else { real_opcode(); GigaHypra_argument(FLAG_INSERT_SPACE); } } // skip comment, if there is one if (GotByte == ';') GigaHypra_comment(); // end of line IO_put_byte('\n'); } } acme-crossassembler-0.96.4/contrib/toacme/src/io.c000066400000000000000000000040641333777125400220360ustar00rootroot00000000000000// ToACME - converts other source codes to ACME format. // Copyright (C) 1999-2006 Marco Baye // Have a look at "main.c" for further info // // input/output #include #include "config.h" #include "io.h" // variables int padding_value; FILE *global_input_stream; FILE *global_output_stream; int GotByte = 0; bool IO_reached_eof = FALSE; // input functions // set byte sent after EOF inline void IO_set_input_padding(int pad) { padding_value = pad; } // fetch and buffer byte int IO_get_byte(void) { int w; if (IO_reached_eof) { GotByte = padding_value; } else { w = getc(global_input_stream); if (w == EOF) IO_reached_eof = TRUE; GotByte = w; } return GotByte; } // read little-endian 16-bit value unsigned int IO_get_le16(void) { unsigned int result = IO_get_byte(); // CAUTION! using // return(IO_get_byte() | (IO_get_byte() << 8)); // would be compiler-dependent return result | (IO_get_byte() << 8); } // read little-endian 24-bit value unsigned int IO_get_le24(void) { unsigned int result = IO_get_le16(); // CAUTION! see above return result | (IO_get_byte() << 16); } // output functions // output string inline void IO_put_string(const char string[]) { fputs(string, global_output_stream); } // write byte to output file inline void IO_put_byte(char b) { putc(b, global_output_stream); } // output low nibble of argument as hexadecimal digit static void put_low_nibble_hex(int v) { putc("0123456789abcdef"[v & 15], global_output_stream); } // output low byte of argument as two hexadecimal digits void IO_put_low_byte_hex(int v) { put_low_nibble_hex(v >> 4); put_low_nibble_hex(v); } // output low 16 bits of arg as four hexadecimal digits void IO_put_low_16b_hex(int w) { IO_put_low_byte_hex(w >> 8); IO_put_low_byte_hex(w); } // read load address from input file and write as comment to output file void IO_process_load_address(void) { int load_address; load_address = IO_get_le16(); IO_put_string("; ToACME: Original source code file had load address $"); IO_put_low_16b_hex(load_address); IO_put_byte('\n'); } acme-crossassembler-0.96.4/contrib/toacme/src/io.h000066400000000000000000000014371333777125400220440ustar00rootroot00000000000000// ToACME - converts other source codes to ACME format. // Copyright (C) 1999-2006 Marco Baye // Have a look at "main.c" for further info // // input/output #ifndef io_H #define io_H #include #include "config.h" // variables extern int GotByte; extern bool IO_reached_eof; extern FILE *global_input_stream; extern FILE *global_output_stream; // prototypes extern void IO_set_input_padding(int); extern int IO_get_byte(void); extern unsigned int IO_get_le16(void); // get little-endian 16-bit value extern unsigned int IO_get_le24(void); // get little-endian 24-bit value extern void IO_put_string(const char string[]); extern void IO_put_byte(char b); extern void IO_put_low_byte_hex(int v); extern void IO_put_low_16b_hex(int w); extern void IO_process_load_address(void); #endif acme-crossassembler-0.96.4/contrib/toacme/src/main.c000066400000000000000000000044031333777125400223500ustar00rootroot00000000000000// ToACME - converts other source codes to ACME format. // Copyright (C) 1999-2015 Marco Baye // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include #include #include #include "config.h" #include "acme.h" #include "io.h" #include "platform.h" #include "version.h" // guess what int main(int argc, char *argv[]) { // handle "toacme -h" and "toacme --help" just like "toacme" if (argc == 2) { if ((strcmp(argv[1], "-h") == 0) || (strcmp(argv[1], "--help") == 0)) argc = 1; } // "toacme" without any switches gives info and exits successfully if (argc == 1) { version_show_info(argv[0]); return EXIT_SUCCESS; } // check argument count if (argc != 4) { fputs("Wrong number of arguments.\n", stderr); return EXIT_FAILURE; } // check format id if (version_parse_id(argv[1])) { fputs("Unknown format id.\n", stderr); return EXIT_FAILURE; } // be nice and ensure input and output are different if (strcmp(argv[2], argv[3]) == 0) { fputs("Input and output files must be different.\n", stderr); return EXIT_FAILURE; } // try to open input file global_input_stream = fopen(argv[2], "rb"); if (global_input_stream == NULL) { fputs("Cannot open input file.\n", stderr); return EXIT_FAILURE; } // try to open output file global_output_stream = fopen(argv[3], "w"); if (global_output_stream == NULL) { fputs("Cannot open output file.\n", stderr); return EXIT_FAILURE; } // do the actual work version_main(); // and then tidy up and exit fclose(global_output_stream); PLATFORM_SETFILETYPE_TEXT(argv[3]); fclose(global_input_stream); return EXIT_SUCCESS; } acme-crossassembler-0.96.4/contrib/toacme/src/mnemo.c000066400000000000000000000060221333777125400225360ustar00rootroot00000000000000// ToACME - converts other source codes to ACME format. // Copyright (C) 1999-2006 Marco Baye // Have a look at "main.c" for further info // // assembler mnemonics // mnemonics of legal 6502 instructions const char MnemonicADC[] = "adc", MnemonicAND[] = "and", MnemonicASL[] = "asl", MnemonicBCC[] = "bcc", MnemonicBCS[] = "bcs", MnemonicBEQ[] = "beq", MnemonicBIT[] = "bit", MnemonicBMI[] = "bmi", MnemonicBNE[] = "bne", MnemonicBPL[] = "bpl", MnemonicBRK[] = "brk", MnemonicBVC[] = "bvc", MnemonicBVS[] = "bvs", MnemonicCLC[] = "clc", MnemonicCLD[] = "cld", MnemonicCLI[] = "cli", MnemonicCLV[] = "clv", MnemonicCMP[] = "cmp", MnemonicCPX[] = "cpx", MnemonicCPY[] = "cpy", MnemonicDEC[] = "dec", MnemonicDEX[] = "dex", MnemonicDEY[] = "dey", MnemonicEOR[] = "eor", MnemonicINC[] = "inc", MnemonicINX[] = "inx", MnemonicINY[] = "iny", MnemonicJMP[] = "jmp", MnemonicJSR[] = "jsr", MnemonicLDA[] = "lda", MnemonicLDX[] = "ldx", MnemonicLDY[] = "ldy", MnemonicLSR[] = "lsr", MnemonicNOP[] = "nop", MnemonicORA[] = "ora", MnemonicPHA[] = "pha", MnemonicPHP[] = "php", MnemonicPLA[] = "pla", MnemonicPLP[] = "plp", MnemonicROL[] = "rol", MnemonicROR[] = "ror", MnemonicRTI[] = "rti", MnemonicRTS[] = "rts", MnemonicSBC[] = "sbc", MnemonicSEC[] = "sec", MnemonicSED[] = "sed", MnemonicSEI[] = "sei", MnemonicSTA[] = "sta", MnemonicSTX[] = "stx", MnemonicSTY[] = "sty", MnemonicTAX[] = "tax", MnemonicTAY[] = "tay", MnemonicTSX[] = "tsx", MnemonicTXA[] = "txa", MnemonicTXS[] = "txs", MnemonicTYA[] = "tya"; // mnemonics of undocumented ("illegal") 6502 instructions const char MnemonicSLO[] = " SLO", MnemonicRLA[] = " RLA", MnemonicSRE[] = " SRE", MnemonicRRA[] = " RRA", MnemonicSAX[] = " SAX", MnemonicLAX[] = " LAX", MnemonicDCP[] = " DCP", MnemonicISC[] = " ISC", MnemonicANC[] = " ANC", MnemonicARR[] = " ARR", MnemonicASR[] = " ASR", MnemonicSBX[] = " SBX", MnemonicDOP[] = " DOP", MnemonicTOP[] = " TOP", MnemonicSHX[] = " SHX", MnemonicJAM[] = " JAM"; // mnemonics of 65c02 instructions const char MnemonicBRA[] = "bra", MnemonicPHX[] = "phx", MnemonicPHY[] = "phy", MnemonicPLX[] = "plx", MnemonicPLY[] = "ply", MnemonicSTZ[] = "stz", MnemonicTRB[] = "trb", MnemonicTSB[] = "tsb"; // mnemonics of 65816 instructions const char MnemonicJML[] = "jml", MnemonicJSL[] = "jsl", MnemonicMVN[] = "mvn", MnemonicMVP[] = "mvp", MnemonicPEI[] = "pei", MnemonicBRL[] = "brl", MnemonicPER[] = "per", MnemonicCOP[] = "cop", MnemonicPEA[] = "pea", MnemonicREP[] = "rep", MnemonicSEP[] = "sep", MnemonicPHB[] = "phb", MnemonicPHD[] = "phd", MnemonicPHK[] = "phk", MnemonicPLB[] = "plb", MnemonicPLD[] = "pld", MnemonicRTL[] = "rtl", MnemonicSTP[] = "stp", MnemonicTCD[] = "tcd", MnemonicTCS[] = "tcs", MnemonicTDC[] = "tdc", MnemonicTSC[] = "tsc", MnemonicTXY[] = "txy", MnemonicTYX[] = "tyx", MnemonicWAI[] = "wai", MnemonicWDM[] = "wdm", MnemonicXBA[] = "xba", MnemonicXCE[] = "xce"; acme-crossassembler-0.96.4/contrib/toacme/src/mnemo.h000066400000000000000000000056501333777125400225510ustar00rootroot00000000000000// ToACME - converts other source codes to ACME format. // Copyright (C) 1999-2006 Marco Baye // Have a look at "main.c" for further info // // 6502 mnemonics #ifndef mnemo_H #define mnemo_H // mnemonics of legal 6502 instructions extern const char MnemonicADC[], MnemonicSBC[]; extern const char MnemonicAND[], MnemonicEOR[], MnemonicORA[]; extern const char MnemonicASL[], MnemonicLSR[]; extern const char MnemonicBCC[], MnemonicBCS[]; extern const char MnemonicBEQ[], MnemonicBNE[]; extern const char MnemonicBMI[], MnemonicBPL[]; extern const char MnemonicBRK[], MnemonicRTI[]; extern const char MnemonicBVC[], MnemonicBVS[]; extern const char MnemonicCLC[], MnemonicSEC[]; extern const char MnemonicCLD[], MnemonicSED[]; extern const char MnemonicCLI[], MnemonicSEI[]; extern const char MnemonicBIT[], MnemonicCLV[], MnemonicNOP[]; extern const char MnemonicCMP[], MnemonicCPX[], MnemonicCPY[]; extern const char MnemonicDEC[], MnemonicDEX[], MnemonicDEY[]; extern const char MnemonicINC[], MnemonicINX[], MnemonicINY[]; extern const char MnemonicJMP[], MnemonicJSR[], MnemonicRTS[]; extern const char MnemonicLDA[], MnemonicLDX[], MnemonicLDY[]; extern const char MnemonicPHA[], MnemonicPLA[]; extern const char MnemonicPHP[], MnemonicPLP[]; extern const char MnemonicROL[], MnemonicROR[]; extern const char MnemonicSTA[], MnemonicSTX[], MnemonicSTY[]; extern const char MnemonicTSX[], MnemonicTXA[], MnemonicTAY[]; extern const char MnemonicTYA[], MnemonicTAX[], MnemonicTXS[]; // mnemonics of undocumented ("illegal") 6502 instructions extern const char MnemonicANC[], MnemonicARR[], MnemonicASR[]; extern const char MnemonicDCP[], MnemonicDOP[], MnemonicISC[]; extern const char MnemonicJAM[], MnemonicLAX[], MnemonicRLA[]; extern const char MnemonicRRA[], MnemonicSAX[], MnemonicSBX[]; extern const char MnemonicSLO[], MnemonicSRE[], MnemonicTOP[]; extern const char MnemonicSHX[]; // mnemonics of 65c02 instructions extern const char MnemonicBRA[]; extern const char MnemonicPHX[], MnemonicPHY[]; extern const char MnemonicPLX[], MnemonicPLY[]; extern const char MnemonicSTZ[]; extern const char MnemonicTRB[], MnemonicTSB[]; // mnemonics of 65816 instructions extern const char MnemonicJML[], MnemonicJSL[]; extern const char MnemonicMVN[], MnemonicMVP[]; extern const char MnemonicPEI[]; extern const char MnemonicBRL[]; extern const char MnemonicPER[]; extern const char MnemonicCOP[]; extern const char MnemonicPEA[]; extern const char MnemonicREP[], MnemonicSEP[]; extern const char MnemonicPHB[]; extern const char MnemonicPHD[]; extern const char MnemonicPHK[]; extern const char MnemonicPLB[]; extern const char MnemonicPLD[]; extern const char MnemonicRTL[]; extern const char MnemonicSTP[]; extern const char MnemonicTCD[], MnemonicTCS[]; extern const char MnemonicTDC[], MnemonicTSC[]; extern const char MnemonicTXY[], MnemonicTYX[]; extern const char MnemonicWAI[]; extern const char MnemonicWDM[]; extern const char MnemonicXBA[], MnemonicXCE[]; #endif acme-crossassembler-0.96.4/contrib/toacme/src/obj.c000066400000000000000000000261221333777125400222000ustar00rootroot00000000000000// ToACME - converts other source codes to ACME format. // Copyright (C) 1999-2006 Marco Baye // Have a look at "main.c" for further info // // disassembly stuff #include #include "config.h" #include "acme.h" #include "mnemo.h" #include "io.h" // constants // 6502 code table (mnemonics only) *illegals* const char *mnemo_of_code[] = { MnemonicBRK, MnemonicORA, " JAM;0x02", MnemonicSLO, // $00-$03 " DOP;0x04", MnemonicORA, MnemonicASL, MnemonicSLO, // $04-$07 MnemonicPHP, MnemonicORA, MnemonicASL, "!by$0b;ANC#", // $08-$0b " TOP;0x0c", MnemonicORA, MnemonicASL, MnemonicSLO, // $0c-$0f MnemonicBPL, MnemonicORA, " JAM;0x12", MnemonicSLO, // $10-$13 " DOP;0x14", MnemonicORA, MnemonicASL, MnemonicSLO, // $14-$17 MnemonicCLC, MnemonicORA, " NOP;0x1a", MnemonicSLO, // $18-$1b " TOP;0x1c", MnemonicORA, MnemonicASL, MnemonicSLO, // $1c-$1f MnemonicJSR, MnemonicAND, " JAM;0x22", MnemonicRLA, // $20-$23 MnemonicBIT, MnemonicAND, MnemonicROL, MnemonicRLA, // $24-$27 MnemonicPLP, MnemonicAND, MnemonicROL, "!by$2b;ANC#", // $28-$2b MnemonicBIT, MnemonicAND, MnemonicROL, MnemonicRLA, // $2c-$2f MnemonicBMI, MnemonicAND, " JAM;0x32", MnemonicRLA, // $30-$33 " DOP;0x34", MnemonicAND, MnemonicROL, MnemonicRLA, // $34-$37 MnemonicSEC, MnemonicAND, " NOP;0x3a", MnemonicRLA, // $38-$3b " TOP;0x3c", MnemonicAND, MnemonicROL, MnemonicRLA, // $3c-$3f MnemonicRTI, MnemonicEOR, " JAM;0x42", MnemonicSRE, // $40-$43 " DOP;0x44", MnemonicEOR, MnemonicLSR, MnemonicSRE, // $44-$47 MnemonicPHA, MnemonicEOR, MnemonicLSR, MnemonicASR, // $48-$4b MnemonicJMP, MnemonicEOR, MnemonicLSR, MnemonicSRE, // $4c-$4f MnemonicBVC, MnemonicEOR, " JAM;0x52", MnemonicSRE, // $50-$53 " DOP;0x54", MnemonicEOR, MnemonicLSR, MnemonicSRE, // $54-$57 MnemonicCLI, MnemonicEOR, " NOP;0x5a", MnemonicSRE, // $58-$5b " TOP;0x5c", MnemonicEOR, MnemonicLSR, MnemonicSRE, // $5c-$5f MnemonicRTS, MnemonicADC, " JAM;0x62", MnemonicRRA, // $60-$63 " DOP;0x64", MnemonicADC, MnemonicROR, MnemonicRRA, // $64-$67 MnemonicPLA, MnemonicADC, MnemonicROR, MnemonicARR, // $68-$6b MnemonicJMP, MnemonicADC, MnemonicROR, MnemonicRRA, // $6c-$6f MnemonicBVS, MnemonicADC, " JAM;0x72", MnemonicRRA, // $70-$73 " DOP;0x74", MnemonicADC, MnemonicROR, MnemonicRRA, // $74-$77 MnemonicSEI, MnemonicADC, " NOP;0x7a", MnemonicRRA, // $78-$7b " TOP;0x7c", MnemonicADC, MnemonicROR, MnemonicRRA, // $7c-$7f " DOP;0x80", MnemonicSTA, " DOP;0x82", MnemonicSAX, // $80-$83 MnemonicSTY, MnemonicSTA, MnemonicSTX, MnemonicSAX, // $84-$87 MnemonicDEY, " DOP;0x89", MnemonicTXA, NULL, // $88-$8b MnemonicSTY, MnemonicSTA, MnemonicSTX, MnemonicSAX, // $8c-$8f MnemonicBCC, MnemonicSTA, " JAM;0x92", NULL, // $90-$93 MnemonicSTY, MnemonicSTA, MnemonicSTX, MnemonicSAX, // $94-$97 MnemonicTYA, MnemonicSTA, MnemonicTXS, NULL, // $98-$9b NULL, MnemonicSTA, MnemonicSHX, NULL, // $9c-$9f MnemonicLDY, MnemonicLDA, MnemonicLDX, MnemonicLAX, // $a0-$a3 MnemonicLDY, MnemonicLDA, MnemonicLDX, MnemonicLAX, // $a4-$a7 MnemonicTAY, MnemonicLDA, MnemonicTAX, NULL, // $a8-$ab MnemonicLDY, MnemonicLDA, MnemonicLDX, MnemonicLAX, // $ac-$af MnemonicBCS, MnemonicLDA, " JAM;0xb2", MnemonicLAX, // $b0-$b3 MnemonicLDY, MnemonicLDA, MnemonicLDX, MnemonicLAX, // $b4-$b7 MnemonicCLV, MnemonicLDA, MnemonicTSX, NULL, // $b8-$bb MnemonicLDY, MnemonicLDA, MnemonicLDX, MnemonicLAX, // $bc-$bf MnemonicCPY, MnemonicCMP, " DOP;0xc2", MnemonicDCP, // $c0-$c3 MnemonicCPY, MnemonicCMP, MnemonicDEC, MnemonicDCP, // $c4-$c7 MnemonicINY, MnemonicCMP, MnemonicDEX, MnemonicSBX, // $c8-$cb MnemonicCPY, MnemonicCMP, MnemonicDEC, MnemonicDCP, // $cc-$cf MnemonicBNE, MnemonicCMP, " JAM;0xd2", MnemonicDCP, // $d0-$d3 " DOP;0xd4", MnemonicCMP, MnemonicDEC, MnemonicDCP, // $d4-$d7 MnemonicCLD, MnemonicCMP, " NOP;0xda", MnemonicDCP, // $d8-$db " TOP;0xdc", MnemonicCMP, MnemonicDEC, MnemonicDCP, // $dc-$df MnemonicCPX, MnemonicSBC, " DOP;0xe2", MnemonicISC, // $e0-$e3 MnemonicCPX, MnemonicSBC, MnemonicINC, MnemonicISC, // $e4-$e7 MnemonicINX, MnemonicSBC, MnemonicNOP, "!by$eb;SBC#", // $e8-$eb MnemonicCPX, MnemonicSBC, MnemonicINC, MnemonicISC, // $ec-$ef MnemonicBEQ, MnemonicSBC, " JAM;0xf2", MnemonicISC, // $f0-$f3 " DOP;0xf4", MnemonicSBC, MnemonicINC, MnemonicISC, // $f4-$f7 MnemonicSED, MnemonicSBC, " NOP;0xfa", MnemonicISC, // $f8-$fb " TOP;0xfc", MnemonicSBC, MnemonicINC, MnemonicISC, // $fc-$ff }; // output 2-digit hex argument with correct addressing mode static void put_argument2(const char pre[], int byte, const char post[]) { IO_put_string(pre); IO_put_low_byte_hex(byte); IO_put_string(post); } // output 4-digit hex argument with correct addressing mode static void put_argument4(const char pre[], int word, const char post[]) { IO_put_string(pre); IO_put_low_16b_hex(word); IO_put_string(post); } static int pc; // needed by "relative" addressing mode handler // addressing mode handler functions // all of these output the opcode's argument and return the number to add // to the program counter // addressing mode handler function for 1-byte-instructions static int am_implied(void) { return 1; } // addressing mode handler functions for 2-byte-instructions static int am_immediate(void) { put_argument2(" #$", IO_get_byte(), ""); if (GotByte > 15) { fprintf(global_output_stream, " ; (= %d", GotByte); if ((GotByte > 31) && (GotByte != 127)) fprintf(global_output_stream, " = '%c'", GotByte); IO_put_byte(')'); } return 2; } static int am_absolute8(void) { put_argument2(" $", IO_get_byte(), ""); return 2; } static int am_abs_x8(void) { put_argument2(" $", IO_get_byte(), ", x"); return 2; } static int am_abs_y8(void) { put_argument2(" $", IO_get_byte(), ", y"); return 2; } static int am_indirect_x(void) { put_argument2(" ($", IO_get_byte(), ", x)"); return 2; } static int am_indirect_y(void) { put_argument2(" ($", IO_get_byte(), "), y"); return 2; } static int am_relative(void) { put_argument4(" L", pc + 2 + (signed char) IO_get_byte(), ""); return 2; } // addressing mode handler functions for 3-byte-instructions static int am_absolute16(void) { put_argument4(" L", IO_get_le16(), ""); return 3; } static int am_abs_x16(void) { put_argument4(" L", IO_get_le16(), ", x"); return 3; } static int am_abs_y16(void) { put_argument4(" L", IO_get_le16(), ", y"); return 3; } static int am_indirect16(void) { put_argument4(" (L", IO_get_le16(), ")"); return 3; } // 6502 code table (addressing mode handler functions) // all ANC/DOP/TOP are given as "implied", so the argument is not processed int (*addressing_mode_of_code[])(void) = { am_implied, am_indirect_x, am_implied, am_indirect_x, // $00-$03 am_implied, am_absolute8, am_absolute8, am_absolute8, // $04-$07 am_implied, am_immediate, am_implied, am_implied, // $08-$0b am_implied, am_absolute16, am_absolute16, am_absolute16, // $0c-$0f am_relative, am_indirect_y, am_implied, am_indirect_y, // $10-$13 am_implied, am_abs_x8, am_abs_x8, am_abs_x8, // $14-$17 am_implied, am_abs_y16, am_implied, am_abs_y16, // $18-$1b am_implied, am_abs_x16, am_abs_x16, am_abs_x16, // $1c-$1f am_absolute16, am_indirect_x, am_implied, am_indirect_x, // $20-$23 am_absolute8, am_absolute8, am_absolute8, am_absolute8, // $24-$27 am_implied, am_immediate, am_implied, am_implied, // $28-$2b am_absolute16, am_absolute16, am_absolute16, am_absolute16, // $2c-$2f am_relative, am_indirect_y, am_implied, am_indirect_y, // $30-$33 am_implied, am_abs_x8, am_abs_x8, am_abs_x8, // $34-$37 am_implied, am_abs_y16, am_implied, am_abs_y16, // $38-$3b am_implied, am_abs_x16, am_abs_x16, am_abs_x16, // $3c-$3f am_implied, am_indirect_x, am_implied, am_indirect_x, // $40-$43 am_implied, am_absolute8, am_absolute8, am_absolute8, // $44-$47 am_implied, am_immediate, am_implied, am_immediate, // $48-$4b am_absolute16, am_absolute16, am_absolute16, am_absolute16, // $4c-$4f am_relative, am_indirect_y, am_implied, am_indirect_y, // $50-$53 am_implied, am_abs_x8, am_abs_x8, am_abs_x8, // $54-$57 am_implied, am_abs_y16, am_implied, am_abs_y16, // $58-$5b am_implied, am_abs_x16, am_abs_x16, am_abs_x16, // $5c-$5f am_implied, am_indirect_x, am_implied, am_indirect_x, // $60-$63 am_implied, am_absolute8, am_absolute8, am_absolute8, // $64-$67 am_implied, am_immediate, am_implied, am_immediate, // $68-$6b am_indirect16, am_absolute16, am_absolute16, am_absolute16, // $6c-$6f am_relative, am_indirect_y, am_implied, am_indirect_y, // $70-$73 am_implied, am_abs_x8, am_abs_x8, am_abs_x8, // $74-$77 am_implied, am_abs_y16, am_implied, am_abs_y16, // $78-$7b am_implied, am_abs_x16, am_abs_x16, am_abs_x16, // $7c-$7f am_implied, am_indirect_x, am_implied, am_indirect_x, // $80-$83 am_absolute8, am_absolute8, am_absolute8, am_absolute8, // $84-$87 am_implied, am_implied, am_implied, am_implied, // $88-$8b am_absolute16, am_absolute16, am_absolute16, am_absolute16, // $8c-$8f am_relative, am_indirect_y, am_implied, am_implied, // $90-$93 am_abs_x8, am_abs_x8, am_abs_y8, am_abs_y8, // $94-$97 am_implied, am_abs_y16, am_implied, am_implied, // $98-$9b am_implied, am_abs_x16, am_abs_y16, am_implied, // $9c-$9f am_immediate, am_indirect_x, am_immediate, am_indirect_x, // $a0-$a3 am_absolute8, am_absolute8, am_absolute8, am_absolute8, // $a4-$a7 am_implied, am_immediate, am_implied, am_implied, // $a8-$ab am_absolute16, am_absolute16, am_absolute16, am_absolute16, // $ac-$af am_relative, am_indirect_y, am_implied, am_indirect_y, // $b0-$b3 am_abs_x8, am_abs_x8, am_abs_y8, am_abs_y8, // $b4-$b7 am_implied, am_abs_y16, am_implied, am_implied, // $b8-$bb am_abs_x16, am_abs_x16, am_abs_y16, am_abs_y16, // $bc-$bf am_immediate, am_indirect_x, am_implied, am_indirect_x, // $c0-$c3 am_absolute8, am_absolute8, am_absolute8, am_absolute8, // $c4-$c7 am_implied, am_immediate, am_implied, am_immediate, // $c8-$cb am_absolute16, am_absolute16, am_absolute16, am_absolute16, // $cc-$cf am_relative, am_indirect_y, am_implied, am_indirect_y, // $d0-$d3 am_implied, am_abs_x8, am_abs_x8, am_abs_x8, // $d4-$d7 am_implied, am_abs_y16, am_implied, am_abs_y16, // $d8-$db am_implied, am_abs_x16, am_abs_x16, am_abs_x16, // $dc-$df am_immediate, am_indirect_x, am_implied, am_indirect_x, // $e0-$e3 am_absolute8, am_absolute8, am_absolute8, am_absolute8, // $e4-$e7 am_implied, am_immediate, am_implied, am_implied, // $e8-$eb am_absolute16, am_absolute16, am_absolute16, am_absolute16, // $ec-$ef am_relative, am_indirect_y, am_implied, am_indirect_y, // $f0-$f3 am_implied, am_abs_x8, am_abs_x8, am_abs_x8, // $f4-$f7 am_implied, am_abs_y16, am_implied, am_abs_y16, // $f8-$fb am_implied, am_abs_x16, am_abs_x16, am_abs_x16, // $fc-$ff }; // output mnemonic of given byte static void output_mnemonic(int byte) { const char *mnemo = mnemo_of_code[byte]; if (mnemo) IO_put_string(mnemo); else put_argument2("$", byte, ""); } // main routine for disassembly void obj_main(void) { IO_set_input_padding(0); // process load address pc = IO_get_le16(); put_argument4("\t\t*=$", pc, "\n"); IO_get_byte(); while (!IO_reached_eof) { put_argument4("L", pc, "\t\t"); output_mnemonic(GotByte); pc += addressing_mode_of_code[GotByte](); IO_put_byte('\n'); IO_get_byte(); } // report end-of-file IO_put_string("; ToACME: Reached end-of-file.\n"); } acme-crossassembler-0.96.4/contrib/toacme/src/pet2iso.c000066400000000000000000000034711333777125400230150ustar00rootroot00000000000000// ToACME - converts other source codes to ACME format. // Copyright (C) 1999-2006 Marco Baye // Have a look at "main.c" for further info // // Converting CBM PetSCII code to ISO 8859/1 #include "pet2iso.h" // constants // conversion table const char PET2ISO_table[256] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, }; acme-crossassembler-0.96.4/contrib/toacme/src/pet2iso.h000066400000000000000000000005741333777125400230230ustar00rootroot00000000000000// ToACME - converts other source codes to ACME format. // Copyright (C) 1999-2006 Marco Baye // Have a look at "main.c" for further info // // Converting CBM PetSCII code to ISO 8859/1 #ifndef pet2iso_H #define pet2iso_H #include "config.h" // constants extern const char PET2ISO_table[256]; // conversion table #define PET2ISO(v) (PET2ISO_table[(unsigned char) v]) #endif acme-crossassembler-0.96.4/contrib/toacme/src/platform.c000066400000000000000000000012441333777125400232500ustar00rootroot00000000000000// ToACME - converts other source codes to ACME format. // Copyright (C) 1999-2006 Marco Baye // Have a look at "main.c" for further info // // Platform specific stuff #include "platform.h" #ifdef __riscos__ // // RISC OS // #include // defines _kernel_swi_regs #define OS_FILE 0x00008 // constant to call relevant SWI // setting the created files' types void platform_set_file_type_text(const char *filename) { _kernel_swi_regs register_set; register_set.r[0] = 18;// = SetFileType register_set.r[1] = (int) filename; register_set.r[2] = 0xfff; _kernel_swi(OS_FILE, ®ister_set, ®ister_set); } #else // // other OS (not that much here) // #endif acme-crossassembler-0.96.4/contrib/toacme/src/platform.h000066400000000000000000000011351333777125400232540ustar00rootroot00000000000000// ToACME - converts other source codes to ACME format. // Copyright (C) 1999-2015 Marco Baye // Have a look at "main.c" for further info // // Platform specific stuff #ifndef platform_H #define platform_H // check for RISC OS #ifdef __riscos__ #define PLATFORM_VERSION "Ported to RISC OS by Marco Baye." #define PLATFORM_SETFILETYPE_TEXT(a) platform_set_file_type_text(a); extern void platform_set_file_type_text(const char *filename); #endif // all other platforms #ifndef PLATFORM_VERSION #define PLATFORM_VERSION "Platform independent version." #define PLATFORM_SETFILETYPE_TEXT(a) #endif #endif acme-crossassembler-0.96.4/contrib/toacme/src/prof.c000066400000000000000000000046361333777125400224020ustar00rootroot00000000000000// ToACME - converts other source codes to ACME format. // Copyright (C) 1999-2016 Marco Baye // Have a look at "main.c" for further info // // "Professional Ass by Oliver Stiller" stuff (NOT "Profi-Ass" by Data Becker!) // Kickoff: // http://www.forum64.de/wbb4/index.php?thread/60047-toacme-demn%C3%A4chst-als-win32-release/ #include //#include "config.h" //#include "acme.h" //#include "mnemo.h" #include "io.h" #include "scr2iso.h" // format of input files: // load address, lo // load address, hi // for each line: // total line length, including this byte // amount of indentation // actual line as screen code (this part might be missing in case of empty line) // total line length, including this byte (yes, the same value again, for upscrolling) // that's it! // special directives: // '.' is for pseudo opcodes? // .setpc is *= // .fill (length, data) ? // .b is !by, .w is !wo, .macro reads a macro argument? // .goto is used to skip macro definitions?! // what do .begin and .end do? local label scope? // .print str$(*-$1060) wow... O_o // '-' prefix means local label definition? // '+' prefix means global label definition? // '_' starts a macro definition? ".endmacro" ends definition? // '*' prefix is a macro call? #define TABSIZE 8 // anything else is heresy #define REV_START ";-=#" #define REV_END "#=-" // main void prof_main(void) { int length1, indent, ii, byte, hibit, length2; IO_set_input_padding(0); IO_process_load_address(); for (;;) { length1 = IO_get_byte(); if (length1 == EOF) break; if (length1 < 3) { fprintf(stderr, "Error: Short line (%d bytes)\n", length1); } // read amount of indentation and output tabs/spaces indent = IO_get_byte(); if (indent < 0) { fprintf(stderr, "Error: Negative indentation (%d)\n", indent); } else { for (ii = 0; ii + TABSIZE <= indent; ii += TABSIZE) IO_put_byte('\t'); for (; ii < indent; ii++) IO_put_byte(' '); } // now convert line hibit = 0; for (ii = 0; ii < length1 - 3; ii++) { byte = IO_get_byte(); if ((byte & 128) != hibit) { hibit = byte & 128; IO_put_string(hibit ? REV_START : REV_END); } IO_put_byte(SCR2ISO(byte & 127)); } if (hibit) IO_put_string(REV_END); // and add newline IO_put_byte('\n'); // now check second length byte length2 = IO_get_byte(); if (length1 != length2) fprintf(stderr, "Error: Length bytes differ (%d != %d)\n", length1, length2); } } acme-crossassembler-0.96.4/contrib/toacme/src/scr2iso.c000066400000000000000000000034701333777125400230130ustar00rootroot00000000000000// ToACME - converts other source codes to ACME format. // Copyright (C) 1999-2006 Marco Baye // Have a look at "main.c" for further info // // Converting CBM screen code to ISO 8859/1 #include "scr2iso.h" // constants // conversion table const char SCR2ISO_table[256] = { 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, }; acme-crossassembler-0.96.4/contrib/toacme/src/scr2iso.h000066400000000000000000000005721333777125400230200ustar00rootroot00000000000000// ToACME - converts other source codes to ACME format. // Copyright (C) 1999-2006 Marco Baye // Have a look at "main.c" for further info // // Converting CBM screen code to ISO 8859/1 #ifndef scr2iso_H #define scr2iso_H #include "config.h" // constants extern const char SCR2ISO_table[256]; // Conversion table #define SCR2ISO(v) (SCR2ISO_table[(unsigned char) v]) #endif acme-crossassembler-0.96.4/contrib/toacme/src/version.c000066400000000000000000000057611333777125400231210ustar00rootroot00000000000000// ToACME - converts other source codes to ACME format. // Copyright (C) 1999-2015 Marco Baye // Have a look at "main.c" for further info // // Version #define RELEASE_NUMBER "0.14" // change before release (FIXME) #define CHANGE_DATE "19 Feb" // change before release #define CHANGE_YEAR "2017" // change before release #define HOME_PAGE "http://sourceforge.net/projects/acme-crossass/" // "http://home.pages.de/~mac_bacon/smorbrod/acme/" #define FILE_TAG ";ACME 0.96.1" // check before release #include #include #include "io.h" #include "platform.h" // variables void (*client_main)(void) = NULL; // functions // show version info and usage void version_show_info(const char program_name[]) { printf( "\n" "ToACME - converts other assemblers' source codes to ACME format.\n" "Release " RELEASE_NUMBER " (" CHANGE_DATE " " CHANGE_YEAR "), Copyright (C) 1999-" CHANGE_YEAR " Marco Baye.\n" PLATFORM_VERSION "\n" "Thanks to Stefan Hübner for fixing the AssBlaster macro conversion code.\n" "Thanks to Andreas Paul for helping with the Giga-Assembler mode.\n" "Thanks to Arndt Dettke for helping with the Hypra-Assembler mode.\n" "Thanks to Hoogo for helping with the Professional Assembler mode.\n" "\n" "The newest version can be found at the ACME homepage:\n" HOME_PAGE "\n" "\n" "ToACME comes with ABSOLUTELY NO WARRANTY; for details read the help file.\n" "This is free software, and you are welcome to redistribute it under\n" "certain conditions; as outlined in the GNU General Public License.\n" "\n" "Syntax: %s FORMAT_ID INPUT_FILE OUTPUT_FILE\n" "\n" "Format ID: source file format quality\n" "--------------------------------------------------\n" "object object code files poor\n" "hypra C64: Hypra-Assembler ok\n" "giga C64: Giga-Assembler ok\n" "vis C64: VisAss untested\n" "ab3 C64: AssBlaster 3.0 to 3.2 good\n" "f8ab C64: Flash8-AssBlaster ok\n" "prof C64: Professional Assembler poor (work in progress)\n" "\n" , program_name); } extern void visass_main(void); extern void ab3_main(void); extern void f8ab_main(void); extern void giga_main(void); extern void hypra_main(void); extern void obj_main(void); extern void prof_main(void); // check id string. returns whether illegal. int version_parse_id(const char id[]) { if (strcmp(id, "vis") == 0) client_main = visass_main; else if (strcmp(id, "ab3") == 0) client_main = ab3_main; else if (strcmp(id, "f8ab") == 0) client_main = f8ab_main; else if (strcmp(id, "giga") == 0) client_main = giga_main; else if (strcmp(id, "hypra") == 0) client_main = hypra_main; else if (strcmp(id, "object") == 0) client_main = obj_main; else if (strcmp(id, "prof") == 0) client_main = prof_main; return client_main ? 0 : 1; } // do the actual work void version_main(void) { IO_put_string( FILE_TAG "\n" "; ToACME: Converted by ToACME, release " RELEASE_NUMBER ".\n" ); client_main(); } acme-crossassembler-0.96.4/contrib/toacme/src/version.h000066400000000000000000000005151333777125400231160ustar00rootroot00000000000000// ToACME - converts other source codes to ACME format. // Copyright (C) 1999-2006 Marco Baye // Have a look at "main.c" for further info // // version #ifndef version_H #define version_H // prototypes extern void version_show_info(const char[]); extern int version_parse_id(const char[]); extern void version_main(void); #endif acme-crossassembler-0.96.4/contrib/toacme/src/vis.c000066400000000000000000000022271333777125400222270ustar00rootroot00000000000000// ToACME - converts other source codes to ACME format. // Copyright (C) 1999-2015 Marco Baye // Have a look at "main.c" for further info // // VisAss stuff #include "ab.h" #include "acme.h" #include "io.h" static int number_parser(void) { return 0; } // config struct for shared VisAss/AB code struct vab visass_conf = { VABFLAG_HASLENGTHBYTE, number_parser, visass_ab3_pseudo_opcode_table, visass_ab3_mnemonic_table, 2, // we fake absolute addressing because VisAss does not encode the addr mode // meaning of input bytes (0x80-0xec differ between VisAss, AB3 and F8AB) 0, // first mnemonic 0x48, // count 0x48, // first pseudo opcode 14, // count 0x100, // first unused value (dummy) 1 // count (dummy) }; // main void visass_main(void) { IO_set_input_padding(AB_ENDOFLINE); visass_ab3_illegals(); IO_process_load_address(); // first byte after load address should be AB_ENDOFLINE in VisAss sources if (IO_get_byte() == AB_ENDOFLINE) { IO_get_byte(); // skip it and pre-read first valid byte fputs("Input has VisAss header.\n", stderr); } else { fputs("Input does not have any known VisAss header.\n", stderr); } AB_main(&visass_conf); } acme-crossassembler-0.96.4/contrib/ultraedit_wordfile/000077500000000000000000000000001333777125400231105ustar00rootroot00000000000000acme-crossassembler-0.96.4/contrib/ultraedit_wordfile/ReadMe.txt000066400000000000000000000026321333777125400250110ustar00rootroot00000000000000 --- UltraEdit wordfile for ACME assembler source codes --- The word list is freeware - anyone may use it as they see fit, commercially or otherwise. My only restriction is that I won't be held responsible for any damages caused by using it. Instructions to use the wordlist: The contents of the wordfile should be pasted in at the end of the file called "Wordfile.txt" in the UltraEdit program directory, and then you should check that the number on the first line of the assembler language definition doesn't conflict with any other language in the wordfile. It currently reads "/L10 ..." but instead of 10 you could put in any number between 1 and 20. 1-9 are normally already taken, so 10 is a good choice if this is the only language you've added to UltraEdit. Restart UltraEdit if it was running. That's it. There's more information in UltraEdit's built-in help and on this page: http://www.ultraedit.com/index.php?name=Content&pa=showpage&pid=40#wordfiles The only thing which I think may seem odd to some people is that some of the opcodes are marked red. That's the instructions that may change the value of the program counter (JMP, JSR, RTS, BEQ etc). I think it makes it easier to follow the program flow when reading the code. Anyway, this can be changed so that they have the same color as the other opcodes. It's in the "Advanced | Configuration" dialog box in UltraEdit. Best regards, Fredrik Ramsberg acme-crossassembler-0.96.4/contrib/ultraedit_wordfile/acme_wordfile.txt000066400000000000000000000020771333777125400264570ustar00rootroot00000000000000/L10"ACME 65xx Asm" Nocase Line Comment = ; Escape Char = \ String Chars = "' File Extensions = A ASM ASS /Delimiters = :;#$"'()<>^-*/%+=&| , ? /Function String = "%[a-zA-Z_][a-zA-Z0-9_]+" /Indent Strings = "{" /Unindent Strings = "}" /C1"General opcodes" , ADC AND ASL CLC COP CLD CLI CLV CMP CPX CPY DEC DEX DEY DCP EOR INC INX INY ISC LDA LDX LDY LSR LAX MVN MVP NOP ORA PHA PEA PEI PER PHB PHD PHK PLB PLD PHX PHY PLX PLY PHP PLA PLP ROL REP ROR RLA RRA SBC SEP STZ S SEC SED SEI STA STX STY SLO SRE SAX TAX TCD TCS TDC TSC TXY TYX TRB TSB TAY TSX TXA TXS TYA WDM X XBA XCE Y /C2"Program flow opcodes" BCC BRL BRA BCS BEQ BIT BMI BNE BPL BRK BVC BVS JMP JML JSL JSR RTI RTL RTS STP WAI /C3"Pseudo opcodes" !08 !by !byte !8 !16 !wo !word !24 !32 !fill !fi !addr !address !convtab !ct !text !tx !pet !raw !scr !scrxor !to !source !src !binary !bin !symbollist !sl !zone !zn !if !ifdef !ifndef !for !set !do !endoffile !eof !macro !initmem !pseudopc !align !cpu !al !as !rl !rs !warn !error !serious else /C4 "operators" >> > >= = + ! != ^ - * // / % + - << < <= & | DIV NOT MOD OR XOR acme-crossassembler-0.96.4/docs/000077500000000000000000000000001333777125400165105ustar00rootroot00000000000000acme-crossassembler-0.96.4/docs/65816.txt000066400000000000000000000065461333777125400177550ustar00rootroot00000000000000 ACME ...the ACME Crossassembler for Multiple Environments --- 65816 support --- This text contains information about the 65816-specific features of ACME. ---------------------------------------------------------------------- Section: Command aliases for "long" JMPs and JSRs ---------------------------------------------------------------------- In addition to the commands JMP and JSR, the 65816 processor also knows JML and JSL, which are JMP and JSR using new (long) addressing modes. ACME also accepts the new addressing modes when using the old mnemonics JMP and JSR, but the old addressing modes cannot be used with the new mnemonics JML and JSL. ---------------------------------------------------------------------- Section: Argument order of MVN/MVP ---------------------------------------------------------------------- According to WDC's official syntax for 65816 assembly language, the argument order of the MVN and MVP instructions differs between assembly language and machine code. To copy bytes from bank $ab to bank $cd, use the following statement: mvn $ab, $cd ; source bank $ab, destination bank $cd ACME will then produce the following machine code: $54 $cd $ab ; opcode mvn, destination bank $cd, source bank $ab ACME 0.05 and earlier did it the wrong way. Several other assemblers still do. Make sure your sources are correct. ---------------------------------------------------------------------- Section: Register lengths ---------------------------------------------------------------------- When assembling "lda #5" for example, ACME has to know whether to create an 8-bit argument or a 16-bit argument. This depends on the current register length. On startup, ACME assumes all registers are 8 bits wide. You can change this at any time using the following pseudo opcodes: !al ; switch to long accumulator !as ; switch to short accumulator !rl ; switch to long index registers !rs ; switch to short index registers Please note that ACME, unlike some other assemblers, does *not* track SEP/REP commands: I don't like that method - it fails when encountering PLPs, for example. So if it doesn't work reliably in the first place, why use it? :) If you don't like that you always have to use a pseudo opcode alongside SEP/REP commands, then have a look at the file <65816/std.a> (in the library). There are some predefined macros that you can use. ---------------------------------------------------------------------- Section: Postfixing stuff ---------------------------------------------------------------------- You can also use the postfix method (which is explained in the file "AddrModes.txt") to specify the immediate argument's length: ldx+2 #5 will always be assembled to a 16-bit argument, regardless of the currently configured index register width. Use at your own risk - this method obviously is not a good example on structured programming. :) ---------------------------------------------------------------------- Section: Miscellaneous ---------------------------------------------------------------------- Note that ACME cannot produce more than 64 KBytes of code. Also note that though the 65816 CPU has an address space of 16 MB, ACME's program counter is only sixteen bits wide. It shouldn't be too hard to make any assembled code run in a non-zero bank, though. acme-crossassembler-0.96.4/docs/AddrModes.txt000066400000000000000000000137061333777125400211220ustar00rootroot00000000000000 ACME ...the ACME Crossassembler for Multiple Environments --- addressing modes --- If a command can be used with different addressing modes, ACME has to decide which one to use. Several commands of the 6502 CPU can be used with either "absolute" addressing or "zeropage-absolute" addressing. The former one means there's a 16-bit argument, the latter one means there's an 8-bit argument. And the 65816 CPU even knows some commands with 24-bit addressing... So how does ACME know which addressing mode to use? The simple approach is to always use the smallest possible argument, of course: If the argument fits in a byte, use zeropage addressing. If it doesn't, use absolute addressing. If it needs more than two bytes and the 65816 CPU is chosen, use 24-bit addressing. In most cases this works - with two exceptions. The remainder of this text may sound somewhat confusing now, so if you don't have any problems with addressing modes, then don't bother trying to understand everything this texts says. :) The two exceptions are: *** 1) Symbols are defined too late If ACME cannot figure out the argument value in the first pass, it assumes that the command uses 16-bit addressing. If it later finds out that the argument only needs 8 bits, ACME gives a warning ("using oversized addressing mode") and continues. However, if it finds out that the argument needs 24 bits, it gives an error. These problems can be solved by defining the symbols *before* using them, so that the value can be figured out in the first pass. If this is not possible, you can use the postfix method, effectively exactly defining what addressing mode to use. The postfix method is described in a separate paragraph below. *** 2) You *want* to use an oversized addressing mode On the 65816 CPU, "zeropage addressing" is called "direct page addressing". The difference is that the position of the "direct page" can be changed. Then, "lda $fa" does not necessarily access the same memory location as "lda $00fa" anymore. The same goes for 16- and 24- bit addressing: "lda $fabc" does not necessarily access the same memory location as "lda $00fabc", because the default bank can be set to something other than zero. But even on the plain 6502 CPU you might want to force ACME to use an oversized addressing mode, for example because of timing issues. Again there are two ways to solve the problem: You can define the target location using leading zeros. ACME will then use an addressing mode that is big enough even if the leading zeros would have been other digits: symbol1 = $fb symbol2 = $00fd symbol3 = $0000ff lda $fa sta $00fc lda $0000fe sta symbol1 lda symbol2 sta symbol3 will be assembled to a5 fa ; lda $fa 8d fc 00 ; sta $00fc af fe 00 00 ; lda $0000fe 85 fb ; sta $fb ad fd 00 ; lda $00fd 8f ff 00 00 ; sta $0000ff The other possibility is to use the postfix method (described in the next paragraph). *** The postfix method Warning: This may sound very complicated at first, but I think that once you get used to it you'll agree it's useful. If you don't want to use this, stick to the "leading zeros" method and don't bother about postfixes. Still with me? Okay: You can force ACME to use a specific addressing mode by adding "+1", "+2" or "+3" to the assembler mnemonic. Each one of these postfixes sets the relevant "Force Bit" in ACME's result. If Force Bit 3 is set, ACME will use 24-bit addressing. Force Bit 2 means 16-bit addressing and Force Bit 1 means 8-bit addressing. Higher Force Bits have higher priorities. Here's an (overly complicated) example: symbol1 = $fb symbol2 = $fd symbol3+3 = $ff ; set Force Bit 3 and store in symbol's flags ldx $fa ldy+2 $fc ; set Force Bit 2 (16-bit addressing) lda+3 $fe ; set Force Bit 3 (24-bit addressing) stx symbol1 sty+2 symbol2 ; set Force Bit 2 (16-bit addressing) sta symbol3 ; no need to set Force Bit 3 as it is ; already set in "symbol3". will be assembled to a6 fa ; ldx $fa ac fc 00 ; ldy $00fc af fe 00 00 ; lda $0000fe 86 fb ; stx $fb 8c fd 00 ; sty $00fd 8f ff 00 00 ; sta $0000ff Postfixes given directly after the command have higher priority than those given to the argument. As you can see, you can add the postfix to the symbol definition as well (equivalent to leading zeros). Applying the byte extraction operators ("<" gives the low byte, ">" gives the high byte and "^" gives the bank byte of a value) to any value will clear the argument's Force Bits 2 and 3 and set Force Bit 1 instead. So "lda quoting (load from library). The file must hold exactly 256 bytes. BLOCK: A block of assembler statements Before encountering this PO, ACME defaults to "raw". This PO supersedes the now deprecated "!cbm". Aliases: "!ct" Examples: !convtab raw !text "Test" ; outputs $54 $65 $73 $74 !ct pet !tx "Test" ; outputs $d4 $45 $53 $54 !ct scr { !tx "Test" ; outputs $54 $05 $13 $14 !ct "my_own_table_file" !tx "abcdefg" ; whatever... :) } !tx "Test" ; outputs $d4 $45 $53 $54 again Hint: If you don't want to fiddle with a hex editor to create a conversion table file, try using ACME: !to "asc2pet.ct", plain ; no load address * = 0 ; pc = table index ; first create "as-is" table !for i, 0, 255 {!byte i} ; now exchange upper and lower case characters * = 65, overlay !for i, 1, 26 {!byte * + 128} * = 97, overlay !for i, 1, 26 {!byte * - 32} The resulting file can be used as a conversion table to convert to PetSCII (which is useless, because ACME can do so anyway. But you get the idea). Call: !text STRING_VALUE [, STRING_VALUE]* Purpose: Output the given string(s) using the current conversion table. Parameters: STRING_VALUE: Can be either a string given in double quotes or any formula the value parser accepts. Please note that formula results won't be converted, but single characters involved in calculations will. Aliases: "!tx" Examples: !text "Loading...", Char_NewLine, "Filename:", 0 !tx "Offset character is ", offset - 1 + 'a', 0 Call: !pet STRING_VALUE [, STRING_VALUE]* Purpose: Output the given string(s) using the PetSCII conversion table (This means to exchange the upper- and lowercase characters; useful for C64 programs). Parameters: STRING_VALUE: Can be either a string given in double quotes or any formula the value parser accepts. Please note that formula results won't be converted, but single characters involved in calculations will. Examples: !pet "Loading...", Char_NewLine, "Filename:", 0 !pet "Offset character is ", offset - 1 + 'a', 0 Call: !raw STRING_VALUE [, STRING_VALUE]* Purpose: Output the given string(s) without any conversion at all. Parameters: STRING_VALUE: Can be either a string given in double quotes or any formula the value parser accepts. Examples: !raw "Loading...", Char_NewLine, "Filename:", 0 !raw "Offset character is ", offset - 1 + 'a', 0 Call: !scr STRING_VALUE [, STRING_VALUE]* Purpose: Output the given string(s) using the C64 screen code conversion table (useful for C64 programs, as you will have guessed). Parameters: STRING_VALUE: Can be either a string given in double quotes or any formula the value parser accepts. Please note that formula results won't be converted, but single characters involved in calculations will. Examples: !scr "Loading...", Char_NewLine, "Filename:", 0 !scr "Offset character is ", offset - 1 + 'a', 0 Call: !scrxor XOR_VALUE, STRING_VALUE [, STRING_VALUE]* Purpose: Output the given string(s) using the C64 screen code conversion table and exclusive-OR-ing the results with the given value (useful for C64 programs when inverse video is needed, or EBC mode, etc.). Parameters: XOR_VALUE: Any formula the value parser accepts. STRING_VALUE: Can be either a string given in double quotes or any formula the value parser accepts. Please note that formula results will be neither converted nor exclusive-OR-d. Single characters involved in calculations will be converted, but not exclusive-OR-d. Examples: !scrxor $80, "Loading..." !scrxor $a0, "Offset char is ", (offset-1+'a') XOR $a0 ---------------------------------------------------------------------- Section: File stuff ---------------------------------------------------------------------- Call: !to FILENAME, FILEFORMAT Purpose: Define the output file name and file type. If this opcode isn't used, ACME still fully processes the source code - as the resulting binary isn't stored, this only serves to check for errors. Instead of using this pseudo opcode, you can also use the command line options "--outfile" and "--format". Parameters: FILENAME: A file name given in "..." quoting. FILEFORMAT: Name of file format. Valid names are: cbm with load address (Commodore format) plain without load address apple with load address and length (Apple II) If FILEFORMAT is omitted, ACME gives a warning and then defaults to "cbm" (this can be changed using the command line option "--format"). Examples: !to "eprom.p", plain ; don't add a load address !to "demo.o", cbm ; add c64-style load address Call: !source FILENAME Purpose: Assemble another source code file. After having processed the new file, ACME continues processing the old one. Parameters: FILENAME: A file name given in "..." quoting (load from current directory) or in <...> quoting (load from library). Aliases: "!src" Examples: !source <6502/std.a> ; Read library file !src "Macros.a" ; Read file from current dir Call: !binary FILENAME [, [SIZE] [, [SKIP]]] Purpose: Insert binary file directly into output file. Parameters: FILENAME: A file name given in "..." quoting (load from current directory) or in <...> quoting (load from library). SIZE: Any formula the value parser accepts, but it must be solvable even in the first pass. If SIZE is given, it is used: If the file is longer, only SIZE bytes are read; if it is shorter, ACME will use padding until SIZE is reached. If SIZE is omitted, ACME will include the file until EOF. SKIP: Any formula the value parser accepts. If SKIP is omitted, it defaults to zero. ACME will start loading the file from file offset SKIP. So C64 coders wanting to include C64 files without their load addresses should use a SKIP value of 2. Aliases: "!bin" Examples: !binary ; insert library file !bin "asc2pet.b", 256, 2 ; insert 256 bytes ; from file offset 2. !bin "table", 2, 9 ; insert 2 bytes from offset 9 !bin "list",, 9 ; insert from offset 9 to EOF ---------------------------------------------------------------------- Section: Symbols ---------------------------------------------------------------------- Call: !zone [TITLE] [ { BLOCK } ] Purpose: Switch to new zone of local symbols. Zones can either be nested or used sequentially. Parameters: TITLE: May consist of letters and digits. Its only purpose is to be displayed in error messages, so it'll be omitted in most cases. BLOCK: A block of assembler statements If no block is given, the previous zone is terminated and the new zone is started. If a block is given, the old zone continues after the block. Aliases: "!zn" Examples: .backgroundcolor = 0 ; some local symbol !zone File_IO ; new zone begins here, so .backgroundcolor = 1 ; this is a different symbol !zn LinkedList_Init .backgroundcolor = 2 !zone LinkedList { ; start of nested zone ; imagine some code here... !zone LinkedList_Init ; imagine some more code here... !zone LinkedList_Body { ; imagine yet some more code here... !zone LinkedList_SecondPart ; imagine still some more code here... } !zone LinkedList_End ; you know what to imagine here... } .backgroundcolor = 3 ; => "Symbol already defined." Call: !symbollist FILENAME Purpose: Write a symbol list to the given file after assembly is finished. The list will contain all global symbols. This table could be loaded during another assembly session using the "!source" pseudo opcode. Parameters: FILENAME: A file name given in "..." quoting. Aliases: "!sl" Examples: !sl "Symbols.a" ; produce symbol list after assembly !sl "global" ; produce symbol list after assembly ---------------------------------------------------------------------- Section: Flow control ---------------------------------------------------------------------- Call: !if CONDITION { BLOCK } [ else { BLOCK } ] Purpose: Conditional assembly. If the given condition is true, the first block of statements will be parsed; if it isn't, the second block will be parsed instead (if present). Parameters: CONDITION: Any formula the value parser accepts, but it must be solvable even in the first pass. BLOCK: A block of assembler statements. Examples: !text "Black", 0 ; Choose wording according to !if country = uk { ; content of "country" symbol. !text "Grey" } else { !text "Gray" } !byte 0 !text "White", 0 ; Insert debug commands if symbol "debug" is not zero: !if debug { lda #"z":jsr char_output } Call: !ifdef SYMBOL { BLOCK } [ else { BLOCK } ] or: !ifdef SYMBOL STATEMENT Call: !ifndef SYMBOL { BLOCK } [ else { BLOCK } ] or: !ifndef SYMBOL STATEMENT Purpose: Conditional assembly, depending on whether a symbol is already defined or not. With "ifdef", if the symbol is defined, the first block of statements will be parsed; if it isn't, the second block will be parsed instead (if present). With "ifndef", it's the other way around: If the symbol isn't defined, the first block of statements will be parsed; if it is defined, the second block will be parsed instead (if present). CAUTION: These opcodes were added to speed up parsing of library files (see example below). They can be used to tell passes apart, therefore only use them in your own files if you're sure you *really* know what you are doing - using them in the wrong way will result in loads of error messages. Parameters: SYMBOL: Any valid symbol name. BLOCK: A block of assembler statements. STATEMENT: Any assembler statement. Examples: ; this was taken from <6502/std.a>: !ifdef Lib_6502_std_a !eof ; in later passes, Lib_6502_std_a = 1 ; skip this file. ; During the first pass, the symbol is not defined, ; therefore the file will get parsed. During all ; further passes, the symbol is already defined, ; therefore the file will be skipped. ; if the following code gets included several times, ; only assemble it at the first location: !ifndef my_label {my_label} ; only define if undefined !if * = my_label { ; imagine some code here... ; this block will only be assembled at the ; first location where it is included. all ; further instances will be skipped. } Call: !for SYMBOL, START, END { BLOCK } Purpose: Looping assembly. The block of statements will be parsed a fixed number of times, as specified by the values of START and END. For a more flexible possibility, have a look at "!do" below. Parameters: SYMBOL: Any valid symbol name. START: Any formula the value parser accepts, but it must be solvable even in the first pass. SYMBOL will have this value during the first loop cycle. END: Any formula the value parser accepts, but it must be solvable even in the first pass. SYMBOL will have this value during the last loop cycle. BLOCK: A block of assembler statements. If START or END are floats, they will be converted to integers (never use floats for loop counters). If START is less than or equal to END, SYMBOL will get incremented at the end of each cycle; if START is greater than END, SYMBOL will get decremented at the end of each cycle. So after leaving the loop, SYMBOL will have an "illegal" value (END + 1 if counting up, END - 1 if counting down). Please note that it is impossible to change the number of loop cycles "inside" the loop by fiddling with the counter using the "!set" pseudo opcode: The "!for" routine keeps its own copy of the counter value and only sets the symbol value, it never reads it back. This was done to eliminate a possibility to hang ACME. Examples: int2BCD ; conversion table: integer to BCD !for Outer, 0, 9 { !for Inner, 0, 9 { !byte (Outer << 4) OR Inner } } !fill 156, $ff ; values above 99 give 255 (invalid) BCD2int ; conversion table: BCD to integer !for Outer, 0, 9 { !for Inner, 0, 9 { !byte 10 * Outer + Inner } !fill 6, $ff ; invalid BCD values give 255 } !fill 96, $ff ; invalid BCD values give 255 quickclear ; generate speedcode to clear C64 screen lda #' ' !for i, 0, 999 { sta $0400 + i } Miscellaneous: The old syntax ("!for SYMBOL, END { BLOCK }" where START was always implied to be 1) is still fully supported, but gives a warning to get people to change to the new syntax. You can disable this warning using the "-Wno-old-for" switch, but then you will get warnings for using the *new* syntax. When migrating your sources, bear in mind that it is no longer possible to skip the block completely by specifying a loop count of zero. Also note that with the new algorithm, SYMBOL has a different value after the block than during the last loop cycle, while the old algorithm kept that last value. Call: !set SYMBOL = VALUE Purpose: Assign given value to symbol even if the symbol already has a different value. Needed for loop counters when using "!do", for example. Only use this opcode for something else if you're sure you *really* know what you are doing... :) Parameters: SYMBOL: Any valid symbol name. VALUE: Any formula the value parser accepts. Example: see "!do" below Call: !do [KEYWORD CONDITION] { BLOCK } [KEYWORD CONDITION] Purpose: Looping assembly. The block of statements can be parsed several times, depending on the given condition(s). Conditions may be placed before or after the block (or even at both places), they are then parsed in every repetition before or after the block respectively. If there is a condition before the block and it isn't met when first checked, the block will be skipped. Parameters: KEYWORD: Either "until" or "while" (without quotes). CONDITION: Any formula the value parser accepts, but it must be solvable even in the first pass. BLOCK: A block of assembler statements. Examples: ; a loop with conditions at both start and end !set a = 0 ; init loop counter !do while loop_flag = TRUE { lda #a sta label + a !set a = a + 1 } until a > 6 ; a loop with a condition at the start !do while * < $c000 { nop } ; a loop with a condition at the end !do { !wo * + base } while * < base + 345 ; a never ending loop - this will cause an error !do while 3 < 4 { nop } until 3 = 4 ; an empty loop - this will hang ACME !do until 3 = 4 { } while 3 < 4 Call: !endoffile Purpose: Stop processing the current source file. Using this pseudo opcode you can add explanatory text inside your source file without having to comment out every single line of it. Aliases: "!eof" Example: rts ; some assembler mnemonic !eof Though this text isn't preceded by a semicolon, it is treated as if it were a comment. In fact, ACME doesn't even parse this anymore - the file gets closed when "!eof" is reached. Call: !warn STRING_VALUE [, STRING_VALUE]* Purpose: Show a warning during assembly. Parameters: STRING_VALUE: Can be either a string given in double quotes or any formula the value parser accepts. Numbers will be output in decimal _and_ hex format. Example: !if * > $a000 { !warn "Program reached ROM: ", * - $a000, " bytes overlap." } Call: !error STRING_VALUE [, STRING_VALUE]* Purpose: Generate an error during assembly (therefore, no output file will be generated). Parameters: STRING_VALUE: Can be either a string given in double quotes or any formula the value parser accepts. Numbers will be output in decimal _and_ hex format. Example: rts ; end of some function start !source "colors.a" end !if end - start > 256 { !error "Color strings are ", end - start - 256, " bytes too long." } Call: !serious STRING_VALUE [, STRING_VALUE]* Purpose: Generate a serious error, immediately stopping assembly. Parameters: STRING_VALUE: Can be either a string given in double quotes or any formula the value parser accepts. Numbers will be output in decimal _and_ hex format. Example: !source "part1.a" ; sets part1_version !source "part2.a" ; sets part2_version !if part1_version != part2_version { !serious "part1.a and part2.a don't match!" } ---------------------------------------------------------------------- Section: Macro usage ---------------------------------------------------------------------- Call: !macro TITLE [[~]SYMBOL [, [~]SYMBOL]*] { BLOCK } Purpose: Define a macro. Parameters: TITLE: The macro's desired name (same rules as for symbol names). If the title's first character is a dot ("."), the macro will be local (though why anyone could want this is beyond me). SYMBOL: The desired name for the parameter value at call time. Normally, these parameter symbols should be local (first character a dot), as different macro calls will almost for sure have different parameter values. If you prefix SYMBOL with a '~' character, it will be called by reference, not by value: Changing the value inside the macro will result in the "outer" symbol to be changed as well. BLOCK: A block of assembler statements. Examples: ; far branch, as defined in <6502/std.a> !macro bne .target { beq * + 5 jmp .target } ; increase 16-bit counters !macro dinc .target { inc .target bne + ; "bne * + 5" would not work in zp inc .target + 1 + } ; Yes, anonymous label references can be used with ; macros (unlike several other assemblers). That's ; because ACME's macros are implemented more like ; real functions. ; load A and X !macro ldax .target { lda .target ldx .target + 1 } ; store A and X !macro stax .target { sta .target stx .target + 1 } ; use call-by-reference for return value !macro reserve ~.address, .amount { .address = external_pc !set external_pc = external_pc + .amount } ; define a pixel row of a C64 hardware sprite !macro SpriteLine .v { !by .v >> 16, (.v >> 8) & 255, .v & 255 } Call: +TITLE [ARGUMENT [, ARGUMENT]*] Purpose: Call a macro, using the given parameter values. Parameters: TITLE: The macro's name as given in its definition. ARGUMENT: This is either any formula the value parser accepts, or (new in release 0.86) a '~' character followed by a symbol name. The '~'-prefix indicates call-by-reference semantics, which means that when the macro changes the symbol's value, the caller's symbol's value will change as well. Examples: inc label bne mark ; "near" branch inc label2 +bne mark2 ; "far" branch inc $fa ; increase 8-bit counter +dinc $fb ; increase 16-bit counter ldy label ; get byte +ldax label2 ; get two bytes ; using macro calls in a macro definition !macro cp16 .source, .target { +ldax .source +stax .target } ; use call-by-reference for return value !set external_pc = $0400 +reserve ~.line_buffer, 80 +reserve ~.in_buffer, 256 +reserve ~.out_buffer, 256 +reserve ~.byte_var, 1 ; define a C64 hardware sprite ; 765432107654321076543210 +SpriteLine %........................ +SpriteLine %.#...................... +SpriteLine %.##..................... +SpriteLine %.###.................... +SpriteLine %.####................... +SpriteLine %.#####.................. +SpriteLine %.######................. +SpriteLine %.#######................ +SpriteLine %.########............... +SpriteLine %.#########.............. +SpriteLine %.########............... +SpriteLine %.######................. +SpriteLine %.######................. +SpriteLine %.##..##................. +SpriteLine %.#....##................ +SpriteLine %......##................ +SpriteLine %.......##............... +SpriteLine %.......##............... +SpriteLine %........##.............. +SpriteLine %........##.............. +SpriteLine %........................ !byte 0 ; pad to 64-byte block Since release 0.86, different macros are allowed to have the same name as long as their parameter lists differ in size (number of arguments) or type (call-by-value vs. call-by-reference). So !macro process_bytes b1, b2 {...whatever...} !macro process_bytes b1, b2, b3 {...whatever...} !macro process_bytes b1, b2, ~b3 {...whatever...} can *all* be used at the same time without any name clash. ---------------------------------------------------------------------- Section: Segment assembly ---------------------------------------------------------------------- Call: * = EXPRESSION [, MODIFIER]* Purpose: Set program counter to given value and start new segment. This opcode must be given at least once (or the command line option "--setpc" must be used). If segments overlap each other, warnings will be issued. Because some people do this overlapping on purpose, the warnings can be suppressed using modifier keywords. Future versions of ACME may issue errors instead of warnings. Parameters: EXPRESSION: Any formula the value parser accepts, but it must be solvable even in the first pass. MODIFIER: "overlay" or "invisible" (without quotes): "overlay" suppresses the warning "Segment starts inside another one, overwriting it". "invisible" makes the new segment invisible, so that _other_ segments will never raise the warning "Segment reached another one, overwriting it". Examples: !to "TinyDemo", cbm ; define output file + format * = $0801 ; start at C64 BASIC start !src "basicmacros.a" ; include macro definitions +basic_header ; call program header macro !src "main.a" ; include main program * = $1000 ; jump to new segment !bin "music.b" ; load music to $1000 * = $8000 ; jump to new segment !bin "pic.b" ; load graphics to $8000 * = $8010, overlay, invisible ; go back and patch ; the graphics, suppressing warnings ; After assembly, ACME will save everything from $0801 ; up to the highest address written to. The resulting ; file will contain some big unused areas (zero'd), ; but demos will get compressed anyway... :) Call: !initmem EXPRESSION Purpose: Define "unchanged" memory. ACME will fill its output buffer completely with the given value before storing the assembled code. So gaps between segments will contain the desired byte when writing the output file. Instead of using this pseudo opcode, you can also use the "--initmem" command line option. If neither is used, the buffer is cleared. Parameters: EXPRESSION: Any formula the value parser accepts, but it must be solvable even in the first pass (because this opcode will be ignored in all later passes). Examples: !to "TinyDemo", cbm ; define output file + format !initmem $ea ; default memory content $ea. * = $0801 ; start at C64 BASIC start !src "basicmacros.a" ; include macro definitions +basic_header ; call program header macro !src "main.a" ; include main program * = $1000 ; jump to new segment !bin "music.b" ; load music to $1000 * = $8000 ; jump to new segment !bin "pic.b" ; load graphics to $8000 * = $8010, overlay, invisible ; go back and patch ; the graphics, suppressing warnings ; This is the same example as before, but now the big ; unused areas will contain the value $ea instead of ; zero. !initmem $ff ; Default memory content is now $ff. ; Useful if you want to store your code in an EPROM. Call: !xor EXPRESSION [ { BLOCK } ] Purpose: Change the value to XOR all output bytes with (the value defaults to zero on startup). This "encryption" facility was added to compensate for the shortcomings of the "!scrxor" pseudo opcode, which only XORs strings and characters, but not numbers. When used with block syntax, the previously chosen value is restored afterwards. Parameters: EXPRESSION: Any formula the value parser accepts. BLOCK: A block of assembler statements. Examples: ; first as normal screencodes: !scr "Hello everybody...", GROUPLOGOCHAR ; and now as inverted screencodes: !xor $80 { !scr "Hello everybody...", GROUPLOGOCHAR } ---------------------------------------------------------------------- Section: Offset assembly ---------------------------------------------------------------------- Call: !pseudopc EXPRESSION { BLOCK } Purpose: Assemble code as if the program counter had the given value, effectively producing a program that has to be copied to a different address space before being run. After having processed the block of statements with the new program counter, the updated (!) old program counter is used again. Thanks to the block syntax, offset assembly can now be nested. Then the old program counter would not necessarily be the *real* program counter, but could be a pseudopc as well. ;) Parameters: EXPRESSION: Any formula the value parser accepts, but it must be solvable even in the first pass. BLOCK: A block of assembler statements. Examples: ldx #.shifted_end - .shifted_start - lda .shifted_start - 1, x sta .target - 1, x dex bne - jmp .target .shifted_start !pseudopc $0400 { .target ; imagine some code here... ; it should be copied to $0400 and executed *there* } .shifted_end ---------------------------------------------------------------------- Section: CPU support pseudo opcodes (especially 65816 support) ---------------------------------------------------------------------- Call: !cpu KEYWORD [ { BLOCK } ] Purpose: Select the processor to produce code for. If this PO isn't used, ACME defaults to the 6502 CPU (or to the one selected by the "--cpu" command line option). ACME will give errors if you try to assemble commands the chosen CPU does not have. You can change the chosen CPU at any time. When used with block syntax, the previously chosen CPU value is restored afterwards. Parameters: KEYWORD: Currently valid keywords are: 6502 for the original MOS 6502 6510 6502 plus undocumented opcodes 65c02 6502 plus BRA,PHX/Y,PLX/Y,STZ,TRB/TSB r65c02 65c02 plus BBRx, BBSx, RMBx, SMBx w65c02 r65c02 plus STP/WAI 65816 65c02 plus 16/24-bit extensions 65ce02 r65c02 plus Z reg, long branches, ... 4502 65ce02 with MAP instead of AUG c64dtv2 6502 plus BRA/SAC/SIR plus some of the undocumented opcodes See "docs/cputypes.txt" for more info. BLOCK: A block of assembler statements. Examples: !if cputype = $65c02 { !cpu 65c02 { ; temporarily allow 65c02 stuff stz .todelete } } else { pha lda #0 sta .todelete pla } rts !cpu 65816 ; allow 65816 commands from here on Call: !al [ { BLOCK } ] Purpose: Assume long (16 bits) accumulator. Only allowed when producing code for the 65816 CPU. When used with block syntax, the previous configuration is restored afterwards. Call: !as [ { BLOCK } ] Purpose: Assume short (8 bits) accumulator. Only needed when producing code for the 65816 CPU. When used with block syntax, the previous configuration is restored afterwards. Short accumulator is the default in every pass. Call: !rl [ { BLOCK } ] Purpose: Assume long (16 bits) index registers. Only allowed when producing code for the 65816 CPU. When used with block syntax, the previous configuration is restored afterwards. Call: !rs [ { BLOCK } ] Purpose: Assume short (8 bits) index registers. Only needed when producing code for the 65816 CPU. When used with block syntax, the previous configuration is restored afterwards. Short registers are the default in every pass. ---------------------------------------------------------------------- Section: Type system ---------------------------------------------------------------------- Call: !address [ { BLOCK } ] or: !address SYMBOL = VALUE Purpose: Mark a block or a statement as "explicitly defined symbols are holding addresses". Parameters: BLOCK: A block of assembler statements Everything inside the block will be parsed as usual, but explicitly defined symbols will be marked as referencing memory. If no block is given, only the current statement will be affected, which should then be an explicit symbol definition. Aliases: "!addr" Examples: !addr k_chrout = $ffd2 ; this is an address CLEAR = 147 ; but this is not !addr { ; these are addresses: sid_v1_control = $d404 sid_v2_control = $d40b sid_v3_control = $d412 } ; these are not: sid_VOICECONTROL_NOISE = %#....... sid_VOICECONTROL_RECTANGLE = %.#...... sid_VOICECONTROL_SAWTOOTH = %..#..... sid_VOICECONTROL_TRIANGLE = %...#.... ---------------------------------------------------------------------- Section: Obsolete pseudo opcodes (they will throw errors if used) ---------------------------------------------------------------------- Call: !cbm Purpose: Use PetSCII as the text conversion table. Now superseded by the "!convtab" pseudo opcode. Old usage: !cbm ; gives "use !ct pet instead" error Now use: !convtab pet ; does the same without error Call: !subzone [TITLE] { BLOCK } Purpose: Allows nesting of zones. Now superseded by "!zone" because that allows nesting as well. Parameters: TITLE: May consist of letters and digits. BLOCK: A block of assembler statements. Aliases: "!sz" Old usage: !subzone graphics { !source "graphics.a" } Now use: !zone graphics { !source "graphics.a" } Call: !realpc Purpose: Restore the program counter to its real value, therefore finishing offset assembly. Because "!pseudopc" now knows block syntax and can be nested, there's no reason to use "!realpc" any more. Old usage: !pseudopc $0400 ; imagine some code here... !realpc Now use: !pseudopc $0400 { ; imagine some code here... } acme-crossassembler-0.96.4/docs/COPYING000066400000000000000000000431271333777125400175520ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. acme-crossassembler-0.96.4/docs/Changes.txt000066400000000000000000000651111333777125400206250ustar00rootroot00000000000000 ACME ...the ACME Crossassembler for Multiple Environments --- change log --- This text only contains descriptions of changes independent of the platform used. There should be another help file in this archive outlining the platform specific changes. ---------------------------------------------------------------------- Section: New in release 0.96.4 ---------------------------------------------------------------------- Bugfix: Removed warnings about zero page wrap-around for the 65816's 24-bit pointers (because wrap-around does not actually happen). Thanks to Johann Klasek for reporting this. Added "!xor" pseudo opcode to compensate for the shortcomings of the "!scrxor" pseudo opcode. Thanks to spider-j for the initial bug report. Added "-I" CLI switch to add search paths for input files. Thanks to peiselulli for the suggestion. ---------------------------------------------------------------------- Section: New in release 0.96.3 ---------------------------------------------------------------------- Added "!h"/"!hex" pseudo opcode: Now external source code generator tools can easily put data in sources with minimal syntax overhead. Added "!skip" pseudo opcode: "!skip N" works like "*=*+N" without starting a new segment. Added "cheap locals": Labels with '@' prefix have automatic scoping, bounded by the preceding and the following global labels. Added "--fullstop" CLI switch to change pseudo opcode prefix from '!' to '.' (so other assemblers' sources need less conversion work) Fixed a bug where expressions like "1)+1" crashed ACME. Thanks to Bitbreaker for reporting this. Added warning when using zp-indirect addressing modes where argument is $ff because pointer wraps around to $00. Thanks to Gerrit for the suggestion. ---------------------------------------------------------------------- Section: New in release 0.96.2 ---------------------------------------------------------------------- Added "--color" CLI switch to enable colored error output using ANSI escape codes. Thanks to Clifford Carnmo for submitting this patch. ---------------------------------------------------------------------- Section: New in release 0.96.1 ---------------------------------------------------------------------- Fixed bug where 65ce02's "(zp),z" addressing mode could be used in 65816 mode. ---------------------------------------------------------------------- Section: New in release 0.96 ---------------------------------------------------------------------- Added experimental support for instruction sets of Rockwell 65C02, WDC 65C02(S), CSG 65CE02 and CSG 4502. Stack indexing can now be given either as ",s" or as ",sp" (only relevant for 65816 and 65CE02). ---------------------------------------------------------------------- Section: New in release 0.95.8 ---------------------------------------------------------------------- Warnings and errors inside macros now cause the call stack to be displayed as well (does not yet work for serious errors, though). ---------------------------------------------------------------------- Section: New in release 0.95.7 ---------------------------------------------------------------------- New pseudo opcodes: "!be16", "!be24" and "!be32" for big-endian byte order output. "!le16", "!le24" and "!le32" for little-endian byte order output. The old pseudo opcodes ("!16", "!24", "!32") will now use the correct byte order for the chosen CPU (which is always little- endian, because there is no support for any big-endian CPU yet.;)) ---------------------------------------------------------------------- Section: New in release 0.95.6 ---------------------------------------------------------------------- Fixed a bug: The "C64 DTV2" does not support the undocumented ("illegal") opcodes 0x0b and 0x2b, therefore the "ANC #8" mnemonic was removed from the "!cpu c64dtv2" mode. Thanks to peiselulli for testing and reporting this. "Value not defined" error message now includes the name of the undefined symbol. Thanks to Thomas Woinke for suggesting this improvement. Fixed a bug in type system: "!for" loop counters were not correctly flagged as "address" or "non-address". The "addr()" function can now also be written as "address()". Fixed a bug in report listing generator: CR characters in input caused additional blank lines in output. Thanks to Johann Klasek for submitting this patch. ---------------------------------------------------------------------- Section: New in release 0.95.5 ---------------------------------------------------------------------- Fixed a bug causing crashes on program exit (a pointer to a local struct kept being used later on, trashing the stack). Thanks to Hoeppie for reporting this. ---------------------------------------------------------------------- Section: New in release 0.95.4 ---------------------------------------------------------------------- Added the last remaining undocumented ("illegal") opcodes: LAS, TAS, SHA, SHX, SHY and ANE. ---------------------------------------------------------------------- Section: New in release 0.95.3 ---------------------------------------------------------------------- Added "c64dtv2" cpu type so you can use its SIR, SAC and BRA opcodes; along with the undocumented ("illegal") opcodes of the 6510. Added Martin Piper's "--msvc" patch so error output can be configured to be in Visual Studio format. Merged third-party patch (who wrote it?) to output label dump in VICE format. Still needs work to be configurable about the types of symbols actually output. ---------------------------------------------------------------------- Section: New in release 0.95.2 ---------------------------------------------------------------------- Changed "save labels" to "symbol list" in a lot of code, error messages and docs. Added "!symbollist" alias for "!sl" pseudo opcode. Change in undocumented ("illegal") opcodes: ANC #8 now generates 0x0b instead of 0x2b (both opcodes do the same thing). Added experimental support for generating a report listing. Thanks to Johann Klasek for writing this extension patch. ---------------------------------------------------------------------- Section: New in release 0.95.1 ---------------------------------------------------------------------- Fixed a bug: Anonymous forward labels were allowed to be redefined. Tweaked type system: When negating a value, address reference count will now be negated as well. When using type system, label dump file will now contain "!addr" to mark addresses. Changed "label" to "symbol" in a lot of code, error messages and docs. ---------------------------------------------------------------------- Section: New in release 0.95 ---------------------------------------------------------------------- Added an experimental type system to tell addresses and non-addresses apart (per default disabled; must be activated using the "-Wtype-mismatch" CLI switch). The new "!address" pseudo opcode is used to mark label definitions as setting address values. Added "-Wno-old-for" switch to disable warning about old "!for" syntax. However, this will in turn enable warnings about using the *new* syntax. ---------------------------------------------------------------------- Section: New in release 0.94.12 ---------------------------------------------------------------------- Finally created new "!for" syntax so counting can start at zero. The old syntax still works, but gives a warning. See AllPOs.txt for more info. Added "0b" as alternative prefix for binary values. ---------------------------------------------------------------------- Section: New in release 0.94.11 ---------------------------------------------------------------------- If !warn, !error and !serious are called with numbers, those will now be output in hex format as well. Also mentioned this in docs. ---------------------------------------------------------------------- Section: New in release 0.94.10 ---------------------------------------------------------------------- Warning about shift-space as first character of label name now works for UTF-8 as well. Operator priority list in docs now contains built-in function calls. ---------------------------------------------------------------------- Section: New in release 0.94.9 ---------------------------------------------------------------------- C-style equality checking ("==" operator) is now recognized (but gives a warning). Thanks to groepaz for the suggestion. ---------------------------------------------------------------------- Section: New in release 0.94.8 ---------------------------------------------------------------------- Finally disabled pseudo opcode "!cbm". It now gives an error instead of a warning. Finally disabled pseudo opcode "!realpc". It now gives an error instead of a warning. Therefore, "!pseudopc" now *must* be used with a block in {} braces. Finally disabled pseudo opcode "!subzone". It now gives an error instead of a warning. Added support for one more undocumented ("illegal") opcode: lxa. See Illegals.txt for more info. Removed "Offset assembly still active at end of segment" warning. New segments can now be started even from within pseudopc sections. ---------------------------------------------------------------------- Section: New in release 0.94.7 ---------------------------------------------------------------------- Fixed bug: Setting the program counter to an address > $ffff and writing a byte hung ACME. Thanks to groepaz/VICEteam for reporting this. Verbose output now uses "0x" instead of "$" to indicate hexadecimal in more places. ---------------------------------------------------------------------- Section: New in release 0.94.6 ---------------------------------------------------------------------- Made "power-of" operator right-associative. ---------------------------------------------------------------------- Section: New in release 0.94.5 ---------------------------------------------------------------------- Fixed bug: wrap-around branches (from $ff80+ to zp and back) were reported as errors. Thanks to Bitbreaker/VOZ for reporting this. Fixed bug: left-shifting float values went wrong for large shifts. New error message: "Target not in bank (0xTARGET)" for branches. Fixed example program to get it to assemble with current library. Streamlined "Floats.txt" docs. Changes in library files: files now contain opcode_* constants in addition to macros. Added , containing a macro to store a float value in the five-byte format used by CBM basic (and while writing that macro, found the left-shift-float bug mentioned above, so the macro needs this fixed version to work). Added , containing definitions for using the BASIC interpreter's float functions and constants. ---------------------------------------------------------------------- Section: New in release 0.94.4 ---------------------------------------------------------------------- Added "apple" output format. ---------------------------------------------------------------------- Section: New in release 0.94.3 ---------------------------------------------------------------------- "Target out of range" error now includes info on how much the range was exceeded. Thanks to Bitbreaker/VOZ for the suggestion. ---------------------------------------------------------------------- Section: New in release 0.94.2 ---------------------------------------------------------------------- Added missing newline after "no output file specified" message. Fixed bug in "!to" and "!sl" (colon in filename is interpreted as command separator in later passes). Changed verbose output hex prefix from $ to 0x. Changed --help output Changed EOR to XOR in docs and comments (the ACME operator, not the 6502 opcode) ---------------------------------------------------------------------- Section: New in release 0.94.1 ---------------------------------------------------------------------- New CLI switch: "-D" allows to set global labels via the command line. New CLI switch: "-Wno-label-indent" switches off warnings about indented implicit label definitions. New PO: "!ifndef" (finally a companion for "!ifdef"...) When setting the program counter via "* =", modifiers ("overlay" and "invisible") allow to suppress warnings about segment overlap. Float values without leading digits are now accepted. ---------------------------------------------------------------------- Section: New in release 0.93 ---------------------------------------------------------------------- Change: If "Offset assembly still active at end of segment", it no longer gets switched off. Change: Operators ASR and LSL/ASL now can also handle FP (LSR still makes no sense). Change: Added distinction between '/' and "DIV" operators: DIV always gives integer results, while '/' depends on operands. New functions: added int() and float() functions. Internal change: default fill value for !align is now CPU-specific (but still 234) New CLI switch: "--use-stdout" prints errors to stdout instead of stderr (a fix for the "Relaunch64" IDE I have nothing to do with) ---------------------------------------------------------------------- Section: New in release 0.92 ---------------------------------------------------------------------- Text versions of arithmetic/logic operators (XOR, DIV, MOD, etc.) no longer need to be in upper case. Experimental support for floating point maths. Support for mathematical functions: sin(), cos(), tan(), arcsin(), arccos(), arctan() New errors: "Argument out of range.", "Unknown function." These operators always deliver ints: not, and, or, xor, lowbyteof, highbyteof, bankbyteof, mod, asl, lsl, asr, lsr ---------------------------------------------------------------------- Section: New in release 0.91 ---------------------------------------------------------------------- Added anonymous labels (- + -- ++ --- +++ etc.). Every other assembler seems to support them, so I added them to ACME as well... :) New POs: "!warn MESSAGE", "!error MESSAGE", "!serious MESSAGE" New CLI option: "--maxdepth NUMBER" sets maximum recursion depth for macro calls and the "!source" pseudo opcode. ACME now gives a warning when assembling JMP($xxff) on 6502/6510 because that instruction is broken on those CPUs. After giving the error "Target out of range", the error "Number out of range" is now suppressed. Corrected code example in QuickRef.txt (why didn't anyone tell me? :)) Added additional example source code. ---------------------------------------------------------------------- Section: New in release 0.90 ---------------------------------------------------------------------- Arithmetic shift right now has some watchdog code and should work regardless of compiler. Corrected some typos in error messages and docs. New CLI option: "--cpu CPU_TYPE" The output file format chosen with "--format FORMAT" is now used as default when "!to" is used without format keyword. Again: Tidier code. ---------------------------------------------------------------------- Section: New in release 0.89 ---------------------------------------------------------------------- Support for more undocumented ("illegal") opcodes: anc, arr, asr, sbx, dop, top, jam. See Illegals.txt for more info. Change in shift operators: Logical shift right (">>" or "LSR") has on most platforms actually been an arithmetic shift right all the time! Therefore, ">>" now *officially* performs an arithmetic shift right (can also be written as "ASR"), while ">>>" has been added to perform a logical shift right (can also be written as "LSR"). Note: This is about ACME's maths parser and has nothing to do with the 6502 mnemonics "asl" and "lsr". Finally added a "-o" command line option to set the output file! See QuickRef.txt for info on the other new CLI options (--format, --labeldump, --maxerrors, --setpc, --initmem, --version). Fixed bug: "!align" could be used while program counter undefined. Fixed bug: Numbers before mnemonics are no longer skipped (or rather, implicit label definitions are no longer accepted if the label name starts with a digit). Change: Much better algorithm to compute to-the-power-of (read: it's no longer braindead). Some more internal tidying. ---------------------------------------------------------------------- Section: New in release 0.88 ---------------------------------------------------------------------- Fixed architecture-dependent bug introduced in release 0.87. Fixed bug: Unknown !cpu keywords could cause crashes. Fixed bug in !ct "filename" nesting. ---------------------------------------------------------------------- Section: New in release 0.87 ---------------------------------------------------------------------- Support for some undocumented ("illegal") opcodes: slo, rla, sre, rra, sax, lax, dcp, isc. To use these, choose the 6510 cpu. Two error messages gone: "Sorry, feature not yet implemented." and "Chosen CPU does not support this command and/or addressing mode." Explanation of new error message ("There's more than one character.") added to docs. ---------------------------------------------------------------------- Section: New in release 0.86 ---------------------------------------------------------------------- The "!convtab" pseudo opcode can now be given the file name of a conversion table. The file must hold exactly 256 bytes. Improved docs a bit (more and better examples, more info on verbosity CLI switch). If no "!to" pseudo opcode has been found, ACME will tell you so. ---------------------------------------------------------------------- Section: New in release 0.86 beta ---------------------------------------------------------------------- Macros can now be used with call-by-reference semantics, therefore allowing some kind of return value. Call-by-reference is indicated by prefixing the relevant parameter(s) with a '~' character. This has to be done at both the macro definition and the macro call. Different macros are allowed to have the same name as long as their parameter lists differ in size (number of arguments) or type (call-by-value vs. call-by-reference) Macros do not have a limit on parameter count anymore. Macro size is unlimited now. The expression parser does not have a limit on recursion depth anymore, so you can use as many parentheses as you like. Loop block size is unlimited now. Label name and string lengths are unlimited now. The recursion depth of "!source" and macro calls is set to 64. The only reason there still *is* a limit is to be able to spot infinite recursions. Offset assembly now has block support and can be nested. Using the old syntax still works, but gives a warning. Pseudo opcodes "!convtab", "!cpu", "!al", "!as", "!rl" and "!rs" now have block support and can be nested. Using "!to" without file format indicator now gives a warning (but still works). Fixed bug: The statement !to "outfile" ANY_SPECIAL_CHARACTER_BUT_COMMA GARBAGE wasn't flagged as an error. Fixed bug: The statement !source "a file that cannot be opened" did not give an error, but was just ignored. If a global label starts with a shift-space character, a warning is issued (because it is highly likely that it is a typing error). *Much* cleaner internals. *Very* *much* cleaner internals actually. More bug checking at runtime. Tree lookups should be a bit faster. Initialising the memory should be a bit faster. Writing the output file should be a bit faster. The expression parser now uses repeated multiplication instead of the math library's pow() call, so it is no longer necessary to include the C math library when compiling. The number of errors displayed before assembly stops was reduced from 20 to 10. I really should make this configurable via a CLI switch. ---------------------------------------------------------------------- Section: New in release 0.85 alpha ---------------------------------------------------------------------- Fixed bug: Handling of parentheses in new expression parser was badly screwed up. Thanks go to Nathan Smith for reporting that bug. Verbosity messages for segments and output file now contain size info. ---------------------------------------------------------------------- Section: New in release 0.84 alpha ---------------------------------------------------------------------- Some changes in documentation (mainly corrected typos) Usage count for labels (Unused ones are marked in label dump file) New PO: "!8" (for 8-bit values, as "!byte" / "!by" / "!08") Finally removed the dreaded only-two-input-files restriction Improved PO: "!to" has parameter for choosing output file format Fixed bug: Blanks after "!for"'s "}" character stopped assembly Rewritten expression parser and label tree handler (should be faster) Generally tidied up the source. Skipped some version numbers to get a "less frightening" one. :) ---------------------------------------------------------------------- Section: New in release 0.08 beta ---------------------------------------------------------------------- Fixed really serious bug: The 65816's indirect DP addressing caused wrong opcodes to be generated. Thanks to Doc Bacardi/The Dreams for reporting it. ---------------------------------------------------------------------- Section: New in release 0.07 beta ---------------------------------------------------------------------- Fixed really serious bug: Indirect JMP / JSR were assembled without target addresses. Thanks to YTM/Alliance for reporting that one. Fixed bug in value parser's handling of parentheses: Expressions like "a*(b-c)+d" gave "a*((b-c)+d)", obviously not the same. Fixed bug: "!set LABEL = VALUE" now *really* works correctly. Fixed bug: ACME gave "too late for postfix" error when reading a predefined label of known size. Only occurred when using macros. Fixed bug: Error messages given from within macro definitions used truncated file names. Fixed bug: Calling of local macros didn't work at all. Fixed bug: "}" chars directly after macro calls were not found. Fixed bug: Spaces after ":" and "{" gave syntax errors. Fixed bug: Line counting inside loops was screwed up. Fixed bug: Changed argument order of MVP and MVN (now it's "opcode, source, target") New PO: "!08" (for 8-bit values, as "!byte" / "!by") New PO: "!16" (for 16-bit values, as "!word" / "!wo") New PO: "!24" (for 24-bit values) New PO: "!32" (for *signed* 32-bit values) New PO: "!pseudopc" (starts offset assembly) New PO: "!realpc" (ends offset assembly) New PO: "!for LABEL, TIMES { LINES }" for easier loops. New PO: "!initmem BYTE" to define empty memory. New PO: "!endoffile" (short "!eof") replaces "!end". New PO: "!ifdef" (only use this if you *really* know what you are doing. Otherwise, just don't use it) New PO: "!convtab CONVERSION" (short "!ct") selects the default character conversion, making "!cbm" obsolete. Improved PO: "!binary" now has "skip" parameter. Change: "!cbm" outputs a warning when used - use "!ct pet" instead. Change: "!end" no longer works - use "!eof" instead. Change: "* = VALUE" is now segment change instead of offset assembly. Change: Argument order of MVN/MVP is now as is standard. The typecast system has been rewritten - now it works as intended. BIT without any parameters no longer works - use a macro instead. Leading zeros are stored in label structures and acted upon. The documentation is in several files now. Negative numbers are now handled much more sensibly. 'ACME' environment variable only needed when *really* needed. ---------------------------------------------------------------------- Section: New in release 0.05 beta ---------------------------------------------------------------------- Fixed bug: No more multiple error messages. Fixed bug: Zone names now work correctly (First char wasn't stored). Fixed bug: "!set label = label" now works correctly (I hope). Fixed bug: "stz ...,y" gave "number too big" instead of "illegal combination of command and addressing mode" New PO: "!subzone" (short "!sz") for nested zones. Added support for library tree when using "!source" or "!binary". Single-character strings can now be given in single quotes as well. Real icons. Startup errors now exit correctly with EXIT_FAILURE code. Example program now includes "Expected_Output" file. Further tidied up the sources. Tidied up the general help file: -Changed "Freeware" to "free software" -Corrected the information given on "!align". -Added examples for most of the pseudo opcodes. ---------------------------------------------------------------------- Section: New in release 0.04 beta ---------------------------------------------------------------------- Corrected some small bugs. New PO: "!zone" (short "!zn") replaces "!module" (short "!mod") Tidied up the sources a lot. Changed bad style C code reported by lint. Added GNU GPL hint in every source file. Added startup message in verbose mode. Added "Error: " to startup error messages. Added Amiga, Linux and OS/2 versions ---------------------------------------------------------------------- Section: New in release 0.03 beta ---------------------------------------------------------------------- Generally tidied up the source. Moved RISC OS-specific CLI options to platform file. Added pathname conversion from UNIX style to current platform style. Added context variables (enabling "!source"s and macros). Translated all documentation to english. Changed string pseudo opcodes to allow numeric values. Added verbose mode (CLI option "v"). Added output buffer, removing the need for additional output pass (and now the "!to" pseudo opcode can be placed anywhere). More than one "label = pc" definition per statement now illegal. Instead added possibility to have several statements on a single line by using ":" as a separator character. Added new keywords: "!set", "!if", "else", "!do", "until", "while" and "!macro" Added support for "!source". Added basic support for blocks. Added support for "!if {...} else {...}". Added support for zone titles. Added support for loops (endless loops are only detected if producing code). Added support for macros (even nested definitions are possible now). Added DOS version. ---------------------------------------------------------------------- Section: New in release 0.02 alpha ---------------------------------------------------------------------- Er, I don't know anymore. It was a bad ugly hack and it only ran on RISC OS. :-) acme-crossassembler-0.96.4/docs/Errors.txt000066400000000000000000000413651333777125400205360ustar00rootroot00000000000000 ACME ...the ACME Crossassembler for Multiple Environments --- error messages --- Here's a sorted list of all error messages ACME can give, possible reasons and what you can do to sort it out. ---------------------------------------------------------------------- Section: Errors on startup ---------------------------------------------------------------------- Cannot open toplevel file "FILENAME". Maybe you mistyped its name? Error in CLI arguments: ... There are several of these errors, but they should be quite self- explanatory. ---------------------------------------------------------------------- Section: Warnings during assembly ---------------------------------------------------------------------- !warn: ... This is given when the pseudo opcode "!warn" is executed. The actual message varies according to the pseudo opcode's arguments. Assembling buggy JMP($xxff) instruction The original 6502 processor has a bug: When executing an indirect JMP instruction where the low byte of the argument equals $ff, it fetches the high byte of the jump target address not from memory location ARGUMENT + 1, but from ARGUMENT - 255. Therefore ACME issues this warning if you are about to generate such an instruction. Note that this warning is only given for CPU types 6502 and 6510, because 65c02 and 65816 have been fixed in this respect. Assembling unstable ANE #NONZERO instruction Assembling unstable LXA #NONZERO instruction These warnings are only ever given for CPU type 6510. ANE and LXA are undocumented ("illegal") opcodes of this CPU, and they only work reliably if the argument is zero or the accumulator contains 0xff. Therefore ACME issues these warnings if it is about to generate these instructions with a non-zero argument. Bug in ACME, code follows A situation has been encountered implying there is a bug in ACME. See the last section in this file. C-style "==" comparison detected. To check for equality, use a single '=' character instead. Converted to integer for binary logic operator. Applying binary logic to float values does not make much sense, therefore floats will be converted to integer in this case. "EOR" is deprecated; use "XOR" instead. This means the operator, not the mnemonic. Found old "!for" syntax. Please update your sources to use the new "!for" syntax. See AllPOs.txt for details. You can suppress this warning using the "-Wno-old-for" switch. Found new "!for" syntax. When using the "-Wno-old-for" switch to disable the warning about the older syntax, the new syntax will trigger this warning. Found SED instruction for CPU with known decimal SBC bug. This warning is only ever given for CPU types 65ce02 and 4502, because they are known to be buggy in decimal mode. Pavel Zima and Eric Smith found an example where $41 minus $08 gave $39 instead of $33. Label name not in leftmost column. A label definition has blanks before the label name. Imagine this source code: lda #00 imx rts Obviously, there's a typo in the middle line (imx instead of inx), but ACME does not recognize this: It looks just like a label definition! Therefore releases 0.89 and higher warn you when a label name does not start in column 1. Releases 0.94 and higher support a command line option to switch off this warning ("-Wno-label-indent"). Label name starts with a shift-space character. The name of a global label starts with a shift-space character. It is highly likely that this is a typing error, therefore this warning is issued. Memory already initialised. The "!initmem" command was given more than once (or in addition to the "--initmem" command line option). Only use it once. Output file already chosen. The "!to" command was given more than once (or in addition to the "--outfile" command line option). Only use it once. Segment reached another one, overwriting it. The program counter has just reached the start of another segment. Because some people might want to assemble "onto" a binary file that was loaded before, this warning can be switched off using modifier keywords when changing the program counter via "* =". Future versions of ACME might throw an error instead of a warning in this case. Segment starts inside another one, overwriting it. The given value in a "* =" command is located inside another segment. Because some people might want to assemble "onto" a binary file that was loaded before, this warning can be switched off using modifier keywords when changing the program counter via "* =". Future versions of ACME might throw an error instead of a warning in this case. Symbol list file name already chosen. The "!sl" command was given more than once (or in addition to the "--symbollist" command line option). Only use it once. Used "!to" without file format indicator. Defaulting to "cbm". Now that "!to" can be given a file format keyword (either "plain" or "cbm"), using "cbm" as default seems inappropriate. It still works though. Using oversized addressing mode. ACME just assembled a command using an addressing mode that was larger than needed. This only happens if ACME could not work out the argument's value in the first pass, therefore assuming a 16- bit addressing mode. If, in a later pass, ACME finds out that the argument is small enough to fit in 8 bits, then this warning is shown. If you define all your zeropage symbols *before* they are first used, this shouldn't happen. If you know that a specific argument fits in 8 bits, you can force ACME to use 8 bits addressing by postfixing the command with "+1". Example: lda+1 label ACME will then use an 8-bit addressing mode, regardless of whether the label is known or not. If the label value happens to be too large to fit in 8 bits, ACME will show an error of course (To always truncate a value to 8 bits, use the '<' operator). More about the postfixing method can be found in "AddrModes.txt". Wrong type - expected address. Wrong type - expected integer. Wrong type for loop's END value - must match type of START value. These warnings are only given when type checking has been enabled using the "-Wtype-mismatch" switch. Make sure the argument type matches the instruction's addressing mode. In "!for" loops, START and END must have the same type, which then gets used for the loop counter. Zeropage pointer wraps around from $ff to $00 A zeropage-indirect addressing mode uses $ff as the argument. The 6502 will then fetch the second pointer byte from $00 instead of $0100, therefore this warning is issued. ...called from here. If warnings and/or errors are output during a macro call, messages with this text are added to display the call stack (because you might need to fix the call instead of the macro itself). ---------------------------------------------------------------------- Section: Errors during assembly ---------------------------------------------------------------------- "ACME" environment variable not found. This will be shown if the source code references any files from the library, but the library location variable wasn't set. This can only be given on systems using the said variable. "!cbm" is obsolete; use "!ct pet" instead. This is given when the now obsolete "!cbm" pseudo opcode is encountered. "!pseudopc/!realpc" is obsolete; use "!pseudopc {}" instead. This is given when one of the now obsolete !pseudopc/!realpc pseudo opcodes is encountered. "!subzone {}" is obsolete; use "!zone {}" instead. This is given when the now obsolete "!subzone" pseudo opcode is encountered. !error: ... This is given when the pseudo opcode "!error" is executed. The actual message varies according to the pseudo opcode's arguments. Cannot open input file. ACME had problems opening an input file ("!bin", "!convtab" or "!src"). Maybe you mistyped its name. Conversion table incomplete. The conversion table file is too small. It needs to be exactly 256 bytes in size. Division by zero. Guess what - you attempted to divide by zero. Exponent is negative. Using negative exponents only give sensible results when using floating point maths. File name quotes not found ("" or <>). File names have to be given in quotes. Either "" quoting for files located in the current directory or <> quoting for library files. Found '}' instead of end-of-file. ACME encountered a '}' character when it expected the file to end instead (because no blocks were open). Garbage data at end of statement. There are still arguments when there should not be any more. Hex digits are not given in pairs. The two digits of a hex byte are separated by another character, or there is an odd number of digits. Illegal combination of command and addressing mode. The given command cannot be used with the given addressing mode on the CPU you have chosen. Illegal combination of command and postfix. The given command cannot be used with the addressing mode indicated by the given postfix. Illegal postfix. You used a postfix other than "+1", "+2" or "+3". Macro already defined. Macros can only be defined once. If you define a macro twice, ACME will help you find the definitions by giving a warning for the first definition and a serious error (stopping assembly) for the second definition. Macro not defined (or wrong signature). You tried to call a macro that either wasn't defined yet (always define macros before using them) or was called with an illegal argument list. There must be a 1:1 match between the definition's formal parameters and the call's actual arguments. Macro parameter twice. The same symbol name is used two (or more) times in the same macro parameter list. Negative size argument. The size argument of "!bin" or "!skip" must be zero or positive, but cannot be negative. Negative value - cannot choose addressing mode. Because the argument is a negative value, ACME does not know what addressing mode (8 bits, 16 bits, on a 65816 even 24 bits) to use. You can overcome this problem using the postfix method. Or correct your program to use positive addresses instead. No string given. ACME expects a string but doesn't find it. Number out of range. A value is too high or too low. Program counter is unset. You didn't set the program counter, so ACME didn't know where to start. Quotes still open at end of line. You forgot the closing quotes. Source file contains illegal character. Your source code file contained a null byte. Symbol already defined. You defined a symbol that already had a different value. To change a symbol's value, use the "!set" pseudo opcode. Syntax error. Guess what - there's a syntax error. Target not in bank (0xTARGET). You tried to branch to an address not in the 0x0000..0xffff range. Relative addressing (branch commands or PER) cannot leave the current code bank of 64 KiB. Target out of range (N; M too far). Branch commands use relative addressing, which only has a limited range. You exceeded it. N is the attempted offset, M is the difference to the limit - so if you succeed in optimizing M bytes away, the code would assemble. There's more than one character. You used a text string in an arithmetic expression, but the string contained more than a single character. Too late for postfix. You can only postfix symbols at the start, before they are used for the first time. Too many '('. A formula ends before all parentheses were closed. Too many ')'. There are more closing than opening parentheses in a formula. Unknown encoding. You used the "!convtab" command with a keyword ACME does not know. Unknown operator. You used an arithmetic/logical operator ACME does not know. Unknown output format. You used the "!to" command with a keyword ACME does not know. Unknown processor. You used the "!cpu" command with a keyword ACME does not know. Unknown pseudo opcode. You have mistyped a "!" command. Unknown "* =" segment modifier. You used a modifier keyword ACME does not know. Value not defined (SYMBOL NAME). A value could not be worked out. Maybe you mistyped a symbol name. Whether this is given as a "normal" or as a serious error depends on the currently parsed pseudo opcode. ---------------------------------------------------------------------- Section: Serious errors (stopping assembly) ---------------------------------------------------------------------- !serious: ... This is given when the pseudo opcode "!serious" is executed. The actual message varies according to the pseudo opcode's arguments. Found end-of-file instead of '}'. The file ended when ACME expected the block to be closed instead (because there was at least one block left open). Loop count is negative. You used the "!for" command with a negative loop count (getting this error is only possible when using the now deprecated syntax). Macro already defined. Macros can only be defined once. If you define a macro twice, ACME will help you find both definitions by giving a warning for the first definition and a serious error (stopping assembly) for the second definition. Missing '{'. ACME didn't find the expected '{' character. Remember that '{' characters must be given on the same line as the command they belong to. Out of memory. When ACME runs out of memory, it stops assembly, giving this error. Free some memory and try again. It's highly unlikely anyone will ever see this error, though. ;) Produced too much code. The program counter reached address $10000, leaving the output buffer. At the moment, ACME can only produce a maximum of 64 KB. Syntax error. This is only given as a _serious_ error if it's in a "!do" loop condition. Too deeply nested. Recursive macro calls? The only reason for ACME to have a limit on macro call nesting at all is to find infinite recursions. Current limit is 64. Too deeply nested. Recursive "!source"? The only reason for ACME to still have a limit on "!source" nesting at all is to find infinite recursions. Current limit is 64. Value not yet defined. A value could not be worked out. Maybe you mistyped a symbol name. Whether this is given as a "normal" or as a serious error depends on the currently parsed pseudo opcode. ---------------------------------------------------------------------- Section: Errors on closedown ---------------------------------------------------------------------- Cannot open symbol list file "FILENAME". Cannot open output file "FILENAME". Make sure the name doesn't contain wildcard characters and you have write access to the directory. No output file specified (use the "-o" option or the "!to" pseudo opcode). You didn't specify the output file, so ACME did not create one. ---------------------------------------------------------------------- Section: Bugs in ACME ---------------------------------------------------------------------- The warning "Bug in ACME, code follows" is always followed by a serious error message, stopping assembly. The second message actually gives a hint about the bug's location in the source code. If you ever get this combination of warning and serious error, please send me an e-mail and tell me about it. If possible, include a piece of source code that triggers it. Please don't get this wrong - there are no known bugs. I just left some debugging code in place in case there is a bug I failed to notice during testing. In practice, this warning is not expected to be given at all. That's the reason why I want to be notified if it *does* decide to show up. The hint messages are of no real interest to the end user, but here they are for completeness' sake. IllegalGroupIndex The mnemonic tree contains a group that I didn't add. IllegalBlockTerminator A RAM block (macro or loop) was terminated incorrectly. IllegalOperatorHandle The expression parser found an operator that does not exist. IllegalImmediateMode The mnemonic tree contains invalid info about the size of immediate arguments. OperandStackNotEmpty The expression parser has finished though there are still operands left to parse. OperatorStackNotEmpty The expression parser has finished though there are still operators left to parse. StrangeInputMode The input state machine has reached a state that does not exist. StrangeParenthesis The expression parser found a non-existing operator. acme-crossassembler-0.96.4/docs/Example.txt000066400000000000000000000020741333777125400206470ustar00rootroot00000000000000 ACME ...the ACME Crossassembler for Multiple Environments --- the example source codes --- To assemble the given example source code files, change to the "examples" directory and type acme -DSYSTEM=64 ddrv.a acme macedit.a ACME will parse the source code files and will then produce files called "ddrv64.prg" and "macedit.o". You may compare them to the files called "ddrv64.exp" and "macedit.exp", to make sure ACME works as it should do. Just in case you wonder: "ddrv64.prg" is a joystick/mouse driver for the C64. The source code is fairly well documented. Have a look at it if you need more examples on how ACME works. By using "-DSYSTEM=128" instead of "-DSYSTEM=64", you can also generate "ddrv128.prg", a C128 binary. "macedit" is an unusably bad text editor for the C128. The source code is not meant to be a good example of ACME's capabilities. Please *don't* look at it. :) "trigono.o" is a simple example written to test the floating-point capabilities. acme-crossassembler-0.96.4/docs/Floats.txt000066400000000000000000000052071333777125400205050ustar00rootroot00000000000000 ACME ...the ACME Crossassembler for Multiple Environments --- floating-point support --- In release 0.92, ACME gained some experimental support for floating- point maths, so you can finally build sin/cos tables directly in ACME. But the expression parser still uses integer calculations per default. Here are the rules: a) if a maths operation is useless when done with integers, it is done with floats and returns a float. Applies to sin(), cos(), tan(), arcsin(), arccos(), arctan() and float(): These are always computed in float mode and always return floats. b) if a maths operation is useles when done with floats, it is done with integers and returns an integer. Applies to NOT, AND, OR, XOR, MOD, DIV, LSR, lowbyteof, highbyteof, bankbyteof and int(). These are always computed in integer mode and always return integers. c) All other mathematical operations are done in float mode if and only if at least one of the operands is a float. So "1/2*2" will give zero because it is done in integer mode, but "1.0/2*2" will give 1 because it is done in float mode. To force a numerical value to be flagged as being a float, just add a decimal point and a zero. If a decimal value ends with a dot character, ACME switches to using the C type "double" and keeps reading digits. The value is then flagged internally as being float. CAUTION: Float usage is only activated when a decimal point has been found, so don't expect "100000000000000000000" to work. "100000000000000000000.0" won't work either, because when the decimal point gets parsed, the integer value will have overflown already. As there is no support yet for the "e" format (1e20 for 1*10^20) either, the only way to enter this value is by writing "1.0 * 10.0 ^ 20.0". Examples: !byte 1 / 2 * 2 ; gives 0 (integer maths) !byte 1 / 2 * 2.0 ; gives 0 (1/2 => 0 in integer maths, ; float usage comes too late) !byte 1 / 2.0 * 2 ; gives 1 (FP in effect) !byte 1 / 2.0 * 2.0 ; gives 1 (FP in effect) !byte 1.0 / 2 * 2 ; gives 1 (FP in effect) !byte 1.0 / 2 * 2.0 ; gives 1 (FP in effect) !byte 1.0 / 2.0 * 2 ; gives 1 (FP in effect) !byte 1.0 / 2.0 * 2.0 ; gives 1 (FP in effect) You can use the new float() and int() functions to ensure the type of maths: !byte a / b * c ; depends on a/b/c's internal flags !byte float(a) / b * c ; calculation is done as float !byte int(a) / int(b) * int(c) ; calculation is done as int As you will have guessed, the trigonometric functions assume radians for measuring angles (90 degrees equals PI/2). Have a look at the example source code "trigono.a", it builds some sin/cos tables. acme-crossassembler-0.96.4/docs/Help.txt000066400000000000000000000170741333777125400201520ustar00rootroot00000000000000 ACME ...the ACME Crossassembler for Multiple Environments - free software - (C) 1998-2017 Marco Baye ---------------------------------------------------------------------- Section: Copyright ---------------------------------------------------------------------- ACME - a crossassembler for producing 6502/6510/65c02/65816 code. Copyright (C) 1998-2017 Marco Baye The ACME icon was designed by Wanja "Brix" Gayk This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ---------------------------------------------------------------------- Section: Introduction ---------------------------------------------------------------------- ACME is a crossassembler for the 65xx range of processors. It knows about the standard 6502, the 65c02 and the 65816. It also supports the undocumented ("illegal") opcodes of the 6510 processor (a 6502- variant that is used in the Commodore C=64), and the extensions added in the C64DTV2. This text and the other files in the same directory only describe the basic functions independent of the platform used. There should be another help file in this archive that outlines the features specific to your platform. The files in the docs directory and what they contain: 65816.txt Stuff specific to the 65816 processor AddrModes.txt How to choose non-standard addressing modes AllPOs.txt Lists ACME's pseudo opcodes. Use as a reference. Changes.txt The change log COPYING Version 2 of the GNU General Public License Errors.txt Lists ACME's error messages and what they mean. Example.txt Information on how to assemble the example sources. Floats.txt About the support for floating-point values Help.txt ...is this text. Illegals.txt Support for undocumented opcodes Lib.txt Information about the library QuickRef.txt All the basic stuff about ACME Source.txt How to compile ACME Upgrade.txt Incompatibilities to earlier versions cputypes.txt Instruction sets of target CPUs IMPORTANT: If you upgrade from ACME 0.05 or earlier, don't forget to read the file "Upgrade.txt" - release 0.07 and all later ones are slightly incompatible to 0.05 and earlier. If you want to start using ACME right away, read the file "QuickRef.txt", it contains the main help text. ---------------------------------------------------------------------- Section: What it can and does ---------------------------------------------------------------------- ACME is a crossassembler. ACME can produce code for the 6502, 6510, 65c02 and 65816 processors. It does this *fast*. It can produce at most 64 KBytes of code. You can use global labels, local labels and anonymous labels. It is fast. You can use global and local macros. You can use conditional assembly. You can use looping assembly (There are two ways to do this; a very simple and a very flexible one). You can include other source files. You can include binary files (either whole or parts) directly into the output. You can use offset assembly (code that is designed to run at a different address). It is fast. ACME's maths parser uses operator priorities, so 1+2*3 will correctly give 7 (unlike some other free assemblers that give 9 instead). ACME's maths parser has no problems concerning parentheses and indirect addressing modes. ACME's maths parser knows a shit load of different operations. ACME supports both integer and floating point maths operations. You can dump the global symbols into a file. ACME supports a library of commonly used macros and symbols. It always takes as many passes as are needed. ACME exists on several platforms, meaning you can easily exchange your sources with other people (preferring other OSes). ACME can convert its strings to PetSCII and screen code (Okay, this is C64-specific). ACME has a rudimentary type checking system to catch errors like missing '#' characters. Did I mention that it is fast? ---------------------------------------------------------------------- Section: What it can't and doesn't ---------------------------------------------------------------------- ACME cannot transfer data to a C64 or another computer. ACME does not produce ".o65"-format linkable object files. ACME cannot produce more than 64 KB (would be useful for the 65816). ACME cannot disassemble or relocate given code files. ---------------------------------------------------------------------- Section: Platform independence ---------------------------------------------------------------------- ACME was initially developed under RISC OS. Currently there are platform-specific versions available for AmigaOS, DOS, Linux, Windows and RISC OS. The Linux sources should be ready to compile on most other UNIX-like systems as well. In the future there will hopefully also be a version that runs on the C64/128. Though the source code does not exactly look like it *g*, ACME was written with portability in mind: Some of its limitations were included on purpose, just to allow a C64/128 version. To successfully assemble multi-file source codes from other platforms, the file names have to be altered as little as possible. Please name all your files that may be distributed in a sensible way, for example by limiting their file names to 8+3 format. I really hate this stupid will-it- ever-die DOS convention, but using it is the only way to ensure portability of files. Please use ".a" as the file name extension of ACME source code files. All file names used inside source code files have to be given in UNIX style, ACME will convert them to the current host platform style if needed. There should be no problems concerning newline characters, ACME was designed to cope with CR, LF and CRLF. A minor problem is the different character tables used on different systems. As all predefined ACME keywords only use 7-bit ASCII, the assembler will work on any system that uses a superset of this character table: UTF-8, ANSI, ISO 8859, etc. Symbol names can contain top-bit-set characters - these may look strange if the sources are edited on a different platform, but ACME will still work. If you want to port ACME to another platform, please inform me so that I can add your version to the ones already present on the ACME homepage. As the sources are released under the GNU General Public License, you are not forced to do this; but it would help to make ACME available to other users. The same goes for any changes or enhancements to the sources: Please send me a copy so that the changes can be incorporated into the next "official" release on the ACME home page. ---------------------------------------------------------------------- Section: Contacting the author ---------------------------------------------------------------------- The newest version of ACME can be found at the ACME homepage: http://sourceforge.net/p/acme-crossass/ If you want to report a bug or make a suggestion, then simply send me an email: mailto:marco@baye.de acme-crossassembler-0.96.4/docs/Illegals.txt000066400000000000000000000151441333777125400210120ustar00rootroot00000000000000 ACME ...the ACME Crossassembler for Multiple Environments --- Undocumented ("illegal") opcodes --- In release 0.87, support for some of the undocumented opcodes of the 6502 processor was added. In release 0.89, some more were added. In release 0.94.8, another one was added (lxa). In release 0.95.3, C64DTV2 support was added, which includes these opcodes as well. In release 0.95.4, the remaining seven were added. In release 0.95.6, "ANC" was removed from C64DTV2 mode. Here are the new mnemonics, possible addressing modes and generated opcodes (mnemonics in parentheses are used by other sources): | addressing mode | mnemonic | 8 8,x 8,y 16 16,x 16,y (8,x) (8),y | performs: ----------------+--------------------------------------+----------- slo (aso) | 07 17 0f 1f 1b 03 13 | asl + ora rla | 27 37 2f 3f 3b 23 33 | rol + and sre (lse) | 47 57 4f 5f 5b 43 53 | lsr + eor rra | 67 77 6f 7f 7b 63 73 | ror + adc sax (axs, aax) | 87 97 8f 83 | stx + sta lax | a7 b7 af bf a3 b3 | ldx + lda dcp (dcm) | c7 d7 cf df db c3 d3 | dec + cmp isc (isb, ins) | e7 f7 ef ff fb e3 f3 | inc + sbc las (lar, lae) | bb | A,X,S = {addr} & S These five are said to be unstable: tas (shs, xas) | 9b | S = A & X {addr} = A&X& {H+1} sha (axa, ahx) | 9f 93 | {addr} = A & X & {H+1} shx (xas, sxa) | 9e | {addr} = X & {H+1} shy (say, sya) | 9c | {addr} = Y & {H+1} | addressing mode | mnemonic | implied #8 8 8,x 16 16,x | performs: ----------------+---------------------------------+----------------------- anc | 0b* | A = A & arg, then C=N asr (alr) | 4b | A = A & arg, then lsr arr | 6b | A = A & arg, then ror sbx (axs, sax) | cb | X = (A & X) - arg dop (nop, skb) | 80** 80 04 14 | skips next byte top (nop, skw) | 0c** 0c 1c | skips next two bytes jam (kil, hlt) | 02 | crash (wait for reset) These two are somewhat unstable, because they involve an arbitrary value: ane (xaa) | 8b*** | A = (A | ??) & X & arg lxa (lax, atx) | ab*** | A,X = (A | ??) & arg Example: !cpu 6510 ; activate additional mnemonics... lax (some_zp_label,x) ; ...and use them. No, this dcp (other_zp_label),y ; example does not make sense. *) Up until ACME version 0.95.1, anc#8 generated opcode 0x2b. Since ACME version 0.95.2, anc#8 generates opcode 0x0b. Both opcodes work the same way on a real 6510 CPU, but they do not work on the C64DTV2. **) Note that DOP ("double nop") and TOP ("triple nop") can be used with implied addressing, but the generated opcodes are those for immediate and 16-bit absolute addressing, respectively. Using dop/top with x-indexed addressing might have its uses when timing is critical (crossing a page border adds a penalty cycle). ***) ANE and LXA first perform an ORA with an arbitrary(!) value and then perform an AND with the given argument. So they are unstable and therefore useless - unless the given argument is zero: ANE #0 reliably clears A - which is still useless; just use LDA #0. LXA #0 reliably clears both A and X. ACME will output a warning if these opcodes get assembled with a nonzero argument. There is no guarantee that these opcodes actually work on a given 6502 (or 6510, or 8500, or 8502) CPU. But as far as I know, nobody ever found an unmodified C64/C128 where these illegals didn't work. That's why I used "6510" as the CPU keyword instead of "6502illegal" or something like that. These illegals will definitely *not* work on 65c02 and 65816 CPUs. But I really should not have to tell you that ;) Because there are no official mnemonics for these opcodes, different people use different names for them. I hope my choices are not too exotic for your taste. Just for the sake of completeness: Here are all the remaining opcodes (the ones ACME won't generate even with "6510" cpu): Opcode| Description C64DTV2 ------+-------------------------------------------------------------- 12 | same as 02 and others jam CRASH bra rel 1a | same as (*legal*) ea nop 22 | same as 02 and others jam CRASH 2b | same as 0b anc #8 dop 32 | same as 02 and others jam CRASH sac #8 34 | same as 14 and others dop 8,x 3a | same as (*legal*) ea nop 3c | same as 1c and others top 16,x 42 | same as 02 and others jam CRASH sir #8 44 | same as 04 dop 8 52 | same as 02 and others jam CRASH 54 | same as 14 and others dop 8,x 5a | same as (*legal*) ea nop 5c | same as 1c and others top 16,x 62 | same as 02 and others jam CRASH 64 | same as 04 dop 8 72 | same as 02 and others jam CRASH 74 | same as 14 and others dop 8,x 7a | same as (*legal*) ea nop 7c | same as 1c and others top 16,x 82 | same as c2/e2 dop #8, but said to CRASH sometimes 89 | same as 80 dop #8 92 | same as 02 and others jam CRASH b2 | same as 02 and others jam CRASH c2 | same as 82/e2 dop #8, but said to CRASH sometimes d2 | same as 02 and others jam CRASH d4 | same as 14 and others dop 8,x da | same as (*legal*) ea nop dc | same as 1c and others top 16,x e2 | same as 82/c2 dop #8, but said to CRASH sometimes eb | same as (*legal*) e9 sbc #8 f2 | same as 02 and others jam CRASH f4 | same as 14 and others dop 8,x fa | same as (*legal*) ea nop fc | same as 1c and others top 16,x For more information about what these opcodes do, see these documents: John West, Marko Mäkelä. '64doc' file, 1994/06/03. Extra Instructions Of The 65XX Series CPU, Adam Vardy, 27 Sept. 1996 6502 Undocumented Opcodes, by Freddy Offenga, 5/17/1997 AAY64 (All About Your 64) NMOS 6510 Unintended Opcodes acme-crossassembler-0.96.4/docs/Lib.txt000066400000000000000000000012531333777125400177600ustar00rootroot00000000000000 ACME ...the ACME Crossassembler for Multiple Environments --- the library --- The files in the "ACME_Lib" directory tree can be accessed from within ACME sources when using the pseudo opcodes "!source" or "!binary": If the file names are given using "..." quoting, ACME will look for the files in the current directory. If the file names are given using <...> quoting, ACME will look for them in the ACME_Lib directory tree however. All files in the ACME_Lib directory tree are in the public domain. They are *NOT* covered by the GNU General Public License, under which the actual ACME program is released. acme-crossassembler-0.96.4/docs/QuickRef.txt000066400000000000000000000437121333777125400207710ustar00rootroot00000000000000 ACME ...the ACME Crossassembler for Multiple Environments --- Quick reference --- This file should give you a basic overview. More specialized stuff like forcing a specific addressing mode is discussed in extra files ("AddrModes.txt" in this case). ---------------------------------------------------------------------- Section: Example of what an ACME source code file looks like ---------------------------------------------------------------------- ;--- Example code fragment, start --- !to "tiny.o", cbm ; set output file and format * = $c000 ; set program counter CLEAR = 147 ; a global symbol definition !addr basout = $ffd2 ; another one, marked as an address ; a string output loop: ldx #0 beq + ; enter loop - jsr basout ; output character inx ; advance pointer + lda .string, x ; get character bne - ; check whether last rts .string !pet "Dumb example", 13, 0 ;--- Example code fragment, end --- Here's the same fragment again, now with some additional info: ;--- Example code fragment, start --- !to "tiny.o", cbm ; set output file and format ; This is a pseudo opcode to select the output filename and format. ; This can also be done using the command line options "-o" and "-f", ; respectively. * = $c000 ; set program counter ; This can also be done using the command line option "--setpc". ; some global symbol definitions CLEAR = 147 ; this is a simple constant ; Now "CLEAR" is defined as a global symbol having the value 147. !addr basout = $ffd2 ; this gets marked as an address ; Now "basout" is defined as a global "address" type symbol having the ; value $ffd2. ; The distinction between addresses and non-addresses only ; matters when the type check system gets activated using ; the "-Wtype-mismatch" switch. Then, a line like ; "lda CLEAR" would trigger a type mismatch warning because ; of the missing '#' character. ; a string output loop: ldx #0 beq + ; enter loop ; "+" is an anonymous forward label. Other ones are "++", "+++", etc. ; They can be used like any other symbol, but they always reference ; their *NEXT* definition. This saves having to think of names for ; unimportant labels. As the label's value is not defined yet, ACME ; will need to perform a second pass. - jsr basout ; output character ; "-" is an anonymous backward label. Other ones are "--", "---", etc. ; They can be used like any other symbol, but they always reference ; their *PREVIOUS* definition. This saves having to think of names for ; unimportant labels. In the line above, the value of "-" is set to ; the current program counter. inx ; advance pointer + lda .string,x ; get character ; Here the value of "+" is set to the current program counter. ; ".string" is a local symbol (because its name starts with a '.' ; character), but as its value is not defined yet, ACME will need to ; perform a second pass. bne - ; check whether last ; Here the last definition of the anonymous "-" label is referenced. rts .string !pet "Dumb example", 13, 0 ; Now the value of the local label ".string" is set to the current ; program counter. All label values are defined now, so after having ; done the second pass, the binary will be saved. The "!pet" pseudo ; opcode stores its string argument in PetSCII encoding to memory, ; followed by the given byte values. ;--- Example code fragment, end --- As you can see, pseudo opcodes are prefixed with an exclamation mark. That's non-standard, but: Backwards compatibility is the root of all evil. :) Summary about symbols: There are global symbols (their names starting with a letter or an underscore character). These can be accessed throughout the whole assembly. Then there are local symbols (their names starting with a '.' character). These can only be accessed from inside the macro or zone they were defined in (for more about macros and zones, see the file "AllPOs.txt"). And then there are anonymous labels (their names being sequences of either '-' or '+' characters). They are also local (bound to their macro/zone), but in addition to that, the "-" labels can only be used for backward references, while the "+" labels can only be used for forward references. In contrast to global and local labels, anonymous labels can not be defined explicitly (as in SYMBOL = VALUE). Save the given example source code to a file called "tiny.a" and start acme by typing acme tiny.a ACME will then parse the file and report any errors. An output file will only be generated if there were no errors and if an output filename has been given. After assembly, the example program can be run on a C64 using LOAD "tiny.o", 8, 1 SYS 49152 Note that ACME does not include any routines for transferring data to a C64. Such tools exist on almost every platform, and I didn't want ACME to become bloatware. ---------------------------------------------------------------------- Section: The pseudo opcodes ---------------------------------------------------------------------- A list with information on how to use all the Pseudo Opcodes can be found in the file "AllPOs.txt". Here's just a short overview: !byte !word !24 !32 !fill !align ...for directly placing values into the output file. !zone !symbollist ...for defining the scope of local symbols and saving global symbols. !convtab !pet !raw !scr !scrxor !text ...for converting and outputting strings. !do !endoffile !for !if !ifdef !ifndef !set ...for flow control; looping assembly and conditional assembly. !binary !source !to ...for handling input and output files. !pseudopc ...for offset assembly. !initmem *= ...for segment assembly. !macro + ...for defining and calling macros. !cpu !al !as !rl !rs ...for CPU support, especially the 65816 processor. !warn !error !serious ...for generating warnings, errors and serious errors. !addr ...to mark symbols as addresses, for the optional type check system. ---------------------------------------------------------------------- Section: Command line arguments ---------------------------------------------------------------------- The command line syntax for calling acme is quite simple: acme [options] [files] Available options are: -h, --help show this help and exit This is more or less useless, because the help is also shown if ACME is run without any arguments at all. -f, --format FORMAT set output file format Use this with a bogus format type to get a list of all supported ones (as of writing: "plain", "cbm" and "apple") -o, --outfile FILE set output file name Output file name and format can also be given using the "!to" pseudo opcode. If the format is not specified, "!to" defaults to "cbm", while the command line option defaults to "plain". -r, --report set report file name This creates a text listing containing the original line number, the resulting memory address, the byte value(s) put there and the original text line from the source file. -l, --symbollist FILE set symbol list file name This can also be given using the "!symbollist"/"!sl" pseudo opcode. The switch was called "--labeldump" in older versions, that name still works, too. --vicelabels FILE set file name for label dump in VICE format The resulting file uses a format suited for the VICE emulator. --setpc NUMBER set program counter This can also be given in the source code using "* = NUMBER". --cpu CPU_TYPE set target processor This can be changed in the source code using the "!cpu" pseudo opcode. Defaults to 6502. Use this with a bogus cpu type to get a list of all supported ones. --initmem NUMBER define 'empty' memory This can also be given using the "!initmem" pseudo opcode. Defaults to zero. --maxerrors NUMBER set number of errors before exiting If not given, defaults to 10. --maxdepth NUMBER set recursion depth for macro calls and !src The default value for this is 64. -vDIGIT set verbosity level Sets how much additional informational output is generated. Higher values mean more output: acme -v0 source.a This is the default: No additional output is generated, ACME will only display warnings and errors. acme -v1 source.a Now the start and end addresses of the generated output file are displayed, along with its size (a CBM-style "load address" is *not* counted). acme -v2 source.a In addition to the "-v1" output, ACME will announce each pass, will show amount and offset of "!binary" loads, and show start and end addresses and size of each segment. acme -v3 source.a In addition to the "-v2" output, ACME will now announce each source file. -DSYMBOL=VALUE define global symbol This option is useful if you build your projects using Makefiles: "-DSYSTEM=64" could build the C64 version while "-DSYSTEM=128" could build the C128 version of the software (using conditional assembly in your source code file). -I PATH/TO/DIR add search path for input files This option allows to add a directory to the search list for input files. If an input file cannot be found in the current working directory, all directories in the search list are tried (the first match is used). -W fine-tune amount and type of warnings -Wno-label-indent Disables warnings about labels not being in the leftmost column. -Wno-old-for Disables warnings about the old "!for" syntax and at the same time enables warnings about the _new_ "!for" syntax. -Wtype-mismatch Enables type checking system (warns about wrong types). --use-stdout fix for 'Relaunch64' IDE With this option, errors are written to the standard output stream instead of to the standard error stream. --msvc output errors in MS VS format This changes the format of the error output to that used by a certain commercial IDE. --color uses ANSI color codes for error output If your terminal emulation supports ANSI escape codes, use this option to have warnings and errors displayed in color. --fullstop use '.' as pseudo opcode prefix This changes the prefix character used to mark pseudo opcodes from '!' to '.' (so sources intended for other assemblers can be converted with less effort). -V, --version show version and exit. Platform-specific versions of ACME might offer more options. Since version 0.89, ACME accepts more than one top-level-filename given on the command line. ---------------------------------------------------------------------- Section: The maths parser ---------------------------------------------------------------------- ACME has a relatively powerful maths parser. This parser is used whenever ACME expects to read a numerical value. Supported operations include addition, subtraction, multiplication, divisions, comparisons, shifts, negation, boolean operations and some assembler-specific stuff like extracting the "low byte", the "high byte" or the "bank byte" of a value. Calculations are done using either signed 32-bit integer arithmetic or floating point arithmetic using the C "double" data type. Symbol values are stored the same way. This is a list of the operators currently known by ACME: Priority Example Meaning Alias ------------------------------------------------------------ 14 sin(v) Trigonometric sine function 14 cos(v) Trigonometric cosine function 14 tan(v) Trigonometric tangent function 14 arcsin(v) Inverse of sin() 14 arccos(v) Inverse of cos() 14 arctan(v) Inverse of tan() 14 address(v) Mark as address addr(v) 14 int(v) Convert to integer 14 float(v) Convert to float 13 ! v Complement of NOT 12 v ^ w To the power of 11 - v Negate 10 v * w Multiply 10 v / w Divide 10 v DIV w Integer-Divide 10 v % w Remainder of DIV MOD 9 v + w Add 9 v - w Subtract 8 v << w Shift left ASL, LSL 8 v >> w Arithmetic shift right ASR 8 v >>> w Logical shift right LSR 7 < v Lowbyte of 7 > v Highbyte of 7 ^ v Bankbyte of 6 v <= w Lower or equal 6 v < w Lower than 6 v >= w Higher or equal 6 v > w Higher than 5 v != w Not equal <>, >< 4 v = w Equal 3 v & w Bit-wise AND AND 2 Bit-wise exclusive OR XOR 1 v | w Bit-wise OR OR Operations with higher priority are done first. Of course you can change this using parentheses. If you prefer the aliases over the shorthand characters, note that they must be written in capital letters. Note that though there are operators to extract the "low byte", the "high byte" and the "bank byte", there is no operator to extract the fourth byte. If you want to access that, shift it down using ">>>" or "LSR". In cases where it's not clear which operator was wanted, ACME takes the longest possible one: v<>w ...checks for "v not equal w" v< >w ...checks for "v smaller than high byte of w" So you may have to separate operators with spaces to make sure ACME does what you want. The "power-of" operator is right-associative, so a^b^c means a^(b^c). Calculating 0^0 (zero to the power of zero) will give 1. If you don't know why I'm telling you this, ask a mathematician. :) This is a list of the value formats currently known by ACME: Examples Notes --------------------------------------------------------------------- 128 a decimal value, integer 128.5 a decimal value, floating point $d011 hexadecimal values are indicated by either a 0xffd2 leading "$" or a leading "0x". &1701 an octal value, indicated by "&" %010010 binary values are indicated by either a leading "%" %....#... or a leading "0b". In binary values, you can 0b01100110 substitute the characters "0" and "1" by "." and "#" respectively. This way the values are much more readable, especially when building bitmapped objects (like C64 sprites or fonts) in your source code. "p" character values are indicated by double or single 'q' quotes. The actual numeric value depends on the current conversion table (none/petscii/screen), chosen using the "!ct" pseudo opcode. poll_joy2 a global symbol .fail a local symbol, indicated by leading "." @loop a "cheap local", indicated by leading "@" * the current program counter. During offset assembly, "*" gives the value of the "Pseudo PC". Just to make sure: The value of the program counter is always the value that was valid at the start of the current statement, so !word *, *, *, * will give the same value four times. I think most assemblers do it this way. ---------------------------------------------------------------------- Section: Almost, but not quite, entirely useless syntax ---------------------------------------------------------------------- Every ACME source code file consists of a non-negative number of "lines". The lines have to be separated from each other using CR, LF or CRLF characters. Every line consists of a non-negative number of "statements" and an optional comment. Statements have to be separated from each other using colon (":") characters, the comment has to be prefixed with a semicolon (";") character. Every statement consists of an optional "label" and an optional "command". These are separated from each other using any number of SPACE or TAB characters. If a label has blanks before it, a warning is issued (to spot typing errors - see Errors.txt for more info). Every symbol name consists of these characters: "a" to "z", "A" to "Z", "0" to "9", the underscore character "_" and all characters with values beyond 127. The first character must not be a digit though. But it can be '.' or '@', making the symbol a local one. Local symbols beginning with '.' are only valid inside the current zone (marked using the "!zone" pseudo opcode) or the current macro. Local symbols beginning with '@' are only valid between the enclosing global labels (or inside the current macro). Two other possibilities for label names are "all-characters-are-minus" (then it is an anonymous backward label) and "all-characters-are-plus" (then it is an anonymous forward label). Every command is one of the following: An assembler opcode A pseudo opcode, beginning with a "!" character A symbol definition (symbol=value) A pc definition, beginning with a "*" character A macro call, beginning with a "+" character ...and the syntax of those things varies. :) Assembler mnemonics and pseudo opcodes are case insensitive, so whether you write "LDA" or "lda" or "LdA" does not make a difference. In earlier releases of ACME, arithmetic operators like MOD, XOR, LSL had to be written in UPPER CASE. This is no longer needed. Symbol names are case sensitive, so "label" and "Label" are two different things. acme-crossassembler-0.96.4/docs/Source.txt000066400000000000000000000011641333777125400205130ustar00rootroot00000000000000 ACME ...the ACME Crossassembler for Multiple Environments --- source files --- This program is free software, released under the terms of the GNU General Public License. Therefore, the sources must be made publicly accessible. If this archive does not contain the source file tree, you can download it from the ACME web page at http://sourceforge.net/p/acme-crossass/ To build ACME, you need a recent C compiler and a "make" utility. On most systems, typing "make" should suffice to build the binary. See your platform's help file for more information. acme-crossassembler-0.96.4/docs/Upgrade.txt000066400000000000000000000074121333777125400206440ustar00rootroot00000000000000 ACME ...the ACME Crossassembler for Multiple Environments --- compatibility problems --- If you haven't used ACME before, you don't need to read this text. It is only of use to people who upgraded from ACME 0.05 (or earlier) to ACME 0.07 (or later). You might encounter some slight incompatibilities: I have done a few changes to ACME's workings. Because backwards compatibility is the root of all evil (*g*), I did not include any possibility to enforce the old behaviour. If one of the following changes applies to your source files, assemble them with this new release of ACME and then compare new and old output files. Sorry for this inconvenience, but at least I think that there won't be any further changes in the future. ---------------------------------------------------------------------- Section: Offset assembly / segment assembly ---------------------------------------------------------------------- Offset assembly is now done using a new pseudo opcode called "!pseudopc". Have a look at "AllPOs.txt" for further information on its syntax and usage. The old way of just redefining the program counter by using more than one "* = EXPRESSION" statements does something totally different now: Whenever the program counter is redefined, ACME will actually change its pointer into the output buffer, so you can write your code in distinct segments. These segments can be given in any order. After assembly, ACME stores everything from the lowest address used to the highest address used. Have a look at "AllPOs.txt" for an example on how to use this facility. ---------------------------------------------------------------------- Section: Argument order of MVP/MVN ---------------------------------------------------------------------- The syntax of the 65816 opcodes MVN and MVP is usually given as MVN source_bank, destination_bank All previous versions of ACME did it the other way round: First the destination bank, then the source bank. This has been fixed, ACME now uses the syntax given above. ---------------------------------------------------------------------- Section: Typecast ---------------------------------------------------------------------- You can use leading zeros to make ACME use a bigger addressing mode than needed. Until now, this did not work when using labels. The source code label1 = $fa label2 = $00fa lda $fa lda $00fa lda label1 lda label2 was assembled to: lda $fa lda $00fa lda $fa lda $fa Release 0.07 of ACME now correctly assembles the given source code to: lda $fa lda $00fa lda $fa lda $00fa ---------------------------------------------------------------------- Section: !endoffile ---------------------------------------------------------------------- Previous versions of ACME knew a pseudo opcode called "!end" that marks the end of a source code file. Because the word "end" doesn't actually specify *what* is about to end, I changed this to "!endoffile". You can also use a short version, called "!eof". The old PO "!end" no longer works. ---------------------------------------------------------------------- Section: Using the BIT command without parameters ---------------------------------------------------------------------- Release 0.07 of ACME will complain if you try to assemble BIT without any parameter. Previous versions did just output the byte $2c - a commonly known trick to mask the following 2-byte command on the 6502 processor. If you still want to do this, use !src <6502/std.a> ; parse library file to include some standard macros. Then you can use +bit8 ; output $24 to mask following 1-byte command and +bit16 ; output $2c to mask following 2-byte command respectively. That's all. Again, sorry for the inconvenience... acme-crossassembler-0.96.4/docs/cputypes.txt000066400000000000000000000075671333777125400211440ustar00rootroot00000000000000 ACME ...the ACME Crossassembler for Multiple Environments --- cpu types --- ACME supports the following cpu types: *** 6502 This is the instruction set of the original NMOS 6502 designed by MOS (later CSG). There are 151 documented opcodes. ACME does not use "A" to indicate "accumulator addressing"; just write the mnemonic without any argument: "LSR" will work, "LSR A" won't. *** 6510 This is the 6502 variant used in the C64 computer. It uses the same instruction set as the 6502, but in addition to that, ACME supports most of the undocumented opcodes as well. See "docs/Illegals.txt" for more info. *** 65c02 This is the CMOS re-design of the 6502. It seems to have also been available from Rockwell, GTE/CMD and others. Features: - new instructions: BRA near_target branch always PHX/PHY/PLX/PLY push/pull X/Y register STZ $12 store zero in zp STZ $12, x store zero in zp, x-indexed STZ $1234 store zero absolute STZ $1234, x store zero absolute, x-indexed TRB $12 test and reset bits in zp TRB $1234 test and reset bits absolute TSB $12 test and set bits in zp TSB $1234 test and set bits absolute - new addressing modes for existing instructions: LDA/STA/ADC/SBC ($12) zp indirect AND/ORA/EOR/CMP ($12) zp indirect BIT #$12 immediate BIT $12, x zp, x-indexed BIT $1234, x absolute, x-indexed INC increment accumulator DEC decrement accumulator JMP ($1234, x) x-indexed indirect - bugfix for flags in decimal mode - bugfix for JMP($xxff) instruction - undocumented opcodes are NOPs (although of different lengths) There are 178 documented opcodes. *** r65c02 This is a superset of 65c02, probably originally by Rockwell. It adds bit manipulation instructions: BBR0 $12, near_target branch on bit reset in zp BBS0 $12, near_target branch on bit set in zp RMB0 $12 reset memory bit in zp SMB0 $12 set memory bit in zp The digit in the mnemonic is the bit number, therefore it must be in the 0..7 range. Chips with this instruction set seem to have been available from Rockwell, GTE/CMD and others. There are 210 documented opcodes. *** w65c02 This is a superset of r65c02, originating at WDC. It adds two new instructions: STP stop (wait for reset) WAI wait for interrupt There are 212 documented opcodes. *** 65816 This is a superset of 65c02, originally designed by WDC (it seems to have been available from GTE/CMD as well). Features: - register sizes can be changed to 16-bit - 24-bit address space - several new instructions (including block transfers) - several new addressing modes for existing instructions There are 256 documented opcodes, but one of them ("WDM") is reserved for future expansion. See "docs/65816.txt" for more info. *** 65ce02 This is a superset of r65c02, originating at CSG. Features: - Z register - 16-bit stack pointer - 16-bit branches - new instructions (including a few 16-bit operations) - new addressing modes for existing instructions There is a known bug: SBC does not work correctly in decimal mode. There are 256 documented opcodes, but one of them ("AUG") is reserved for future expansion. ACME uses different mnemonics for old and new (long) branch instructions: BEQ near_target old, 8-bit offset LBEQ far_target new, 16-bit offset The original datasheet called BRA ("branch always") BRU ("branch unconditional") instead. ACME accepts both mnemonics. *** 4502 This is basically the same as 65ce02, but - MAP replaces AUG - EOM is synonymous to NOP This cpu core can be found in the CSG4510 chip in the C65. There are 256 documented opcodes. *** c64dtv2 This is the cpu in version 2 of the C64DTV. It uses a superset of the 6502 instruction set. Features: - new instructions: BRA near_target branch always SAC #$12 set accumulator mapping SIR #$12 set index register mapping - support for some of the undocumented opcodes. acme-crossassembler-0.96.4/examples/000077500000000000000000000000001333777125400173765ustar00rootroot00000000000000acme-crossassembler-0.96.4/examples/Makefile000066400000000000000000000011001333777125400210260ustar00rootroot00000000000000ASSEMBLER6502 = acme AS_FLAGS = -v9 -Wtype-mismatch RM = rm PROGS = ddrv128.prg ddrv64.prg macedit.o trigono.o all: $(PROGS) ddrv128.prg: ddrv.a $(ASSEMBLER6502) $(AS_FLAGS) --outfile ddrv128.prg --format cbm -DSYSTEM=128 ddrv.a ddrv64.prg: ddrv.a $(ASSEMBLER6502) $(AS_FLAGS) --outfile ddrv64.prg --format cbm -DSYSTEM=64 ddrv.a macedit.o: macedit.a $(ASSEMBLER6502) $(AS_FLAGS) --outfile macedit.o --format cbm macedit.a trigono.o: trigono.a $(ASSEMBLER6502) $(AS_FLAGS) --outfile trigono.o --format plain trigono.a clean: -$(RM) -f *.o *.tmp $(PROGS) *~ core acme-crossassembler-0.96.4/examples/ddrv.a000066400000000000000000000401541333777125400205030ustar00rootroot00000000000000;ACME 0.95 ;!sl "ddrv.l" ; Name DuoDriver ; Purpose Input driver for mouse and joystick ; Author (c) Marco Baye, 1999 ; Licence Free software ; Changes: ; 23 Apr 1999 Release 2.20. Internal info: ; DuoDriver v2.20 by Mac Bacon 23 Apr 1999. Freeware! ; Somewhen Added self-calibration, forming release 3.00. Internal info: ; Mac Bacon:DuoDrv3,PD ; 21 Jul 1999 Used reverse subtraction, forming release 3.01. Internal info: ; Mac Bacon:DuoDrv3,PD ; 1 Aug 1999 Release 4.00. ; Both 128 and 64 versions ; Now supports overlay-sprite mouse pointer ; Binary includes sprites ; Released in GO64 8/1999 (without release number). ; 3 Aug 1999 Same source file for both 128 and 64 versions. Release 4.01. ; Apart from that, virtually identical to release 4.00. ; 04 Feb 2003 Beautified ; 05 Feb 2003 Added "SpriteLine" macro and made sprites inline ; 26 May 2005 Release 4.02. All changes since release 4.00 are source-only! ; The resulting binaries are identical to those of release 4.00 ; (which were included in GO64 magazine 8/1999) ; 26 Mar 2006 Release 4.03. Adjusted source to ACME 0.91 capabilities. ; 25 Nov 2007 Release 4.04. Adjusted source to ACME 0.94 capabilities. ; 7 Apr 2013 Slightly reformatted. ; 1 Jun 2014 Adjusted to experimental type system of ACME 0.95 ; This source code file uses conditional assembly ; to decide which version to produce (C64 or C128). ; Select type of binary to assemble (64 => c64, anything else => c128) !ifndef SYSTEM { !warn "Label SYSTEM not defined. Use -DSYSTEM=64 to build C64 version, -DSYSTEM=128 to build C128 version. Will now default to C64 version." SYSTEM = 64 } !if SYSTEM != 64 & SYSTEM != 128 { !serious "Please use either -DSYSTEM=64 or -DSYSTEM=128 when assembling this project." } ; --- Configurable values ; Start address, output file name and VIC location !if SYSTEM = 64 { * = $c000 !to "ddrv64.prg", cbm !addr VIC_Base = $d000 } !if SYSTEM = 128 { * = $0c00 !to "ddrv128.prg", cbm !addr VIC_Base = $11d6 ; Location of mirror registers } ; Pointer's maximum coordinates MaximumCoordinateX = 319 ; VIC value ; MaximumCoordinateX = 639 ; VDC value MaximumCoordinateY = 199 ; Maximum pixel step size ("speed") for joystick acceleration routine. MaxStep = $10 ; max $7f ; Distance before acceleration starts, in pixels. MaxTime = $04 ; max $7f ; Sprites to use for overlay pointer Sprite_A = 0 Sprite_B = 1 ; Coordinates of "pointer pixel" within pointer sprites; adjust these ; if you use different sprites. (0,0) is sprite's upper left pixel. Sprite_HotspotX = 1 Sprite_HotspotY = 1 ; address definitions !addr { ; Locations to store button states, $ff = pressed, $00 = not pressed. ; Mouse uses both buttons, joystick only uses "LeftButton". ; Location to store pointer's current character coordinates. !if SYSTEM = 64 { LeftButton = $a4 RightButton = $a5 CharX = $b3 CharY = $b4 tapebuf = $0340 spr_ptrs = 2040 } !if SYSTEM = 128 { LeftButton = $fa RightButton = $ff CharX = $9b CharY = $9c } ; Location to store pointer's current pixel coordinates. The driver ; code relies on having *four consecutive* bytes: ; x low, x high, y low, y high Coordinates = $fb ; $fb..$fe ; --- System constants ; Interrupt vector sys_iirq = $0314 ; I/O registers sid_pot = $d419 cia1_pra = $dc00 cia1_prb = $dc01 cia1_ddrb = $dc03 mmu_cr = $ff00 ; c128 only ; dummy value for self mod MODIFIED16 = $ffff };addr ; --- Label definitions ; New names for some precalculated values, only to improve ; readability. Don't change these. PointerXnow = Coordinates PointerYnow = Coordinates + 2 SpriteA_X = VIC_Base + 2 * Sprite_A SpriteA_Y = VIC_Base + 2 * Sprite_A + 1 SpriteB_X = VIC_Base + 2 * Sprite_B SpriteB_Y = VIC_Base + 2 * Sprite_B + 1 Sprites_OF = VIC_Base + 16 ; X Overflow ; The character "^" in the following calculation means "to the power ; of". It is ACME syntax - if your assembler cannot do this, you may ; want to use hardcoded values here instead of calculations. Sprites_Bitmask = 2 ^ Sprite_A + 2 ^ Sprite_B ;alternative: ; Sprites_Bitmask = 1 << Sprite_A | 1 << Sprite_B SpriteOffset_X = $18 - Sprite_HotspotX SpriteOffset_Y = $32 - Sprite_HotspotY ; In the sprite coordinate system, the graphics pixel (0,0) has the ; coordinates ($18,$32), so these are needed for converting. Blame the ; VIC. ; --- Entry point ; Because this routine is the first, the file can be BOOTed on a c128. ; Initialisation code, installs driver on IRQ vector. ; Fetch IRQ vector and write to end Init lda sys_iirq ldx sys_iirq + 1 sta mod16 stx mod16 + 1 ; Let IRQ vector point to driver code lda #Entry php sei sta sys_iirq stx sys_iirq + 1 plp !if SYSTEM = 128 { lda mmu_cr tay and #$fe ; activate I/O chips sta mmu_cr } ; Init mouse buttons lda #$11 sta cia1_prb !if SYSTEM = 128 { sty mmu_cr } !if SYSTEM = 64 { ; Copy sprites to tape buffer ldx #127 - lda Sprites, x sta tapebuf, x dex bpl - lda #Sprites_Bitmask ; Set sprite block pointers ldx #$0d stx spr_ptrs + Sprite_A inx stx spr_ptrs + Sprite_B ; Activate pointer sprites ora VIC_Base + 21 sta VIC_Base + 21 } rts ; --- Variables ; Pixel counter before accelerating JoyWaittime !byte 0 ; --- Main code Entry ; The driver consists of several distinct parts. To minimise ; performance wastage, you should remove all parts you don't need for ; the specific application. ; --- Part 0, initialisations ; Make sure decimal mode is off cld ; Set button states to "not pressed", so the other parts only have to ; deal with setting them to "pressed". lda #$00 sta LeftButton sta RightButton ; --- Part 1, handling mouse movements ; mouse x ldx #$00 ; 0 means "x stuff" jsr PotDelta ; Now signed x movement is in A/Y. Add to current x value. clc adc PointerXnow sta PointerXnow tya adc PointerXnow + 1 sta PointerXnow + 1 ; mouse y ldx #$01 ; 1 means "y stuff" jsr PotDelta ; Now signed y movement is in A/Y. Mouse and computer use different y ; directions, so don't add to, but subtract from current y value. ; This is a reverse subtraction - it might be harder to understand, ; but it is both faster and smaller than the usual way. clc sbc PointerYnow eor #$ff sta PointerYnow tya sbc PointerYnow + 1 eor #$ff sta PointerYnow + 1 ; --- Part 2, handling mouse buttons ; Prepare CIA by setting bits to input ldy #$11 sty cia1_ddrb ldx #$ff ; $ff means "pressed" lda #$10 ; check left button bit cia1_prb bne + stx LeftButton ; store state + lda #$01 ; check right button bit cia1_prb bne + stx RightButton ; store state + ; Reset CIA to normal state ldy #$00 sty cia1_ddrb ; --- Part 3, handling the joystick ; Fetch byte holding direction flags lda cia1_pra tax ; ...and remember it ; Check 'up' direction ror bcs ++ ; Subtract current step size from y value if needed. tay sec lda PointerYnow sbc JoyStepsize sta PointerYnow bcs + dec PointerYnow + 1 + tya ++ ; Check 'down' direction ror bcs ++ ; Add current step size to y value if needed. tay ;clc ; C is always clear here lda PointerYnow adc JoyStepsize sta PointerYnow bcc + inc PointerYnow + 1 + tya ++ ; Check 'left' direction ror bcs ++ ; Subtract current step size from x value if needed. tay sec lda PointerXnow sbc JoyStepsize sta PointerXnow bcs + dec PointerXnow + 1 + tya ++ ; Check 'right' direction ror bcs ++ ; Add current step size to x value if needed. tay ;clc ; C is always clear here lda PointerXnow adc JoyStepsize sta PointerXnow bcc + inc PointerXnow + 1 + tya ++ ; --- Part 4, handling joystick button ror bcs + lda #$ff ; $ff means "pressed" sta LeftButton + ; --- Part 5, joystick acceleration ; Restore joystick direction bits and check whether to set speed to ; zero. txa and #$0f ; Clear unneeded bits cmp #$0f ; Any direction bit ? bne + ; No direction was used, so reset speed and wait counter to normal. lda #$01 sta JoyStepsize lda #MaxTime sta JoyWaittime jmp Part5End + ; A direction bit was used, so check whether to accelerate: If speed ; is already maximum speed, don't accelerate. JoyStepsize = * + 1 lda #$00 ; (self-modifying) ; If the variable "JoyStepsize" would have been defined as a separate ; location (using "!byte"), it would have taken a byte of memory. By ; storing the value inside an LDA command's argument, we save that one ; byte. It might make a difference. :) cmp #MaxStep ; If speed is max., bcs Part5End ; don't accelerate. ; Speed isn't maximum yet. Check whether ; we have to wait before accelerating. dec JoyWaittime bpl Part5End ; Counter has underrun, so accelerate. inc JoyWaittime ; reset counter inc JoyStepsize ; increase speed Part5End ; --- Part 6, restrict coordinate range ; restrict x value ldx #$00 ; 0 means "x stuff" jsr Restrict ; restrict y value ldx #$02 ; 2 means "y stuff" jsr Restrict ; --- Part 7, positioning sprites ; Set sprites' x positions lda PointerXnow clc adc #SpriteOffset_X sta SpriteA_X ; set both sprites sta SpriteB_X lda Sprites_OF ; get x overflow bcs SetOF ldx PointerXnow + 1 bne SetOF and #Sprites_Bitmask XOR $ff bcc StoreOF ; C is clear here SetOF ora #Sprites_Bitmask StoreOF sta Sprites_OF ; set x overflow ; Set sprites' y positions lda PointerYnow clc adc #SpriteOffset_Y sta SpriteA_Y sta SpriteB_Y ; The y value's high byte is useless in this case. ; --- Part 8, making char coordinates ; Convert x coordinate. There are different "best" routines for ; different resolutions, so I've given the VIC and VDC routines. lda PointerXnow lsr lsr lsr ldx PointerXnow + 1 ;ora OrTable,x ; VDC only (see below for data table) beq + ; VIC only ora #$20 ; VIC only + sta CharX ; Convert y coordinate. lda PointerYnow lsr lsr lsr sta CharY ; --- Add further parts here ; Here you can add further routines, for example to use the button ; states to fake keypresses etc. ; --- The end ; The initialisation routine sets the argument to the address of the ; previous IRQ routine. mod16 = * + 1: jmp MODIFIED16 ; (self-modifying) ; This table is for part 8. ;OrTable !byte 0, 32, 64 ; VDC only ; --- "Restrict" subroutine PointerXmax !word MaximumCoordinateX PointerYmax !word MaximumCoordinateY ; "y" word must follow directly after "x" word in memory. Restrict ; Restrict internal coordinates to configured range. Entry conditions: ; X is direction handle (0 = x, 2 = y) lda PointerXnow + 1, x bmi SetTo0 cmp PointerXmax + 1, x bcc Eosr bne + lda PointerXmax, x cmp PointerXnow, x bcs Eosr + lda PointerXmax, x ldy PointerXmax + 1, x jmp DefCo SetTo0 lda #0 tay DefCo sta PointerXnow, x sty PointerXnow + 1, x Eosr rts ; --- "Pot" subroutine ; This routine computes the mouse movements and therefore contains the ; self-calibration stuff and the other improvements over the standard ; 1351 driver. PotMax !word 0 ; max. POTs yet plus 1 ! PotMin !word $ffff ; lowest POTs yet PotOld !word 0 ; old values PotWidth !word 0 ; interval width HalfPotWidth !word 0 ; half width ; (buffered for speed increase) ; The above variables are not really words: The first byte is the x ; value, the second byte is the y value respectively. ; Compute the signed distance of mouse movement. ; Entry conditions: X is direction handle (0 = x, 1 = y) ; Exit conditions: A/Y are signed distance (low/high) ; First, get new value and clear "recalculate signal width" flag. PotDelta lda sid_pot, x ldy #$00 ; Check whether new value is lower than lowest known. cmp PotMin, x bcs + ; Store new "lowest" und set "recalculate signal width" flag. sta PotMin, x ldy #$ff + ; Check whether new value is higher than highest known. cmp PotMax, x bcc + ; Set "recalculate signal width" flag and store new "highest". ldy #$ff pha ; Remember current value adc #$00 ; Add one (C is set) sta PotMax, x ; Value $ff (0 after adding) means that there is no mouse connected, ; so reset min/max in that case. beq ResetMM ; Stack is untidy... pla ; Restore current value + ; If flag is set, recalculate signal width. iny ; Check flag bne ++ tay ; Buffer current value. lda PotMax,x ; Get highest + 1 sec ; Subtract lowest sbc PotMin, x bcc + sta PotWidth, x ; Store signal lsr ; width and half signal sta HalfPotWidth, x ; width + tya ; Restore current value. ++ ; Calculate distance tay ; Buffer current value. sec sbc PotOld, x pha tya sta PotOld, x pla beq zero ; If not moved, exit. bcc minus ; Negative difference ; Positive difference: ; Check whether movement caused a value wrap-around. cmp HalfPotWidth, x bcc Decrease beq Decrease ; It did, so calculate "real" distance and jump to exit ;sec ; C is always set here sbc PotWidth, x ; Fix distance ; We now know that the (fixed) distance is really negative, so we ; finally wipe out that annoying bit 0 noise by incrementing the ; value. Increase ;clc ; C is always clear here adc #$01 beq zero ; If increasing gives zero, jump to zero handler. ldy #$ff ; Set up high byte for negative values. rts ; Negative difference: ; Check whether movement caused a value wrap-around. minus eor #$ff ; Complement ; If we would do a real negation (by adding "1"), then we would need ; to branch using BCC *and* BEQ. So the above way might be harder to ; understand, but it is both shorter *and* faster - which I like. :) cmp HalfPotWidth, x eor #$ff ; Restore value bcc Increase ; Movement caused a value wrap-around, so calculate "real" distance and exit. clc adc PotWidth, x ; Fix distance ; We now know that the (fixed) distance is really positive, so we ; finally wipe out that annoying bit 0 noise by decrementing the value. Decrease sec sbc #$01 ; No difference or positive difference; both need zero as the high byte. zero ldy #0 rts ; If there is no mouse, reset "lowest" ("highest" will have been reset ; already) and return zero. ResetMM tay ; Set Y to zero. pla ; Tidy stack lda #$ff ; Reset "lowest" sta PotMin, x tya ; Return with A/Y = 0 rts ; --- Include sprites ; Because the c64 version copies the sprite data into the tape buffer ; on initialisation, the data is included right here. ; In the c128 version, we skip memory until we reach $0e00 - this is ; where the sprites are stored by default. !if SYSTEM = 128 { !align $ffff, $e00, $0 } !macro SpriteLine .v { !by .v >> 16, (.v >> 8) & 255, .v & 255 } Sprites ; 765432107654321076543210 +SpriteLine %........................ +SpriteLine %.#...................... +SpriteLine %.##..................... +SpriteLine %.###.................... +SpriteLine %.####................... +SpriteLine %.#####.................. +SpriteLine %.######................. +SpriteLine %.#######................ +SpriteLine %.########............... +SpriteLine %.#########.............. +SpriteLine %.########............... +SpriteLine %.######................. +SpriteLine %.######................. +SpriteLine %.##..##................. +SpriteLine %.#....##................ +SpriteLine %......##................ +SpriteLine %.......##............... +SpriteLine %.......##............... +SpriteLine %........##.............. +SpriteLine %........##.............. +SpriteLine %........................ !byte 0 ; pad to 64-byte block ; 765432107654321076543210 +SpriteLine %##...................... +SpriteLine %###..................... +SpriteLine %####.................... +SpriteLine %#####................... +SpriteLine %######.................. +SpriteLine %#######................. +SpriteLine %########................ +SpriteLine %#########............... +SpriteLine %##########.............. +SpriteLine %###########............. +SpriteLine %###########............. +SpriteLine %#########............... +SpriteLine %########................ +SpriteLine %########................ +SpriteLine %###..####............... +SpriteLine %##...####............... +SpriteLine %......####.............. +SpriteLine %......####.............. +SpriteLine %.......####............. +SpriteLine %.......####............. +SpriteLine %........###............. acme-crossassembler-0.96.4/examples/ddrv128.exp000066400000000000000000000012011333777125400213000ustar00rootroot00000000000000 , - , x()܌`ة _ ee _ IIܢ,,ܭܪj8 j m j8 j m j)  + L  + + 2  2 i) i1JJJ JJJL?0/ . . / LP `ԠW W U HiU IhU 8W [ J] 8Y HY h#] [ i`I] I}[ 8`hW `@`px|~~~fCǀacme-crossassembler-0.96.4/examples/ddrv64.exp000066400000000000000000000011401333777125400212210ustar00rootroot00000000000000<=<x(ܢ@  Ѝ`ة oee oIIܢ,,ܭܪj8j mj8j mj) ;L ;; B BiЍЭа) Хi1ЍХJJJ JJJL?0?>>?L``Ԡgge HieIhe8gkJm8iHih#mki`ImI}k8`hg`@`px|~~~fCǀacme-crossassembler-0.96.4/examples/macedit.a000066400000000000000000000067701333777125400211600ustar00rootroot00000000000000;ACME 0.95 ; ist der komplette Sourcecode von MacEdit ; (80-Zeichen-Version) ; Version 0.7 ; Weitere Informationen am Ende der Datei ; Parameter: !to "macedit.o", cbm ;!sl "macedit.l" *= $1300 !ct pet !source <6502/std.a> !ifndef lib_6502_std_a { !serious "To assemble this program, you need to install the current ACME library." } !source "me/macros.a" !source "me/const.a" ; Code: jmp init ; zum Programm !text "TekFile", 0 ; DateiFormat + 'program' !word progend - keyb ; length ; Gelinkt wird: keyb !binary "me/tables.bin", 826 keytabs = keyb + 12 ; 6 Tastaturtabs & atst = keytabs + $22e ; ASCII-2-Screen-Tabelle !source "me/vars.a" !source "me/core.a" !source "me/file.a" !source "me/out.a" !source "me/cursor.a" linebuf progend = linebuf+128 !byte 0 ; 128 Byte Zeilenpuffer !eof Änderungen von Version 0.6 zu Version 0.7: Das DCS-Window wurde implementiert, dadurch wurde auch ein Unterschied zwischen "Beenden" und "Basic" eingebaut (Bei ersterem erfolgt die DCS-Abfrage). Die Strings der Windows liegen jetzt nicht mehr als Screencodes vor, sondern als PetSCII-Werte; die Routine ".makewin" konvertiert dies also. Die Bedeutung des Flags "unnamed" wurde invertiert. Spätere Änderungen am Source: 19. 4.1997: Durch Weiterentwicklung von V0.6 erzeugt (kommentarlos) 24. 9.1998: Kommentare von V0.6 wieder hinzugefügt 25. 9.1998: Umformatierung auf ACME-Syntax 10.10.1998: Ersetzen von "{" und "}" in Labels durch "_" und "__" 12.10.1998: Unterschiede zu v0.6 dokumentiert. 30.10.1998: "+ =" wieder zu "+=" korrigiert. 1.11.1998: Alle Labels wieder globalisiert. 2.11.1998: Tabulatorlayout wieder korrigiert und "~" durch "___" ersetzt. 3.11.1998: Label "notmany!" durch "notmany" ersetzt. Wo kam das bloß her ? 4.11.1998: Zwei fehlerhafte Auskommentierungen entsorgt. Die Stellen wurden mit "**mark**" gekennzeichnet. Wo kam das bloß her ? Außerdem wurde "< = >" in einem Textstring wieder zu "<=>" korrigiert. Das ist wohl beim automatischen Layout passiert. 4.11.1998: Top-Bit-Set-Zeichen aus Textstrings enfernt und byteweise eingefügt, z.B. auch "Cursor up/down/left/right"-Werte. Außerdem alle Filenamen angepaßt. 5.11.1998: Auch die Umlaute nun zu Bytewerten gewandelt. 19.11.1998: "!cbm" eingefügt, da geänderte ACME-Funktion "!text". 24.11.1998: Filenamen bei "!to" und "!bin" auf UNIX-Stil gebracht. 27.11.1998: Aufeinanderfolgende "!tx" und "!by" gemerged, BIT-Trick benutzt, Hexzahlen auf lowercase gebracht, Binärzahlen auf Spezialformat gebracht, Einrückungen dezimiert, Zahlen durch Label ersetzt, "firsttry" in "repeatedtry" umbenannt (war vorher unlogisch). 28.11.1998: Auf Benutzung von Modulen und lokalen Labels umgestellt. 30.11.1998: Alle "!module" durch "!zone" ersetzt (wegen ACME-Änderung). 1.12.1998: Mehrere Labels pro Zeile entzerrt (wegen ACME-Änderung). 2.12.1998: Multifile-Version, Änderungstext ans Ende verschoben. 10.12.1998: Makros eingebaut. 8. 1.1999: Library benutzt und daher Branch-Makros gelöscht, außerdem BIT-Trick durch Makroaufruf ersetzt. 24. 8.1999: An die leicht geänderte Syntax von ACME 007 angepaßt. 04 Jun 2005: Adjusted to ACME 0.86 syntax (added output file format indicator). 26 Mar 2006: Adjusted to ACME 0.91 syntax (anonymous labels) Now throws serious error if the library file could not be loaded. 7 Apr 2013: Converted to UTF-8 27 Jun 2013: Adjusted to change in library. 1 Jun 2014: Adjusted to experimental type system of ACME 0.95 acme-crossassembler-0.96.4/examples/macedit.exp000066400000000000000000000164271333777125400215340ustar00rootroot00000000000000L&EKILE0vwzy|{u~pt瑖)0˷Ϋ̡>[<]s=?_85x2471+- 6930.q}@$%&/()=?:;^*ۓ'\_!>"уn2t71+- r30. 3WA4YSE5RD6CFTX7ZG8BHUV9IJ0MKONPL.,[+#]-1<2 Q85 2471+- 6930. 34567890ξ̯.,[+#]-1<2 85 2471+- 6930. 3WA4YSE5RD6CFTX7ZG8BHUV9IJ0MKONPL.,[+#]-1<2 Q85 2471+- 6930._!"#$%&'()*+,-./0123456789:;<=>? @ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^߀`abcdefghijklmnopqrstuvwxyz{|}~@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^`abcdefghijklmnopqrstuvwxyz{|}~ACDIT WAS WRITTEN BY AC ACON IN 1994-97. HIS IS REEWARE !ACD0S0:-NLEITUNG .TXT,P,RORMATERSION1.0͏χUNBENANNT .TXTMERGE .TXTUNBENANNT .TXTUNBENANNT .TXTP7Afowq.qqq !qkqqqqqqq!qqqqqqqqqqqqqqqqqqqq q+qqqqqqqqqqqr) qqqqqqqqqqqtq8+qqqqqqqqm)q#qq qqqqqqqqq&8%8 */ ) * u)` I` Lw Lw 2* x. 2*.I@)`) 8 7L`I )`) `ЅL3I x !w L` r G `"% #&ʎ!$'/0`Lx ҹ `xJTSИSX` *mm` ȱ`%P %O'&&'`8&''8&`,-+莀@JiAiii8ʆ䄐-䇰$ȑ,-L{` +` /0  ``%L!%L!&'&L!&'%+,'07 &2 5L+%   "Ȍ%L!   L!&ʎ'L/0&'L%&' `%&'L!.  %. U!L   U!`ʆ+L `    U!L!$Ȇ+L% ` 8 8ƈƄ)*奅ȥ` 5 iiȱ)*楅ȥ` 8)*`````       `ʎ` ILU! /0/0 L!ʎ`` L  ?`%L  "Ȍ%L!I``  L% &'&8&'L!%&'L!ii% &'&i&'L!%&'L!%&ʎ'L!݉`@dAe8` !LX`` / # |, 2 */ u ". / Lr ". /` ". /`L` r? :+ `[LH / # |, R   ٳʈ   Gȩ  0+0 .-0 0+0 00+0? #000+Ƚ00L   ". /h`` 8+?*,:ܙWH / # |,     Ȍ*Ȍ)8/0?0  00+00 -  0 0 )*ЦТ  ". /h` ,`L H># / |, u ". g/h`I`    !%+&2'3 !`+L!L 23 !h+ _Ա - 1ei+81  ! ? 81 0  !  " `23`  " " (`` 865 5ץ  " I+48` -Ȍ4+84𡨭4ei - L"+8"eiȱ ȑ+8`m4568`i`+ `"H ".h#### |, # >* u## 0ɍ,ɑL#L#ɝ&$L#2$L# ".`HHH$$L#ȱȱ#  /h#h# #h L(##L### # >*##L##--#8iP8##`  >#=Ow @$$%%%%%%&&&&---ACDIT---ERSION0.7VOM19.4.97GESCHRIEBENVON:ACACON/EKORDSIESISTREEWARE!ITTEEINEASTEDRCKENoEUuADENvACHLADENPEICHERNPEICHERNUNTERASICaEENDENyUTzOPY{ASTEELETEADENPEICHERNINKSECHTSENTRIERTLOCKSATZORDRAPEEPUTONSERTETTURSIVNTERSTRICHENEVERSOCHIX---CHTUNG!---ERPEICHERISTVOLL!ITTEEINEASTEDRCKENITTEILENAMENEINGEBEN::NGESPEICHERTENDERUNGEN![]ERWERFEN...[]PEICHERN...*[]KTIONABBRECHEN...ADE...PEICHERE...0ʆʆ  / 0 .ͩo3 '/(5 *d)? * }ATEI EARBEITENLOCK ORMAT CHALTER ILFE }:00 ESCAPE <=>@֭,֩ > = ? G   Lw}mAoU` r B `L) . /80Ȍ)8ȱPG,֍ǩ Ɖ0P,ֈL*`n**Z*\*ei* .,ֈ` $0<  2* 2* 2*ۅW *`.+0+** (+ȱ,`.+0++#+ (+ȱG,` .`HH>7+ /# |,# #i0+戥.++ + 7+i */ u)`I` D,7+ ,L+@ɝBDtCɔE ©7+ ". /hh7+` ,L+ ,L+ , -,L+ D,L+7+L,7+7+  7+`7+ L0, ` 7+ LF, `..Lj,..֠,L.#H>#  2* [,.... !/ !/# !/--i#-Ȍ.$- - - /.Ȍ /.֩ !/# . !/֩ !/Peܮ#--##$$ .ʩh !/f !/j !/Pe .a !/ G !/L- !/L-a !/8eкPe .ʩb !/f !/d !/h`P\ht뗒# ֥ !/Ȍ֥ !/֩ !/Pe`H> d,.. /. /. /--i#Ȍ. / !/ /! !/ . .  2*#h`#֥ !/Ȍ֥ !/֩ !/Pe`Ѝ.. /### # /`,Ȍ,֠`,`,`\/Ȍ;/ / & & & & &eiei/ 0,օ 0,֩`/ 0,֩`//// /?`/ʽ///Lg//L/!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!L.acme-crossassembler-0.96.4/examples/me/000077500000000000000000000000001333777125400177775ustar00rootroot00000000000000acme-crossassembler-0.96.4/examples/me/const.a000066400000000000000000000042721333777125400212740ustar00rootroot00000000000000;ACME 0.95.1 ; Konstanten: FALSE = 0 ; Das Programm verläßt sich an etlichen Stellen TRUE = $ff ; darauf, daß genau diese Werte zugewiesen wurden. MODIFIED8 = $ff ; Defaultwerte für !addr MODIFIED16 = $ffff ; Selbstmodifikationen Char_NUL = $00 Char_STOP = $03 Char_RETURN = $0d Char_CursorDown = $11 Char_HOME = $13 Char_DEL = $14 Char_ESCAPE = $1b Char_CursorRight = $1d Char_At = $40 CharOwn_Delete = $74 Char_ShiftRETURN = $8d Char_CursorUp = $91 ; Diese Werte waren früher als Strings angegeben. Char_CLEAR = $93 Char_INST = $94 Char_Grey2 = $98 Char_BlueL = $9a Char_Grey3 = $9b Char_CursorLeft = $9d _ = 1 ; Dieser Code steht für das unsichtbare Space in den Windows. ä = $bb ; Werte um Umlaute verwenden zu können. ö = $bc ü = $bd ß = $be Ä = $db Ö = $dc Ü = $dd chrol = 104 ; Fensterrahmen chroo = 102 chror = 106 chrll = 97 chrmm = 32 chrrr = 97 chrul = 98 chruu = 102 chrur = 100 lf = 8 ; Filenr. & Sek.-Addy ; Zeropage: !addr { D8502 = $00 ; Direction R8502 = $01 ; Register vvek = $83 ; Vektor auf LineVektor lvek = $85 ; LineVektor tmp1 = $87 tmp2 = $89 vtemp = $8d ; crsr-address (3) ; zeropage (**mark**) status = $90 ; System variable ST fnlen = $b7 ; Dateiparameter fnbank = $c7 ; Bank of file name ndx = $d0 ; Tasten- & kyndx = $d1 ; F- Buffer keyidx = $d2 ; F-Zeichenzähler mode = $d7 ; Bit 7 = Cursorscreen (40/80) color = $f1 ; current attribute locks = $f7 ; Verhindert CBM-Shift beep = $f9 ; Tastenklick lftb = $fa ; Maustasten rgtb = $fb line = $fc ; Zähler col = $fd zahl = $fe ; fürs Wrap ; System: nmivek = $0318 ; NMI keybuffer = $034a pkydef = $100a ; Strings der F-Tasten texttop = $1210 ; Basic-Ende+1 maxmem0 = $1212 ; Ende Bank 0 basic = $12fd ; Basic-IRQ e_copyfont = $c027 ; Systemroutine, kopiert Font in VDC-RAM e_cls = $c142 ; Systemroutine, löscht Screen e_switchmode = $cd2e ; Systemroutine, switcht aktiven Monitor takt = $d030 ; 2 MHz ; register (**mark**) vdc = $d600 ; VDC reg = $d601 conreg = $ff00 ; MMU-CR nmiend = $ff33 ; NMI-Ende primm = $ff7d ; Kernal open = $ffc0 close = $ffc3 chkin = $ffc6 chkout = $ffc9 clrchn = $ffcc basin = $ffcf basout = $ffd2 } acme-crossassembler-0.96.4/examples/me/core.a000066400000000000000000000376251333777125400211060ustar00rootroot00000000000000;ACME 0.94.4 !zone ; Programm: mainloop ; Cursor setzen: lda posy ; screeny = posy-spry sec sbc scry tay ; y in Y ; ab hier X lda posx ; screenx = posx-scrx sec sbc scrx jsr crsrset ; set crsr ; hier eigentliche Hauptroutine lda nwfrm ; new frame ? beq + jsr newframe ; yes = > + lda updatewbi ; update flags? beq + jsr showwbi ; yes = > + jsr getchar ; get CHARACTER tax ; & buffer and #%.##..... ; command ? beq + ; yes = > eor #%.##..... ; command ? beq + ; yes = > jsr chrout ; char out jmp mainloop + jsr execom ; execute command jmp mainloop !zone ; Pseudo-Sub: (ESC uses jmp) F_esc clc ; 'ESC' on! lda clraktv ldx #hFlag_Escape jsr setflagdata - jsr getkey ; get KEY beq - sta byte ; & buffer clc ; 'ESC' off! lda clrinak ldx #hFlag_Escape jsr setflagdata ldx byte ; get byte txa ; & buffer eor #%.#...... ; a-z ? and #%.##..... bne + ; no = > txa ; get byte and #%...##### ; & short asl ; *2 & tax ; as index lda etab + 1, x ; get Hi beq .no ; 0 = > sta .m + 1 ; set lda etab, x ; get Lo sta .m ; set .m = * + 1: jmp MODIFIED16 ; execute sequence .no rts ; nothing... + txa ; get byte ( = FKey?) bpl .no ; out = > eor #%..#..... ; convert and #%.##..... ; test beq .no ; out = > txa ; get byte and #%...##### ; convert cmp #$05 ; test bottom border bcc .no ; too low = > cmp #$0d ; test upper border bcs .no ; too high = > ; here: define f-keys ! rts !zone ; NMI nmirtn lda #0 ; clear keybuffers sta ndx sta kyndx jmp nmiend !zone ; Subs: execom txa ; get & convert Byte bpl + ; (therefore strange eor #%#.#..... ; vectorlist) + asl tax lda ctab + 1, x ; get Hi beq noroutine ; 0 = > sta .m + 1 ; and set lda ctab, x ; get Lo sta .m ; and set .m = * + 1: jmp MODIFIED16 ; use command noroutine rts ; not defined (fixme - could save a byte here) !zone F_new jsr willblost beq noroutine jsr newtext jsr needline ldx #$0f ; use counter as "TRUE" stx nwfrm stx updatewbi stx unnamed - lda newname, x sta txtname, x dex bpl - inx stx changes rts !zone newtext ldx #1 ; '1' stx scrx ; as X of screen, stx anfx ; blockstart, -end & stx endx ; crsr. stx posx stx scry ; ...as Y-Lo stx anfy stx endy stx posy dex ; '0' stx scry + 1 ; ...as Y-Hi stx anfy + 1 stx endy + 1 stx posy + 1 stx zzbe ; no lines stx zzbe + 1 ; used rts !zone ; 'key' ist der Kern, holt einen Code ; ausm Puffer. 'char' wuerde, wenns ein ; F-Key oder Accent ist, den Puffer ; aendern und dann das erste Byte ; abliefern. Hier nur das Standardprog: getchar jmp getkey .defrag ;{check fragjob} getkey ;{check mousejob} ;{check clockjob} ldx kyndx ; F-keys as standard beq .std ldy keyidx lda pkydef,y dec kyndx inc keyidx rts .std ldx ndx ; chars in buffer ? beq .defrag ; 0 = > sei ; else ldy keybuffer ; get first byte ldx #255 - 8 ; loop to shift other 9 chars down - lda keybuffer - 255 + 9, x sta keybuffer - 255 + 8, x inx ; (f7 to ff) bne - dec ndx ; dec number tya ; byte = >A stx keybuffer + 9 ; clear lastbyte cli rts !zone getvvek lda scry, x ; get y-Lo asl ; *2 tay ; buffer lda scry + 1, x ; get y-Hi rol ; *2 ( = clc) sta vvek + 1 ; in Hi tya ; get Lo adc memin ; + BaseLo sta vvek ; = VectorLo lda vvek + 1 ; get Hi adc memin + 1 ; + BaseHi sta vvek + 1 ; = VectorHi rts ; (VekVek) ; stellt Vektor auf Cursor-Y poslvek ldx #POS ; stellt Vektor auf Zeile !zone getlvek jsr getvvek ; get VekVek ldy #0 ; Y-Init lda (vvek), y ; get Lo-Byte sta lvek ; store iny ; inc vector lda (vvek), y ; get Hi-Byte sta lvek + 1 ; store rts !zone windowproof lda posx ;crsr-X cmp scrx ; screen(home)-X bcs + ; bigger = > sta scrx ; else set screen-X sta nwfrm ; and NewFrame bcc .UpDown + sbc scrx ; difference cmp #scrcols ; cmp screenwidth bcc .UpDown ; ok = > lda posx ; else NewFrame, sta nwfrm sbc #scrcols - 1 ; set screen-X sta scrx ; & store .UpDown lda scry + 1 ; HiByte screen- cmp posy + 1 ; Y and crsr-Y bcc crsrweiter ; shorter = > bne .set ; equal = > lda posy ; else cmp Lo-bytes cmp scry bcs crsrweiter ; shorter = > .set ldx posy ; get crsrpos as lda posy + 1 ; new screenstart stx scry sta scry + 1 lda #TRUE ; NewFrame sta nwfrm rts !zone crsrweiter sec ; for sbc lda posy ; calculate sbc scry ; Lo-difference tax ; in X lda posy + 1 ; calculate sbc scry + 1 ; Hi-difference bne + ; if Hi = 0 cpx #scrlins ; & Lo ok, bcc ++ ; ready = > + lda posy + 1 ; else: copy Hibyte sta scry + 1 sec ; for sbc lda posy ; calculate & save sbc #scrlins - 1 ; new Hibyte sta scry bcs + ; ggfs. = > dec scry + 1 ; correct Hibyte + lda #TRUE ; NewFrame sta nwfrm ++ rts ; Scrollroutines missing ! !zone ; fuellt Speicher mit Zeilen fillmem lda #0 ; Keine Zeilen da sta zzan sta zzan + 1 ldx llen ; Zeilenlaenge inx ; + Info-Byte stx .m1 ; in SBC #$dummy lda maxmem0 ; holt MAX-MEM-0 sta txts ; und nimmt es als lda maxmem0 + 1 ; Obergrenze ! sta txts + 1 lda mod_id ; Holt ID-Adresse (Lo) tax ; sichern lsr ; Bit 0 ins Carry txa ; zurueck adc #6 ; +ID-2+C sta memin ; wird Vektorstart (Lo) lda mod_id + 1 ; Hi-Byte adc #0 ; entsprechend sta memin + 1 ; anpassen (Auto-CLC) ; Carry wird addiert, damit Vektoren bei ; einer GERADEN Adresse starten! lda memin ; Die VekVeks adc #2 ; werden ab dem sta vvek ; Vektorstart+2 lda memin + 1 ; abgelegt, da es adc #0 ; keine nullte Zeile sta vvek + 1 ; gibt .Check lda txts ; TextstartLo sec .m1 = * + 1: sbc #MODIFIED8 ; -Zeilenlänge sta tmp1 ; wird gepuffert ldx txts + 1 bcs + dex + stx tmp1 + 1 cpx vvek + 1 ; Vektorkollision ? bcc .NoLine ; Ja = > keine Zeile ! bne .MakeLn ; Nein = > neue Zeile ! ldx vvek ; Gleich: Lo-Bytes inx ; vergleichen cpx tmp1 ; Wieder: Kollision ? bcs .NoLine ; Ja = > keine Zeile ! .MakeLn lda tmp1 ; Nein: dann temp als sta txts ; Textstart und in den ldy #0 ; Linevektor sta (vvek), y lda tmp1 + 1 ; dito, Highbyte sta txts + 1 iny sta (vvek), y inc vvek ; VekVek 2 Byte weiter +inc16 vvek inc zzan ; angelegte Zeilen bne .Check ; eins hoeher inc zzan + 1 jmp .Check .NoLine rts !zone clearline lda #" " ; Space ldy llen ; Y auf Zeilenende - sta (lvek), y ; Space setzen dey ; zurueck bne - ; Infobyte ? tya ; Dann auf sta (lvek), y ; Null setzen dey ; Y auf $ff fuer sty nwfrm ; NewFrame sty changes ; Veraendert ! ; WordWrap sinnlos ! rts !zone ; stellt Zeilen zur Verfuegung oder gibt Fehlermeldung needline +cmp16bit ZZA, ZZB ; vergleichen beq + ; Wenn gleich, wirds gesetzte Carry 'failure' +inc16 zzbe ; sonst: Zahl der genutzten Zeilen hoeher ldx #ZZB stx changes ; Veraendert ! jsr getlvek ; Holt Vektor jsr clearline ; und leert Zeile clc ; 'success' ; EIGENTLICH ist das Carrybit hier schon ; durch die beiden Subs gelöscht... + rts cmp16bit lda scry + 1, x ; Hi-Bytes vergleichen cmp scry + 1, y bne + ; wenn gleich, lda scry, x ; Lo-Bytes vergleichen cmp scry, y + rts F_gcr inc posx jmp proofpos F_gcl dec posx jmp proofpos F_gcu ldx posy bne + dec posy + 1 + dec posy jmp proofpos F_gcd +inc16 posy !zone proofpos ldx posx ; CRSR-X beq .jBack ; Null = > dex ; verringern und mit cpx llen ; Laenge vergl. bcs jump ; zu weit rechts = > lda posy + 1 ; CRSR-Y (Hi) bmi firstline ; >32K = > 1. Zeile = > ora posy ; ODERt Low-Byte beq firstline ; = 0 = > 1. Zeile = > +cmp16bit ZZB, POS ; vergleichen bcc F_geot ; CRSR zu weit = > jmp windowproof ; okay .jBack ldx llen ; Zeilenlaenge wird stx posx ; neue Position & hoch jsr F_gcu jsr poslvek ; LineVek holen jsr findend ; Ende suchen iny ; dahintersetzen sty posx jmp proofpos jump jsr newline ; naechste Zeile bcs + ; CRSR zu weit, jsr needline ; Zeile anfordern bcc + ; Bei Fehlschlag jsr memfull ; Warnung zeigen + jmp proofpos !zone firstline ldx #1 ; CRSR in erste Zeile stx posy dex stx posy + 1 jmp windowproof F_geot +cp16 zzbe, posy; CRSR in letzte Zeile jmp windowproof !zone newline lda #1 ; X-Pos : = 1 & Y += 1 sta posx +inc16 posy +cmp16bit ZZB, POS ; vergleichen rts !zone F_cs lda #1 ; CRSR 2 next linestart sta posx +inc16 posy jmp proofpos !zone chrout stx byte ; sichert Zeichen jsr poslvek ; LineVek ldy esca ; Autoinsert ? beq + ; ggfs. kein jsr insert1 ; insert + ldy posx ; Versatz lda byte ; Akku holen sta (lvek), y ; und setzen jsr poswrap ; Wrap ? lda #TRUE ; NewFrame fordern sta nwfrm sta changes ; Veraendert ! jmp F_gcr !zone F_insert jsr poslvek ; LineVek jsr insert1 ; insert jsr poswrap ; Wrap ? rts ; fixme - could save a byte here !zone insert1 ldy scrx, x ; X-Wert holen & in sty .mod ; Immediate ldy lvek + 1 ; Quell-Vektor = ldx lvek ; aktueller Vektor-1 bne + dey + dex stx tmp1 sty tmp1 + 1 ldy llen ; Shiftstart LineEnd - .mod = * + 1: cpy #MODIFIED8 ; X beq + ; Ende = > lda (tmp1), y ; Zeichen holen sta (lvek), y ; und shiften dey ; neue Pos jmp - + lda #" " ; 'Space' an sta (lvek), y ; Pos setzen sta nwfrm ; NewFrame fordern sta changes ; Veraendert ! rts !zone F_dcl jsr F_gcl F_dcr jsr poslvek ; LineVek jsr delchr1 ; Delete jsr poswrap ; Wrap ? jmp proofpos !zone delchr1 ldy scrx, x ; X-Wert in Immediate sty .m ldx lvek ; Zielvektor = aktueller ldy lvek + 1 ; Vektor+1 inx bne + iny + stx tmp1 sty tmp1 + 1 .m = * + 1: ldy #MODIFIED8 ; X - cpy llen ; Zeilenende ? beq + ; Dann = > lda (tmp1), y ; Zeichen holen sta (lvek), y ; und shiften iny ; neue Pos jmp - + lda #" " ; Space an letzte sta (lvek), y ; Pos setzen lda #TRUE ; NewFrame fordern sta nwfrm sta changes ; Veraendert ! rts !zone ; Einsprung: X = StartIndex, Y = EndIndex ; Bewegt Zeilenbloecke: X bis Y-1 werden nach X+1 bis Y geschoben. Danach ; liegt die Endzeile in der Startzeile rollfwd jsr howmany ; Zeilenanzahl ? beq ++ ; ggfs Abbruch ! tya ; Y in X tax jsr getlvek ; lvek in lvek puffern sec ; Quellvektor = Vektor-2 lda vvek sbc #2 sta tmp1 lda vvek + 1 sbc #0 sta tmp1 + 1 ldy #2 ; Versatz 2 - tya ; Y pruefen bne + ; ggfs dec tmp1 + 1 ; Page sichern dec vvek + 1 + dey ; Versatz runter lda (tmp1), y ; High-Byte oben setzen sta (vvek), y dey ; Versatz runter lda (tmp1), y ; Low-Byte oben setzen sta (vvek), y inc tmpy ; Anzahl der Shifts bne - ; pruefen, ggfs loop inc tmpy + 1 bne - lda lvek ; alten Vektor holen sta (tmp1), y ; und in letzte iny ; Position setzen lda lvek + 1 sta (tmp1), y ++ lda #TRUE ; NewFrame fordern sta nwfrm sta changes ; Veraendert ! rts !zone ; Einsprung: X = StartIndex, Y = Endzeile ; Bewegt Zeilenbloecke: X+1 bis Y werden nach X bis Y-1 geschoben. Danach ; liegt die Startzeile in der Endzeile ! rollrwd jsr howmany ; Zeilenanzahl ? beq ++ ; ggfs Abbruch ! jsr getlvek ; lvek in lvek puffern clc ; Quellvektor = Vektor+2 lda vvek adc #2 sta tmp1 lda vvek + 1 adc #0 sta tmp1 + 1 ldy #0 ; Versatz 0 - lda (tmp1), y ; Hi-Byte unten setzen sta (vvek), y iny ; weiter lda (tmp1), y ; Lo-Byte unten setzen sta (vvek), y iny ; weiter bne + ; Page sichern inc tmp1 + 1 inc vvek + 1 + inc tmpy ; Anzahl Shifts bne - ; pruefen, ggfs loop inc tmpy + 1 bne - lda lvek ; alten Vektor an die sta (vvek), y ; letzte Pos setzen iny lda lvek + 1 sta (vvek), y ++ lda #TRUE ; NewFrame fordern sta nwfrm sta changes ; Veraendert ! rts !zone howmany jsr cmp16bit ; Sicherheit bcs + ; ggfs Abbruch = > sec ; Negativ, um INC statt DEC nutzen zu können lda scry, x sbc scry, y sta tmpy lda scry + 1, x sbc scry + 1, y sta tmpy + 1 rts + lda #0 ; Zeilen rts !zone movx2y lda scrx, x ; Copy X-indexed Werte in Y-indexed Variablen sta scrx, y lda scry, x sta scry, y lda scry + 1, x sta scry + 1, y rts ESC_at rts ; fixme - could save one byte here ESC_a lda #TRUE ; Set AutoInsert sta esca sta updatewbi ; Update fordern rts ESC_b ldx #POS ; BlockEnd: = Cursorposition ldy #END jsr movx2y !zone ; Block legal ? Vertauscht ggfs Zeiger nblck +cmp16bit ANF, END ; Blockstart und -Ende vergleichen bcc ++ ; anfend: not ok lda scrx, y ; Bei Gleichheit noch cmp scrx, x ; X pruefen bcs ++ ; end> = anf: ok + ldy #TMP ; (Anf) in Temp jsr movx2y ldx #END ; Ende in Anf ldy #ANF jsr movx2y ldx #TMP ; Temp in Ende ldy #END jsr movx2y ++ lda #TRUE ; NewFrame fordern sta nwfrm ; (Blockanzeige) sta blockflag ; Block ein rts !zone ESC_c ldx #FALSE ; Clear AutoInsert stx esca dex ; Update fordern stx updatewbi rts ESC_d ldx #POS ; Start: Cursorposition jsr delline ; Zeile weg jmp poswrap ; und wrap !zone delline ldy #ZZB ; Ende: LastLine jsr rollrwd ; runterrollen lda zzbe ; Anzahl der benutzten Zeilen runter bne + dec zzbe + 1 + dec zzbe bne + ; Low = 0 ? lda zzbe + 1 ; Dann High pruefen und ggfs Zeile fordern bne + jsr needline + jmp proofpos !zone ESC_g ldx #FALSE ; Beep On stx beep dex ; Update fordern stx updatewbi rts ESC_h lda #TRUE ; Beep Off sta beep sta updatewbi ; Update fordern rts !zone ESC_i jsr needline ; Zeile fordern +bcs memfull ; bei Fehlschlag Warnung = > ldx #POS ; Start: Cursorposition ldy #ZZB ; Ende: LastLine jsr rollfwd ; raufrollen rts ; fixme - could save a byte here !zone F_gsol ESC_j lda #1 ; Cursor-X: = 1 sta posx jmp windowproof F_geol ESC_k jsr poslvek ; LineVek jsr findend ; sucht letztes Byte, dahinter steht dann Cursor iny sty posx jmp proofpos ESC_o lda blockflag ; toggle Flag eor #$ff sta blockflag rts ESC_p ESC_q rts ; fixme - could save a byte here ESC_t ldx #POS ; Blockstart = Cursorposition ldy #ANF jsr movx2y jmp nblck ; legal ? F_home ldx scrx ; Normal HOME only, ldy scry ; if CRSR not there lda scry + 1 cpx posx ; Otherwise ScreenUp bne scrnhome cpy posy bne scrnhome cmp posy + 1 bne scrnhome !zone F_scrnu lda posy ; Displaystart = sec ; Displaystart sbc #scrlins ; - Zeilenzahl sta posy bcs + dec posy + 1 + lda #TRUE ; NewFrame fordern sta nwfrm jmp proofpos !zone scrnhome stx posx ; Cursor: = Display sty posy sta posy + 1 jmp proofpos F_ahome clc ; errechnet Werte lda scry ; fuer antih1 adc #scrlins - 1 ; in A, X, Y tax lda scry + 1 adc #0 tay lda scrx ; CRSR dort ? cmp posx bne antih1 cpx posy ; Nein = > dorthin bne antih1 cpy posy + 1 ; Ja = > ScreenDown ! bne antih1 !zone F_scrnd lda posy ; One screen down clc adc #scrlins sta posy bcc + inc posy + 1 + lda #TRUE ; NewFrame fordern sta nwfrm jmp proofpos !zone antih1 sta posx ; Cursor: = DisplayEnd stx posy sty posy + 1 jmp proofpos F_gsot ldx #1 ; X = Y = 1 stx posx stx posy stx nwfrm ; NewFrame fordern dex ; Y-Hi: = 0 stx posy + 1 jmp proofpos !zone handleid ldx #7 ; 8 Byte Kennung - mod_id = * + 1: lda MODIFIED16, x; Schleife, um evtl. vorhandenen Text zu cmp idtext, x bne makeid ; erkennen & zu reaktivieren dex bpl - clc ; 'OldText' rts makeid lda texttop ; Neue ID wird ans Basic-Ende gesetzt sta mod_id sta .m1 lda texttop + 1 sta mod_id + 1 sta .m1 + 1 ldx #7 - lda idtext, x .m1 = * + 1: sta MODIFIED16, x dex bpl - sec ; 'NewText' rts F_cr jsr F_lfeed jmp jump F_c :F_f :F_ffeed :F_dir F_fbox :F_hlp :F_bell :F_tab F_text :F_middle :F_graphic F_fn :F_ff :F_un :F_uf :F_rn :F_rf F_sf :F_sk :F_su :F_st :F_sl F_gld :F_glu :F_gad :F_gau :F_gpd :F_gpu F_gtr :F_gtl :F_gwr :F_gwl F_bttnn :F_bttnf :F_find :F_print :F_mode :F_dword F_cut :F_copy :F_paste :F_move F_fmtl :F_fmtr :F_fmtm :F_fmtb rts ; (yet) missing acme-crossassembler-0.96.4/examples/me/cursor.a000066400000000000000000000037401333777125400214620ustar00rootroot00000000000000;ACME 0.94.4 ; ab hier liegt die Cursorsteuerung ; A = screenx, Y = screeny !zone crsrset sta .m ; buffer x iny ; adjust height iny iny sty .n ; buffer y jsr crsroff lda #0 ; clear Hi sta vtemp + 1 .n = * + 1: lda #MODIFIED8 ; y asl ; *2 asl ; *4 rol vtemp + 1 asl ; *8 rol vtemp + 1 asl ; *16 rol vtemp + 1 sta vtemp ; stash Lo ldy vtemp + 1 ; copy Hi sty vtemp + 2 asl ; *32 rol vtemp + 2 asl ; *64 rol vtemp + 2 adc vtemp ; + 16er-Lo sta vtemp ; 80er-Lo in vtemp bcc + ; page inc vtemp + 1 clc + .m = * + 1: adc #MODIFIED8 ; x sta vtemp ; store Lo lda vtemp + 1 ; get 16er-Hi adc vtemp + 2 ; add 64er-Hi adc #attrhi ; add base sta vtemp + 1 ; store Hi !zone crsron lda conreg ; buffert CR sta .m +bank15 jsr vpntcrsr ; set address +wait_for_vdc lda reg ; get attribute sta tcolor ; buffer it jsr vpntcrsr ; set address lda clrcrsr ; get crsr +wait_for_vdc sta reg ; set crsr .m = * + 1: lda #MODIFIED8 ; bank sta conreg ; restore CR rts !zone crsroff lda conreg ; buffer CR sta .m +bank15 jsr vpntcrsr ; set address lda tcolor ; get attribute +wait_for_vdc sta reg ; set attribute .m = * + 1: lda #MODIFIED8 ; bank sta conreg ; restore CR rts ; push data !zone crsrnew ldx crsrheap ; get stackpointer lda vtemp ; get low sta crsrheap, x ; push lda vtemp + 1 ; get high sta crsrheap + 1, x ; push inx ; inc stackpointer inx stx crsrheap ; set stackpointer jsr crsroff !zone crsrhide ldx #$3f ; place cursor stx vtemp + 1 ; outside visible ldx #$ff ; area stx vtemp rts !zone crsrold ldx crsrheap ; get stackpointer dex ; previous entry ! dex lda crsrheap, x ; get lo sta vtemp ; set lo lda crsrheap + 1, x ; get hi sta vtemp + 1 ; set hi stx crsrheap ; set stackpointer jmp crsron !zone crsrinit ldx #1 ; init cursorstack stx crsrheap jmp crsrhide ; and hide cursor crsrheap !fill 33, 33 vpntcrsr +ldax vtemp jmp ramaccess ; set vdc acme-crossassembler-0.96.4/examples/me/file.a000066400000000000000000000122371333777125400210650ustar00rootroot00000000000000;ACME 0.94.4 ; ChangesNotSaved.Save? !zone willblost ldx changes bne + inx rts ; return with X=1 ("Changes safe, go on") + jsr crsrnew ldx #hWindow_DCS stx menunr jsr makewin ldy #$0b ; y-pos of cursor in window lda #$32 ; x-pos jsr crsrset wblchoice jsr getchar cmp #Char_DEL beq wblchoiced cmp #Char_STOP beq wblchoicec cmp #Char_RETURN bne wblchoice jsr pullscr jsr crsrold jsr F_saveas jmp willblost wblchoiced jsr pullscr jsr crsrold ldx #FALSE stx changes ldx #2 rts ; return with X=2 ("Changes discarded, go on") wblchoicec jsr pullscr jsr crsrold ldx #0 rts ; return with X=1 ("Cancel operation !") eotflag !byte 0 ; End-Flag !zone F_mergeas lda #$1f ; get Mergename sta loadflag ; Mode MERGE jmp + noload rts ; fixme - could save a byte here F_loadas jsr willblost ; Changes saved ? beq noload lda #0 ; Mode LOAD sta loadflag lda #$3f ; get LOADname + jsr rename bne load ; ggfs Abbruch rts !zone loadalien lda loadflag bne loadfirst jmp noheader load lda conreg ; Bank sichern pha jsr crsrnew ; new copy (hidden) ldx #hWindow_Load stx menunr jsr makewin jsr copypara ; Parameter setzen lda #"r" ; Lesemodus sta dosmode +bank15 jsr open ; Open File ldx #lf ; File: = Input jsr chkin ldy #$0f ; Header pruefen - jsr basin cmp idfile, y bne loadalien dey bpl - ldy #$0f ; Namen holen - jsr basin sta dosname, y dey bpl - lda loadflag ; Bei LOAD bne loadfirst ; Name kopieren, sta unnamed ; (clear Flag) ldy #$0f - lda dosname, y sta txtname, y sta lodname, y dey bpl - sty updatewbi ; Update verlangen, jsr newtext ; Defaultwerte loadfirst ldy #FALSE ; Pufferstart sty eotflag ; init Flag !zone loadline +xbank15 - iny ; Eins weiter lda #" " ; get Space ldx status bne + ; ggfs jsr basin ; get Byte + sta linebuf, y ; und setzen cpy llen bne - ldy #1 ; Neustart - lda linebuf, y cmp #Char_RETURN beq ++ cmp #"-" bne + sty linebuf ; Dann Pos merken + cmp #" " bne + sty linebuf ; Dann Pos merken + iny ; weiter cpy llen bne - lda linebuf, y ; LineEnd = Space ? cmp #" " ; Dann Grenze: = Y & bne + sty linebuf lda status beq + ; ggfs setflag sta eotflag + ldy linebuf ; get Grenze bne + ldy llen dey ++ sty linebuf + +xram0 jsr needline ; fordert Zeile bcs nomemleft ; ggfs Abbruch ldy linebuf ; copy buffer2line - lda linebuf, y sta (lvek), y dey bne - lda eotflag ; Ende ? bne endoffile ldx linebuf ; shift buffer - cpx llen ; fertig ? beq loadline ; Dann lesen ! inx iny lda linebuf, x sta linebuf, y jmp - nomemleft jsr memfull ; Warnung endoffile +bank15 lda loadflag sta changes noheader jsr clrchn ; Standard lda #lf ; Close File jsr close jsr pullscr ; Win weg jsr crsrold ; restore cursor pla ; alte Bank sta conreg rts !zone nosave rts ; Abbruch (fixme - could save a byte here) F_saveas jsr F_rnmtxt ; get Textname beq nosave ; ggfs Abbruch lda #FALSE ; Name vorhanden sta unnamed F_save lda unnamed ; Name ? bne F_saveas ; ggfs holen ldy #$0f ; proof "?" - lda txtname, y cmp #"?" beq F_saveas cmp #"*" beq F_saveas cmp #"," beq F_saveas cmp #":" beq F_saveas sta dosname, y dey bpl - lda #"w" ; Schreibmodus sta dosmode lda conreg ; Bank sichern pha +bank15 jsr crsrnew ; new copy (hidden) ldx #hWindow_Save ; Save-Win stx menunr jsr makewin jsr copykill ; Killparameter jsr open ; Open CmdChannel lda killpara + 1 ; (Scratch) jsr close ; Close CC jsr copypara ; Dateiparameter jsr open ; Open Outputfile ldx #lf jsr chkout ldy #$0f ; Sendet Header - lda idfile, y jsr basout dey bpl - ldy #$0f ; Sendet Name - lda txtname, y jsr basout dey bpl - iny ; Y: = 0, tmpy wird fuers sty tmpy + 1 ; Speichern init. tya ; A: = 0 iny ; Y: = 1 sty tmpy sec ; errechnet negativen sbc zzbe ; Zeilenzaehler (tmp2) sta tmp2 lda #0 sbc zzbe + 1 sta tmp2 + 1 -- +xram0 ; volles RAM ldx #1 ; mind. 1 Byte/Zeile stx linebuf ldx #TMP jsr getlvek ; LineVek ldy #1 ; Versatz: = 1 - lda (lvek), y ; Byte in Puffer cmp #" " beq + sty linebuf ; Pos sichern + sta linebuf, y iny cpy llen bne - ldx linebuf lda linebuf, x ; letztes Byte cmp #Char_RETURN beq + cmp #"-" beq + cmp #" " beq + inx ; Dann Space hinter lda #" " ; die Zeile sta linebuf, x + stx .m ; Ende speichern +xbank15 - inx ; X = 1 lda linebuf, x ; Zeichen senden jsr basout .m = * + 1: cpx #MODIFIED8 ; Länge bne - ; alle ? +inc16 tmpy ; tmpy += 1 inc tmp2 ; zaehler += 1 bne -- inc tmp2 + 1 bne -- jsr clrchn ; Standardkanaele lda #lf jsr close ; Close File jsr pullscr ; Win weg jsr crsrold ; restore cursor pla ; alte Bank sta conreg lda #FALSE ; Changes saved ! sta changes rts !zone copykill ldy #$0b ; Scratchparameter +bit16 ; BIT-Trick ! copypara ldy #$05 ; Fileparameter ldx #5 ; 6 Bytes - lda filepara, y ; ins System sta fnlen, x dey dex bpl - rts acme-crossassembler-0.96.4/examples/me/macros.a000066400000000000000000000013661333777125400214330ustar00rootroot00000000000000;ACME 0.94.4 !macro cmp16bit .data1, .data2 { ldx #.data1 ldy #.data2 jsr cmp16bit } a = 0 x = 1 y = 2 !macro bank .r, .v { !if .r = a { lda #.v sta conreg } !if .r = x { ldx #.v stx conreg } !if .r = y { ldy #.v sty conreg } } CR_BANK15 = 0 CR_RAM0_IO = $3e CR_RAM0 = $3f !macro bank15 { +bank a, CR_BANK15 } !macro xbank15 { +bank x, CR_BANK15 } !macro ybank15 { +bank y, CR_BANK15 } !macro ram0io { +bank a, CR_RAM0_IO } !macro yram0io { +bank y, CR_RAM0_IO } !macro xram0 { +bank x, CR_RAM0 } !macro inc16x .a { inc .a, x bne + inc .a + 1, x + } !macro ldax .a { lda .a + 1 ldx .a } !macro cp16 .s, .t { ldx .s lda .s + 1 stx .t sta .t + 1 } !macro wait_for_vdc { - bit vdc bpl - } acme-crossassembler-0.96.4/examples/me/out.a000066400000000000000000000730131333777125400207540ustar00rootroot00000000000000;ACME 0.95 !zone F_info ldx #hWindow_InfoBox jmp xwindow memfull ldx #hWindow_NoMemLeft xwindow lda conreg ; Bank sichern pha +yram0io stx menunr jsr crsroff ; Cursor aus jsr makewin ; Win bauen jsr getchar ; Auf Taste warten jsr pullscr ; Win weg jsr crsron ; Cursor an pla ; alte Bank sta conreg rts F_sw lda wrapflag ; Toggle Flag eor #$ff sta wrapflag lda #TRUE ; Update fordern sta updatewbi rts F_lfeed jsr poslvek ; LineVek jsr insert1 ; immer insert lda #Char_RETURN ; 'CR' an Cursorpos sta (lvek), y sta nwfrm ; NewFrame fordern sta changes ; Veraendert ! jsr posparw ; PARW ? ldy posx ; Cursor am Rand ? cpy llen beq poswrap ; Nein: Wrap lda posy ; Zeile in wrap sta wrpy lda posy + 1 sta wrpy + 1 jsr found ; und runterwrap rts ; splittet VOLLE Wrap-Zeilen linefull ldy llen ; Zeilenlaenge-1 dey jmp found ; wrap cancelwrp jmp memfull ; Zeilen alle ;multiwrap: ;jsr getlvek ; LineVek ;!by 44 ; Wrap (bit-Trick) !zone poswrap ldx #POS stx xindex ; sichern lda scry, x ; Zahl umkopieren sta wrpy lda scry + 1, x sta wrpy + 1 jsr posparw ; PARW ? bcs proofparw ; Dann kein WRAP ldy llen ; LineEnd = Space ? lda (lvek), y cmp #" " beq proofparw ; Ja = > PARW - dey ; sonst zurueck bis zu beq linefull ; Space, "-", CR oder lda (lvek), y ; LineStart (bekommt cmp #" " ; Sonderbehandlung) beq found cmp #"-" beq found cmp #Char_RETURN bne - !zone found sty wrpx ; puffert Y und addiert tya ; es zum Vektor, damit clc ; gut geshiftet wird adc lvek sta tmp2 lda lvek + 1 adc #0 sta tmp2 + 1 lda llen ; errechnet Anzahl zu sec ; shiftender Bytes sbc wrpx ; als 'zahl' sta zahl jsr needline ; fordert Zeile bcs cancelwrp ; ggfs Abbruch jsr incwrp ; naechste Zeile holen ldx #WRP ; neue Zeile in die ldy #ZZB ; naechste jsr rollfwd ldy zahl ; Anzahl Bytes ldx #" " ; Space - lda (tmp2), y ; holt Byte und setzt sta (lvek), y txa ; loescht Quelle sta (tmp2), y dey bne - ldx xindex ; errechnet neue lda scrx, x ; Variablenwerte sec ; Sind nur relevant, sbc wrpx ; wenn Location beq proofparw ; mitgewrapped wurde. bmi proofparw sta scrx, x +inc16x scry !zone proofparw ldx wrapflag ; Wrap erlaubt ? beq + ; Nein = > Ende .pl jsr incwrp ; naechste Zeile ldx #WRP jsr getlvek ; LineVek jsr parw ; PARWen bcs .pl ; ggfs Schleife + ldx xindex ; Restore LineVek jsr getlvek rts !zone incwrp +inc16 wrpy ; Y hoeher rts !zone posparw clc ldx #POS ; Cursor-Index lda wrapflag ; Wrap erlaubt ? beq + ; ggfs Ende jsr parw ; PARWed bcc + ; ggfs Variablen jsr moveloc ; angleichen + php ; C sichern ldx xindex ; Restore LineVek jsr getlvek plp ; C zurueck rts !zone .no clc ; C = 0 < = > nix PARWed rts parw stx xindex ; Sichert Index ldx #ZZB ; mit PARW-Zeile vergleichen ldy xindex jsr cmp16bit bcc .no ; kleiner = > Abbruch ldx xindex ; X zurueck lda scry + 1, x ; holt Vektor und ldy scry, x ; zaehlt runter bne + sec sbc #1 + sta prwy + 1 ; Hi speichern dey ; Lo runter & speichern sty prwy ora prwy ; Vorige Zeile = 0 ? beq .no ; Dann Ende ! lda lvek ; lvek in tmp2 sta tmp2 lda lvek + 1 sta tmp2 + 1 ldx #PRW ; lvek auf vorige Zeile jsr getlvek jsr findend ; sucht Zeilenende bne + ; Zeile leer ? jsr delline ; loescht Zeile und lda llen ; stellt Werte ein, sta zahl ; die die Location lda #0 ; entsprechend sta prwx ; verschieben sec ; C = 1 < = > gePARWt rts + cmp #Char_RETURN ; letztes Zeichen CR ? beq .no ; Dann Ende ! cmp #"-" beq + ; Dann kein Space iny ; reservieren + sty prwx ; Y auf LastByte lda llen ; errechnet Versatz, sec ; bei dem Suche sbc prwx ; gestartet wird beq .no ; nix frei = > Ende tay lda prwx ; LineVek += Versatz, um clc ; besser zu shiften adc lvek sta lvek lda lvek + 1 adc #0 sta lvek + 1 lda (tmp2), y ; 1. Quellbyte holen - cmp #" " beq + dey beq .no ; LineStart = > Abbruch lda (tmp2),y ; next Quellbyte cmp #"-" beq + cmp #Char_RETURN bne - + sty zahl ; Anzahl sichern - sta (lvek), y ; Zielbyte setzen dey beq + lda (tmp2), y ; Quellbyte holen jmp - ; Schleife + lda llen ; Zeilenlaenge minus sec ; 'zahl' Bytes muessen sbc zahl ; noch shifted werden sta .m lda tmp2 ; Muss zum Schieben clc ; wieder Vektor adc zahl ; aendern sta lvek lda tmp2 + 1 adc #0 sta lvek + 1 ; Y noch 0 ! - iny ; Y weiter lda (lvek), y ; holt & setzt Byte sta (tmp2), y .m = * + 1: cpy #MODIFIED8 ; Anzahl Bytes bne - lda #" " - iny ; new line damit sta (tmp2), y ; auffuellen cpy llen ; bis Ende bne - sec ; C = 1 < = > PARWed rts moveloc ldx xindex lda zahl ; X-Pos vor PARWgrenze? cmp scrx, x bcc inline ; Dann Pos += Anzahl clc lda scrx, x adc prwx sta scrx, x lda prwy ; verringerten Y-Wert sta scry, x ; einkopieren lda prwy + 1 sta scry + 1, x sec ; C = 1 rts inline lda scrx, x ; (C = 1) sbc zahl ; setzt Cursor zurueck adc #0 ; +Carry sta scrx, x rts ; (C = 0) !zone findend ldy llen ; Versatz auf Ende - lda (lvek), y ; holt Zeichen bis cmp #" " ; Zeilenstart oder bne endfound ; nicht-Space dey bne - endfound rts ; Z = 1 = > Zeile leer (fixme - save a byte) ; Menuevariablen manywins !byte 0 ; Anzahl Wins menunr !byte 0 ; Menunr menupoint !word 0 ; Menupunkt+Puffer changewin beq lp19 ; kein Nachbar = > Ende pha ; neue Nr sichern jsr pullscr ; altes Win weg pla ; neue Nr holen tax ; prueft ob Menupunkt stx menunr ; beibehalten werden lda menupoint ; kann. Ggfs auf 1 cmp winhe, x ; setzen bcc menuloop lda #1 sta menupoint !zone menuloop jsr makewin ; Window bauen pointloop jsr calcpoint ; berechnet Adresse lda clraktl ; holt Farbe jsr changeflg ; aendert Flag lp19 jsr getchar ; Auf Zeichen warten ldx menunr ; Verkuerzt folgende ldy menupoint ; Abfragen cmp #Char_RETURN beq menucom ; Ausfuehren cmp #Char_ShiftRETURN beq menucom ; Ausfuehren ;cmp #$80 ; Linke Maustaste ? ;beq mouselft ; => cmp #Char_CursorUp bne + dey ; Punkt hoch & wech jmp prfpoint + cmp #Char_CursorDown bne + iny ; Punkt runter & wech jmp prfpoint + cmp #Char_CursorLeft bne + lda lftwin, x ; Nachbar holen & wech jmp changewin + cmp #Char_CursorRight bne + lda rgtwin, x ; Nachbar holen & wech jmp changewin + cmp #Char_STOP bne lp19 ; Nein = > Schleife menuend jsr pullscr ; sonst restaurieren rts ; und Ende (fixme - could save a byte here) !zone menucom pha ; CR/CS sichern, dazu txa ; die aktuelle Menunr pha ; und den Menupunkt tya pha lda winadlo, x ; stellt Zeiger auf sta tmp1 ; Menutexte lda winadhi, x sta tmp1 + 1 ldy #0 ; Versatz Null jmp + ; in die Schleife - iny ; naechstes Byte holen lda (tmp1), y bne - ; sucht Ende-Null iny ; Versatz auf Command + lda (tmp1), y ; Command holen dec menupoint ; richtiger Punkt ? bne - ; Nein = > erneut tax ; Ja = > Command ! jsr execom jsr crsroff ; Sicherheitshalber... pla ; Werte zurueck sta menupoint pla sta menunr jsr calcpoint ; Vektor stellen pla ; Ausloesecode zurueck cmp #Char_RETURN beq menuend ; Dann Ende jmp lp19 ; Sonst Schleife (ShiftRETURN) ;----- ;mouselft: ; jmp lp19 ; Linke Maustaste ;----- !zone prfpoint tya ; Wenn Punkt = 0, bne ++ ; dann Punkt: = Letzter ldy winhe, x dey jmp + ++ cmp winhe, x ; Wenn Punkt zu gross, bcc + ; dann Punkt: = 1 ldy #1 + sty menupoint + 1 ; neuen Punkt sichern jsr calcpoint ; alten berechnen und lda clrboxs ; mit WinFarbe faerben jsr changeflg ldy menupoint + 1 ; neuen Punkt holen, sty menupoint ; wird aktueller jmp pointloop ; errechnet Adresse des Menupunkts !zone calcpoint ldx menunr ; holt Nummer & damit lda winhi, x ; Screen-Addy sta tmp1 + 1 ; Hi setzen lda winlo, x ldx menupoint ; holt Punkt sec ; +1 (Rand weg) - adc #scrcols ; addiert Zeile bcc + ; Page sichern inc tmp1 + 1 clc + dex ; ggfs bne - ; Schleife sta tmp1 ; Lo setzen sec ; Erledigt Arbeit fuer ldx menunr ; aufrufende Routinen, ldy winwi, x ; da X, Y, C richtig dey ; belegt werden rts ; Windowdaten: hWindow_InfoBox = 0 ; 1 bis 6 sind Menuleiste hWindow_FirstAfterMenu = 7 hWindow_NoMemLeft = 7 hWindow_Filename = 8 hWindow_DCS = 9 hWindow_Load = 10 hWindow_Save = 11 winwi !byte 31, 16, 7, 13, 10, 14, 8, 25, 25, 32, 10, 15 ; Breite - 1 winhe !byte 8, 8, 5, 3, 5, 8, 2, 5, 5, 8, 2, 2 ; Hoehe - 1 ; Vektoren auf Texte: winadlo !byte info, >datei, >edit, >block, >format,>switch !byte >hilfe, >nomem, >namewin, >dcswin,>lwin, >swin winclr !byte 1, 0, 0, 0, 0, 0, 0, 2, 1, 0, 1, 1 ; FarbIndex lftwin !byte 0, 6, 1, 2, 3, 4, 5, 0, 0, 0, 0, 0 ; Linker Nachbar rgtwin !byte 0, 2, 3, 4, 5, 6, 1, 0, 0, 0, 0, 0 ; Rechter Nachbar ; Menutexte info !text 0,_,_,_,_,_,_,_,_, "---MacEdit---", 0 !text 0, "Version",_, "0.7",_,_,_,_,_,_,_,_, "vom",_, "19.4.97",0 !text 0, "geschrieben",_, "von:", 0 !text 0,_,_,_,_,_,_,_,_,_,_,_,_, "Mac",_, "Bacon/TekLords",0 !text 0,_,_,_,_,_, "Dies",_, "ist",_, "Freeware",_, "!",0 !byte 0, 0 !text 0,_,_,_, "Bitte",_, "eine",_, "Taste",_, "dr",ü, "cken", 0 datei !text $6f, "Neu", 0 !text $75, "Laden", 0 !text $76, "Nachladen", 0 !text $f5, "Speichern", 0 !text $f6, "Speichern",_, "unter", 0 !text $e1, "Basic", 0 !text $61, "Beenden", 0 edit !text $79, "Cut", 0 !text $7a, "Copy", 0 !text $7b, "Paste", 0 !text $14, "Delete", 0 block !text 0, "Laden", 0 !text 0, "Speichern", 0 format !text $f9, "Links", 0 !text $fa,_,_,_, "Rechts", 0 !text $fb, "Zentriert", 0 !text $fc, "Blocksatz", 0 switch !text $19, "WordWrap", 0 !text $07, "Beep", 0 !text 0, "AutoInsert", 0 !text $15, "Fett", 0 !text $16, "Kursiv", 0 !text $17, "Unterstrichen", 0 !text 0, "Revers", 0 hilfe !text $e4, "NochNix" nomem !byte 0 ; Auch 'hilfe'-Ende !text "---",_,_,_,_, "Achtung",_, "!",_,_,_, "---", 0 !text 0, "Der",_, "Speicher",_, "ist",_, "voll",_, "!", 0 !byte 0, 0 !text 0, "Bitte",_, "eine",_, "Taste",_, "dr", ü, "cken" namewin !byte 0 ; Auch 'nomem'-Ende !text "Bitte",_, "Filenamen",_, "eingeben", 0 !byte 0, 0 !text 0,_,_,_, ":",_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_, ":", 0 !byte 0 ; Rest unten dcswin !byte 0, 0 ; Auch fuer 'namewin' !byte 0 ; Auch fuer 'namewin' !text _,_, "Ungespeicherte",_, Ä, "nderungen",_, "!",_,_, 0 !byte 0, 0 !byte 0, 0 !text 0,_, "[DEL]",_,_,_,_, "Verwerfen...", 0 !text 0,_, "[RETURN]",_, "Speichern...",_,_,_,_, "*", 0 !text 0,_, "[STOP]",_,_,_, "Aktion",_, "abbrechen...", 0 lwin !byte 0 ; Auch fuer 'dcswin' !text _, "Lade..." swin !byte 0 ; Auch fuer 'lwin' !text _, "Speichere...", 0 ;-----Screen-Routines (VDC-Version)----- ; V1 R1 rev4 vom 4.5.96 ; labels scrlo = $f0 ; screentextstart scrhi = 0 attrhi = 8 ; colorRAM Hi scrcols = 80 ; columns & lines scrlins = 22 ; (screen) preflen = 80 ; pref-linelength bckgrnd = 26 ; screencolor-register flnmpos = 87 ; filenameposition wsstart = $0fd0 ; windowstack-start !addr tcolor = $f2 ; attribute-buffer ; Subs: !zone init ldx repeatedtry ; first start ? bne + sta repeatedtry + tsx ; buffer stack, bank & stx stck ; NMI for end ldx conreg stx bank ldy nmivek ldx nmivek + 1 sty nmibuf stx nmibuf + 1 +xbank15 inx ; X = 1 => 2 MHz stx takt dex stx fnbank ; filename-CR dex stx locks ; 'CBM-shift' off stx addr($0ac5) ; 'ASC/DIN' off stx basic ; 'Basic-IRQ' off jsr crsrinit ; init crsr ldx #$0b ; VIC off stx addr($11e7) lda mode ; which mode ? bmi + ; if 40 then jsr e_switchmode ; switch mode + lda #111 ; DIN on sta D8502 lda #%..##..## sta R8502 jsr e_copyfont lda #47 ; DIN off sta D8502 lda #addfont1_start sta tmp1 + 1 lda #>addfont1_target ; Hi ldx #addfont2_start sta tmp1 + 1 lda #>addfont2_target ; Hi ldx #", Char_BlueL !fill 80, $a6 ; separatorline (TABs) !byte Char_Grey3, Char_ESCAPE, Char_At, Char_NUL !zone lda #26 ; Backgroundcolor sta vdc lda clrback +wait_for_vdc sta reg lda #nmirtn sta nmivek stx nmivek + 1 sta conreg + 1 ; full RAM ldx #$0b ; loop for new - lda keyb, x ; keyboard-tabs sta addr($033e), x dex bpl - jsr handleid ; old ID ? bcc + ; then ok ! jsr fillmem ; else fill memory jsr newtext ; defaults jsr needline ; wanting line ldx #FALSE stx changes + jsr F_info ; credits lda #TRUE ; update screen sta nwfrm sta updatewbi ; update flags ldx repeatedtry ; first start ? bne + ; then load defaulttext dec repeatedtry jsr load + jmp mainloop addfont1_target = $35f1 addfont1_start !byte %.####### !byte %.#####.# !byte %.##.##.# !byte %.#.....# !byte %.##.#### !byte %.####### !fill 15, 0 !byte %.#.#.#.# !fill 9, 0 !byte %....#... !byte %....#... !byte %....#... !byte %....#... !byte %....#... !byte %....#... !byte %....#... !byte %....#... !byte %....#... !byte %....#... !byte %....#... !byte %....#... !byte %....#... !byte %....#... !byte %....#... !byte %....#... !byte %....#... !byte %....#... !byte %....#... !byte %....#... !byte %....#### !fill 11, 0 !byte %....#... !byte %....#... !byte %....#... !byte %....#... !byte %######## !fill 11, 0 !byte %....#... !byte %....#... !byte %....#... !byte %....#... !byte %#####... !fill 11, 0 !byte %....#... !byte %....#... !byte %....#... !byte %....#... !byte %....#### !byte %....#... !byte %....#... !byte %....#... !byte %....#... !byte %....#... !byte %....#... !byte %....#... !byte %....#... !byte %....#... !byte %....#... !byte %....#... !fill 4, 0 !byte %######## !fill 11, 0 !byte %....#... !byte %....#... !byte %....#... !byte %....#... !byte %#####... !byte %....#... !byte %....#... !byte %....#... !byte %....#... !byte %....#... !byte %....#... !byte %....#... !byte %....#... !byte %....#... !byte %....#... !byte %....#... !fill 4, 0 !byte %....#### !byte %....#... !byte %....#... !byte %....#... !byte %....#... !byte %....#... !byte %....#... !byte %....#... !byte %....#... !byte %....#... !byte %....#... !byte %....#... !fill 4, 0 !byte %######## !byte %....#... !byte %....#... !byte %....#... !byte %....#... !byte %....#... !byte %....#... !byte %....#... !byte %....#... !byte %....#... !byte %....#... !byte %....#... !fill 4, 0 !byte %#####... !byte %....#... !byte %....#... !byte %....#... !byte %....#... !byte %....#... !byte %....#... !byte %....#... !byte %....#... !byte %....#... !byte %....#... !byte %....#... !byte %....#... !byte %....#... !byte %....#... !byte %....#... !byte %######## !byte %....#... !byte %....#... !byte %....#... !byte %....#... !byte %....#... !byte %....#... addfont1_end addfont2_target = $3ff0 addfont2_start !byte %........ !byte %........ !byte %........ !byte %....#... !byte %........ !byte %........ !byte %........ !byte %........ addfont2_end gonot rts ; fixme - could save a byte here F_goout jsr willblost ; changes saved ? beq gonot F_gosys +bank15 jsr e_cls ; CLS lda #0 ; '0' for sta locks ; 'CBM-Shift' on sta addr($0ac5) ; 'ASC/DIN' on sta basic ; 'Basic-IRQ' on lda nmibuf ; restore NMI ldx nmibuf + 1 sta nmivek stx nmivek + 1 ldx bank ; restore bank & stack stx conreg ldx stck txs rts !zone bigtext lda #0 ; no empty lines, ldy #scrlins ; full text jmp m61 newframe +ybank15 lda #scrhi ; textstart on screen ldx #scrlo jsr ramaccess ; set vdc sty conreg + 1 ; full RAM ldx #SCR jsr getvvek ; set VekVek stx nwfrm ; clear jobflag lda zzbe ; Test: less text than sec ; screen ? sbc scry ; calculates both tay ; numbers lda zzbe + 1 sbc scry + 1 bne bigtext cpy #scrlins - 1 bcs bigtext iny ; Base 1 sty .m1 lda #scrlins sec .m1=*+1: sbc #MODIFIED8 ; errechnet Leerzeilen ; Parameter: A = Leerzeilen, Y = Textzeilen !zone m61 sta tmp2 ; Leerzeilen sty line ; zu zeigende Zeilen -- ldy #0 ; Low-Byte lda (vvek), y ; holen sta lvek ; & speichern iny ; High-Byte lda (vvek), y ; holen sta lvek + 1 ; & speichern lda #scrcols ; darzustellende sta col ; Spalten ldy scrx ; x-Versatz holen - lda (lvek), y ; Zeichen holen +xbank15 tax ; & in Screencode lda atst,x ; konvertieren +wait_for_vdc sta reg ; in VDC sta conreg + 1 ; full RAM iny ; Versatz erhoehen dec col ; naechste Spalte bne - ; schon alle = > inc vvek ; naechster Vektor (2 Byte !) +inc16 vvek dec line ; naechste Zeile bne -- ; schon alle ? = > +bank15 lda #" " -- dec tmp2 ; Anzahl runter bmi + ; ggfs Ende ldy #scrcols ; Spaltenzahl - +wait_for_vdc sta reg ; Space setzen dey ; alle Spalten ? bne - jmp -- + sta conreg + 1 ; full RAM rts ; Folgende Routine aendert ScreenFlag, ; z.B. esc, Menupunkte. Parameter: ;-A = Attribut-Code ; Einsprung setflagdata: ;-X = Flagnr (Routine holt Daten) ; Einsprung changeflg: ;-Y = Laenge ;-tmp1 = Vektor auf Position setflagdata ldy flgadr, x ; Setzt Adresse (Lo) sty tmp1 ldy #scrhi sty tmp1 + 1 ldy flglen, x ; Holt Laenge !zone changeflg sta .m3 ; buffer color sty .m2 ; buffer length lda #attrhi - scrhi clc ; bend vector to adc tmp1 + 1 ; attributes sta tmp1 + 1 ldy conreg ; buffer bank sty .m1 +ybank15 jsr tmpaccess ; set vdc .m3 = * + 1: lda #MODIFIED8 ; attribute .m2 = * + 1: ldy #MODIFIED8 ; length - +wait_for_vdc sta reg ; set attributes dey ; count bne - .m1 = * + 1: ldy #MODIFIED8 ; bank sty conreg rts hFlag_Escape = 0 ; 1-6 = datei, edit, block, format, switch, hilfe hFlag_Wrap = 7 hFlag_Beep = 8 hFlag_Insert = 9 flgadr !byte 135, 0, 12, 24, 36, 48, 60, 144, 145, 146 !byte 150, 151, 152, 153, 157, 158, 159 ; f k u r < = > flglen !byte 6, 10, 10, 10, 10, 10, 10, 1, 1, 1 !byte 1, 1, 1, 1, 1, 1, 1 !zone showwbi lda clraktv ; Aktiv-Farbe ldy wrapflag ; Flag pruefen bne + ; ggfs andere Farbe lda clrinak + ldx #hFlag_Wrap jsr setflagdata ; Farbe setzen lda clraktv ; Ab hier Wiederholung ldy beep beq + lda clrinak + ldx #hFlag_Beep jsr setflagdata lda clraktv ldy esca bne + lda clrinak + ldx #hFlag_Insert jsr setflagdata lda #>txtname ; set vector sta tmp1 + 1 lda #flnmpos ; show filename ldx #dosname sty tmp1 + 1 ldy #$0f ; set length sty vimm6 !zone showname jsr copystr1 ; show name -- ldy #9 ; Y- und X-Wert lda xpos ; errechnen clc adc #$20 jsr crsrset ; Cursor setzen - jsr getchar ; Byte holen und tax ; sichern and #%.##..... ; Command ? beq + ; ggfs => eor #%.##..... beq + lda esca ; AutoInsert ? beq ++ ; ggfs jsr einf1 ; insert ++ txa ; Zeichen an Position ldy xpos ; setzen sta dosname, y jsr rechts1 ; eins rechts jmp showname + txa ; Byte holen cmp #Char_CursorRight beq rechts cmp #Char_CursorLeft beq links cmp #Char_DEL beq back cmp #CharOwn_Delete beq dele cmp #Char_INST beq einf cmp #Char_RETURN beq + cmp #Char_STOP bne - ; Nein = > lda #0 ; Stop- + sta xpos ; Flag sichern jsr pullscr ; Window weg jsr crsrold ; old copy pla ; alte Bank sta conreg pla ; Y: = Namepointer tay lda xpos ; STOP ? beq .noname ; Nein: Namen kopieren ldx #$0f - lda dosname, x sta txtname, y dey dex bpl - lda #TRUE ; und Update fordern sta updatewbi .noname rts ; Z = Stopflag rechts jsr rechts1 ; Eins weiter jmp -- links jsr links1 ; Eins zurück jmp -- back jsr links1 ; Erst zurück, dann dele jsr dele1 ; löschen jmp showname einf jsr einf1 ; Eins freimachen jmp showname !zone rechts1 inc xpos ; Pos += 1 & pruefen jmp + links1 dec xpos ; Pos- = 1 & pruefen + lda xpos ; Negativ ? bpl + lda #0 ; Dann Pos 0 + cmp #$0c ; Ende ? bcc + lda #$0b ; Dann zum Rand + sta xpos ; Pos setzen rts !zone dele1 ldy xpos ; Start bei Cursor - cpy #$0b ; schon am Rand ? beq + ; Dann Ende ! lda dosname + 1, y ; Sonst shiften sta dosname, y iny jmp - + lda #" " ; Space setzen sta dosname, y rts !zone einf1 ldy #$0b ; Start am Rand - cpy xpos ; schon fertig ? beq + ; Ja = > lda dosname - 1, y ; Zeichen shiften sta dosname, y dey jmp - + lda #" " ; Space an Pos sta dosname, y rts !zone ; access for new window wsnew +ldax wspntr jmp + ; access for old window wsold +ldax wsback + ldy #24 ; VDC (Vscroll) sty vdc ldy #$a0 ; 'block-copy' +wait_for_vdc sty reg ; set jmp ramaccess ; sichert Screen & erzeugt Rahmen und BG fuer Window ; Windownr in menunr ! !zone makewin inc manywins ; eins mehr ! lda conreg ; Bank sichern pha +ram0io ldx menunr ; Holt Nummer beq + ; Ist das Window ein cpx #hWindow_FirstAfterMenu ; Menu-Window, wird bcs + ; das Flag aktiviert lda clrmenu1 jsr setflagdata + jsr wsnew ; macht VDC zum Stack ldy wsback + 1 ; alter end-pointer sta wsback + 1 ; wird zu neuem lda wsback ; last-pointer stx wsback jsr stashbt ; buffer last pointer tya jsr stashbt ; (Hi) ldx menunr ; get Win-Nr txa jsr stashbt ; wird gestacked lda winlo, x ; Holt mit X die sta tmp1 ; anderen Parameter sta tmp2 lda winhi, x sta tmp1 + 1 clc adc #attrhi - scrhi sta tmp2 + 1 ldy winwi, x ; get width sty .m1 ; for colorate iny ; convert sty mWidth ; for push lda winclr, x ; get colorcode tay ; convert lda clrboxs, y ; get color sta .m2 ; in Immediate jsr scrn2stck ; push chars lda tmp2 ; attrib-vector sta tmp1 lda tmp2 + 1 sta tmp1 + 1 jsr scrn2stck ; push attributes ldy #18 ; VDC (access Hi) sty vdc jsr fetchbt ; get pointerHi sta wspntr + 1 ; set pointerHi iny ; VDC (access Lo) sty vdc jsr fetchbt ; get pointerLo sta wspntr ; set pointerLo ; ab hier wird gefaerbt ldy #24 ; VDC (Vscroll) sty vdc lda #$20 ; 'block-write' jsr stashbt lda winhe, x ; get height sta line - +ldax tmp2 ; get target jsr ramaccess ; set vdc .m2 = * + 1: lda #MODIFIED8 ; attribute jsr stashbt ; set attribute dey ; VDC (counter) sty vdc .m1 = * + 1: lda #MODIFIED8 ; width jsr stashbt ; start write lda #scrcols ; address for next row clc adc tmp2 sta tmp2 bcc + inc tmp2 + 1 + dec line ; alle Zeilen ? bpl - ; Nein = > ; bisher Backup und Faerben, jetzt Win ldx menunr ; get Win-Nr lda winlo, x ; get location, width, sta tmp1 ; heigth & textvector lda winhi, x sta tmp1 + 1 lda winwi, x sta col lda winhe, x sta line lda winadlo, x sta tmp2 lda winadhi, x sta tmp2 + 1 ; hier oberer Rand jsr tmpaccess ; set vdc ldx col ; get width & dex ; convert lda #chrol ; 'Oben Links' jsr stashbt ; set lda #chroo ; 'Oben' - jsr stashbt ; set dex bne - lda #chror ; 'Oben Rechts' jsr stashbt ; set dec line ; naechste Zeile ; jetzt die Zeilen .field lda #scrcols ; rechnet den clc ; Vektor auf die adc tmp1 ; naechste Zeile sta tmp1 ; um bcc + inc tmp1 + 1 + jsr tmpaccess ; set vdc ldy #1 ; textindex 1 lda #chrll ; 'Links' jsr stashbt ; set - lda (tmp2), y ; get char beq + ; end ? if not then tax ; convert to screencode lda atst, x jsr stashbt ; output iny ; next column jmp - + tya ; if string has ended then tax ; remember memory offset in x - cpy col ; finished ? beq + ; if not then lda #chrmm ; output inner space jsr stashbt iny ; next column jmp - + lda #chrrr ; when finished, output right border jsr stashbt txa ; calculate new memory pointer sec adc tmp2 ; line sta tmp2 bcc + inc tmp2 + 1 + dec line ; schon alle Zeilen ? bne .field ; ggfs Schleife ; jetzt der Unterrand lda #scrcols ; calculate next line clc adc tmp1 sta tmp1 bcc + inc tmp1 + 1 + jsr tmpaccess ; set vdc ldx col ; get width & dex ; convert lda #chrul ; 'Unten Links' jsr stashbt ; set lda #chruu ; 'Unten' - jsr stashbt ; set dex bne - lda #chrur ; 'Unten Rechts' jsr stashbt ; set pla ; alte Bank sta conreg rts ; Windowdaten: ; 0 = StartBox ; 1 bis 6 sind Menuleiste ; 7 = MemFull ; 8 = FileName ; 9 = SaveChanges? ; 10 = Load ; 11 = Save ; Addys (Ecke links oben): winlo !byte 152, 80, 92, 104, 116, 128, 140, 235, 235, 151, 146, 144 winhi !byte 2, 0, 0, 0, 0, 0, 0, 2, 2, 2, 3, 3 !zone scrn2stck lda winhe, x ; get height sta line - ldy #32 ; VDC (source Hi) sty vdc lda tmp1 + 1 jsr stashbt ; set sourceHi iny ; VDC (source Lo) sty vdc lda tmp1 jsr stashbt ; set sourceLo ldy #30 ; VDC (counter) sty vdc mWidth = * + 1: lda #MODIFIED8 ; width jsr stashbt ; start copy lda #scrcols ; address for next row clc adc tmp1 sta tmp1 bcc + inc tmp1 + 1 + dec line ; alle Zeilen ? bpl - ; Nein = > rts !zone pullscr lda conreg ; Bank sichern pha +ram0io jsr wsold ; init stack for read sta wspntr + 1 ; new write-start stx wspntr ; new write-start jsr fetchbt ; get old laststart sta wsback ; and use jsr fetchbt sta wsback + 1 jsr fetchbt ; get handle from stack tax lda winlo, x ; Holt mit X die sta tmp1 ; anderen Parameter sta tmp2 lda winhi, x sta tmp1 + 1 clc adc #attrhi - scrhi sta tmp2 + 1 ldy winwi, x ; get width iny ; convert sty .m1 ldy #18 ; VDC (access Hi) sty vdc jsr fetchbt ; get Hi ldy #32 ; VDC (source Hi) sty vdc jsr stashbt ; set Hi ldy #19 ; VDC (access Lo) sty vdc jsr fetchbt ; get Lo ldy #33 ; VDC (source Lo) sty vdc jsr stashbt ; set Lo jsr stck2scrn ; pull chars lda tmp2 ; attrib-vector sta tmp1 lda tmp2 + 1 sta tmp1 + 1 jsr stck2scrn ; pull attributes txa ; if menu-window, beq + ; then deactivate flag cmp #7 bcs + lda clrmenu jsr setflagdata + dec manywins ; minus one win pla ; restore bank sta conreg rts stck2scrn lda winhe, x ; get height sta line - ldy #18 ; VDC (access Hi) sty vdc lda tmp1 + 1 jsr stashbt ; set targetHi iny ; VDC (access Lo) sty vdc lda tmp1 jsr stashbt ; set targetLo ldy #30 ; VDC (counter) sty vdc .m1 = * + 1: lda #MODIFIED8 ; width jsr stashbt ; start copy lda #scrcols ; address for next row clc adc tmp1 sta tmp1 bcc + inc tmp1 + 1 + dec line ; alle Zeilen ? bpl - ; Nein = > rts wspntr !word wsstart ; win-stackpointer wsback !word wsstart ; last pointer F_menu lda #wsstart sta wspntr + 1 jsr crsrnew ; new copy ldx #0 ; yet no windows stx manywins inx ; menu 1 & point 1 stx menunr stx menupoint jsr menuloop ; menu-routine jsr crsrold ; old copy rts ; fixme - could save one byte here tmpaccess +ldax tmp1 ; A = Highbyte, X = Lowbyte !zone ramaccess ldy #18 ; VDC (access Hi) sty vdc +wait_for_vdc sta reg ; set Hi iny ; VDC (access Lo) sty vdc +wait_for_vdc stx reg ; set Lo ldy #31 ; VDC (ram data) sty vdc rts fetchbt +wait_for_vdc lda reg ; fetch byte rts stashbt +wait_for_vdc sta reg ; stash byte rts acme-crossassembler-0.96.4/examples/me/tables.bin000066400000000000000000000014721333777125400217470ustar00rootroot000000000000000vwzy|{u~pt瑖)0˷Ϋ̡>[<]s=?_85x2471+- 6930.q}@$%&/()=?:;^*ۓ'\_!>"уn2t71+- r30. 3WA4YSE5RD6CFTX7ZG8BHUV9IJ0MKONPL.,[+#]-1<2 Q85 2471+- 6930. 34567890ξ̯.,[+#]-1<2 85 2471+- 6930. 3WA4YSE5RD6CFTX7ZG8BHUV9IJ0MKONPL.,[+#]-1<2 Q85 2471+- 6930._!"#$%&'()*+,-./0123456789:;<=>? @ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^߀`abcdefghijklmnopqrstuvwxyz{|}~@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^`abcdefghijklmnopqrstuvwxyz{|}~acme-crossassembler-0.96.4/examples/me/vars.a000066400000000000000000000065141333777125400211220ustar00rootroot00000000000000;ACME 0.94.4 ; Vermerk: !text "MacEdit was written by Mac Bacon in 1994-97." !text " This is Freeware !" ; Variablen: stck !byte 0 ; Stackbuffer nmibuf !word 0 ; NMI-Buffer idtext !text "MacEdV0" ; RAM-Kennung scratch !byte "s" ; DOS-String dospre !text "0:" dosname !text "-Anleitung .txt,p," ; Default dosmode !text "r" filepara !byte 22, lf, lf, 8 !word dospre killpara !byte 19, 15, 15, 8 !word scratch idfile !text "FormatVersion1.0" ; Farben: ; 2rufRGBI-Format clrcrsr !byte %##.##..# ; Cursor clrback !byte %........ ; Screen (xxxxRGBI-Format) clraktv !byte %#...###. ; Aktive Flags clrinak !byte %#......# ; Inaktive clrmenu !byte %##..##.# ; Menu clrmenu1 !byte %#...#### ; aktives Menu clraktl !byte %##..#### ; Menupunkt clrboxs !byte %#....### ; Menuboxen !byte %#....#.# ; Dialogboxen !byte %#..##..# ; Warnungen ; Vars bank !byte 0 ; Bankbuffer memin !word 0 ; Startaddy Vektoren txts !word 0 ; Startaddy Text unnamed !byte TRUE ; ist Text benannt ? changes !byte FALSE ; Sind Changes saved ? nwfrm !byte FALSE ; neues Bild ? blockflag !byte FALSE ; Block definiert ? wrapflag !byte TRUE ; PARWing ? esca !byte TRUE ; AutoInsert ? updatewbi !byte FALSE ; Flag-Redraw nötig ? repeatedtry !byte FALSE ; Schon früher gestartet ? loadflag !byte 0 ; 0 = LOAD (/MERGE) txtname !text "unbenannt .txt" mrgname !text "merge .txt" newname !text "unbenannt .txt" lodname !text "unbenannt .txt" xindex !byte 0 ; Index-Puffer ; Folgende Vars werden per x indiziert SCR = 0 ; x-Wert scrx !byte 0 ; Display scry !word 0 ANF = 3 ; x-Wert anfx !byte 0 ; Blockstart anfy !word 0 END = 6 ; x-Wert endx !byte 0 ; Ende endy !word 0 POS = 9 ; x-Wert posx !byte 0 ; Cursor posy !word 0 TMP = 12 ; x-Wert tmpx !byte 0 ; temp tmpy !word 0 ZZA = 15 ; x-Wert llen !byte preflen ; Zeilenlaenge zzan !word 0 ; vorhandene Zeilen ZZB = 18 ; x-Wert byte !byte 0 ; akt. Zeichen zzbe !word 0 ; benutzte Zeilen WRP = 21 ; x-Wert wrpx !byte 0 ; Wrap wrpy !word 0 PRW = 24 ; x-Wert prwx !byte 0 ; Parw prwy !word 0 ; Tabs: etab ; ESC-Jumps !word ESC_at, ESC_a, ESC_b, ESC_c !word ESC_d, 0, 0, ESC_g !word ESC_h, ESC_i, ESC_j, ESC_k !word 0, 0, 0, ESC_o !word ESC_p, ESC_q, 0, 0 !word ESC_t, 0, 0, 0 !word 0, 0, 0, 0 !word 0, 0, 0, 0 ctab ; Command-Jumps 1. Achtel !word 0, 0, F_un, F_menu !word 0, F_c, 0, F_bell !word 0, F_tab, F_lfeed, 0 !word F_ffeed, F_cr, F_text, F_fn !word 0, F_gcd, F_rn, F_home !word F_dcl, F_sf, F_sk, F_su !word F_st, F_sw, F_sl, F_esc !word F_c, F_gcr, F_c, F_c ; 5. Achtel !word F_dir, F_c, F_uf, F_fbox !word F_hlp, F_f, F_f, F_f !word F_f, F_f, F_f, F_f !word F_f, F_cs, F_graphic, F_ff !word F_c, F_gcu, F_rf, F_gsot !word F_insert, F_c, F_c, F_c !word F_c, F_c, F_c, F_c !word F_c, F_gcl, F_c, F_c ; 8. Achtel !word F_bttnf, F_gosys, 0, 0 !word F_info, F_f, F_f, F_f !word F_f, F_f, F_f, F_f !word F_f, 0, F_geol, F_print !word F_glu, F_gau, F_scrnu, F_geot !word F_dword, F_save, F_saveas, F_rnmtxt !word F_gtl, F_fmtl, F_fmtr, F_fmtm !word F_fmtb, F_gwl, F_gpu, 0 ; 4. Achtel !word F_bttnn, F_goout, 0, 0 !word F_mode, 0, 0, 0 !word 0, 0, 0, 0 !word 0, 0, F_gsol, F_new !word F_gld, F_gad, F_scrnd, F_ahome !word F_dcr, F_loadas, F_mergeas, F_find !word F_gtr, F_cut, F_copy, F_paste !word F_move, F_gwr, F_gpd, F_middle acme-crossassembler-0.96.4/examples/trigono.a000066400000000000000000000020721333777125400212220ustar00rootroot00000000000000;ACME 0.94.12 !to "trigono.o", plain * = $c000 PI = 3.14159265358979323846 !raw "cos[0,pi/2] scaled to 0-255 range" !align $f, 0, 0 ; make output file look better in hex editor :) !for x, 0, 255 { !byte cos(float(x) / 255 * PI/2) * 255 + 0.5 } ; "float()" makes sure this calculation is done in float mode now ; "/255*half_PI" converts interval [0,255] to interval [0,PI/2] ; "cos()" returns cosine. Wow. ; "*255" converts interval [0,1] to interval [0,255] ; "+0.5" ensures correct rounding to integer !align $f, 0, 0 !raw "sin[-pi/2,pi/2] scaled to full range of 16b.16b fixed point" !align $f, 0, 0 !for x, 0, 1023 { !32 sin(float(x - 512) / 1024 * PI) * 65536 + 0.5 } ;undefined = 0.0 / 0.0 ; throws error when active ;range = arcsin(-10) ; throws error when active !by 1 / 2 * 2 ; should give 0 !by 1 / 2 * 2.0 ; should give 0 !by 1 / 2.0 * 2 ; should give 1 !by 1 / 2.0 * 2.0 ; should give 1 !by 1.0 / 2 * 2 ; should give 1 !by 1.0 / 2 * 2.0 ; should give 1 !by 1.0 / 2.0 * 2 ; should give 1 !by 1.0 / 2.0 * 2.0 ; should give 1 acme-crossassembler-0.96.4/examples/trigono.exp000066400000000000000000000105701333777125400216000ustar00rootroot00000000000000cos[0,pi/2] scaled to 0-255 range~}{zywvtsrpomlkihfecb`_^\[YXVUSRPOMLJIGFDCA@>=;:875320/-,*)'&$"! sin[-pi/2,pi/2] scaled to full range of 16b.16b fixed point  &-5=FPZep|)=Qez :Uq"Aa 0Ty<e9fM|Bu G } " [  B } 2 p , l /r< R(qN1~gVIB?BJW h";Z ~ C!! "o""9##$m$$=%%&x&&M''%(((k))G**&++,w,,[--@..(//000s11b22S33E44:55066(77#8899::;;<<#==)>>0??:@@EAARBBaCCrDDEFF$GG;HHTIInJJKLL8MMYNN{O PP2QQYRRSTT@UUmVWW3XXcYYZ/[[c\\]5^^m_ ``Eaab cc^dde?ffg#hhgi jjQkkl=mmn-ooyp qqnrssgtuubv ww`x yyaz {{e|}}l~u$ҁ1ATiΊ4Om"בCfԖCl$ݛP Þ}8h#ߣWϦHé>x6s2q0q2u6|>¾G Sb&s7J`%w=Vq8Tr:Y!zB c,Nq:^'Ko8[$Hl 5 Y !{D f.Pp8V !t";#$$%W&''(q)8**+,P-../g0,112|3@4556S7889c:';;<q=4>>?|@?ABBCGD EEFMGHHIPJKKLPMNNOMP QQRHSTTU>VVWuX2YYZf["\\]T^__`>aabkc$ddeNfggvh-iijRklltm*nnoIppqfrsst3uuvJwwx_yzzp{ ||}/~~:CILLIC:.ԐyĒh T<ޗ!™cBZ0̡h8Ҥl6Χf*V<Ϯb9ɲXw :ƷRݸh}(6BǿLU\`cdca\UMB5'zeM4kK(pH\,b-[!Fg$<P^glkf [K6hG!h; LTS H4nN$X#TAnIs<b;];Xt+CZq0?N]kxacme-crossassembler-0.96.4/src/000077500000000000000000000000001333777125400163475ustar00rootroot00000000000000acme-crossassembler-0.96.4/src/Makefile000066400000000000000000000042001333777125400200030ustar00rootroot00000000000000CFLAGS = -O3 -Wall -Wstrict-prototypes LIBS = -lm CC = gcc RM = rm #SRC = PROGS = acme BINDIR = /usr/local/bin USERBIN = $(HOME)/bin OBJS = acme.o alu.o cliargs.o cpu.o dynabuf.o encoding.o flow.o global.o input.o macro.o mnemo.o output.o platform.o pseudoopcodes.o section.o symbol.o tree.o typesystem.o all: $(PROGS) acme: $(OBJS) $(CC) $(CFLAGS) -o acme $(OBJS) $(LIBS) strip acme acme.o: config.h platform.h acme.h alu.h cpu.h dynabuf.h encoding.h flow.h global.h input.h macro.h mnemo.h output.h pseudoopcodes.h section.h symbol.h version.h acme.h acme.c alu.o: config.h platform.h cpu.h dynabuf.h encoding.h global.h input.h section.h symbol.h tree.h alu.h alu.c cliargs.o: cliargs.h cliargs.c cpu.o: config.h alu.h dynabuf.h global.h input.h mnemo.h output.h tree.h cpu.h cpu.c dynabuf.o: config.h acme.h global.h input.h dynabuf.h dynabuf.c encoding.o: config.h alu.h acme.h dynabuf.h global.h output.h input.h tree.h encoding.h encoding.c flow.o: config.h acme.h alu.h dynabuf.h global.h input.h mnemo.h symbol.h tree.h flow.h flow.c global.o: config.h platform.h acme.h cpu.h input.h macro.h pseudoopcodes.h section.h symbol.h global.h global.c input.o: config.h alu.h dynabuf.h global.h section.h symbol.h tree.h input.h input.c macro.o: config.h acme.h alu.h dynabuf.h global.h input.h section.h symbol.h tree.h macro.h macro.c mnemo.o: config.h alu.h cpu.h dynabuf.h global.h input.h output.h tree.h mnemo.h mnemo.c output.o: config.h acme.h alu.h cpu.h dynabuf.h global.h input.h tree.h output.h output.c platform.o: config.h platform.h platform.c pseudoopcodes.o: acme.h alu.h global.h input.h macro.h output.h pseudoopcodes.h pseudoopcodes.c section.o: config.h dynabuf.h global.h symbol.h tree.h section.h section.c symbol.o: config.h acme.h alu.h cpu.h dynabuf.h global.h input.h section.h tree.h symbol.h symbol.c tree.o: config.h dynabuf.h global.h symbol.h tree.h tree.c typesystem.o: config.h global.h typesystem.h typesystem.c clean: -$(RM) -f *.o $(PROGS) *~ core install: all install -d $(BINDIR) install $(PROGS) $(BINDIR) userinstall: all install -d $(USERBIN) install $(PROGS) $(USERBIN) # DO NOT DELETE acme-crossassembler-0.96.4/src/Makefile.dos000066400000000000000000000043371333777125400206020ustar00rootroot00000000000000CFLAGS = -Wall -s -Wstrict-prototypes LIBS = -lm CC = gcc RM = rm #SRC = PROGS = acme #BINDIR = /usr/local/bin #USERBIN = $(HOME)/bin OBJS = acme.o alu.o cliargs.o cpu.o dynabuf.o encoding.o flow.o global.o input.o macro.o mnemo.o output.o platform.o pseudoopcodes.o section.o symbol.o tree.o typesystem.o all: $(PROGS) acme: $(OBJS) $(CC) $(CFLAGS) -o acme.out $(OBJS) $(LIBS) copy /b \djgpp\bin\pmodstub.exe + acme.out acmepmod.exe djp acme.exe djp acmepmod.exe acme.o: config.h platform.h acme.h alu.h cpu.h dynabuf.h encoding.h flow.h global.h input.h macro.h mnemo.h output.h pseudoopcodes.h section.h symbol.h version.h acme.h acme.c alu.o: config.h platform.h cpu.h dynabuf.h encoding.h global.h input.h section.h symbol.h tree.h alu.h alu.c cliargs.o: cliargs.h cliargs.c cpu.o: config.h alu.h dynabuf.h global.h input.h mnemo.h output.h tree.h cpu.h cpu.c dynabuf.o: config.h acme.h global.h input.h dynabuf.h dynabuf.c encoding.o: config.h alu.h acme.h dynabuf.h global.h output.h input.h tree.h encoding.h encoding.c flow.o: config.h acme.h alu.h dynabuf.h global.h input.h mnemo.h symbol.h tree.h flow.h flow.c global.o: config.h platform.h acme.h cpu.h input.h macro.h pseudoopcodes.h section.h symbol.h global.h global.c input.o: config.h alu.h dynabuf.h global.h section.h symbol.h tree.h input.h input.c macro.o: config.h acme.h alu.h dynabuf.h global.h input.h section.h symbol.h tree.h macro.h macro.c mnemo.o: config.h alu.h cpu.h dynabuf.h global.h input.h output.h tree.h mnemo.h mnemo.c output.o: config.h acme.h alu.h cpu.h dynabuf.h global.h input.h tree.h output.h output.c platform.o: config.h platform.h platform.c pseudoopcodes.o: acme.h alu.h global.h input.h macro.h output.h pseudoopcodes.h pseudoopcodes.c section.o: config.h dynabuf.h global.h symbol.h tree.h section.h section.c symbol.o: config.h acme.h alu.h cpu.h dynabuf.h global.h input.h section.h tree.h symbol.h symbol.c tree.o: config.h dynabuf.h global.h symbol.h tree.h tree.c typesystem.o: config.h global.h typesystem.h typesystem.c clean: del *.o # -$(RM) -f *.o $(PROGS) *~ core #install: all # install -d $(BINDIR) # install $(PROGS) $(BINDIR) #userinstall: all # install -d $(USERBIN) # install $(PROGS) $(USERBIN) # DO NOT DELETE acme-crossassembler-0.96.4/src/Makefile.mingw000066400000000000000000000051001333777125400211230ustar00rootroot00000000000000# # makefile for MingW # CFLAGS = -O3 -Wall -Wstrict-prototypes LIBS = -lm CC = gcc RM = rm #SRC = PROGS = acme.exe BINDIR = /usr/local/bin USERBIN = $(HOME)/bin all: $(PROGS) acme.exe: acme.o alu.o cliargs.o cpu.o dynabuf.o encoding.o flow.o global.o input.o macro.o mnemo.o output.o platform.o pseudoopcodes.o section.o symbol.o tree.o typesystem.o _dos.o resource.res $(CC) $(LIBS) $(CFLAGS) -o acme acme.o alu.o cliargs.o cpu.o dynabuf.o encoding.o flow.o global.o input.o macro.o mnemo.o output.o platform.o pseudoopcodes.o section.o symbol.o tree.o typesystem.o resource.res strip acme.exe acme.o: config.h platform.h acme.h alu.h cpu.h dynabuf.h encoding.h flow.h global.h input.h macro.h mnemo.h output.h pseudoopcodes.h section.h symbol.h version.h acme.h _dos.h acme.c alu.o: config.h platform.h cpu.h dynabuf.h encoding.h global.h input.h section.h symbol.h tree.h alu.h alu.c cliargs.o: cliargs.h cliargs.c cpu.o: config.h alu.h dynabuf.h global.h input.h mnemo.h output.h tree.h cpu.h cpu.c dynabuf.o: config.h acme.h global.h input.h dynabuf.h dynabuf.c encoding.o: config.h alu.h acme.h dynabuf.h global.h output.h input.h tree.h encoding.h encoding.c flow.o: config.h acme.h alu.h dynabuf.h global.h input.h mnemo.h symbol.h tree.h flow.h flow.c global.o: config.h platform.h acme.h cpu.h input.h macro.h pseudoopcodes.h section.h symbol.h global.h global.c input.o: config.h alu.h dynabuf.h global.h section.h symbol.h tree.h input.h input.c macro.o: config.h acme.h alu.h dynabuf.h global.h input.h section.h symbol.h tree.h macro.h macro.c mnemo.o: config.h alu.h cpu.h dynabuf.h global.h input.h output.h tree.h mnemo.h mnemo.c output.o: config.h acme.h alu.h cpu.h dynabuf.h global.h input.h tree.h output.h output.c platform.o: config.h platform.h platform.c pseudoopcodes.o: acme.h alu.h global.h input.h macro.h output.h pseudoopcodes.h pseudoopcodes.c section.o: config.h dynabuf.h global.h symbol.h tree.h section.h section.c symbol.o: config.h acme.h alu.h cpu.h dynabuf.h global.h input.h section.h tree.h symbol.h symbol.c tree.o: config.h dynabuf.h global.h symbol.h tree.h tree.c typesystem.o: config.h global.h typesystem.h typesystem.c # _dos.o: _dos.h win/resource.rc: acme.c cd win; sh setRelease.sh resource.res: win/resource.rc win/logo.ico cd win; windres resource.rc -O coff -o ../resource.res cp -f win/logo.ico . clean: -$(RM) -f *.o $(PROGS) *~ core resource.res logo.ico win/resource.rc install: all install -d $(BINDIR) install $(PROGS) $(BINDIR) userinstall: all install -d $(USERBIN) install $(PROGS) $(USERBIN) # DO NOT DELETE acme-crossassembler-0.96.4/src/Makefile.riscos000066400000000000000000000043161333777125400213140ustar00rootroot00000000000000CFLAGS = -O3 -mthrowback -mlibscl -Wall -Wstrict-prototypes LIBS = -lm CC = gcc RM = rm #SRC = PROGS = acme #BINDIR = /usr/local/bin #USERBIN = $(HOME)/bin OBJS = acme.o alu.o cliargs.o cpu.o dynabuf.o encoding.o flow.o global.o input.o macro.o mnemo.o output.o platform.o pseudoopcodes.o section.o symbol.o tree.o typesystem.o all: $(PROGS) acme: $(OBJS) $(CC) $(CFLAGS) -o !Unsqueezed $(OBJS) $(LIBS) Squeeze -f -v !Unsqueezed !ACME.!RunImage acme.o: config.h platform.h acme.h alu.h cpu.h dynabuf.h encoding.h flow.h global.h input.h macro.h mnemo.h output.h pseudoopcodes.h section.h symbol.h version.h acme.h acme.c alu.o: config.h platform.h cpu.h dynabuf.h encoding.h global.h input.h section.h symbol.h tree.h alu.h alu.c cliargs.o: cliargs.h cliargs.c cpu.o: config.h alu.h dynabuf.h global.h input.h mnemo.h output.h tree.h cpu.h cpu.c dynabuf.o: config.h acme.h global.h input.h dynabuf.h dynabuf.c encoding.o: config.h alu.h acme.h dynabuf.h global.h output.h input.h tree.h encoding.h encoding.c flow.o: config.h acme.h alu.h dynabuf.h global.h input.h mnemo.h symbol.h tree.h flow.h flow.c global.o: config.h platform.h acme.h cpu.h input.h macro.h pseudoopcodes.h section.h symbol.h global.h global.c input.o: config.h alu.h dynabuf.h global.h section.h symbol.h tree.h input.h input.c macro.o: config.h acme.h alu.h dynabuf.h global.h input.h section.h symbol.h tree.h macro.h macro.c mnemo.o: config.h alu.h cpu.h dynabuf.h global.h input.h output.h tree.h mnemo.h mnemo.c output.o: config.h acme.h alu.h cpu.h dynabuf.h global.h input.h tree.h output.h output.c platform.o: config.h platform.h platform.c pseudoopcodes.o: acme.h alu.h global.h input.h macro.h output.h pseudoopcodes.h pseudoopcodes.c section.o: config.h dynabuf.h global.h symbol.h tree.h section.h section.c symbol.o: config.h acme.h alu.h cpu.h dynabuf.h global.h input.h section.h tree.h symbol.h symbol.c tree.o: config.h dynabuf.h global.h symbol.h tree.h tree.c typesystem.o: config.h global.h typesystem.h typesystem.c clean: wipe o.* ~c # -$(RM) -f *.o $(PROGS) *~ core #install: all # install -d $(BINDIR) # install $(PROGS) $(BINDIR) #userinstall: all # install -d $(USERBIN) # install $(PROGS) $(USERBIN) # DO NOT DELETE acme-crossassembler-0.96.4/src/_amiga.h000066400000000000000000000027471333777125400177470ustar00rootroot00000000000000// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. // Copyright (C) 1998-2016 Marco Baye // Have a look at "acme.c" for further info // // Platform specific stuff (in this case, for AmigaOS) #ifndef platform_H #define platform_H // symbolic constants and macros // called once on program startup (could register exit handler, if needed) #define PLATFORM_INIT // convert UNIX-style pathname to Amiga-style pathname (no change) #define PLATFORM_CONVERTPATHCHAR(a) (a) // directory separator for include paths #define DIRECTORY_SEPARATOR '\0' // actually '/', but paths ending on ':' are ok, so auto-adding '/' is bad) // string containing the prefix for accessing files from the library tree #define PLATFORM_LIBPREFIX "progdir:acme_lib/" #define NO_NEED_FOR_ENV_VAR // setting file types of created files #define PLATFORM_SETFILETYPE_APPLE(a) #define PLATFORM_SETFILETYPE_CBM(a) #define PLATFORM_SETFILETYPE_PLAIN(a) #define PLATFORM_SETFILETYPE_TEXT(a) // platform specific message output #define PLATFORM_WARNING(a) #define PLATFORM_ERROR(a) #define PLATFORM_SERIOUS(a) // integer-to-character conversion #define PLATFORM_UINT2CHAR(x) \ do { \ x ^= x >> 16; \ x ^= x >> 8; \ x &= 255; \ } while (0) // output of platform-specific command line switches #define PLATFORM_OPTION_HELP // processing of platform-specific command line switches #define PLATFORM_SHORTOPTION_CODE #define PLATFORM_LONGOPTION_CODE // other stuff #ifdef __SASC__ #define inline #endif #endif acme-crossassembler-0.96.4/src/_dos.c000066400000000000000000000021551333777125400174420ustar00rootroot00000000000000// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. // Copyright (C) 1998-2016 Marco Baye // Have a look at "acme.c" for further info // // Platform specific stuff (in this case, for DOS, OS/2 and Windows) #ifndef platform_C #define platform_C #include #include "dynabuf.h" // variables char *DOS_lib_prefix = NULL; // header string of library tree // used as PLATFORM_INIT: reads "ACME" environment variable void DOS_entry(void) { char *env_var; // Find out the path of ACME's library env_var = getenv("ACME"); // if environment variable was found, make a copy if (env_var) { DYNABUF_CLEAR(GlobalDynaBuf); // copy environment variable to global dynamic buffer DynaBuf_add_string(GlobalDynaBuf, env_var); DynaBuf_append(GlobalDynaBuf, '\\'); // add dir separator DynaBuf_append(GlobalDynaBuf, '\0'); // add terminator DOS_lib_prefix = DynaBuf_get_copy(GlobalDynaBuf); } } // convert UNIX-style pathname character to DOS-style pathname character char DOS_convert_path_char(char byte) { if (byte == '/') return '\\'; if (byte == '\\') return '/'; return byte; } #endif acme-crossassembler-0.96.4/src/_dos.h000066400000000000000000000032251333777125400174460ustar00rootroot00000000000000// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. // Copyright (C) 1998-2016 Marco Baye // Have a look at "acme.c" for further info // // Platform specific stuff (in this case, for DOS, OS/2 and Windows) #ifndef platform_H #define platform_H #include "config.h" // symbolic constants and macros // called once on program startup (could register exit handler, if needed) #define PLATFORM_INIT DOS_entry() // convert UNIX-style pathname to DOS-style pathname #define PLATFORM_CONVERTPATHCHAR(a) DOS_convert_path_char(a) // directory separator for include paths #define DIRECTORY_SEPARATOR '\\' // string containing the prefix for accessing files from the library tree #define PLATFORM_LIBPREFIX DOS_lib_prefix // setting file types of created files #define PLATFORM_SETFILETYPE_APPLE(a) #define PLATFORM_SETFILETYPE_CBM(a) #define PLATFORM_SETFILETYPE_PLAIN(a) #define PLATFORM_SETFILETYPE_TEXT(a) // platform specific message output #define PLATFORM_WARNING(a) #define PLATFORM_ERROR(a) #define PLATFORM_SERIOUS(a) // integer-to-character conversion #define PLATFORM_UINT2CHAR(x) \ do { \ x ^= x >> 16; \ x ^= x >> 8; \ x &= 255; \ } while (0) // output of platform-specific command line switches #define PLATFORM_OPTION_HELP // processing of platform-specific command line switches #define PLATFORM_SHORTOPTION_CODE #define PLATFORM_LONGOPTION_CODE // variables extern char *DOS_lib_prefix; // header string of library tree // used as PLATFORM_INIT: reads "ACME" environment variable extern void DOS_entry(void); // Convert UNIX-style pathname character to DOS-style pathname character extern char DOS_convert_path_char(char); #endif acme-crossassembler-0.96.4/src/_riscos.c000066400000000000000000000044311333777125400201560ustar00rootroot00000000000000// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. // Copyright (C) 1998-2016 Marco Baye // Have a look at "acme.c" for further info // // Platform specific stuff (in this case, for RISC OS) #ifndef platform_C #define platform_C #include #include #include "acme.h" #include "input.h" // constants // SWIs #define OS_FILE 0x00008 #define XDDEUTILS_THROWBACKSTART 0x62587 #define XDDEUTILS_THROWBACKSEND 0x62588 #define XDDEUTILS_THROWBACKEND 0x62589 // variables int RISCOS_flags = 0; // used to store platform-specific flags // exit handler: if throwback was used, de-register now void RISCOS_exit(void) { _kernel_swi_regs regs; if (RISCOS_flags & RISCOSFLAG_THROWN) { _kernel_swi(XDDEUTILS_THROWBACKEND, ®s, ®s); RISCOS_flags &= ~RISCOSFLAG_THROWN; } } // used as PLATFORM_INIT: registers exit handler void RISCOS_entry(void) { atexit(RISCOS_exit); } // convert UNIX-style pathname to RISC OS-style pathname char RISCOS_convert_path_char(char byte) { if (byte == '.') return '/'; if (byte == '/') return '.'; if (byte == '?') return '#'; if (byte == '#') return '?'; return byte; } // setting the created files' types void RISCOS_set_filetype(const char *filename, int file_type) { _kernel_swi_regs regs; regs.r[0] = 18; // reason code (set file type) regs.r[1] = (int) filename; regs.r[2] = file_type; _kernel_swi(OS_FILE, ®s, ®s); } // throwback protocol: "type" can be 0, 1 or 2 (DDEUtils message types) void RISCOS_throwback(const char *message, int type) { _kernel_swi_regs regs; // only use throwback protocol if wanted if ((RISCOS_flags & RISCOSFLAG_THROWBACK) == 0) return; // if this is the first throwback, set it up and send info if ((RISCOS_flags & RISCOSFLAG_THROWN) == 0) { RISCOS_flags |= RISCOSFLAG_THROWN; _kernel_swi(XDDEUTILS_THROWBACKSTART, ®s, ®s); regs.r[0] = 0; regs.r[1] = 0; // regs.r[2] = (int) toplevel_source; regs.r[2] = (int) Input_now->original_filename; _kernel_swi(XDDEUTILS_THROWBACKSEND, ®s, ®s); } // send throwback message regs.r[0] = 1; regs.r[1] = 0; regs.r[2] = (int) Input_now->original_filename; regs.r[3] = Input_now->line_number; regs.r[4] = type; regs.r[5] = (int) message; _kernel_swi(XDDEUTILS_THROWBACKSEND, ®s, ®s); } #endif acme-crossassembler-0.96.4/src/_riscos.h000066400000000000000000000047321333777125400201670ustar00rootroot00000000000000// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. // Copyright (C) 1998-2016 Marco Baye // Have a look at "acme.c" for further info // // Platform specific stuff (in this case, for RISC OS) #ifndef platform_H #define platform_H #include "config.h" // symbolic constants and macros // called once on program startup (could register exit handler, if needed) #define PLATFORM_INIT RISCOS_entry() // convert UNIX-style pathname to RISC OS-style pathname #define PLATFORM_CONVERTPATHCHAR(a) RISCOS_convert_path_char(a) // directory separator for include paths #define DIRECTORY_SEPARATOR '\0' // actually '.', but paths ending on ':' are ok, so auto-adding '.' is bad) // string containing the prefix for accessing files from the library tree #define PLATFORM_LIBPREFIX "ACME_Lib:" #define NO_NEED_FOR_ENV_VAR // setting file types of created files #define PLATFORM_SETFILETYPE_APPLE(a) RISCOS_set_filetype(a, 0xffd) // FIXME - wrong value! #define PLATFORM_SETFILETYPE_CBM(a) RISCOS_set_filetype(a, 0x064) #define PLATFORM_SETFILETYPE_PLAIN(a) RISCOS_set_filetype(a, 0xffd) #define PLATFORM_SETFILETYPE_TEXT(a) RISCOS_set_filetype(a, 0xfff) // platform specific message output #define PLATFORM_WARNING(a) RISCOS_throwback(a, 0) #define PLATFORM_ERROR(a) RISCOS_throwback(a, 1) #define PLATFORM_SERIOUS(a) RISCOS_throwback(a, 2) // integer-to-character conversion #define PLATFORM_UINT2CHAR(x) \ do { \ x ^= x >> 16; \ x ^= x >> 8; \ x &= 255; \ } while (0) // output of platform-specific command line switches #define PLATFORM_OPTION_HELP \ " -t, --throwback use the DDEUtils module's \"throwback\" protocol\n" // processing of platform-specific command line switches #define PLATFORM_SHORTOPTION_CODE \ case 't': \ RISCOS_flags |= RISCOSFLAG_THROWBACK; \ break; #define PLATFORM_LONGOPTION_CODE \ else if (strcmp(string, "throwback") == 0) \ RISCOS_flags |= RISCOSFLAG_THROWBACK; // variables extern int RISCOS_flags; // Holds platform-specific flags #define RISCOSFLAG_THROWBACK (1u << 0) // use throwback protocol #define RISCOSFLAG_THROWN (1u << 1) // throwback is active // used as PLATFORM_INIT: registers exit handler extern void RISCOS_entry(void); // convert UNIX-style pathname to RISC OS-style pathname extern char RISCOS_convert_path_char(char); // setting the created files' types extern void RISCOS_set_filetype(const char *, int); // use DDEUtils module's "Throwback" protocol extern void RISCOS_throwback(const char *, int); #endif acme-crossassembler-0.96.4/src/_std.c000066400000000000000000000016441333777125400174510ustar00rootroot00000000000000// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. // Copyright (C) 1998-2016 Marco Baye // Have a look at "acme.c" for further info // // Platform specific stuff (in this case, for unknown OSes) #ifndef platform_C #define platform_C #include #include "dynabuf.h" // variables char *AnyOS_lib_prefix = NULL; // header string of library tree // used as PLATFORM_INIT: reads "ACME" environment variable void AnyOS_entry(void) { char *env_var; // Find out the path of ACME's library env_var = getenv("ACME"); // if environment variable was found, make a copy if (env_var) { DYNABUF_CLEAR(GlobalDynaBuf); // copy environment variable to global dynamic buffer DynaBuf_add_string(GlobalDynaBuf, env_var); DynaBuf_append(GlobalDynaBuf, '/'); // add dir separator DynaBuf_append(GlobalDynaBuf, '\0'); // add terminator AnyOS_lib_prefix = DynaBuf_get_copy(GlobalDynaBuf); } } #endif acme-crossassembler-0.96.4/src/_std.h000066400000000000000000000030011333777125400174430ustar00rootroot00000000000000// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. // Copyright (C) 1998-2016 Marco Baye // Have a look at "acme.c" for further info // // Platform specific stuff (in this case, for unknown OSes) #ifndef platform_H #define platform_H // symbolic constants and macros // called once on program startup (could register exit handler, if needed) #define PLATFORM_INIT AnyOS_entry() // convert UNIX-style pathname to AnyOS-style pathname (no change) #define PLATFORM_CONVERTPATHCHAR(a) (a) // directory separator for include paths #define DIRECTORY_SEPARATOR '/' // string containing the prefix for accessing files from the library tree #define PLATFORM_LIBPREFIX AnyOS_lib_prefix // setting the created files' types #define PLATFORM_SETFILETYPE_APPLE(a) #define PLATFORM_SETFILETYPE_CBM(a) #define PLATFORM_SETFILETYPE_PLAIN(a) #define PLATFORM_SETFILETYPE_TEXT(a) // platform specific message output #define PLATFORM_WARNING(a) #define PLATFORM_ERROR(a) #define PLATFORM_SERIOUS(a) // integer-to-character conversion #define PLATFORM_UINT2CHAR(x) \ do { \ x ^= x >> 16; \ x ^= x >> 8; \ x &= 255; \ } while (0) // output of platform-specific command line switches #define PLATFORM_OPTION_HELP // processing of platform-specific command line switches #define PLATFORM_SHORTOPTION_CODE #define PLATFORM_LONGOPTION_CODE // variables extern char *AnyOS_lib_prefix; // header string of library tree // used as PLATFORM_INIT: reads "ACME" environment variable extern void AnyOS_entry(void); #endif acme-crossassembler-0.96.4/src/acme.c000066400000000000000000000444061333777125400174300ustar00rootroot00000000000000// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. // Copyright (C) 1998-2017 Marco Baye // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "acme.h" #include #include #include #include "alu.h" #include "cliargs.h" #include "config.h" #include "cpu.h" #include "dynabuf.h" #include "encoding.h" #include "flow.h" #include "global.h" #include "input.h" #include "macro.h" #include "mnemo.h" #include "output.h" #include "platform.h" #include "pseudoopcodes.h" #include "section.h" #include "symbol.h" #include "version.h" // constants static const char FILE_WRITETEXT[] = "w"; static const char FILE_WRITEBINARY[] = "wb"; // names for error messages static const char name_outfile[] = "output filename"; static const char arg_symbollist[] = "symbol list filename"; static const char arg_reportfile[] = "report filename"; static const char arg_vicelabels[] = "VICE labels filename"; // long options #define OPTION_HELP "help" #define OPTION_FORMAT "format" #define OPTION_OUTFILE "outfile" #define OPTION_LABELDUMP "labeldump" // old #define OPTION_SYMBOLLIST "symbollist" // new #define OPTION_VICELABELS "vicelabels" #define OPTION_REPORT "report" #define OPTION_SETPC "setpc" #define OPTION_CPU "cpu" #define OPTION_INITMEM "initmem" #define OPTION_MAXERRORS "maxerrors" #define OPTION_MAXDEPTH "maxdepth" #define OPTION_USE_STDOUT "use-stdout" #define OPTION_VERSION "version" #define OPTION_MSVC "msvc" #define OPTION_COLOR "color" #define OPTION_FULLSTOP "fullstop" // options for "-W" #define OPTIONWNO_LABEL_INDENT "no-label-indent" #define OPTIONWNO_OLD_FOR "no-old-for" #define OPTIONWTYPE_MISMATCH "type-mismatch" // variables static const char **toplevel_sources; static int toplevel_src_count = 0; #define ILLEGAL_START_ADDRESS (-1) static signed long start_address = ILLEGAL_START_ADDRESS; static signed long fill_value = MEMINIT_USE_DEFAULT; static const struct cpu_type *default_cpu = NULL; const char *symbollist_filename = NULL; const char *vicelabels_filename = NULL; const char *output_filename = NULL; const char *report_filename = NULL; // maximum recursion depth for macro calls and "!source" signed long macro_recursions_left = MAX_NESTING; signed long source_recursions_left = MAX_NESTING; // show release and platform info (and exit, if wanted) static void show_version(int exit_after) { puts( "This is ACME, release " RELEASE " (\"" CODENAME "\"), " CHANGE_DATE " " CHANGE_YEAR "\n" " " PLATFORM_VERSION); if (exit_after) exit(EXIT_SUCCESS); } // show full help (headline, release/platform/version, copyright, dedication, // warranty disclaimer, usage) and exit program (SUCCESS) static void show_help_and_exit(void) { puts( "ACME - the ACME Crossassembler for Multiple Environments\n" " Copyright (C) 1998-" CHANGE_YEAR " Marco Baye"); show_version(FALSE); puts( "ACME comes with ABSOLUTELY NO WARRANTY; for details read the help file.\n" " This is free software, and you are welcome to redistribute it under\n" " certain conditions; as outlined in the GNU General Public License.\n" "Dedicated to the wisest being I ever had the pleasure of reading\n" " books of (currently spending some time dead for tax reasons).\n" "The newest version can be found at the ACME homepage:\n" " " HOME_PAGE "\n" "\n" "Usage:\n" "acme [OPTION...] [FILE]...\n" "\n" "Options:\n" " -h, --" OPTION_HELP " show this help and exit\n" " -f, --" OPTION_FORMAT " FORMAT set output file format\n" " -o, --" OPTION_OUTFILE " FILE set output file name\n" " -r, --" OPTION_REPORT " FILE set report file name\n" " -l, --" OPTION_SYMBOLLIST " FILE set symbol list file name\n" " --" OPTION_LABELDUMP " (old name for --" OPTION_SYMBOLLIST ")\n" " --" OPTION_VICELABELS " FILE set file name for label dump in VICE format\n" " --" OPTION_SETPC " NUMBER set program counter\n" " --" OPTION_CPU " CPU set target processor\n" " --" OPTION_INITMEM " NUMBER define 'empty' memory\n" " --" OPTION_MAXERRORS " NUMBER set number of errors before exiting\n" " --" OPTION_MAXDEPTH " NUMBER set recursion depth for macro calls and !src\n" " -vDIGIT set verbosity level\n" " -DSYMBOL=VALUE define global symbol\n" " -I PATH/TO/DIR add search path for input files\n" // as long as there is only one -W option: #define OPTIONWNO_LABEL_INDENT "no-label-indent" " -W" OPTIONWNO_LABEL_INDENT " suppress warnings about indented labels\n" " -W" OPTIONWNO_OLD_FOR " suppress warnings about old \"!for\" syntax\n" " -W" OPTIONWTYPE_MISMATCH " enable type checking (warn about type mismatch)\n" // when there are more, use next line and add a separate function: //" -W show warning level options\n" " --" OPTION_USE_STDOUT " fix for 'Relaunch64' IDE (see docs)\n" " --" OPTION_MSVC " output errors in MS VS format\n" " --" OPTION_COLOR " uses ANSI color codes for error output\n" " --" OPTION_FULLSTOP " use '.' as pseudo opcode prefix\n" PLATFORM_OPTION_HELP " -V, --" OPTION_VERSION " show version and exit\n"); exit(EXIT_SUCCESS); } // initialise report struct static void report_init(struct report *report) { report->fd = NULL; report->asc_used = 0; report->bin_used = 0; report->last_input = NULL; } // open report file static int report_open(struct report *report, const char *filename) { report->fd = fopen(filename, FILE_WRITETEXT); if (report->fd == NULL) { fprintf(stderr, "Error: Cannot open report file \"%s\".\n", filename); return 1; } return 0; // success } // close report file static void report_close(struct report *report) { if (report && report->fd) { fclose(report->fd); report->fd = NULL; } } // error handling // tidy up before exiting by saving symbol list and close other output files int ACME_finalize(int exit_code) { FILE *fd; report_close(report); if (symbollist_filename) { fd = fopen(symbollist_filename, FILE_WRITETEXT); // FIXME - what if filename is given via !sl in sub-dir? fix path! if (fd) { symbols_list(fd); fclose(fd); PLATFORM_SETFILETYPE_TEXT(symbollist_filename); } else { fprintf(stderr, "Error: Cannot open symbol list file \"%s\".\n", symbollist_filename); exit_code = EXIT_FAILURE; } } if (vicelabels_filename) { fd = fopen(vicelabels_filename, FILE_WRITETEXT); if (fd) { symbols_vicelabels(fd); fclose(fd); PLATFORM_SETFILETYPE_TEXT(vicelabels_filename); } else { fprintf(stderr, "Error: Cannot open VICE label dump file \"%s\".\n", vicelabels_filename); exit_code = EXIT_FAILURE; } } return exit_code; } // save output file static void save_output_file(void) { FILE *fd; // if no output file chosen, tell user and do nothing if (output_filename == NULL) { fputs("No output file specified (use the \"-o\" option or the \"!to\" pseudo opcode).\n", stderr); return; } fd = fopen(output_filename, FILE_WRITEBINARY); // FIXME - what if filename is given via !to in sub-dir? fix path! if (fd == NULL) { fprintf(stderr, "Error: Cannot open output file \"%s\".\n", output_filename); return; } Output_save_file(fd); fclose(fd); } // perform a single pass. Returns number of "NeedValue" type errors. static int perform_pass(void) { FILE *fd; int ii; // call modules' "pass init" functions Output_passinit(); // disable output, PC undefined cputype_passinit(default_cpu); // set default cpu type // if start address was given on command line, use it: if (start_address != ILLEGAL_START_ADDRESS) vcpu_set_pc(start_address, 0); encoding_passinit(); // set default encoding section_passinit(); // set initial zone (untitled) // init variables pass_undefined_count = 0; // no "NeedValue" errors yet pass_real_errors = 0; // no real errors yet // Process toplevel files for (ii = 0; ii < toplevel_src_count; ++ii) { if ((fd = fopen(toplevel_sources[ii], FILE_READBINARY))) { flow_parse_and_close_file(fd, toplevel_sources[ii]); } else { fprintf(stderr, "Error: Cannot open toplevel file \"%s\".\n", toplevel_sources[ii]); if (toplevel_sources[ii][0] == '-') fprintf(stderr, "Options (starting with '-') must be given _before_ source files!\n"); ++pass_real_errors; } } if (pass_real_errors) exit(ACME_finalize(EXIT_FAILURE)); else Output_end_segment(); return pass_undefined_count; } static struct report global_report; // do passes until done (or errors occurred). Return whether output is ready. static int do_actual_work(void) { int undefined_prev, // "NeedValue" errors of previous pass undefined_curr; // "NeedValue" errors of current pass report = &global_report; // let global pointer point to something report_init(report); // we must init struct before doing passes if (config.process_verbosity > 1) puts("First pass."); pass_count = 0; undefined_curr = perform_pass(); // First pass // now pretend there has been a pass before the first one undefined_prev = undefined_curr + 1; // As long as the number of "NeedValue" errors is decreasing but // non-zero, keep doing passes. while (undefined_curr && (undefined_curr < undefined_prev)) { ++pass_count; undefined_prev = undefined_curr; if (config.process_verbosity > 1) puts("Further pass."); undefined_curr = perform_pass(); } // any errors left? if (undefined_curr == 0) { // if listing report is wanted and there were no errors, // do another pass to generate listing report if (report_filename) { if (config.process_verbosity > 1) puts("Extra pass to generate listing report."); if (report_open(report, report_filename) == 0) { ++pass_count; perform_pass(); report_close(report); } } return 1; } // There are still errors (unsolvable by doing further passes), // so perform additional pass to find and show them. if (config.process_verbosity > 1) puts("Extra pass needed to find error."); // activate error output ALU_optional_notdef_handler = Throw_error; ++pass_count; perform_pass(); // perform pass, but now show "value undefined" return 0; } // copy string to DynaBuf static void keyword_to_dynabuf(const char keyword[]) { DYNABUF_CLEAR(GlobalDynaBuf); DynaBuf_add_string(GlobalDynaBuf, keyword); DynaBuf_append(GlobalDynaBuf, '\0'); DynaBuf_to_lower(GlobalDynaBuf, GlobalDynaBuf); // convert to lower case } // check output format (the output format tree must be set up at this point!) static void set_output_format(void) { keyword_to_dynabuf(cliargs_safe_get_next("output format")); if (outputfile_set_format()) { fprintf(stderr, "%sUnknown output format (known formats are: %s).\n", cliargs_error, outputfile_formats); exit(EXIT_FAILURE); } } // check CPU type (the cpu type tree must be set up at this point!) static void set_starting_cpu(void) { const struct cpu_type *new_cpu_type; keyword_to_dynabuf(cliargs_safe_get_next("CPU type")); new_cpu_type = cputype_find(); if (new_cpu_type) { default_cpu = new_cpu_type; } else { fprintf(stderr, "%sUnknown CPU type (known types are: %s).\n", cliargs_error, cputype_names); exit(EXIT_FAILURE); } } static void could_not_parse(const char strange[]) { fprintf(stderr, "%sCould not parse '%s'.\n", cliargs_error, strange); exit(EXIT_FAILURE); } // return signed long representation of string. // copes with hexadecimal if prefixed with "$", "0x" or "0X". // copes with octal if prefixed with "&". // copes with binary if prefixed with "%". // assumes decimal otherwise. static signed long string_to_number(const char *string) { signed long result; char *end; int base = 10; if (*string == '%') { base = 2; ++string; } else if (*string == '&') { base = 8; ++string; } else if (*string == '$') { base = 16; ++string; } else if ((*string == '0') && ((string[1] == 'x') || (string[1] == 'X'))) { base = 16; string += 2; } result = strtol(string, &end, base); if (*end) could_not_parse(end); return result; } // set program counter static void set_starting_pc(void) { start_address = string_to_number(cliargs_safe_get_next("program counter")); if ((start_address > -1) && (start_address < 65536)) return; fprintf(stderr, "%sProgram counter out of range (0-0xffff).\n", cliargs_error); exit(EXIT_FAILURE); } // set initial memory contents static void set_mem_contents(void) { fill_value = string_to_number(cliargs_safe_get_next("initmem value")); if ((fill_value >= -128) && (fill_value <= 255)) return; fprintf(stderr, "%sInitmem value out of range (0-0xff).\n", cliargs_error); exit(EXIT_FAILURE); } // define symbol static void define_symbol(const char definition[]) { const char *walk = definition; signed long value; // copy definition to GlobalDynaBuf until '=' reached DYNABUF_CLEAR(GlobalDynaBuf); while ((*walk != '=') && (*walk != '\0')) DynaBuf_append(GlobalDynaBuf, *walk++); if ((*walk == '\0') || (walk[1] == '\0')) could_not_parse(definition); value = string_to_number(walk + 1); DynaBuf_append(GlobalDynaBuf, '\0'); symbol_define(value); } // handle long options (like "--example"). Return unknown string. static const char *long_option(const char *string) { if (strcmp(string, OPTION_HELP) == 0) show_help_and_exit(); else if (strcmp(string, OPTION_FORMAT) == 0) set_output_format(); else if (strcmp(string, OPTION_OUTFILE) == 0) output_filename = cliargs_safe_get_next(name_outfile); else if (strcmp(string, OPTION_LABELDUMP) == 0) // old symbollist_filename = cliargs_safe_get_next(arg_symbollist); else if (strcmp(string, OPTION_SYMBOLLIST) == 0) // new symbollist_filename = cliargs_safe_get_next(arg_symbollist); else if (strcmp(string, OPTION_VICELABELS) == 0) vicelabels_filename = cliargs_safe_get_next(arg_vicelabels); else if (strcmp(string, OPTION_REPORT) == 0) report_filename = cliargs_safe_get_next(arg_reportfile); else if (strcmp(string, OPTION_SETPC) == 0) set_starting_pc(); else if (strcmp(string, OPTION_CPU) == 0) set_starting_cpu(); else if (strcmp(string, OPTION_INITMEM) == 0) set_mem_contents(); else if (strcmp(string, OPTION_MAXERRORS) == 0) config.max_errors = string_to_number(cliargs_safe_get_next("maximum error count")); else if (strcmp(string, OPTION_MAXDEPTH) == 0) macro_recursions_left = (source_recursions_left = string_to_number(cliargs_safe_get_next("recursion depth"))); // else if (strcmp(string, "strictsyntax") == 0) // strict_syntax = TRUE; else if (strcmp(string, OPTION_USE_STDOUT) == 0) msg_stream = stdout; else if (strcmp(string, OPTION_MSVC) == 0) config.format_msvc = TRUE; else if (strcmp(string, OPTION_FULLSTOP) == 0) config.pseudoop_prefix = '.'; PLATFORM_LONGOPTION_CODE else if (strcmp(string, OPTION_COLOR) == 0) config.format_color = TRUE; else if (strcmp(string, OPTION_VERSION) == 0) show_version(TRUE); else return string; return NULL; } // handle short options (like "-e"). Return unknown character. static char short_option(const char *argument) { while (*argument) { switch (*argument) { case 'D': // "-D" define constants define_symbol(argument + 1); goto done; case 'f': // "-f" selects output format set_output_format(); break; case 'h': // "-h" shows help show_help_and_exit(); break; case 'I': // "-I" adds an include directory if (argument[1]) includepaths_add(argument + 1); else includepaths_add(cliargs_safe_get_next("include path")); goto done; case 'l': // "-l" selects symbol list filename symbollist_filename = cliargs_safe_get_next(arg_symbollist); break; case 'o': // "-o" selects output filename output_filename = cliargs_safe_get_next(name_outfile); break; case 'r': // "-r" selects report filename report_filename = cliargs_safe_get_next(arg_reportfile); break; case 'v': // "-v" changes verbosity ++config.process_verbosity; if ((argument[1] >= '0') && (argument[1] <= '9')) config.process_verbosity = *(++argument) - '0'; break; // platform specific switches are inserted here PLATFORM_SHORTOPTION_CODE case 'V': // "-V" shows version show_version(TRUE); break; case 'W': // "-W" tunes warning level if (strcmp(argument + 1, OPTIONWNO_LABEL_INDENT) == 0) { config.warn_on_indented_labels = FALSE; goto done; } else if (strcmp(argument + 1, OPTIONWNO_OLD_FOR) == 0) { config.warn_on_old_for = FALSE; goto done; } else if (strcmp(argument + 1, OPTIONWTYPE_MISMATCH) == 0) { config.warn_on_type_mismatch = TRUE; goto done; } else { fprintf(stderr, "%sUnknown warning level.\n", cliargs_error); exit(EXIT_FAILURE); } break; default: // unknown ones: program termination return *argument; } ++argument; } done: return '\0'; } // guess what int main(int argc, const char *argv[]) { config_default(&config); // if called without any arguments, show usage info (not full help) if (argc == 1) show_help_and_exit(); msg_stream = stderr; cliargs_init(argc, argv); DynaBuf_init(); // inits *global* dynamic buffer - important, so first // Init platform-specific stuff. // For example, this could read the library path from an // environment variable, which in turn may need DynaBuf already. PLATFORM_INIT; // prepare a buffer large enough to hold pointers to "-D" switch values // cli_defines = safe_malloc(argc * sizeof(*cli_defines)); includepaths_init(); // must be done before cli arg handling // handle command line arguments cliargs_handle_options(short_option, long_option); // generate list of files to process cliargs_get_rest(&toplevel_src_count, &toplevel_sources, "No top level sources given"); // Init modules (most of them will just build keyword trees) ALU_init(); Macro_init(); Mnemo_init(); Output_init(fill_value); pseudoopcodes_init(); // setup keyword tree for pseudo opcodes if (do_actual_work()) save_output_file(); return ACME_finalize(EXIT_SUCCESS); // dump labels, if wanted } acme-crossassembler-0.96.4/src/acme.h000066400000000000000000000012421333777125400174240ustar00rootroot00000000000000// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. // Copyright (C) 1998-2016 Marco Baye // Have a look at "acme.c" for further info // // Main definitions #ifndef acme_H #define acme_H #include "config.h" // Variables extern const char *symbollist_filename; extern const char *output_filename; // TODO - put in "part" struct extern const char *report_filename; // TODO - put in "part" struct // maximum recursion depth for macro calls and "!source" extern signed long macro_recursions_left; extern signed long source_recursions_left; // Prototypes // tidy up before exiting by saving symbol dump extern int ACME_finalize(int exit_code); #endif acme-crossassembler-0.96.4/src/alu.c000066400000000000000000001461421333777125400173040ustar00rootroot00000000000000// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. // Copyright (C) 1998-2017 Marco Baye // Have a look at "acme.c" for further info // // Arithmetic/logic unit // 11 Oct 2006 Improved float reading in parse_decimal_value() // 24 Nov 2007 Now accepts floats starting with decimal point // 31 Jul 2009 Changed ASR again, just to be on the safe side. // 14 Jan 2014 Changed associativity of "power-of" operator, // so a^b^c now means a^(b^c). // 7 May 2014 C-style "==" operators are now recognized (but // give a warning). // 31 May 2014 Added "0b" binary number prefix as alternative to "%". // 28 Apr 2015 Added symbol name output to "value not defined" error. #include "alu.h" #include #include // only for fp support #include "platform.h" #include "dynabuf.h" #include "encoding.h" #include "global.h" #include "input.h" #include "output.h" #include "section.h" #include "symbol.h" #include "tree.h" // constants #define ERRORMSG_DYNABUF_INITIALSIZE 256 // ad hoc #define FUNCTION_DYNABUF_INITIALSIZE 8 // enough for "arctan" #define UNDEFSYM_DYNABUF_INITIALSIZE 256 // ad hoc #define HALF_INITIAL_STACK_SIZE 8 static const char exception_div_by_zero[] = "Division by zero."; static const char exception_no_value[] = "No value given."; static const char exception_paren_open[] = "Too many '('."; #define s_or (s_eor + 1) // Yes, I know I'm sick #define s_xor (s_scrxor + 3) // Yes, I know I'm sick static const char s_arcsin[] = "arcsin"; #define s_sin (s_arcsin + 3) // Yes, I know I'm sick static const char s_arccos[] = "arccos"; #define s_cos (s_arccos + 3) // Yes, I know I'm sick static const char s_arctan[] = "arctan"; #define s_tan (s_arctan + 3) // Yes, I know I'm sick // operator handles (FIXME - use function pointers instead? or too slow?) enum operator_handle { // special values (pseudo operators) OPHANDLE_EXPRSTART, // "start of expression" OPHANDLE_EXPREND, // "end of expression" // functions OPHANDLE_ADDR, // addr(v) OPHANDLE_INT, // int(v) OPHANDLE_FLOAT, // float(v) OPHANDLE_SIN, // sin(v) OPHANDLE_COS, // cos(v) OPHANDLE_TAN, // tan(v) OPHANDLE_ARCSIN, // arcsin(v) OPHANDLE_ARCCOS, // arccos(v) OPHANDLE_ARCTAN, // arctan(v) // monadic operators OPHANDLE_OPENING, // (v '(', starts subexpression OPHANDLE_NOT, // !v NOT v bit-wise NOT OPHANDLE_NEGATE, // -v Negate OPHANDLE_LOWBYTEOF, // v Highbyte of OPHANDLE_BANKBYTEOF, // ^v Bankbyte of // dyadic operators OPHANDLE_CLOSING, // v) ')', ends subexpression OPHANDLE_POWEROF, // v^w OPHANDLE_MULTIPLY, // v*w OPHANDLE_DIVIDE, // v/w (Integer) Division OPHANDLE_INTDIV, // v/w v DIV w Integer Division OPHANDLE_MODULO, // v%w v MOD w Remainder OPHANDLE_SL, // v<>w v ASR w Arithmetic shift right OPHANDLE_LSR, // v>>>w v LSR w Logical shift right OPHANDLE_ADD, // v+w OPHANDLE_SUBTRACT, // v-w OPHANDLE_EQUALS, // v=w OPHANDLE_LE, // v<=w OPHANDLE_LESSTHAN, // v< w OPHANDLE_GE, // v>=w OPHANDLE_GREATERTHAN, // v> w OPHANDLE_NOTEQUAL, // v!=w v<>w v>buffer); DynaBuf_add_string(errormsg_dyna_buf, ")."); DynaBuf_append(errormsg_dyna_buf, '\0'); return errormsg_dyna_buf->buffer; } // function pointer for "value undefined" error output. set to NULL to suppress those errors. void (*ALU_optional_notdef_handler)(const char *) = NULL; // function to handle "result is undefined" type errors. // maybe split this into "_result_ is undefined" (in that case, count) and "symbol is undefined" (in that case, call handler) static void result_is_undefined(void) { ++pass_undefined_count; if (ALU_optional_notdef_handler) ALU_optional_notdef_handler(value_not_defined()); } // enlarge operator stack static void enlarge_operator_stack(void) { operator_stk_size *= 2; operator_stack = realloc(operator_stack, operator_stk_size * sizeof(*operator_stack)); if (operator_stack == NULL) Throw_serious_error(exception_no_memory_left); } // enlarge operand stack static void enlarge_operand_stack(void) { operand_stk_size *= 2; operand_stack = realloc(operand_stack, operand_stk_size * sizeof(*operand_stack)); if (operand_stack == NULL) Throw_serious_error(exception_no_memory_left); } // create dynamic buffer, operator/function trees and operator/operand stacks void ALU_init(void) { errormsg_dyna_buf = DynaBuf_create(ERRORMSG_DYNABUF_INITIALSIZE); function_dyna_buf = DynaBuf_create(FUNCTION_DYNABUF_INITIALSIZE); undefsym_dyna_buf = DynaBuf_create(UNDEFSYM_DYNABUF_INITIALSIZE); Tree_add_table(&operator_tree, operator_list); Tree_add_table(&function_tree, function_list); enlarge_operator_stack(); enlarge_operand_stack(); } // not-so-braindead algorithm for calculating "to the power of" function for // integer operands. // my_pow(whatever, 0) returns 1. my_pow(0, whatever_but_zero) returns 0. static intval_t my_pow(intval_t mantissa, intval_t exponent) { intval_t result = 1; while (exponent) { // handle exponent's lowmost bit if (exponent & 1) result *= mantissa; // square the mantissa, halve the exponent mantissa *= mantissa; exponent >>= 1; } return result; } // arithmetic shift right (works even if C compiler does not support it) static intval_t my_asr(intval_t left, intval_t right) { // if first operand is positive or zero, ASR and LSR are equivalent, // so just do it and return the result: if (left >= 0) return left >> right; // However, if the first operand is negative, the result is // implementation-defined: While most compilers will do ASR, some others // might do LSR instead, and *theoretically*, it is even possible for a // compiler to define silly stuff like "shifting a negative value to the // right will always return -1". // Therefore, in case of a negative operand, we'll use this quick and // simple workaround: return ~((~left) >> right); } // if undefined, remember name for error output static void check_for_def(int flags, char optional_prefix_char, char *name, size_t length) { if ((flags & MVALUE_DEFINED) == 0) { DYNABUF_CLEAR(undefsym_dyna_buf); if (optional_prefix_char) { DynaBuf_append(undefsym_dyna_buf, optional_prefix_char); length++; } DynaBuf_add_string(undefsym_dyna_buf, name); if (length > undefsym_dyna_buf->size) { Bug_found("Illegal symbol name length", undefsym_dyna_buf->size - length); } else { undefsym_dyna_buf->size = length; } DynaBuf_append(undefsym_dyna_buf, '\0'); } } // Lookup (and create, if necessary) symbol tree item and return its value. // DynaBuf holds the symbol's name and "scope" its scope. // The name length must be given explicitly because of anonymous forward labels; // their internal name is different (longer) than their displayed name. // This function is not allowed to change DynaBuf because that's where the // symbol name is stored! static void get_symbol_value(scope_t scope, char optional_prefix_char, size_t name_length) { struct symbol *symbol; // if the symbol gets created now, mark it as unsure symbol = symbol_find(scope, MVALUE_UNSURE); // if needed, remember name for "undefined" error output check_for_def(symbol->result.flags, optional_prefix_char, GLOBALDYNABUF_CURRENT, name_length); // in first pass, count usage if (pass_count == 0) symbol->usage++; // push operand, regardless of whether int or float operand_stack[operand_sp] = symbol->result; operand_stack[operand_sp++].flags |= MVALUE_EXISTS; } // Parse quoted character. // The character will be converted using the current encoding. static void parse_quoted_character(char closing_quote) { intval_t value; // read character to parse - make sure not at end of statement if (GetQuotedByte() == CHAR_EOS) return; // on empty string, complain if (GotByte == closing_quote) { Throw_error(exception_missing_string); alu_state = STATE_ERROR; return; } // parse character value = (intval_t) encoding_encode_char(GotByte); // Read closing quote (hopefully) if (GetQuotedByte() == closing_quote) { GetByte(); // if length == 1, proceed with next byte } else { if (GotByte) { // if longer than one character Throw_error("There's more than one character."); alu_state = STATE_ERROR; } } PUSH_INTOPERAND(value, MVALUE_GIVEN | MVALUE_ISBYTE, 0); // Now GotByte = char following closing quote (or CHAR_EOS on error) } // Parse binary value. Apart from '0' and '1', it also accepts the characters // '.' and '#', this is much more readable. The current value is stored as soon // as a character is read that is none of those given above. static void parse_binary_value(void) // Now GotByte = "%" or "b" { intval_t value = 0; int go_on = TRUE, // continue loop flag flags = MVALUE_GIVEN, digits = -1; // digit counter do { ++digits; switch (GetByte()) { case '0': case '.': value <<= 1; break; case '1': case '#': value = (value << 1) | 1; break; default: go_on = 0; } } while (go_on); // set force bits if (digits > 8) { if (digits > 16) { if (value < 65536) flags |= MVALUE_FORCE24; } else { if (value < 256) flags |= MVALUE_FORCE16; } } PUSH_INTOPERAND(value, flags, 0); // Now GotByte = non-binary char } // Parse hexadecimal value. It accepts "0" to "9", "a" to "f" and "A" to "F". // Capital letters will be converted to lowercase letters using the flagtable. // The current value is stored as soon as a character is read that is none of // those given above. static void parse_hexadecimal_value(void) // Now GotByte = "$" or "x" { char byte; int go_on, // continue loop flag digits = -1, // digit counter flags = MVALUE_GIVEN; intval_t value = 0; do { ++digits; go_on = 0; byte = GetByte(); // first, convert "A-F" to "a-f" byte |= (BYTEFLAGS(byte) & BYTEIS_UPCASE); // if digit, add digit value if ((byte >= '0') && (byte <= '9')) { value = (value << 4) + (byte - '0'); go_on = 1; // keep going } // if legal ("a-f") character, add character value if ((byte >= 'a') && (byte <= 'f')) { value = (value << 4) + (byte - 'a') + 10; go_on = 1; // keep going } } while (go_on); // set force bits if (digits > 2) { if (digits > 4) { if (value < 65536) flags |= MVALUE_FORCE24; } else { if (value < 256) flags |= MVALUE_FORCE16; } } PUSH_INTOPERAND(value, flags, 0); // Now GotByte = non-hexadecimal char } // parse fractional part of a floating-point value static void parse_frac_part(int integer_part) // Now GotByte = first digit after decimal point { double denominator = 1, fpval = integer_part; // parse digits until no more while ((GotByte >= '0') && (GotByte <= '9')) { fpval = 10 * fpval + (GotByte & 15); // this works. it's ASCII. denominator *= 10; GetByte(); } // FIXME - add possibility to read 'e' and exponent! PUSH_FPOPERAND(fpval / denominator, MVALUE_GIVEN); } // Parse a decimal value. As decimal values don't use any prefixes, this // function expects the first digit to be read already. // If the first two digits are "0x", this function branches to the one for // parsing hexadecimal values. // If the first two digits are "0b", this function branches to the one for // parsing binary values. // If a decimal point is read, this function branches to the one for parsing // floating-point values. // This function accepts '0' through '9' and one dot ('.') as the decimal // point. The current value is stored as soon as a character is read that is // none of those given above. Float usage is only activated when a decimal // point has been found, so don't expect "100000000000000000000" to work. // CAUTION: "100000000000000000000.0" won't work either, because when the // decimal point gets parsed, the integer value will have overflown already. static void parse_decimal_value(void) // Now GotByte = first digit { intval_t intval = (GotByte & 15); // this works. it's ASCII. GetByte(); // check for "0b" (binary) and "0x" (hexadecimal) prefixes if (intval == 0) { if (GotByte == 'b') { parse_binary_value(); return; } if (GotByte == 'x') { parse_hexadecimal_value(); return; } } // parse digits until no more while ((GotByte >= '0') && (GotByte <= '9')) { intval = 10 * intval + (GotByte & 15); // ASCII, see above GetByte(); } // check whether it's a float if (GotByte == '.') { // read fractional part GetByte(); parse_frac_part(intval); } else { PUSH_INTOPERAND(intval, MVALUE_GIVEN, 0); } // Now GotByte = non-decimal char } // Parse octal value. It accepts "0" to "7". The current value is stored as // soon as a character is read that is none of those given above. static void parse_octal_value(void) // Now GotByte = "&" { intval_t value = 0; int flags = MVALUE_GIVEN, digits = 0; // digit counter GetByte(); while ((GotByte >= '0') && (GotByte <= '7')) { value = (value << 3) + (GotByte & 7); // this works. it's ASCII. ++digits; GetByte(); } // set force bits if (digits > 3) { if (digits > 6) { if (value < 65536) flags |= MVALUE_FORCE24; } else { if (value < 256) flags |= MVALUE_FORCE16; } } PUSH_INTOPERAND(value, flags, 0); // Now GotByte = non-octal char } // Parse program counter ('*') static void parse_program_counter(void) // Now GotByte = "*" { struct result pc; GetByte(); vcpu_read_pc(&pc); // if needed, remember name for "undefined" error output check_for_def(pc.flags, 0, "*", 1); PUSH_INTOPERAND(pc.val.intval, pc.flags | MVALUE_EXISTS, pc.addr_refs); } // Parse function call (sin(), cos(), arctan(), ...) static void parse_function_call(void) { void *node_body; // make lower case version of name in local dynamic buffer DynaBuf_to_lower(function_dyna_buf, GlobalDynaBuf); // search for tree item if (Tree_easy_scan(function_tree, &node_body, function_dyna_buf)) { PUSH_OPERATOR((struct operator *) node_body); } else { Throw_error("Unknown function."); alu_state = STATE_ERROR; } } // Expect operand or monadic operator (hopefully inlined) static void expect_operand_or_monadic_operator(void) { struct operator *operator; int ugly_length_kluge; int perform_negation; SKIPSPACE(); switch (GotByte) { case '+': // anonymous forward label // count plus signs to build name of anonymous label DYNABUF_CLEAR(GlobalDynaBuf); do DYNABUF_APPEND(GlobalDynaBuf, '+'); while (GetByte() == '+'); ugly_length_kluge = GlobalDynaBuf->size; // FIXME - get rid of this! symbol_fix_forward_anon_name(FALSE); // FALSE: do not increment counter get_symbol_value(section_now->local_scope, 0, ugly_length_kluge); goto now_expect_dyadic; case '-': // NEGATION operator or anonymous backward label // count minus signs in case it's an anonymous backward label perform_negation = FALSE; DYNABUF_CLEAR(GlobalDynaBuf); do { DYNABUF_APPEND(GlobalDynaBuf, '-'); perform_negation = !perform_negation; } while (GetByte() == '-'); SKIPSPACE(); if (BYTEFLAGS(GotByte) & FOLLOWS_ANON) { DynaBuf_append(GlobalDynaBuf, '\0'); get_symbol_value(section_now->local_scope, 0, GlobalDynaBuf->size - 1); // -1 to not count terminator goto now_expect_dyadic; } if (perform_negation) PUSH_OPERATOR(&ops_negate); // State doesn't change break; // Real monadic operators (state doesn't change, still ExpectMonadic) case '!': // NOT operator operator = &ops_not; goto get_byte_and_push_monadic; case '<': // LOWBYTE operator operator = &ops_lowbyteof; goto get_byte_and_push_monadic; case '>': // HIGHBYTE operator operator = &ops_highbyteof; goto get_byte_and_push_monadic; case '^': // BANKBYTE operator operator = &ops_bankbyteof; goto get_byte_and_push_monadic; // Faked monadic operators case '(': // left parenthesis operator = &ops_opening; goto get_byte_and_push_monadic; case ')': // right parenthesis // this makes "()" also throw a syntax error Throw_error(exception_syntax); alu_state = STATE_ERROR; break; // Operands (values, state changes to ExpectDyadic) case '"': // Quoted character case '\'': // Quoted character // Character will be converted using current encoding parse_quoted_character(GotByte); // Now GotByte = char following closing quote goto now_expect_dyadic; case '%': // Binary value parse_binary_value(); // Now GotByte = non-binary char goto now_expect_dyadic; case '&': // Octal value parse_octal_value(); // Now GotByte = non-octal char goto now_expect_dyadic; case '$': // Hexadecimal value parse_hexadecimal_value(); // Now GotByte = non-hexadecimal char goto now_expect_dyadic; case '*': // Program counter parse_program_counter(); // Now GotByte = char after closing quote goto now_expect_dyadic; // FIXME - find a way to tell decimal point and LOCAL_PREFIX apart! case '.': // local symbol or fractional part of float value GetByte(); // start after '.' // check for fractional part of float value if ((GotByte >= '0') && (GotByte <= '9')) { parse_frac_part(0); // Now GotByte = non-decimal char goto now_expect_dyadic; } if (Input_read_keyword()) { // Now GotByte = illegal char get_symbol_value(section_now->local_scope, LOCAL_PREFIX, GlobalDynaBuf->size - 1); // -1 to not count terminator goto now_expect_dyadic; } // if we're here, Input_read_keyword() will have thrown an error (like "no string given"): alu_state = STATE_ERROR; break; case CHEAP_PREFIX: // cheap local symbol //printf("looking in cheap scope %d\n", section_now->cheap_scope); GetByte(); // start after '@' if (Input_read_keyword()) { // Now GotByte = illegal char get_symbol_value(section_now->cheap_scope, CHEAP_PREFIX, GlobalDynaBuf->size - 1); // -1 to not count terminator goto now_expect_dyadic; } // if we're here, Input_read_keyword() will have thrown an error (like "no string given"): alu_state = STATE_ERROR; break; // decimal values and global symbols default: // all other characters if ((GotByte >= '0') && (GotByte <= '9')) { parse_decimal_value(); // Now GotByte = non-decimal char goto now_expect_dyadic; } if (BYTEFLAGS(GotByte) & STARTS_KEYWORD) { register int length; // Read global label (or "NOT") length = Input_read_keyword(); // Now GotByte = illegal char // Check for NOT. Okay, it's hardcoded, // but so what? Sue me... if ((length == 3) && ((GlobalDynaBuf->buffer[0] | 32) == 'n') && ((GlobalDynaBuf->buffer[1] | 32) == 'o') && ((GlobalDynaBuf->buffer[2] | 32) == 't')) { PUSH_OPERATOR(&ops_not); // state doesn't change } else { if (GotByte == '(') { parse_function_call(); // i thought about making the parentheses optional, so you can write "a = sin b" // just like "a = not b". but then each new function name would have to be made // a reserved keyword, otherwise stuff like "a = sin * < b" would be ambiguous: // it could mean either "compare sine of PC to b" or "multiply 'sin' by low byte // of b". // however, apart from that check above, function calls have nothing to do with // parentheses: "sin(x+y)" gets parsed just like "not(x+y)". } else { get_symbol_value(SCOPE_GLOBAL, 0, GlobalDynaBuf->size - 1); // -1 to not count terminator goto now_expect_dyadic; } } } else { // illegal character read - so don't go on // we found end-of-expression instead of an operand, // that's either an empty expression or an erroneous one! PUSH_INTOPERAND(0, 0, 0); // push dummy operand so stack is ok // push pseudo value, EXISTS flag is clear if (operator_stack[operator_sp - 1] == &ops_exprstart) { PUSH_OPERATOR(&ops_exprend); alu_state = STATE_TRY_TO_REDUCE_STACKS; } else { Throw_error(exception_syntax); alu_state = STATE_ERROR; } } break; // no other possibilities, so here are the shared endings get_byte_and_push_monadic: GetByte(); PUSH_OPERATOR(operator); // State doesn't change break; now_expect_dyadic: // bugfix: if in error state, do not change state back to valid one if (alu_state < STATE_MAX_GO_ON) alu_state = STATE_EXPECT_DYADIC_OPERATOR; break; } } // Expect dyadic operator (hopefully inlined) static void expect_dyadic_operator(void) { void *node_body; struct operator *operator; SKIPSPACE(); switch (GotByte) { // Single-character dyadic operators case '^': // "to the power of" operator = &ops_powerof; goto get_byte_and_push_dyadic; case '+': // add operator = &ops_add; goto get_byte_and_push_dyadic; case '-': // subtract operator = &ops_subtract; goto get_byte_and_push_dyadic; case '*': // multiply operator = &ops_multiply; goto get_byte_and_push_dyadic; case '/': // divide operator = &ops_divide; goto get_byte_and_push_dyadic; case '%': // modulo operator = &ops_modulo; goto get_byte_and_push_dyadic; case '&': // bitwise AND operator = &ops_and; goto get_byte_and_push_dyadic; case '|': // bitwise OR operator = &ops_or; goto get_byte_and_push_dyadic; // This part is commented out because there is no XOR character defined // case ???: // bitwise exclusive OR // operator = &ops_xor; // goto get_byte_and_push_dyadic; case '=': // is equal operator = &ops_equals; // if it's "==", accept but warn if (GetByte() == '=') { Throw_first_pass_warning("C-style \"==\" comparison detected."); goto get_byte_and_push_dyadic; } goto push_dyadic; case ')': // closing parenthesis operator = &ops_closing; goto get_byte_and_push_dyadic; // Multi-character dyadic operators case '!': // "!=" if (GetByte() == '=') { operator = &ops_notequal; goto get_byte_and_push_dyadic; } Throw_error(exception_syntax); alu_state = STATE_ERROR; break; case '<': // "<", "<=", "<<" and "<>" switch (GetByte()) { case '=': // "<=", less or equal operator = &ops_le; goto get_byte_and_push_dyadic; case '<': // "<<", shift left operator = &ops_sl; goto get_byte_and_push_dyadic; case '>': // "<>", not equal operator = &ops_notequal; goto get_byte_and_push_dyadic; default: // "<", less than operator = &ops_lessthan; goto push_dyadic; } //break; unreachable case '>': // ">", ">=", ">>", ">>>" and "><" switch (GetByte()) { case '=': // ">=", greater or equal operator = &ops_ge; goto get_byte_and_push_dyadic; case '<': // "><", not equal operator = &ops_notequal; goto get_byte_and_push_dyadic; case '>': // ">>" or ">>>", shift right operator = &ops_asr; // arithmetic shift right if (GetByte() != '>') goto push_dyadic; operator = &ops_lsr; // logical shift right goto get_byte_and_push_dyadic; default: // ">", greater than operator = &ops_greaterthan; goto push_dyadic; } //break; unreachable // end of expression or text version of dyadic operator default: // check string versions of operators if (BYTEFLAGS(GotByte) & STARTS_KEYWORD) { Input_read_and_lower_keyword(); // Now GotByte = illegal char // search for tree item if (Tree_easy_scan(operator_tree, &node_body, GlobalDynaBuf)) { operator = node_body; goto push_dyadic; } // goto means we don't need an "else {" here Throw_error("Unknown operator."); alu_state = STATE_ERROR; } else { // we found end-of-expression when expecting an operator, that's ok. operator = &ops_exprend; goto push_dyadic; } } return; // shared endings get_byte_and_push_dyadic: GetByte(); push_dyadic: PUSH_OPERATOR(operator); alu_state = STATE_TRY_TO_REDUCE_STACKS; } // call C's sin/cos/tan function static void perform_fp(double (*fn)(double)) { if ((RIGHT_FLAGS & MVALUE_IS_FP) == 0) { RIGHT_FPVAL = RIGHT_INTVAL; RIGHT_FLAGS |= MVALUE_IS_FP; } RIGHT_FPVAL = fn(RIGHT_FPVAL); RIGHT_ADDRREFS = 0; // result now is a non-address } // make sure arg is in [-1, 1] range before calling function static void perform_ranged_fp(double (*fn)(double)) { if ((RIGHT_FLAGS & MVALUE_IS_FP) == 0) { RIGHT_FPVAL = RIGHT_INTVAL; RIGHT_FLAGS |= MVALUE_IS_FP; } if ((RIGHT_FPVAL >= -1) && (RIGHT_FPVAL <= 1)) { RIGHT_FPVAL = fn(RIGHT_FPVAL); } else { if (RIGHT_FLAGS & MVALUE_DEFINED) Throw_error("Argument out of range."); RIGHT_FPVAL = 0; } RIGHT_ADDRREFS = 0; // result now is a non-address } // convert right-hand value from fp to int static void right_fp_to_int(void) { RIGHT_INTVAL = RIGHT_FPVAL; RIGHT_FLAGS &= ~MVALUE_IS_FP; } // check both left-hand and right-hand values. if float, convert to int. // in first pass, throw warning static void both_ensure_int(int warn) { if (LEFT_FLAGS & MVALUE_IS_FP) { LEFT_INTVAL = LEFT_FPVAL; LEFT_FLAGS &= ~MVALUE_IS_FP; } if (RIGHT_FLAGS & MVALUE_IS_FP) { RIGHT_INTVAL = RIGHT_FPVAL; RIGHT_FLAGS &= ~MVALUE_IS_FP; } // FIXME - warning is never seen if both operands are undefined in first pass! Throw_first_pass_warning("Converted to integer for binary logic operator."); } // check both left-hand and right-hand values. if int, convert to float. static void both_ensure_fp(void) { if ((LEFT_FLAGS & MVALUE_IS_FP) == 0) { LEFT_FPVAL = LEFT_INTVAL; LEFT_FLAGS |= MVALUE_IS_FP; } if ((RIGHT_FLAGS & MVALUE_IS_FP) == 0) { RIGHT_FPVAL = RIGHT_INTVAL; RIGHT_FLAGS |= MVALUE_IS_FP; } } // make sure both values are float, but mark left one as int (will become one) static void ensure_int_from_fp(void) { both_ensure_fp(); LEFT_FLAGS &= ~MVALUE_IS_FP; } // Try to reduce stacks by performing high-priority operations // (if the previous operator has a higher priority than the current one, do it) static void try_to_reduce_stacks(int *open_parentheses) { if (operator_sp < 2) { alu_state = STATE_EXPECT_OPERAND_OR_MONADIC_OPERATOR; return; } // previous operator has lower piority than current one? then do nothing. if (operator_stack[operator_sp - 2]->priority_and_associativity < operator_stack[operator_sp - 1]->priority_and_associativity) { alu_state = STATE_EXPECT_OPERAND_OR_MONADIC_OPERATOR; return; } // previous operator has same priority as current one? then check associativity if ((operator_stack[operator_sp - 2]->priority_and_associativity == operator_stack[operator_sp - 1]->priority_and_associativity) && IS_RIGHT_ASSOCIATIVE(operator_stack[operator_sp - 1]->priority_and_associativity)) { alu_state = STATE_EXPECT_OPERAND_OR_MONADIC_OPERATOR; return; } // process previous operator switch (operator_stack[operator_sp - 2]->handle) { // special (pseudo) operators case OPHANDLE_EXPRSTART: // the only operator with a lower priority than this // "start-of-expression" operator is "end-of-expression", // therefore we know we are done. // don't touch indirect_flag; needed for INDIRECT flag --operator_sp; // decrement operator stack pointer alu_state = STATE_END; break; case OPHANDLE_OPENING: indirect_flag = MVALUE_INDIRECT; // parentheses found switch (operator_stack[operator_sp - 1]->handle) { case OPHANDLE_CLOSING: // matching parentheses operator_sp -= 2; // remove both of them alu_state = STATE_EXPECT_DYADIC_OPERATOR; break; case OPHANDLE_EXPREND: // unmatched parenthesis ++(*open_parentheses); // count goto RNTLObutDontTouchIndirectFlag; default: Bug_found("StrangeParenthesis", operator_stack[operator_sp - 1]->handle); } break; case OPHANDLE_CLOSING: Throw_error("Too many ')'."); alu_state = STATE_ERROR; return; // functions case OPHANDLE_ADDR: RIGHT_ADDRREFS = 1; // result now is an address goto remove_next_to_last_operator; case OPHANDLE_INT: if (RIGHT_FLAGS & MVALUE_IS_FP) right_fp_to_int(); RIGHT_ADDRREFS = 0; // result now is a non-address goto remove_next_to_last_operator; case OPHANDLE_FLOAT: // convert right-hand value from int to fp if ((RIGHT_FLAGS & MVALUE_IS_FP) == 0) { RIGHT_FPVAL = RIGHT_INTVAL; RIGHT_FLAGS |= MVALUE_IS_FP; } RIGHT_ADDRREFS = 0; // result now is a non-address goto remove_next_to_last_operator; case OPHANDLE_SIN: perform_fp(sin); // also zeroes addr_refs goto remove_next_to_last_operator; case OPHANDLE_COS: perform_fp(cos); // also zeroes addr_refs goto remove_next_to_last_operator; case OPHANDLE_TAN: perform_fp(tan); // also zeroes addr_refs goto remove_next_to_last_operator; case OPHANDLE_ARCSIN: perform_ranged_fp(asin); // also zeroes addr_refs goto remove_next_to_last_operator; case OPHANDLE_ARCCOS: perform_ranged_fp(acos); // also zeroes addr_refs goto remove_next_to_last_operator; case OPHANDLE_ARCTAN: perform_fp(atan); // also zeroes addr_refs goto remove_next_to_last_operator; // monadic operators case OPHANDLE_NOT: // fp becomes int if (RIGHT_FLAGS & MVALUE_IS_FP) right_fp_to_int(); RIGHT_INTVAL = ~(RIGHT_INTVAL); RIGHT_FLAGS &= ~MVALUE_ISBYTE; goto remove_next_to_last_operator; case OPHANDLE_NEGATE: // different operations for fp and int if (RIGHT_FLAGS & MVALUE_IS_FP) RIGHT_FPVAL = -(RIGHT_FPVAL); else RIGHT_INTVAL = -(RIGHT_INTVAL); RIGHT_FLAGS &= ~MVALUE_ISBYTE; RIGHT_ADDRREFS = -RIGHT_ADDRREFS; // negate address ref count as well goto remove_next_to_last_operator; case OPHANDLE_LOWBYTEOF: // fp becomes int if (RIGHT_FLAGS & MVALUE_IS_FP) right_fp_to_int(); RIGHT_INTVAL = (RIGHT_INTVAL) & 255; RIGHT_FLAGS |= MVALUE_ISBYTE; RIGHT_FLAGS &= ~MVALUE_FORCEBITS; RIGHT_ADDRREFS = 0; // result now is a non-address goto remove_next_to_last_operator; case OPHANDLE_HIGHBYTEOF: // fp becomes int if (RIGHT_FLAGS & MVALUE_IS_FP) right_fp_to_int(); RIGHT_INTVAL = ((RIGHT_INTVAL) >> 8) & 255; RIGHT_FLAGS |= MVALUE_ISBYTE; RIGHT_FLAGS &= ~MVALUE_FORCEBITS; RIGHT_ADDRREFS = 0; // result now is a non-address goto remove_next_to_last_operator; case OPHANDLE_BANKBYTEOF: // fp becomes int if (RIGHT_FLAGS & MVALUE_IS_FP) right_fp_to_int(); RIGHT_INTVAL = ((RIGHT_INTVAL) >> 16) & 255; RIGHT_FLAGS |= MVALUE_ISBYTE; RIGHT_FLAGS &= ~MVALUE_FORCEBITS; RIGHT_ADDRREFS = 0; // result now is a non-address goto remove_next_to_last_operator; // dyadic operators case OPHANDLE_POWEROF: if ((RIGHT_FLAGS | LEFT_FLAGS) & MVALUE_IS_FP) { both_ensure_fp(); LEFT_FPVAL = pow(LEFT_FPVAL, RIGHT_FPVAL); LEFT_ADDRREFS = 0; // result now is a non-address goto handle_flags_and_dec_stacks; } if (RIGHT_INTVAL >= 0) { LEFT_INTVAL = my_pow(LEFT_INTVAL, RIGHT_INTVAL); } else { if (RIGHT_FLAGS & MVALUE_DEFINED) Throw_error("Exponent is negative."); LEFT_INTVAL = 0; } LEFT_ADDRREFS = 0; // result now is a non-address goto handle_flags_and_dec_stacks; case OPHANDLE_MULTIPLY: if ((RIGHT_FLAGS | LEFT_FLAGS) & MVALUE_IS_FP) { both_ensure_fp(); LEFT_FPVAL *= RIGHT_FPVAL; } else { LEFT_INTVAL *= RIGHT_INTVAL; } LEFT_ADDRREFS = 0; // result now is a non-address goto handle_flags_and_dec_stacks; case OPHANDLE_DIVIDE: if ((RIGHT_FLAGS | LEFT_FLAGS) & MVALUE_IS_FP) { both_ensure_fp(); if (RIGHT_FPVAL) { LEFT_FPVAL /= RIGHT_FPVAL; } else { if (RIGHT_FLAGS & MVALUE_DEFINED) Throw_error(exception_div_by_zero); LEFT_FPVAL = 0; } } else { if (RIGHT_INTVAL) { LEFT_INTVAL /= RIGHT_INTVAL; } else { if (RIGHT_FLAGS & MVALUE_DEFINED) Throw_error(exception_div_by_zero); LEFT_INTVAL = 0; } } LEFT_ADDRREFS = 0; // result now is a non-address goto handle_flags_and_dec_stacks; case OPHANDLE_INTDIV: if ((RIGHT_FLAGS | LEFT_FLAGS) & MVALUE_IS_FP) { both_ensure_fp(); if (RIGHT_FPVAL) { LEFT_INTVAL = LEFT_FPVAL / RIGHT_FPVAL; } else { if (RIGHT_FLAGS & MVALUE_DEFINED) Throw_error(exception_div_by_zero); LEFT_INTVAL = 0; } LEFT_FLAGS &= ~MVALUE_IS_FP; } else { if (RIGHT_INTVAL) { LEFT_INTVAL /= RIGHT_INTVAL; } else { if (RIGHT_FLAGS & MVALUE_DEFINED) Throw_error(exception_div_by_zero); LEFT_INTVAL = 0; } } LEFT_ADDRREFS = 0; // result now is a non-address goto handle_flags_and_dec_stacks; case OPHANDLE_MODULO: if ((RIGHT_FLAGS | LEFT_FLAGS) & MVALUE_IS_FP) both_ensure_int(FALSE); if (RIGHT_INTVAL) { LEFT_INTVAL %= RIGHT_INTVAL; } else { if (RIGHT_FLAGS & MVALUE_DEFINED) Throw_error(exception_div_by_zero); LEFT_INTVAL = 0; } LEFT_ADDRREFS = 0; // result now is a non-address goto handle_flags_and_dec_stacks; case OPHANDLE_ADD: if ((RIGHT_FLAGS | LEFT_FLAGS) & MVALUE_IS_FP) { both_ensure_fp(); LEFT_FPVAL += RIGHT_FPVAL; } else { LEFT_INTVAL += RIGHT_INTVAL; } LEFT_ADDRREFS += RIGHT_ADDRREFS; // add address references goto handle_flags_and_dec_stacks; case OPHANDLE_SUBTRACT: if ((RIGHT_FLAGS | LEFT_FLAGS) & MVALUE_IS_FP) { both_ensure_fp(); LEFT_FPVAL -= RIGHT_FPVAL; } else { LEFT_INTVAL -= RIGHT_INTVAL; } LEFT_ADDRREFS -= RIGHT_ADDRREFS; // subtract address references goto handle_flags_and_dec_stacks; case OPHANDLE_SL: if (RIGHT_FLAGS & MVALUE_IS_FP) right_fp_to_int(); if (LEFT_FLAGS & MVALUE_IS_FP) LEFT_FPVAL *= pow(2.0, RIGHT_INTVAL); else LEFT_INTVAL <<= RIGHT_INTVAL; LEFT_ADDRREFS = 0; // result now is a non-address goto handle_flags_and_dec_stacks; case OPHANDLE_ASR: if (RIGHT_FLAGS & MVALUE_IS_FP) right_fp_to_int(); if (LEFT_FLAGS & MVALUE_IS_FP) LEFT_FPVAL /= (1 << RIGHT_INTVAL); else LEFT_INTVAL = my_asr(LEFT_INTVAL, RIGHT_INTVAL); LEFT_ADDRREFS = 0; // result now is a non-address goto handle_flags_and_dec_stacks; case OPHANDLE_LSR: // fp become int if ((RIGHT_FLAGS | LEFT_FLAGS) & MVALUE_IS_FP) both_ensure_int(TRUE); LEFT_INTVAL = ((uintval_t) LEFT_INTVAL) >> RIGHT_INTVAL; LEFT_ADDRREFS = 0; // result now is a non-address goto handle_flags_and_dec_stacks; case OPHANDLE_LE: if ((RIGHT_FLAGS | LEFT_FLAGS) & MVALUE_IS_FP) { ensure_int_from_fp(); LEFT_INTVAL = (LEFT_FPVAL <= RIGHT_FPVAL); } else { LEFT_INTVAL = (LEFT_INTVAL <= RIGHT_INTVAL); } LEFT_ADDRREFS = 0; // result now is a non-address goto handle_flags_and_dec_stacks; case OPHANDLE_LESSTHAN: if ((RIGHT_FLAGS | LEFT_FLAGS) & MVALUE_IS_FP) { ensure_int_from_fp(); LEFT_INTVAL = (LEFT_FPVAL < RIGHT_FPVAL); } else { LEFT_INTVAL = (LEFT_INTVAL < RIGHT_INTVAL); } LEFT_ADDRREFS = 0; // result now is a non-address goto handle_flags_and_dec_stacks; case OPHANDLE_GE: if ((RIGHT_FLAGS | LEFT_FLAGS) & MVALUE_IS_FP) { ensure_int_from_fp(); LEFT_INTVAL = (LEFT_FPVAL >= RIGHT_FPVAL); } else { LEFT_INTVAL = (LEFT_INTVAL >= RIGHT_INTVAL); } LEFT_ADDRREFS = 0; // result now is a non-address goto handle_flags_and_dec_stacks; case OPHANDLE_GREATERTHAN: if ((RIGHT_FLAGS | LEFT_FLAGS) & MVALUE_IS_FP) { ensure_int_from_fp(); LEFT_INTVAL = (LEFT_FPVAL > RIGHT_FPVAL); } else { LEFT_INTVAL = (LEFT_INTVAL > RIGHT_INTVAL); } LEFT_ADDRREFS = 0; // result now is a non-address goto handle_flags_and_dec_stacks; case OPHANDLE_NOTEQUAL: if ((RIGHT_FLAGS | LEFT_FLAGS) & MVALUE_IS_FP) { ensure_int_from_fp(); LEFT_INTVAL = (LEFT_FPVAL != RIGHT_FPVAL); } else { LEFT_INTVAL = (LEFT_INTVAL != RIGHT_INTVAL); } LEFT_ADDRREFS = 0; // result now is a non-address goto handle_flags_and_dec_stacks; case OPHANDLE_EQUALS: if ((RIGHT_FLAGS | LEFT_FLAGS) & MVALUE_IS_FP) { ensure_int_from_fp(); LEFT_INTVAL = (LEFT_FPVAL == RIGHT_FPVAL); } else { LEFT_INTVAL = (LEFT_INTVAL == RIGHT_INTVAL); } LEFT_ADDRREFS = 0; // result now is a non-address goto handle_flags_and_dec_stacks; case OPHANDLE_AND: // fp become int if ((RIGHT_FLAGS | LEFT_FLAGS) & MVALUE_IS_FP) both_ensure_int(TRUE); LEFT_INTVAL &= RIGHT_INTVAL; LEFT_ADDRREFS += RIGHT_ADDRREFS; // add address references goto handle_flags_and_dec_stacks; case OPHANDLE_EOR: Throw_first_pass_warning("\"EOR\" is deprecated; use \"XOR\" instead."); /*FALLTHROUGH*/ case OPHANDLE_XOR: // fp become int if ((RIGHT_FLAGS | LEFT_FLAGS) & MVALUE_IS_FP) both_ensure_int(TRUE); LEFT_INTVAL ^= RIGHT_INTVAL; LEFT_ADDRREFS += RIGHT_ADDRREFS; // add address references goto handle_flags_and_dec_stacks; case OPHANDLE_OR: // fp become int if ((RIGHT_FLAGS | LEFT_FLAGS) & MVALUE_IS_FP) both_ensure_int(TRUE); LEFT_INTVAL |= RIGHT_INTVAL; LEFT_ADDRREFS += RIGHT_ADDRREFS; // add address references goto handle_flags_and_dec_stacks; default: Bug_found("IllegalOperatorHandle", operator_stack[operator_sp - 2]->handle); } return; // shared endings: // entry point for dyadic operators handle_flags_and_dec_stacks: // Handle flags and decrement value stack pointer // "OR" EXISTS, UNSURE and FORCEBIT flags LEFT_FLAGS |= RIGHT_FLAGS & (MVALUE_EXISTS | MVALUE_UNSURE | MVALUE_FORCEBITS); // "AND" DEFINED flag LEFT_FLAGS &= (RIGHT_FLAGS | ~MVALUE_DEFINED); LEFT_FLAGS &= ~MVALUE_ISBYTE; // clear ISBYTE flag --operand_sp; // entry point for monadic operators remove_next_to_last_operator: // toplevel operation was something other than parentheses indirect_flag = 0; // entry point for '(' operator (has set indirect_flag, so don't clear now) RNTLObutDontTouchIndirectFlag: // Remove operator and shift down next one operator_stack[operator_sp - 2] = operator_stack[operator_sp - 1]; --operator_sp; // decrement operator stack pointer } // The core of it. Returns number of parentheses left open. // FIXME - make state machine using function pointers? or too slow? static int parse_expression(struct result *result) { int open_parentheses = 0; operator_sp = 0; // operator stack pointer operand_sp = 0; // value stack pointer // begin by reading value (or monadic operator) alu_state = STATE_EXPECT_OPERAND_OR_MONADIC_OPERATOR; indirect_flag = 0; // Contains either 0 or MVALUE_INDIRECT PUSH_OPERATOR(&ops_exprstart); do { // check stack sizes. enlarge if needed if (operator_sp >= operator_stk_size) enlarge_operator_stack(); if (operand_sp >= operand_stk_size) enlarge_operand_stack(); switch (alu_state) { case STATE_EXPECT_OPERAND_OR_MONADIC_OPERATOR: expect_operand_or_monadic_operator(); break; case STATE_EXPECT_DYADIC_OPERATOR: expect_dyadic_operator(); break; // no fallthrough; state might // have been changed to END or ERROR case STATE_TRY_TO_REDUCE_STACKS: try_to_reduce_stacks(&open_parentheses); break; case STATE_MAX_GO_ON: // suppress case STATE_ERROR: // compiler case STATE_END: // warnings break; } } while (alu_state < STATE_MAX_GO_ON); // done. check state. if (alu_state == STATE_END) { // check for bugs if (operand_sp != 1) Bug_found("OperandStackNotEmpty", operand_sp); if (operator_sp != 1) Bug_found("OperatorStackNotEmpty", operator_sp); // copy result *result = operand_stack[0]; result->flags |= indirect_flag; // OR indirect flag // only allow *one* force bit if (result->flags & MVALUE_FORCE24) result->flags &= ~(MVALUE_FORCE16 | MVALUE_FORCE08); else if (result->flags & MVALUE_FORCE16) result->flags &= ~MVALUE_FORCE08; // if there was nothing to parse, mark as undefined // (so ALU_defined_int() can react) if ((result->flags & MVALUE_EXISTS) == 0) result->flags &= ~MVALUE_DEFINED; // do some checks depending on int/float if (result->flags & MVALUE_IS_FP) { /*float*/ // if undefined, return zero if ((result->flags & MVALUE_DEFINED) == 0) result->val.fpval = 0; // if value is sure, check to set ISBYTE else if (((result->flags & MVALUE_UNSURE) == 0) && (result->val.fpval <= 255.0) && (result->val.fpval >= -128.0)) result->flags |= MVALUE_ISBYTE; } else { /*int*/ // if undefined, return zero if ((result->flags & MVALUE_DEFINED) == 0) result->val.intval = 0; // if value is sure, check to set ISBYTE else if (((result->flags & MVALUE_UNSURE) == 0) && (result->val.intval <= 255) && (result->val.intval >= -128)) result->flags |= MVALUE_ISBYTE; } } else { // State is STATE_ERROR. Errors have already been reported, // but we must make sure not to pass bogus data to caller. result->flags = 0; // maybe set DEFINED flag to suppress follow-up errors? result->val.intval = 0; result->addr_refs = 0; // make sure no additional (spurious) errors are reported: Input_skip_remainder(); // FIXME - remove this when new function interface gets used: // callers must decide for themselves what to do when expression parser returns error // (currently LDA'' results in both "no string given" AND "illegal combination of command and addressing mode"!) } // return number of open (unmatched) parentheses return open_parentheses; } // Store int value if given. Returns whether stored. Throws error if undefined. // This function needs either a defined value or no expression at all. So // empty expressions are accepted, but undefined ones are not. // If the result's "defined" flag is clear and the "exists" flag is set, it // throws a serious error and therefore stops assembly. // OPEN_PARENTHESIS: complain // EMPTY: allow // UNDEFINED: complain _seriously_ // FLOAT: convert to int int ALU_optional_defined_int(intval_t *target) // ACCEPT_EMPTY { struct result result; if (parse_expression(&result)) Throw_error(exception_paren_open); if ((result.flags & MVALUE_GIVEN) == MVALUE_EXISTS) Throw_serious_error(value_not_defined()); if ((result.flags & MVALUE_EXISTS) == 0) return 0; // something was given, so store if (result.flags & MVALUE_IS_FP) *target = result.val.fpval; else *target = result.val.intval; return 1; } // Store int value and flags (floats are transformed to int) // It the result's "exists" flag is clear (=empty expression), it throws an // error. // If the result's "defined" flag is clear, result_is_undefined() is called. // OPEN_PARENTHESIS: complain // EMPTY: complain // UNDEFINED: allow // FLOAT: convert to int void ALU_int_result(struct result *intresult) // ACCEPT_UNDEFINED { if (parse_expression(intresult)) Throw_error(exception_paren_open); // make sure result is not float if (intresult->flags & MVALUE_IS_FP) { intresult->val.intval = intresult->val.fpval; intresult->flags &= ~MVALUE_IS_FP; } if ((intresult->flags & MVALUE_EXISTS) == 0) Throw_error(exception_no_value); else if ((intresult->flags & MVALUE_DEFINED) == 0) result_is_undefined(); } // return int value (if result is undefined, returns zero) // If the result's "exists" flag is clear (=empty expression), it throws an // error. // If the result's "defined" flag is clear, result_is_undefined() is called. // OPEN_PARENTHESIS: complain // EMPTY: complain // UNDEFINED: allow // FLOAT: convert to int intval_t ALU_any_int(void) // ACCEPT_UNDEFINED { // FIXME - replace this fn with a call to ALU_int_result() above! struct result result; if (parse_expression(&result)) Throw_error(exception_paren_open); if ((result.flags & MVALUE_EXISTS) == 0) Throw_error(exception_no_value); else if ((result.flags & MVALUE_DEFINED) == 0) result_is_undefined(); if (result.flags & MVALUE_IS_FP) return result.val.fpval; else return result.val.intval; } // stores int value and flags (floats are transformed to int) // if result was undefined, serious error is thrown // OPEN_PARENTHESIS: complain // EMPTY: treat as UNDEFINED <= this is a problem - maybe use a wrapper fn for this use case? // UNDEFINED: complain _seriously_ // FLOAT: convert to int extern void ALU_defined_int(struct result *intresult) // no ACCEPT constants? { if (parse_expression(intresult)) Throw_error(exception_paren_open); if ((intresult->flags & MVALUE_DEFINED) == 0) Throw_serious_error(value_not_defined()); if (intresult->flags & MVALUE_IS_FP) { intresult->val.intval = intresult->val.fpval; intresult->flags &= ~MVALUE_IS_FP; } } // Store int value and flags. // This function allows for one '(' too many. Needed when parsing indirect // addressing modes where internal indices have to be possible. Returns number // of parentheses still open (either 0 or 1). // OPEN_PARENTHESIS: allow // UNDEFINED: allow // EMPTY: allow // FLOAT: convert to int int ALU_liberal_int(struct result *intresult) // ACCEPT_EMPTY | ACCEPT_UNDEFINED | ACCEPT_OPENPARENTHESIS { int parentheses_still_open; parentheses_still_open = parse_expression(intresult); // make sure result is not float if (intresult->flags & MVALUE_IS_FP) { intresult->val.intval = intresult->val.fpval; intresult->flags &= ~MVALUE_IS_FP; } if (parentheses_still_open > 1) { parentheses_still_open = 0; Throw_error(exception_paren_open); } if ((intresult->flags & MVALUE_EXISTS) && ((intresult->flags & MVALUE_DEFINED) == 0)) result_is_undefined(); return parentheses_still_open; } // Store value and flags (result may be either int or float) // It the result's "exists" flag is clear (=empty expression), it throws an // error. // If the result's "defined" flag is clear, result_is_undefined() is called. // OPEN_PARENTHESIS: complain // EMPTY: complain // UNDEFINED: allow // FLOAT: keep void ALU_any_result(struct result *result) // ACCEPT_UNDEFINED | ACCEPT_FLOAT { if (parse_expression(result)) Throw_error(exception_paren_open); if ((result->flags & MVALUE_EXISTS) == 0) Throw_error(exception_no_value); else if ((result->flags & MVALUE_DEFINED) == 0) result_is_undefined(); } acme-crossassembler-0.96.4/src/alu.h000066400000000000000000000067671333777125400173210ustar00rootroot00000000000000// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. // Copyright (C) 1998-2016 Marco Baye // Have a look at "acme.c" for further info // // ALU stuff (the expression parser) #ifndef alu_H #define alu_H #include "config.h" // constants // meaning of bits in "flags" of struct result: TODO - this is only for future "number" result type! #define MVALUE_IS_FP (1u << 8) // floating point value #define MVALUE_INDIRECT (1u << 7) // needless parentheses indicate use of indirect addressing modes #define MVALUE_EXISTS (1u << 6) // 0: expression was empty. 1: there was *something* to parse. TODO - get rid of this, make "nothing" its own result type instead! #define MVALUE_UNSURE (1u << 5) // value once was related to undefined // expression. Needed for producing the same addresses in all passes; because in // the first pass there will almost for sure be labels that are undefined, you // can't simply get the addressing mode from looking at the parameter's value. #define MVALUE_DEFINED (1u << 4) // 0: undefined expression (value will be zero). 1: known result #define MVALUE_ISBYTE (1u << 3) // value is guaranteed to fit in one byte #define MVALUE_FORCE24 (1u << 2) // value usage forces 24-bit usage #define MVALUE_FORCE16 (1u << 1) // value usage forces 16-bit usage #define MVALUE_FORCE08 (1u << 0) // value usage forces 8-bit usage #define MVALUE_FORCEBITS (MVALUE_FORCE08|MVALUE_FORCE16|MVALUE_FORCE24) #define MVALUE_GIVEN (MVALUE_DEFINED | MVALUE_EXISTS) // bit mask for fixed values (defined and existing) // create dynamic buffer, operator/function trees and operator/operand stacks extern void ALU_init(void); // function pointer for "value undefined" error output. // set to NULL to suppress those errors, // set to Throw_error to show them. extern void (*ALU_optional_notdef_handler)(const char *); // FIXME - replace all the functions below with a single one using a "flags" arg! /* its return value would then be: enum expression_result { EXRE_ERROR, // error (has been reported, so skip remainder of statement) EXRE_NOTHING, // next char after space was comma or end-of-statement EXRE_NUMBER, // int or float (what is returned by the current functions) EXRE_STRING, // TODO EXRE_RESERVED, // reserved cpu constant (register names), TODO EXRE_LIST // TODO }; // input flags: #define ACCEPT_EMPTY (1u << 0) // if not given, throws error #define ACCEPT_UNDEFINED (1u << 1) // if not given, undefined throws serious error //#define ACCEPT_INT (1u << ) needed when strings come along! #define ACCEPT_FLOAT (1u << 2) // if not given, floats are converted to integer #define ACCEPT_OPENPARENTHESIS (1u << 3) // if not given, throws syntax error //#define ACCEPT_STRING // do I need ACCEPT_INT and/or ACCEPT_ADDRESS? */ // stores int value if given. Returns whether stored. Throws error if undefined. extern int ALU_optional_defined_int(intval_t *target); // returns int value (0 if result was undefined) extern intval_t ALU_any_int(void); // stores int value and flags (floats are transformed to int) extern void ALU_int_result(struct result *intresult); // stores int value and flags (floats are transformed to int) // if result was undefined, serious error is thrown extern void ALU_defined_int(struct result *intresult); // stores int value and flags, allowing for one '(' too many (x-indirect addr). // returns number of additional '(' (1 or 0). extern int ALU_liberal_int(struct result *intresult); // stores value and flags (result may be either int or float) extern void ALU_any_result(struct result *result); #endif acme-crossassembler-0.96.4/src/cliargs.c000066400000000000000000000046511333777125400201450ustar00rootroot00000000000000// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. // Copyright (C) 1998-2016 Marco Baye // Have a look at "acme.c" for further info // // CLI argument stuff #include "cliargs.h" #include #include #include "config.h" // constants const char cliargs_error[] = "Error in CLI arguments: "; // variables static int arguments_left; // number of CLI arguments left static const char **next_argument; // next argument pointer // return pointer to next command line argument (NULL if no more) const char *cliargs_get_next(void) { if (arguments_left == 0) return NULL; --arguments_left; return *next_argument++; } // parse command line switches void cliargs_handle_options(char (*fn_short)(const char *), const char *(*fn_long)(const char *)) { const char *problem_string, *argument; char problem_char; for (;;) { // if there are no more arguments, return immediately if (arguments_left == 0) return; // if next argument is not an option, return immediately if ((**next_argument) != '-') return; // officially fetch argument. We already know the // first character is a '-', so advance pointer. argument = cliargs_get_next() + 1; // Check for "--" if (*argument == '-') { // long argument if (argument[1] == '\0') return; // when encountering "--", return problem_string = fn_long(argument + 1); if (problem_string) { fprintf(stderr, "%sUnknown option (--%s).\n", cliargs_error, problem_string); exit(EXIT_FAILURE); } } else { problem_char = fn_short(argument); if (problem_char) { fprintf(stderr, "%sUnknown switch (-%c).\n", cliargs_error, problem_char); exit(EXIT_FAILURE); } } } } // return next arg. If there is none, complain and exit const char *cliargs_safe_get_next(const char name[]) { const char *string; string = cliargs_get_next(); if (string) return string; fprintf(stderr, "%sMissing %s.\n", cliargs_error, name); exit(EXIT_FAILURE); } // init command line handling stuff const char *cliargs_init(int argc, const char *argv[]) { arguments_left = argc; next_argument = argv; return cliargs_get_next(); } // return unhandled (non-option) arguments. Complains if none. void cliargs_get_rest(int *argc, const char ***argv, const char error[]) { *argc = arguments_left; *argv = next_argument; if (error && (arguments_left == 0)) { fprintf(stderr, "%s%s.\n", cliargs_error, error); exit(EXIT_FAILURE); } } acme-crossassembler-0.96.4/src/cliargs.h000066400000000000000000000016421333777125400201470ustar00rootroot00000000000000// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. // Copyright (C) 1998-2016 Marco Baye // Have a look at "acme.c" for further info // // CLI argument stuff #ifndef cliargs_H #define cliargs_H // constants extern const char cliargs_error[]; // handle options. Call fn_short for short options, fn_long for long ones. extern void cliargs_handle_options(char (*fn_short)(const char *), const char *(*fn_long)(const char *)); // return next argument. extern const char *cliargs_get_next(void); // return next argument. If none left, complain with given name. extern const char *cliargs_safe_get_next(const char name[]); // initialise argument handler. Returns program name (argv[0]). extern const char *cliargs_init(int argc, const char *argv[]); // get unhandled args. If none left, complain with given error message. extern void cliargs_get_rest(int *argc, const char ***argv, const char error[]); #endif acme-crossassembler-0.96.4/src/config.h000066400000000000000000000023571333777125400177740ustar00rootroot00000000000000// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. // Copyright (C) 1998-2016 Marco Baye // Have a look at "acme.c" for further info // // Configuration #ifndef config_H #define config_H // types typedef unsigned int scope_t; typedef signed long intval_t; // at least 32 bits typedef unsigned long uintval_t; // just for logical shift right // result structure type definition with support for floating point // future result types: EMPTY, UNDEFINED, INT, FLOAT (, STRING) struct result { // either int or float int flags; // expression flags union { intval_t intval; // integer value double fpval; // floating point value } val; // Expression value int addr_refs; // address reference count (only look at this if value is DEFINED) }; // debugging flag, should be undefined in release version // #define FDEBUG // maximum nesting depth of "!src" and macro calls // is not actually a limitation, but a means of finding recursions #define MAX_NESTING 64 // default value for output buffer #define FILLVALUE_INITIAL 0 // default value for "!fill" #define FILLVALUE_FILL 0 // Nullpointer definition #ifndef NULL #define NULL ((void *) 0) #endif // Boolean values #ifndef FALSE #define FALSE 0 #define TRUE (!FALSE) #endif #endif acme-crossassembler-0.96.4/src/cpu.c000066400000000000000000000073031333777125400173050ustar00rootroot00000000000000// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. // Copyright (C) 1998-2016 Marco Baye // Have a look at "acme.c" for further info // // CPU type stuff #include "cpu.h" #include "config.h" #include "alu.h" #include "dynabuf.h" #include "global.h" // FIXME - remove when no longer needed #include "input.h" #include "mnemo.h" #include "output.h" #include "tree.h" // constants static struct cpu_type cpu_type_6502 = { keyword_is_6502_mnemo, CPUFLAG_INDIRECTJMPBUGGY, // JMP ($xxFF) is buggy 234 // !align fills with "NOP" }; static struct cpu_type cpu_type_6510 = { keyword_is_6510_mnemo, CPUFLAG_INDIRECTJMPBUGGY | CPUFLAG_8B_AND_AB_NEED_0_ARG, // JMP ($xxFF) is buggy, ANE/LXA #$xx are unstable unless arg is $00 234 // !align fills with "NOP" }; static struct cpu_type cpu_type_c64dtv2 = { keyword_is_c64dtv2_mnemo, CPUFLAG_INDIRECTJMPBUGGY | CPUFLAG_8B_AND_AB_NEED_0_ARG, // JMP ($xxFF) is buggy, ANE/LXA #$xx are unstable unless arg is $00 234 // !align fills with "NOP" }; static struct cpu_type cpu_type_65c02 = { keyword_is_65c02_mnemo, 0, // no flags 234 // !align fills with "NOP" }; static struct cpu_type cpu_type_r65c02 = { keyword_is_r65c02_mnemo, 0, // no flags 234 // !align fills with "NOP" }; static struct cpu_type cpu_type_w65c02 = { keyword_is_w65c02_mnemo, 0, // no flags 234 // !align fills with "NOP" }; static struct cpu_type cpu_type_65816 = { keyword_is_65816_mnemo, CPUFLAG_SUPPORTSLONGREGS, // allows A and XY to be 16bits wide 234 // !align fills with "NOP" }; static struct cpu_type cpu_type_65ce02 = { keyword_is_65ce02_mnemo, CPUFLAG_DECIMALSUBTRACTBUGGY, // SBC does not work reliably in decimal mode 234 // !align fills with "NOP" }; static struct cpu_type cpu_type_4502 = { keyword_is_4502_mnemo, CPUFLAG_DECIMALSUBTRACTBUGGY, // SBC does not work reliably in decimal mode 234 // !align fills with "NOP" }; // variables // predefined stuff static struct ronode *cputype_tree = NULL; static struct ronode cputype_list[] = { #define KNOWN_TYPES "'6502', '6510', '65c02', 'r65c02', 'w65c02', '65816', '65ce02', '4502', 'c64dtv2'" // shown in CLI error message for unknown types // PREDEFNODE("z80", &cpu_type_Z80), PREDEFNODE("6502", &cpu_type_6502), PREDEFNODE("6510", &cpu_type_6510), PREDEFNODE("65c02", &cpu_type_65c02), PREDEFNODE("r65c02", &cpu_type_r65c02), PREDEFNODE("w65c02", &cpu_type_w65c02), PREDEFNODE("65816", &cpu_type_65816), PREDEFNODE("65ce02", &cpu_type_65ce02), PREDEFNODE("4502", &cpu_type_4502), PREDEFLAST("c64dtv2", &cpu_type_c64dtv2), // ^^^^ this marks the last element }; const char cputype_names[] = KNOWN_TYPES; // string to show if cputype_find() returns NULL // lookup cpu type held in DynaBuf and return its struct pointer (or NULL on failure) const struct cpu_type *cputype_find(void) { void *node_body; // make sure tree is initialised if (cputype_tree == NULL) Tree_add_table(&cputype_tree, cputype_list); // perform lookup if (!Tree_easy_scan(cputype_tree, &node_body, GlobalDynaBuf)) return NULL; return node_body; } // if cpu type and value match, set register length variable to value. // if cpu type and value don't match, complain instead. // FIXME - error message might be confusing if it is thrown not because of // initial change, but because of reverting back to old cpu type after "{}" block! void vcpu_check_and_set_reg_length(int *var, int make_long) { if (((CPU_state.type->flags & CPUFLAG_SUPPORTSLONGREGS) == 0) && make_long) Throw_error("Chosen CPU does not support long registers."); else *var = make_long; } // set default values for pass void cputype_passinit(const struct cpu_type *cpu_type) { // handle cpu type (default is 6502) CPU_state.type = cpu_type ? cpu_type : &cpu_type_6502; } acme-crossassembler-0.96.4/src/cpu.h000066400000000000000000000026321333777125400173120ustar00rootroot00000000000000// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. // Copyright (C) 1998-2016 Marco Baye // Have a look at "acme.c" for further info // // CPU type stuff #ifndef cpu_type_H #define cpu_type_H #include "config.h" // CPU type structure definition struct cpu_type { // This function is not allowed to change GlobalDynaBuf // because that's where the mnemonic is stored! int (*keyword_is_mnemonic)(int); int flags; // see below for bit meanings char default_align_value; }; #define CPUFLAG_INDIRECTJMPBUGGY (1u << 0) // warn if "jmp ($xxff)" is assembled #define CPUFLAG_SUPPORTSLONGREGS (1u << 1) // allow "!al" and "!rl" pseudo opcodes #define CPUFLAG_8B_AND_AB_NEED_0_ARG (1u << 2) // warn if "ane/lxa #$xx" uses non-zero arg #define CPUFLAG_ISBIGENDIAN (1u << 3) // for 16/24/32-bit values, output msb first #define CPUFLAG_DECIMALSUBTRACTBUGGY (1u << 4) // warn if "sed" is assembled // if cpu type and value match, set register length variable to value. // if cpu type and value don't match, complain instead. extern void vcpu_check_and_set_reg_length(int *var, int make_long); // set default value for pass extern void cputype_passinit(const struct cpu_type *cpu_type); // lookup cpu type held in DynaBuf and return its struct pointer (or NULL on failure) extern const struct cpu_type *cputype_find(void); extern const char cputype_names[]; // string to show if cputype_find() returns NULL #endif acme-crossassembler-0.96.4/src/dynabuf.c000066400000000000000000000067271333777125400201570ustar00rootroot00000000000000// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. // Copyright (C) 1998-2016 Marco Baye // Have a look at "acme.c" for further info // // Dynamic buffer stuff #include "dynabuf.h" #include #include #include #include "acme.h" #include "global.h" #include "input.h" // Constants and macros // macro to grow dynabuf (CAUTION - fails if a < 1) #define MAKE_LARGER_THAN(a) (2 * (a)) // if someone requests a dynabuf smaller than this, use this size instead #define DYNABUF_MINIMUM_INITIALSIZE 128 // should be >0 (see above) // initial size for global dynabuf // (as it holds macros, loop bodies, etc., make it large to begin with) #define GLOBALDYNABUF_INITIALSIZE 1024 // should be >0 (see above) // Variables struct dynabuf *GlobalDynaBuf; // global dynamic buffer // Functions // get new buffer of given size static void resize(struct dynabuf *db, size_t new_size) { char *new_buf; new_buf = realloc(db->buffer, new_size); if (new_buf == NULL) Throw_serious_error(exception_no_memory_left); db->reserved = new_size; db->buffer = new_buf; } // Exported functions // Create and init a dynamic buffer and return pointer struct dynabuf *DynaBuf_create(int initial_size) { struct dynabuf *db; if (initial_size < DYNABUF_MINIMUM_INITIALSIZE) initial_size = DYNABUF_MINIMUM_INITIALSIZE; if ((db = malloc(sizeof(*db)))) { db->size = 0; db->reserved = initial_size; db->buffer = malloc(initial_size); if (db->buffer) return db; // if both pointers are != NULL, no error } // otherwise, complain fputs("Error: No memory for dynamic buffer.\n", stderr); exit(EXIT_FAILURE); } // Enlarge buffer void DynaBuf_enlarge(struct dynabuf *db) { resize(db, MAKE_LARGER_THAN(db->reserved)); } // Claim enough memory to hold a copy of the current buffer contents, // make that copy and return it. // The copy must be released by calling free(). char *DynaBuf_get_copy(struct dynabuf *db) { char *copy; copy = safe_malloc(db->size); memcpy(copy, db->buffer, db->size); return copy; } // add char to buffer void DynaBuf_append(struct dynabuf *db, char byte) { DYNABUF_APPEND(db, byte); } // Append string to buffer (without terminator) void DynaBuf_add_string(struct dynabuf *db, const char *string) { char byte; while ((byte = *string++)) DYNABUF_APPEND(db, byte); } /* // make sure DynaBuf is large enough to take "size" more bytes // return pointer to end of current contents static char *ensure_free_space(struct dynabuf *db, int size) { while ((db->reserved - db->size) < size) resize(db, MAKE_LARGER_THAN(db->reserved)); return db->buffer + db->size; }*/ // Convert buffer contents to lower case (target and source may be identical) void DynaBuf_to_lower(struct dynabuf *target, struct dynabuf *source) { char *read, *write; // make sure target can take it if (source->size > target->reserved) resize(target, source->size); // convert to lower case read = source->buffer; // CAUTION - ptr may change when buf grows! write = target->buffer; // CAUTION - ptr may change when buf grows! while (*read) *write++ = (*read++) | 32; // Okay, so this method of converting to lowercase is lousy. // But actually it doesn't matter, because only pre-defined // keywords are converted, and all of those are plain // old-fashioned 7-bit ASCII anyway. So I guess it'll do. *write = '\0'; // terminate } // Initialisation - allocate global dynamic buffer void DynaBuf_init(void) { GlobalDynaBuf = DynaBuf_create(GLOBALDYNABUF_INITIALSIZE); } acme-crossassembler-0.96.4/src/dynabuf.h000066400000000000000000000031111333777125400201440ustar00rootroot00000000000000// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. // Copyright (C) 1998-2016 Marco Baye // Have a look at "acme.c" for further info // // Dynamic buffer stuff #ifndef dynabuf_H #define dynabuf_H #include "config.h" // macros #define DYNABUF_CLEAR(db) do {db->size = 0;} while (0) #define DYNABUF_APPEND(db, byte) \ do { \ if (db->size == db->reserved) \ DynaBuf_enlarge(db); \ db->buffer[(db->size)++] = byte;\ } while (0) // the next one is dangerous - the buffer location can change when a character // is appended. So after calling this, don't change the buffer as long as you // use the address. #define GLOBALDYNABUF_CURRENT (GlobalDynaBuf->buffer) // dynamic buffer structure struct dynabuf { char *buffer; // pointer to buffer int size; // size of buffer's used portion int reserved; // total size of buffer }; // variables extern struct dynabuf *GlobalDynaBuf; // global dynamic buffer // create global DynaBuf (call once on program startup) extern void DynaBuf_init(void); // create (private) DynaBuf extern struct dynabuf *DynaBuf_create(int initial_size); // call whenever buffer is too small extern void DynaBuf_enlarge(struct dynabuf *db); // return malloc'd copy of buffer contents extern char *DynaBuf_get_copy(struct dynabuf *db); // copy string to buffer (without terminator) extern void DynaBuf_add_string(struct dynabuf *db, const char *); // converts buffer contents to lower case extern void DynaBuf_to_lower(struct dynabuf *target, struct dynabuf *source); // add char to buffer extern void DynaBuf_append(struct dynabuf *db, char); #endif acme-crossassembler-0.96.4/src/encoding.c000066400000000000000000000062411333777125400203040ustar00rootroot00000000000000// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. // Copyright (C) 1998-2016 Marco Baye // Have a look at "acme.c" for further info // // Character encoding stuff #include "encoding.h" #include #include #include "alu.h" #include "acme.h" #include "dynabuf.h" #include "global.h" // FIXME - remove when no longer needed #include "output.h" #include "input.h" #include "tree.h" // struct definition struct encoder { char (*fn)(char); // maybe add table pointer? }; // variables static char outermost_table[256]; // space for encoding table... const struct encoder *encoder_current; // gets set before each pass char *encoding_loaded_table = outermost_table; // ...loaded from file // encoder functions: // convert raw to raw (do not convert at all) static char encoderfn_raw(char byte) { return byte; } // convert raw to petscii static char encoderfn_pet(char byte) { if ((byte >= 'A') && (byte <= 'Z')) return (char) (byte | 0x80); // FIXME - check why SAS-C if ((byte >= 'a') && (byte <= 'z')) // wants these casts. return (char) (byte - 32); // There are more below. return byte; } // convert raw to C64 screencode static char encoderfn_scr(char byte) { if ((byte >= 'a') && (byte <= 'z')) return (char) (byte - 96); // shift uppercase down if ((byte >= '[') && (byte <= '_')) return (char) (byte - 64); // shift [\]^_ down if (byte == '`') return 64; // shift ` down if (byte == '@') return 0; // shift @ down return byte; } // convert raw to whatever is defined in table static char encoderfn_file(char byte) { return encoding_loaded_table[(unsigned char) byte]; } // predefined encoder structs: const struct encoder encoder_raw = { encoderfn_raw }; const struct encoder encoder_pet = { encoderfn_pet }; const struct encoder encoder_scr = { encoderfn_scr }; const struct encoder encoder_file = { encoderfn_file }; // keywords for "!convtab" pseudo opcode static struct ronode *encoder_tree = NULL; // tree to hold encoders static struct ronode encoder_list[] = { //no! PREDEFNODE("file", &encoder_file), "!ct file" is not needed; just use {} after initial loading of table! PREDEFNODE(s_pet, &encoder_pet), PREDEFNODE(s_raw, &encoder_raw), PREDEFLAST(s_scr, &encoder_scr), // ^^^^ this marks the last element }; // exported functions // convert character using current encoding (exported for use by alu.c and pseudoopcodes.c) char encoding_encode_char(char byte) { return encoder_current->fn(byte); } // set "raw" as default encoding void encoding_passinit(void) { encoder_current = &encoder_raw; } // try to load encoding table from given file void encoding_load_from_file(char target[256], FILE *stream) { if (fread(target, sizeof(char), 256, stream) != 256) Throw_error("Conversion table incomplete."); } // lookup encoder held in DynaBuf and return its struct pointer (or NULL on failure) const struct encoder *encoding_find(void) { void *node_body; // make sure tree is initialised if (encoder_tree == NULL) Tree_add_table(&encoder_tree, encoder_list); // perform lookup if (!Tree_easy_scan(encoder_tree, &node_body, GlobalDynaBuf)) { Throw_error("Unknown encoding."); return NULL; } return node_body; } acme-crossassembler-0.96.4/src/encoding.h000066400000000000000000000020151333777125400203040ustar00rootroot00000000000000// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. // Copyright (C) 1998-2016 Marco Baye // Have a look at "acme.c" for further info // // Character encoding stuff #ifndef encoding_H #define encoding_H #include // for FILE* //struct encoder; extern const struct encoder *encoder_current; // gets set before each pass TODO - set for each part extern const struct encoder encoder_raw; extern const struct encoder encoder_pet; extern const struct encoder encoder_scr; extern const struct encoder encoder_file; extern char *encoding_loaded_table; // ...loaded from file // prototypes // convert character using current encoding extern char encoding_encode_char(char byte); // set "raw" as default encoding extern void encoding_passinit(void); // try to load encoding table from given file extern void encoding_load_from_file(char target[256], FILE *stream); // lookup encoder held in DynaBuf and return its struct pointer (or NULL on failure) extern const struct encoder *encoding_find(void); #endif acme-crossassembler-0.96.4/src/flow.c000066400000000000000000000161011333777125400174610ustar00rootroot00000000000000// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. // Copyright (C) 1998-2017 Marco Baye // Have a look at "acme.c" for further info // // Flow control stuff (loops, conditional assembly etc.) // // Macros, conditional assembly, loops and sourcefile-includes are all based on // parsing blocks of code. When defining macros or using loops or conditional // assembly, the block starts with "{" and ends with "}". In the case of // "!source", the given file is treated like a block - the outermost assembler // function uses the same technique to parse the top level file. // // 24 Nov 2007 Added "!ifndef" #include "flow.h" #include #include "acme.h" #include "alu.h" #include "config.h" #include "dynabuf.h" #include "global.h" // FIXME - remove when no longer needed #include "input.h" #include "mnemo.h" #include "symbol.h" #include "tree.h" // helper functions for "!for" and "!do" // parse a loop body (TODO - also use for macro body?) static void parse_ram_block(struct block *block) { Input_now->line_number = block->start; // set line number to loop start Input_now->src.ram_ptr = block->body; // set RAM read pointer to loop // parse block Parse_until_eob_or_eof(); if (GotByte != CHAR_EOB) Bug_found("IllegalBlockTerminator", GotByte); } // back end function for "!for" pseudo opcode void flow_forloop(struct for_loop *loop) { struct input loop_input, *outer_input; struct result loop_counter; // switching input makes us lose GotByte. But we know it's '}' anyway! // set up new input loop_input = *Input_now; // copy current input structure into new loop_input.source_is_ram = TRUE; // set new byte source // remember old input outer_input = Input_now; // activate new input // (not yet useable; pointer and line number are still missing) Input_now = &loop_input; // init counter loop_counter.flags = MVALUE_DEFINED | MVALUE_EXISTS; loop_counter.val.intval = loop->counter.first; loop_counter.addr_refs = loop->counter.addr_refs; symbol_set_value(loop->symbol, &loop_counter, TRUE); if (loop->old_algo) { // old algo for old syntax: // if count == 0, skip loop if (loop->counter.last) { do { loop_counter.val.intval += loop->counter.increment; symbol_set_value(loop->symbol, &loop_counter, TRUE); parse_ram_block(&loop->block); } while (loop_counter.val.intval < loop->counter.last); } } else { // new algo for new syntax: do { parse_ram_block(&loop->block); loop_counter.val.intval += loop->counter.increment; symbol_set_value(loop->symbol, &loop_counter, TRUE); } while (loop_counter.val.intval != (loop->counter.last + loop->counter.increment)); } // restore previous input: Input_now = outer_input; } // try to read a condition into DynaBuf and store copy pointer in // given loop_condition structure. // if no condition given, NULL is written to structure. // call with GotByte = first interesting character void flow_store_doloop_condition(struct loop_condition *condition, char terminator) { // write line number condition->line = Input_now->line_number; // set defaults condition->is_until = FALSE; condition->body = NULL; // check for empty condition if (GotByte == terminator) return; // seems as if there really *is* a condition, so check for until/while if (Input_read_and_lower_keyword()) { if (strcmp(GlobalDynaBuf->buffer, "while") == 0) { //condition.is_until = FALSE; } else if (strcmp(GlobalDynaBuf->buffer, "until") == 0) { condition->is_until = TRUE; } else { Throw_error(exception_syntax); return; } // write given condition into buffer SKIPSPACE(); DYNABUF_CLEAR(GlobalDynaBuf); Input_until_terminator(terminator); DynaBuf_append(GlobalDynaBuf, CHAR_EOS); // ensure terminator condition->body = DynaBuf_get_copy(GlobalDynaBuf); } } // check a condition expression static int check_condition(struct loop_condition *condition) { struct result intresult; // first, check whether there actually *is* a condition if (condition->body == NULL) return TRUE; // non-existing conditions are always true // set up input for expression evaluation Input_now->line_number = condition->line; Input_now->src.ram_ptr = condition->body; GetByte(); // proceed with next char ALU_defined_int(&intresult); if (GotByte) Throw_serious_error(exception_syntax); return condition->is_until ? !intresult.val.intval : !!intresult.val.intval; } // back end function for "!do" pseudo opcode void flow_doloop(struct do_loop *loop) { struct input loop_input; struct input *outer_input; // set up new input loop_input = *Input_now; // copy current input structure into new loop_input.source_is_ram = TRUE; // set new byte source // remember old input outer_input = Input_now; // activate new input (not useable yet, as pointer and // line number are not yet set up) Input_now = &loop_input; for (;;) { // check head condition if (!check_condition(&loop->head_cond)) break; parse_ram_block(&loop->block); // check tail condition if (!check_condition(&loop->tail_cond)) break; } // restore previous input: Input_now = outer_input; GotByte = CHAR_EOS; // CAUTION! Very ugly kluge. // But by switching input, we lost the outer input's GotByte. We know // it was CHAR_EOS. We could just call GetByte() to get real input, but // then the main loop could choke on unexpected bytes. So we pretend // that we got the outer input's GotByte value magically back. } // helper functions for "!if", "!ifdef" and "!ifndef" // parse or skip a block. Returns whether block's '}' terminator was missing. // afterwards: GotByte = '}' static int skip_or_parse_block(int parse) { if (!parse) { Input_skip_or_store_block(FALSE); return 0; } // if block was correctly terminated, return FALSE Parse_until_eob_or_eof(); // if block isn't correctly terminated, complain and exit if (GotByte != CHAR_EOB) Throw_serious_error(exception_no_right_brace); return 0; } // parse {block} [else {block}] void flow_parse_block_else_block(int parse_first) { // Parse first block. // If it's not correctly terminated, return immediately (because // in that case, there's no use in checking for an "else" part). if (skip_or_parse_block(parse_first)) return; // now GotByte = '}'. Check for "else" part. // If end of statement, return immediately. NEXTANDSKIPSPACE(); if (GotByte == CHAR_EOS) return; // read keyword and check whether really "else" if (Input_read_and_lower_keyword()) { if (strcmp(GlobalDynaBuf->buffer, "else")) { Throw_error(exception_syntax); } else { SKIPSPACE(); if (GotByte != CHAR_SOB) Throw_serious_error(exception_no_left_brace); skip_or_parse_block(!parse_first); // now GotByte = '}' GetByte(); } } Input_ensure_EOS(); } // parse a whole source code file void flow_parse_and_close_file(FILE *fd, const char *filename) { // be verbose if (config.process_verbosity > 2) printf("Parsing source file '%s'\n", filename); // set up new input Input_new_file(filename, fd); // Parse block and check end reason Parse_until_eob_or_eof(); if (GotByte != CHAR_EOF) Throw_error("Found '}' instead of end-of-file."); // close sublevel src fclose(Input_now->src.fd); } acme-crossassembler-0.96.4/src/flow.h000066400000000000000000000032141333777125400174670ustar00rootroot00000000000000// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. // Copyright (C) 1998-2016 Marco Baye // Have a look at "acme.c" for further info // // flow control stuff (loops, conditional assembly etc.) #ifndef flow_H #define flow_H #include #include "config.h" struct block { int start; // line number of start of block char *body; }; // struct to pass "!for" loop stuff from pseudoopcodes.c to flow.c struct for_loop { struct symbol *symbol; int old_algo; // actually bool struct { intval_t first, last, increment; int addr_refs; // address reference count } counter; struct block block; }; // structs to pass "!do" loop stuff from pseudoopcodes.c to flow.c struct loop_condition { int line; // original line number int is_until; // actually bool (0 for WHILE, 1 for UNTIL) char *body; // pointer to actual expression }; struct do_loop { struct loop_condition head_cond; struct block block; struct loop_condition tail_cond; }; // back end function for "!for" pseudo opcode extern void flow_forloop(struct for_loop *loop); // try to read a condition into DynaBuf and store copy pointer in // given loop_condition structure. // if no condition given, NULL is written to structure. // call with GotByte = first interesting character extern void flow_store_doloop_condition(struct loop_condition *condition, char terminator); // back end function for "!do" pseudo opcode extern void flow_doloop(struct do_loop *loop); // parse a whole source code file extern void flow_parse_and_close_file(FILE *fd, const char *filename); // parse {block} [else {block}] extern void flow_parse_block_else_block(int parse_first); #endif acme-crossassembler-0.96.4/src/global.c000066400000000000000000000333701333777125400177610ustar00rootroot00000000000000// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. // Copyright (C) 1998-2017 Marco Baye // Have a look at "acme.c" for further info // // Global stuff - things that are needed by several modules // 4 Oct 2006 Fixed a typo in a comment // 22 Nov 2007 Added warn_on_indented_labels // 2 Jun 2014 Added warn_on_old_for and warn_on_type_mismatch // 19 Nov 2014 Merged Johann Klasek's report listing generator patch // 23 Nov 2014 Merged Martin Piper's "--msvc" error output patch #include "global.h" #include #include "platform.h" #include "acme.h" #include "cpu.h" #include "dynabuf.h" #include "input.h" #include "macro.h" #include "output.h" #include "pseudoopcodes.h" #include "section.h" #include "symbol.h" #include "tree.h" #include "typesystem.h" // constants const char s_and[] = "and"; const char s_asl[] = "asl"; const char s_asr[] = "asr"; const char s_bra[] = "bra"; const char s_brl[] = "brl"; const char s_cbm[] = "cbm"; const char s_eor[] = "eor"; const char s_error[] = "error"; const char s_lsr[] = "lsr"; const char s_scrxor[] = "scrxor"; char s_untitled[] = ""; // FIXME - this is actually const const char s_Zone[] = "Zone"; const char s_subzone[] = "subzone"; const char s_pet[] = "pet"; const char s_raw[] = "raw"; const char s_scr[] = "scr"; // Exception messages during assembly const char exception_cannot_open_input_file[] = "Cannot open input file."; const char exception_missing_string[] = "No string given."; const char exception_negative_size[] = "Negative size argument."; const char exception_no_left_brace[] = "Missing '{'."; const char exception_no_memory_left[] = "Out of memory."; const char exception_no_right_brace[] = "Found end-of-file instead of '}'."; //const char exception_not_yet[] = "Sorry, feature not yet implemented."; const char exception_number_out_of_range[] = "Number out of range."; const char exception_pc_undefined[] = "Program counter undefined."; const char exception_syntax[] = "Syntax error."; // default value for number of errors before exiting #define MAXERRORS 10 // Flag table: // This table contains flags for all the 256 possible byte values. The // assembler reads the table whenever it needs to know whether a byte is // allowed to be in a label name, for example. // Bits Meaning when set // 7....... Byte allowed to start keyword // .6...... Byte allowed in keyword // ..5..... Byte is upper case, can be lowercased by OR-ing this bit(!) // ...4.... special character for input syntax: 0x00 TAB LF CR SPC : ; } // ....3... preceding sequence of '-' characters is anonymous backward // label. Currently only set for ')', ',' and CHAR_EOS. // .....210 currently unused const char Byte_flags[256] = { /*$00*/ 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,// control characters 0x00, 0x10, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*$20*/ 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,// " !"#$%&'" 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,// "()*+,-./" 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,// "01234567" 0x40, 0x40, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00,// "89:;<=>?" /*$40*/ 0x00, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0,// "@ABCDEFG" 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0,// "HIJKLMNO" 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0,// "PQRSTUVW" 0xe0, 0xe0, 0xe0, 0x00, 0x00, 0x00, 0x00, 0xc0,// "XYZ[\]^_" /*$60*/ 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,// "`abcdefg" 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,// "hijklmno" 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,// "pqrstuvw" 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x10, 0x00, 0x00,// "xyz{|}~" BACKSPACE /*$80*/ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,// umlauts etc. ... 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /*$a0*/ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /*$c0*/ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, /*$e0*/ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, }; // variables int pass_count; // number of current pass (starts 0) char GotByte; // Last byte read (processed) // global counters int pass_undefined_count; // "NeedValue" type errors int pass_real_errors; // Errors yet FILE *msg_stream = NULL; // set to stdout by --use-stdout struct report *report = NULL; // configuration struct config config; // set configuration to default values void config_default(struct config *conf) { conf->pseudoop_prefix = '!'; // can be changed to '.' by CLI switch conf->process_verbosity = 0; // level of additional output conf->warn_on_indented_labels = TRUE; // warn if indented label is encountered conf->warn_on_old_for = TRUE; // warn if "!for" with old syntax is found conf->warn_on_type_mismatch = FALSE; // use type-checking system conf->max_errors = MAXERRORS; // errors before giving up conf->format_msvc = FALSE; // actually bool, enabled by --msvc conf->format_color = FALSE; // actually bool, enabled by --color } // memory allocation stuff // allocate memory and die if not available void *safe_malloc(size_t size) { void *block; if ((block = malloc(size)) == NULL) Throw_serious_error(exception_no_memory_left); return block; } // Parser stuff // Parse (re-)definitions of program counter static void parse_pc_def(void) // Now GotByte = "*" { NEXTANDSKIPSPACE(); // proceed with next char // re-definitions of program counter change segment if (GotByte == '=') { GetByte(); // proceed with next char notreallypo_setpc(); Input_ensure_EOS(); } else { Throw_error(exception_syntax); Input_skip_remainder(); } } // Check and return whether first label of statement. Complain if not. static int first_label_of_statement(int *statement_flags) { if ((*statement_flags) & SF_IMPLIED_LABEL) { Throw_error(exception_syntax); Input_skip_remainder(); return FALSE; } (*statement_flags) |= SF_IMPLIED_LABEL; // now there has been one return TRUE; } // Parse global symbol definition or assembler mnemonic static void parse_mnemo_or_global_symbol_def(int *statement_flags) { // It is only a label if it isn't a mnemonic if ((CPU_state.type->keyword_is_mnemonic(Input_read_keyword()) == FALSE) && first_label_of_statement(statement_flags)) { // Now GotByte = illegal char // 04 Jun 2005: this fix should help to explain "strange" error messages. // 17 May 2014: now it works for UTF-8 as well. if ((*GLOBALDYNABUF_CURRENT == (char) 0xa0) || ((GlobalDynaBuf->size >= 2) && (GLOBALDYNABUF_CURRENT[0] == (char) 0xc2) && (GLOBALDYNABUF_CURRENT[1] == (char) 0xa0))) Throw_first_pass_warning("Label name starts with a shift-space character."); symbol_parse_definition(SCOPE_GLOBAL, *statement_flags); } } // parse (cheap) local symbol definition static void parse_local_symbol_def(int *statement_flags, scope_t scope) { if (!first_label_of_statement(statement_flags)) return; GetByte(); // start after '.'/'@' if (Input_read_keyword()) symbol_parse_definition(scope, *statement_flags); } // parse anonymous backward label definition. Called with GotByte == '-' static void parse_backward_anon_def(int *statement_flags) { if (!first_label_of_statement(statement_flags)) return; DYNABUF_CLEAR(GlobalDynaBuf); do DYNABUF_APPEND(GlobalDynaBuf, '-'); while (GetByte() == '-'); DynaBuf_append(GlobalDynaBuf, '\0'); symbol_set_label(section_now->local_scope, *statement_flags, 0, TRUE); // this "TRUE" is the whole secret } // parse anonymous forward label definition. called with GotByte == ? static void parse_forward_anon_def(int *statement_flags) { if (!first_label_of_statement(statement_flags)) return; DYNABUF_CLEAR(GlobalDynaBuf); DynaBuf_append(GlobalDynaBuf, '+'); while (GotByte == '+') { DYNABUF_APPEND(GlobalDynaBuf, '+'); GetByte(); } symbol_fix_forward_anon_name(TRUE); // TRUE: increment counter DynaBuf_append(GlobalDynaBuf, '\0'); //printf("[%d, %s]\n", section_now->local_scope, GlobalDynaBuf->buffer); symbol_set_label(section_now->local_scope, *statement_flags, 0, FALSE); } // Parse block, beginning with next byte. // End reason (either CHAR_EOB or CHAR_EOF) can be found in GotByte afterwards // Has to be re-entrant. void Parse_until_eob_or_eof(void) { int statement_flags; // // start with next byte, don't care about spaces // NEXTANDSKIPSPACE(); // start with next byte GetByte(); // loop until end of block or end of file while ((GotByte != CHAR_EOB) && (GotByte != CHAR_EOF)) { // process one statement statement_flags = 0; // no "label = pc" definition yet typesystem_force_address_statement(FALSE); // Parse until end of statement. Only loops if statement // contains "label = pc" definition and something else; or // if "!ifdef/ifndef" is true/false, or if "!addr" is used without block. do { // check for pseudo opcodes was moved out of switch, // because prefix character is now configurable. if (GotByte == config.pseudoop_prefix) { pseudoopcode_parse(); } else { switch (GotByte) { case CHAR_EOS: // end of statement // Ignore now, act later // (stops from being "default") break; case ' ': // space statement_flags |= SF_FOUND_BLANK; /*FALLTHROUGH*/ case CHAR_SOL: // start of line GetByte(); // skip break; case '-': parse_backward_anon_def(&statement_flags); break; case '+': GetByte(); if ((GotByte == LOCAL_PREFIX) // TODO - allow "cheap macros"?! || (BYTEFLAGS(GotByte) & CONTS_KEYWORD)) Macro_parse_call(); else parse_forward_anon_def(&statement_flags); break; case '*': parse_pc_def(); break; case LOCAL_PREFIX: parse_local_symbol_def(&statement_flags, section_now->local_scope); break; case CHEAP_PREFIX: parse_local_symbol_def(&statement_flags, section_now->cheap_scope); break; default: if (BYTEFLAGS(GotByte) & STARTS_KEYWORD) { parse_mnemo_or_global_symbol_def(&statement_flags); } else { Throw_error(exception_syntax); Input_skip_remainder(); } } } } while (GotByte != CHAR_EOS); // until end-of-statement vcpu_end_statement(); // adjust program counter // go on with next byte GetByte(); //NEXTANDSKIPSPACE(); } } // Skip space. If GotByte is CHAR_SOB ('{'), parse block and return TRUE. // Otherwise (if there is no block), return FALSE. // Don't forget to call EnsureEOL() afterwards. int Parse_optional_block(void) { SKIPSPACE(); if (GotByte != CHAR_SOB) return FALSE; Parse_until_eob_or_eof(); if (GotByte != CHAR_EOB) Throw_serious_error(exception_no_right_brace); GetByte(); return TRUE; } // Error handling // error/warning counter so macro calls can find out whether to show a call stack static int throw_counter = 0; int Throw_get_counter(void) { return throw_counter; } // This function will do the actual output for warnings, errors and serious // errors. It shows the given message string, as well as the current // context: file name, line number, source type and source title. // TODO: make un-static so !info and !debug can use this. static void throw_message(const char *message, const char *type) { ++throw_counter; if (config.format_msvc) fprintf(msg_stream, "%s(%d) : %s (%s %s): %s\n", Input_now->original_filename, Input_now->line_number, type, section_now->type, section_now->title, message); else fprintf(msg_stream, "%s - File %s, line %d (%s %s): %s\n", type, Input_now->original_filename, Input_now->line_number, section_now->type, section_now->title, message); } // Output a warning. // This means the produced code looks as expected. But there has been a // situation that should be reported to the user, for example ACME may have // assembled a 16-bit parameter with an 8-bit value. void Throw_warning(const char *message) { PLATFORM_WARNING(message); if (config.format_color) throw_message(message, "\033[33mWarning\033[0m"); else throw_message(message, "Warning"); } // Output a warning if in first pass. See above. void Throw_first_pass_warning(const char *message) { if (pass_count == 0) Throw_warning(message); } // Output an error. // This means something went wrong in a way that implies that the output // almost for sure won't look like expected, for example when there was a // syntax error. The assembler will try to go on with the assembly though, so // the user gets to know about more than one of his typos at a time. void Throw_error(const char *message) { PLATFORM_ERROR(message); if (config.format_color) throw_message(message, "\033[31mError\033[0m"); else throw_message(message, "Error"); ++pass_real_errors; if (pass_real_errors >= config.max_errors) exit(ACME_finalize(EXIT_FAILURE)); } // Output a serious error, stopping assembly. // Serious errors are those that make it impossible to go on with the // assembly. Example: "!fill" without a parameter - the program counter cannot // be set correctly in this case, so proceeding would be of no use at all. void Throw_serious_error(const char *message) { PLATFORM_SERIOUS(message); if (config.format_color) throw_message(message, "\033[1m\033[31mSerious error\033[0m"); else throw_message(message, "Serious error"); // FIXME - exiting immediately inhibits output of macro call stack! exit(ACME_finalize(EXIT_FAILURE)); } // Handle bugs void Bug_found(const char *message, int code) { Throw_warning("Bug in ACME, code follows"); fprintf(stderr, "(0x%x:)", code); Throw_serious_error(message); } acme-crossassembler-0.96.4/src/global.h000066400000000000000000000127031333777125400177630ustar00rootroot00000000000000// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. // Copyright (C) 1998-2017 Marco Baye // Have a look at "acme.c" for further info // // Global stuff - things that are needed by several modules // 19 Nov 2014 Merged Johann Klasek's report listing generator patch // 23 Nov 2014 Merged Martin Piper's "--msvc" error output patch #ifndef global_H #define global_H #include #include #include #include "config.h" #define LOCAL_PREFIX '.' // FIXME - this is not yet used consistently! #define CHEAP_PREFIX '@' // prefix character for cheap locals // Constants #define SF_FOUND_BLANK (1u << 0) // statement had space or tab #define SF_IMPLIED_LABEL (1u << 1) // statement had implied label def extern const char s_and[]; extern const char s_asl[]; extern const char s_asr[]; extern const char s_bra[]; extern const char s_brl[]; extern const char s_cbm[]; extern const char s_eor[]; extern const char s_error[]; extern const char s_lsr[]; extern const char s_scrxor[]; extern char s_untitled[]; extern const char s_Zone[]; #define s_zone (s_subzone + 3) // Yes, I know I'm sick extern const char s_subzone[]; extern const char s_pet[]; extern const char s_raw[]; extern const char s_scr[]; // error messages during assembly extern const char exception_cannot_open_input_file[]; extern const char exception_missing_string[]; extern const char exception_negative_size[]; extern const char exception_no_left_brace[]; extern const char exception_no_memory_left[]; extern const char exception_no_right_brace[]; //extern const char exception_not_yet[]; extern const char exception_number_out_of_range[]; extern const char exception_pc_undefined[]; extern const char exception_syntax[]; // byte flags table extern const char Byte_flags[]; #define BYTEFLAGS(c) (Byte_flags[(unsigned char) c]) #define STARTS_KEYWORD (1u << 7) // Byte is allowed to start a keyword #define CONTS_KEYWORD (1u << 6) // Byte is allowed in a keyword #define BYTEIS_UPCASE (1u << 5) // Byte is upper case and can be // converted to lower case by OR-ing this bit(!) #define BYTEIS_SYNTAX (1u << 4) // special character for input syntax #define FOLLOWS_ANON (1u << 3) // preceding '-' are backward label // bits 2, 1 and 0 are currently unused // TODO - put in runtime struct: extern int pass_count; extern char GotByte; // Last byte read (processed) extern int pass_undefined_count; // "NeedValue" type errors in current pass extern int pass_real_errors; // Errors yet extern FILE *msg_stream; // set to stdout by --errors_to_stdout // configuration struct config { char pseudoop_prefix; // '!' or '.' int process_verbosity; // level of additional output int warn_on_indented_labels; // warn if indented label is encountered int warn_on_old_for; // warn if "!for" with old syntax is found int warn_on_type_mismatch; // use type-checking system signed long max_errors; // errors before giving up int format_msvc; // actually bool, enabled by --msvc int format_color; // actually bool, enabled by --color }; extern struct config config; // report stuff #define REPORT_ASCBUFSIZE 1024 #define REPORT_BINBUFSIZE 9 // eight are shown, then "..." struct report { FILE *fd; // report file descriptor (NULL => no report) struct input *last_input; size_t asc_used; size_t bin_used; int bin_address; // address at start of bin_buf[] char asc_buf[REPORT_ASCBUFSIZE]; // source bytes char bin_buf[REPORT_BINBUFSIZE]; // output bytes }; extern struct report *report; // TODO - put in "part" struct // Macros for skipping a single space character #define SKIPSPACE() \ do { \ if (GotByte == ' ') \ GetByte(); \ } while (0) #define NEXTANDSKIPSPACE() \ do { \ if (GetByte() == ' ') \ GetByte(); \ } while (0) // Prototypes // set configuration to default values extern void config_default(struct config *conf); // allocate memory and die if not available extern void *safe_malloc(size_t); // Parse block, beginning with next byte. // End reason (either CHAR_EOB or CHAR_EOF) can be found in GotByte afterwards // Has to be re-entrant. extern void Parse_until_eob_or_eof(void); // Skip space. If GotByte is CHAR_SOB ('{'), parse block and return TRUE. // Otherwise (if there is no block), return FALSE. // Don't forget to call EnsureEOL() afterwards. extern int Parse_optional_block(void); // error/warning counter so macro calls can find out whether to show a call stack extern int Throw_get_counter(void); // Output a warning. // This means the produced code looks as expected. But there has been a // situation that should be reported to the user, for example ACME may have // assembled a 16-bit parameter with an 8-bit value. extern void Throw_warning(const char *); // Output a warning if in first pass. See above. extern void Throw_first_pass_warning(const char *); // Output an error. // This means something went wrong in a way that implies that the output // almost for sure won't look like expected, for example when there was a // syntax error. The assembler will try to go on with the assembly though, so // the user gets to know about more than one of his typos at a time. extern void Throw_error(const char *); // Output a serious error, stopping assembly. // Serious errors are those that make it impossible to go on with the // assembly. Example: "!fill" without a parameter - the program counter cannot // be set correctly in this case, so proceeding would be of no use at all. extern void Throw_serious_error(const char *); // handle bugs extern void Bug_found(const char *, int); #endif acme-crossassembler-0.96.4/src/input.c000066400000000000000000000456261333777125400176670ustar00rootroot00000000000000// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. // Copyright (C) 1998-2017 Marco Baye // Have a look at "acme.c" for further info // // Input stuff // 19 Nov 2014 Merged Johann Klasek's report listing generator patch #include "input.h" #include "config.h" #include "alu.h" #include "dynabuf.h" #include "global.h" // FIXME - remove when no longer needed #include "platform.h" #include "section.h" #include "symbol.h" #include "tree.h" // Constants const char FILE_READBINARY[] = "rb"; #define CHAR_TAB (9) // Tab character #define CHAR_LF (10) // line feed (in file) // (10) // start of line (in high-level format) #define CHAR_CR (13) // carriage return (in file) // (13) // end of file (in high-level format) #define CHAR_STATEMENT_DELIMITER ':' #define CHAR_COMMENT_SEPARATOR ';' // if the characters above are changed, don't forget to adjust ByteFlags[]! // fake input structure (for error msgs before any real input is established) static struct input outermost = { "", // file name 0, // line number FALSE, // Faked file access, so no RAM read INPUTSTATE_EOF, // state of input { NULL // RAM read pointer or file handle } }; // variables struct input *Input_now = &outermost; // current input structure // functions // let current input point to start of file void Input_new_file(const char *filename, FILE *fd) { Input_now->original_filename = filename; Input_now->line_number = 1; Input_now->source_is_ram = FALSE; Input_now->state = INPUTSTATE_NORMAL; Input_now->src.fd = fd; } // remember source code character for report generator #define HEXBUFSIZE 9 // actually, 4+1 is enough, but for systems without snprintf(), let's be extra-safe. #define IF_WANTED_REPORT_SRCCHAR(c) do { if (report->fd) report_srcchar(c); } while(0) static void report_srcchar(char new_char) { static char prev_char = '\0'; int ii; char hex_address[HEXBUFSIZE]; char hexdump[2 * REPORT_BINBUFSIZE + 2]; // +2 for '.' and terminator // if input has changed, insert explanation if (Input_now != report->last_input) { fprintf(report->fd, "\n; ******** Source: %s\n", Input_now->original_filename); report->last_input = Input_now; report->asc_used = 0; // clear buffer prev_char = '\0'; } if (prev_char == '\n') { // line start after line break detected and EOS processed, // build report line: // show line number... fprintf(report->fd, "%6d ", Input_now->line_number - 1); // prepare outbytes' start address if (report->bin_used) #if _BSD_SOURCE || _XOPEN_SOURCE >= 500 || _ISOC99_SOURCE || _POSIX_C_SOURCE >= 200112L snprintf(hex_address, HEXBUFSIZE, "%04x", report->bin_address); #else sprintf(hex_address, "%04x", report->bin_address); #endif else hex_address[0] = '\0'; // prepare outbytes hexdump[0] = '\0'; for (ii = 0; ii < report->bin_used; ++ii) sprintf(hexdump + 2 * ii, "%02x", (unsigned int) (unsigned char) (report->bin_buf[ii])); // if binary buffer is full, overwrite last byte with "..." if (report->bin_used == REPORT_BINBUFSIZE) sprintf(hexdump + 2 * (REPORT_BINBUFSIZE - 1), "..."); // show address and bytes fprintf(report->fd, "%-4s %-19s", hex_address, hexdump); // at this point the output should be a multiple of 8 characters // so far to preserve tabs of the source... if (report->asc_used == REPORT_ASCBUFSIZE) --report->asc_used; report->asc_buf[report->asc_used] = '\0'; fprintf(report->fd, "%s\n", report->asc_buf); // show source line report->asc_used = 0; // reset buffers report->bin_used = 0; } if (new_char != '\n' && new_char != '\r') { // detect line break if (report->asc_used < REPORT_ASCBUFSIZE) report->asc_buf[report->asc_used++] = new_char; } prev_char = new_char; } // Deliver source code from current file (!) in shortened high-level format static char get_processed_from_file(void) { int from_file = 0; for (;;) { switch (Input_now->state) { case INPUTSTATE_NORMAL: // fetch a fresh byte from the current source file from_file = getc(Input_now->src.fd); IF_WANTED_REPORT_SRCCHAR(from_file); // now process it /*FALLTHROUGH*/ case INPUTSTATE_AGAIN: // Process the latest byte again. Of course, this only // makes sense if the loop has executed at least once, // otherwise the contents of from_file are undefined. // If the source is changed so there is a possibility // to enter INPUTSTATE_AGAIN mode without first having // defined "from_file", trouble may arise... Input_now->state = INPUTSTATE_NORMAL; // EOF must be checked first because it cannot be used // as an index into Byte_flags[] if (from_file == EOF) { // remember to send an end-of-file Input_now->state = INPUTSTATE_EOF; return CHAR_EOS; // end of statement } // check whether character is special one // if not, everything's cool and froody, so return it if ((BYTEFLAGS(from_file) & BYTEIS_SYNTAX) == 0) return (char) from_file; // check special characters ("0x00 TAB LF CR SPC :;}") switch (from_file) { case CHAR_TAB: // TAB character case ' ': // remember to skip all following blanks Input_now->state = INPUTSTATE_SKIPBLANKS; return ' '; case CHAR_LF: // LF character // remember to send a start-of-line Input_now->state = INPUTSTATE_LF; return CHAR_EOS; // end of statement case CHAR_CR: // CR character // remember to check CRLF + send start-of-line Input_now->state = INPUTSTATE_CR; return CHAR_EOS; // end of statement case CHAR_EOB: // remember to send an end-of-block Input_now->state = INPUTSTATE_EOB; return CHAR_EOS; // end of statement case CHAR_STATEMENT_DELIMITER: // just deliver an EOS instead return CHAR_EOS; // end of statement case CHAR_COMMENT_SEPARATOR: // remember to skip remainder of line Input_now->state = INPUTSTATE_COMMENT; return CHAR_EOS; // end of statement default: // complain if byte is 0 Throw_error("Source file contains illegal character."); return (char) from_file; } case INPUTSTATE_SKIPBLANKS: // read until non-blank, then deliver that do { from_file = getc(Input_now->src.fd); IF_WANTED_REPORT_SRCCHAR(from_file); } while ((from_file == CHAR_TAB) || (from_file == ' ')); // re-process last byte Input_now->state = INPUTSTATE_AGAIN; break; case INPUTSTATE_LF: // return start-of-line, then continue in normal mode Input_now->state = INPUTSTATE_NORMAL; return CHAR_SOL; // new line case INPUTSTATE_CR: // return start-of-line, remember to check for LF Input_now->state = INPUTSTATE_SKIPLF; return CHAR_SOL; // new line case INPUTSTATE_SKIPLF: from_file = getc(Input_now->src.fd); IF_WANTED_REPORT_SRCCHAR(from_file); // if LF, ignore it and fetch another byte // otherwise, process current byte if (from_file == CHAR_LF) Input_now->state = INPUTSTATE_NORMAL; else Input_now->state = INPUTSTATE_AGAIN; break; case INPUTSTATE_COMMENT: // read until end-of-line or end-of-file do { from_file = getc(Input_now->src.fd); IF_WANTED_REPORT_SRCCHAR(from_file); } while ((from_file != EOF) && (from_file != CHAR_CR) && (from_file != CHAR_LF)); // re-process last byte Input_now->state = INPUTSTATE_AGAIN; break; case INPUTSTATE_EOB: // deliver EOB Input_now->state = INPUTSTATE_NORMAL; return CHAR_EOB; // end of block case INPUTSTATE_EOF: // deliver EOF Input_now->state = INPUTSTATE_NORMAL; return CHAR_EOF; // end of file default: Bug_found("StrangeInputMode", Input_now->state); } } } // This function delivers the next byte from the currently active byte source // in shortened high-level format. FIXME - use fn ptr? // When inside quotes, use GetQuotedByte() instead! char GetByte(void) { // for (;;) { // If byte source is RAM, then no conversions are // necessary, because in RAM the source already has // high-level format // Otherwise, the source is a file. This means we will call // GetFormatted() which will do a shit load of conversions. if (Input_now->source_is_ram) GotByte = *(Input_now->src.ram_ptr++); else GotByte = get_processed_from_file(); // // if start-of-line was read, increment line counter and repeat // if (GotByte != CHAR_SOL) // return GotByte; // Input_now->line_number++; // } if (GotByte == CHAR_SOL) Input_now->line_number++; return GotByte; } // This function delivers the next byte from the currently active byte source // in un-shortened high-level format. // This function complains if CHAR_EOS (end of statement) is read. char GetQuotedByte(void) { int from_file; // must be an int to catch EOF // if byte source is RAM, then no conversion is necessary, // because in RAM the source already has high-level format if (Input_now->source_is_ram) { GotByte = *(Input_now->src.ram_ptr++); // Otherwise, the source is a file. } else { // fetch a fresh byte from the current source file from_file = getc(Input_now->src.fd); IF_WANTED_REPORT_SRCCHAR(from_file); switch (from_file) { case EOF: // remember to send an end-of-file Input_now->state = INPUTSTATE_EOF; GotByte = CHAR_EOS; // end of statement break; case CHAR_LF: // LF character // remember to send a start-of-line Input_now->state = INPUTSTATE_LF; GotByte = CHAR_EOS; // end of statement break; case CHAR_CR: // CR character // remember to check for CRLF + send a start-of-line Input_now->state = INPUTSTATE_CR; GotByte = CHAR_EOS; // end of statement break; default: GotByte = from_file; } } // now check for end of statement if (GotByte == CHAR_EOS) Throw_error("Quotes still open at end of line."); return GotByte; } // Skip remainder of statement, for example on error void Input_skip_remainder(void) { while (GotByte) GetByte(); // Read characters until end-of-statement } // Ensure that the remainder of the current statement is empty, for example // after mnemonics using implied addressing. void Input_ensure_EOS(void) // Now GotByte = first char to test { SKIPSPACE(); if (GotByte) { Throw_error("Garbage data at end of statement."); Input_skip_remainder(); } } // Skip or store block (starting with next byte, so call directly after // reading opening brace). // If "Store" is TRUE, the block is read into GlobalDynaBuf, then a copy // is made and a pointer to that is returned. // If "Store" is FALSE, NULL is returned. // After calling this function, GotByte holds '}'. Unless EOF was found first, // but then a serious error would have been thrown. // FIXME - use a struct block *ptr argument! char *Input_skip_or_store_block(int store) { char byte; int depth = 1; // to find matching block end // prepare global dynamic buffer DYNABUF_CLEAR(GlobalDynaBuf); do { byte = GetByte(); // if wanted, store if (store) DYNABUF_APPEND(GlobalDynaBuf, byte); // now check for some special characters switch (byte) { case CHAR_EOF: // End-of-file in block? Sorry, no way. Throw_serious_error(exception_no_right_brace); case '"': // Quotes? Okay, read quoted stuff. case '\'': do { GetQuotedByte(); // if wanted, store if (store) DYNABUF_APPEND(GlobalDynaBuf, GotByte); } while ((GotByte != CHAR_EOS) && (GotByte != byte)); break; case CHAR_SOB: ++depth; break; case CHAR_EOB: --depth; break; } } while (depth); // in case of skip, return now if (!store) return NULL; // otherwise, prepare to return copy of block // add EOF, just to make sure block is never read too far DynaBuf_append(GlobalDynaBuf, CHAR_EOS); DynaBuf_append(GlobalDynaBuf, CHAR_EOF); // return pointer to copy return DynaBuf_get_copy(GlobalDynaBuf); } // Read bytes and add to GlobalDynaBuf until the given terminator (or CHAR_EOS) // is found. Act upon single and double quotes by entering (and leaving) quote // mode as needed (So the terminator does not terminate when inside quotes). void Input_until_terminator(char terminator) { char byte = GotByte; for (;;) { // Terminator? Exit. EndOfStatement? Exit. if ((byte == terminator) || (byte == CHAR_EOS)) return; // otherwise, append to GlobalDynaBuf and check for quotes DYNABUF_APPEND(GlobalDynaBuf, byte); if ((byte == '"') || (byte == '\'')) { do { // Okay, read quoted stuff. GetQuotedByte(); // throws error on EOS DYNABUF_APPEND(GlobalDynaBuf, GotByte); } while ((GotByte != CHAR_EOS) && (GotByte != byte)); // on error, exit now, before calling GetByte() if (GotByte != byte) return; } byte = GetByte(); } } // Append to GlobalDynaBuf while characters are legal for keywords. // Throws "missing string" error if none. // Returns number of characters added. int Input_append_keyword_to_global_dynabuf(void) { int length = 0; // add characters to buffer until an illegal one comes along while (BYTEFLAGS(GotByte) & CONTS_KEYWORD) { DYNABUF_APPEND(GlobalDynaBuf, GotByte); ++length; GetByte(); } if (length == 0) Throw_error(exception_missing_string); return length; } // Check GotByte. // If LOCAL_PREFIX ('.'), store current local scope value and read next byte. // If CHEAP_PREFIX ('@'), store current cheap scope value and read next byte. // Otherwise, store global scope value. // Then jump to Input_read_keyword(), which returns length of keyword. int Input_read_scope_and_keyword(scope_t *scope) { SKIPSPACE(); if (GotByte == LOCAL_PREFIX) { GetByte(); *scope = section_now->local_scope; } else if (GotByte == CHEAP_PREFIX) { GetByte(); *scope = section_now->cheap_scope; } else { *scope = SCOPE_GLOBAL; } return Input_read_keyword(); } // Clear dynamic buffer, then append to it until an illegal (for a keyword) // character is read. Zero-terminate the string. Return its length (without // terminator). // Zero lengths will produce a "missing string" error. int Input_read_keyword(void) { int length; DYNABUF_CLEAR(GlobalDynaBuf); length = Input_append_keyword_to_global_dynabuf(); // add terminator to buffer (increments buffer's length counter) DynaBuf_append(GlobalDynaBuf, '\0'); return length; } // Clear dynamic buffer, then append to it until an illegal (for a keyword) // character is read. Zero-terminate the string, then convert to lower case. // Return its length (without terminator). // Zero lengths will produce a "missing string" error. int Input_read_and_lower_keyword(void) { int length; DYNABUF_CLEAR(GlobalDynaBuf); length = Input_append_keyword_to_global_dynabuf(); // add terminator to buffer (increments buffer's length counter) DynaBuf_append(GlobalDynaBuf, '\0'); DynaBuf_to_lower(GlobalDynaBuf, GlobalDynaBuf); // convert to lower case return length; } // Try to read a file name. // If "allow_library" is TRUE, library access by using <...> quoting // is possible as well. If "uses_lib" is non-NULL, info about library // usage is stored there. // The file name given in the assembler source code is converted from // UNIX style to platform style. // Returns whether error occurred (TRUE on error). Filename in GlobalDynaBuf. // Errors are handled and reported, but caller should call // Input_skip_remainder() then. int Input_read_filename(int allow_library, int *uses_lib) { char *lib_prefix, end_quote; DYNABUF_CLEAR(GlobalDynaBuf); SKIPSPACE(); // check for library access if (GotByte == '<') { if (uses_lib) *uses_lib = 1; // if library access forbidden, complain if (allow_library == FALSE) { Throw_error("Writing to library not supported."); return TRUE; } // read platform's lib prefix lib_prefix = PLATFORM_LIBPREFIX; #ifndef NO_NEED_FOR_ENV_VAR // if lib prefix not set, complain if (lib_prefix == NULL) { Throw_error("\"ACME\" environment variable not found."); return TRUE; } #endif // copy lib path and set quoting char DynaBuf_add_string(GlobalDynaBuf, lib_prefix); end_quote = '>'; } else { if (uses_lib) *uses_lib = 0; if (GotByte == '"') { end_quote = '"'; } else { Throw_error("File name quotes not found (\"\" or <>)."); return TRUE; } } // read first character, complain if closing quote if (GetQuotedByte() == end_quote) { Throw_error("No file name given."); return TRUE; } // read characters until closing quote (or EOS) is reached // append platform-converted characters to current string while ((GotByte != CHAR_EOS) && (GotByte != end_quote)) { DYNABUF_APPEND(GlobalDynaBuf, PLATFORM_CONVERTPATHCHAR(GotByte)); GetQuotedByte(); } // on error, return if (GotByte == CHAR_EOS) return TRUE; GetByte(); // fetch next to forget closing quote // terminate string DynaBuf_append(GlobalDynaBuf, '\0'); // add terminator return FALSE; // no error } // Try to read a comma, skipping spaces before and after. Return TRUE if comma // found, otherwise FALSE. int Input_accept_comma(void) { SKIPSPACE(); if (GotByte != ',') return FALSE; NEXTANDSKIPSPACE(); return TRUE; } // read optional info about parameter length int Input_get_force_bit(void) { char byte; int force_bit = 0; if (GotByte == '+') { byte = GetByte(); if (byte == '1') force_bit = MVALUE_FORCE08; else if (byte == '2') force_bit = MVALUE_FORCE16; else if (byte == '3') force_bit = MVALUE_FORCE24; if (force_bit) GetByte(); else Throw_error("Illegal postfix."); } SKIPSPACE(); return force_bit; } // include path stuff - should be moved to its own file: // ring list struct struct ipi { struct ipi *next, *prev; const char *path; }; static struct ipi ipi_head; // head element static struct dynabuf *pathbuf; // buffer to combine search path and file spec // init list void includepaths_init(void) { // init ring list ipi_head.next = &ipi_head; ipi_head.prev = &ipi_head; // init dynabuf pathbuf = DynaBuf_create(256); } // add entry void includepaths_add(const char *path) { struct ipi *ipi; ipi = safe_malloc(sizeof(*ipi)); ipi->path = path; ipi->next = &ipi_head; ipi->prev = ipi_head.prev; ipi->next->prev = ipi; ipi->prev->next = ipi; } // open file for reading (trying list entries as prefixes) // "uses_lib" tells whether to access library or to make use of include paths // file name is expected in GlobalDynaBuf FILE *includepaths_open_ro(int uses_lib) { FILE *stream; struct ipi *ipi; // first try directly, regardless of whether lib or not: stream = fopen(GLOBALDYNABUF_CURRENT, FILE_READBINARY); // if failed and not lib, try include paths: if ((stream == NULL) && !uses_lib) { for (ipi = ipi_head.next; ipi != &ipi_head; ipi = ipi->next) { DYNABUF_CLEAR(pathbuf); // add first part DynaBuf_add_string(pathbuf, ipi->path); // if wanted and possible, ensure last char is directory separator if (DIRECTORY_SEPARATOR && pathbuf->size && (pathbuf->buffer[pathbuf->size - 1] != DIRECTORY_SEPARATOR)) DynaBuf_append(pathbuf, DIRECTORY_SEPARATOR); // add second part DynaBuf_add_string(pathbuf, GLOBALDYNABUF_CURRENT); // terminate DynaBuf_append(pathbuf, '\0'); // try stream = fopen(pathbuf->buffer, FILE_READBINARY); //printf("trying <<%s>> - ", pathbuf->buffer); if (stream) { //printf("ok\n"); break; } else { //printf("failed\n"); } } } if (stream == NULL) Throw_error(exception_cannot_open_input_file); return stream; } acme-crossassembler-0.96.4/src/input.h000066400000000000000000000124621333777125400176640ustar00rootroot00000000000000// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. // Copyright (C) 1998-2017 Marco Baye // Have a look at "acme.c" for further info // // Input stuff #ifndef input_H #define input_H #include // for FILE #include "config.h" // for scope_t // type definitions // values for input component "src.state" enum inputstate { INPUTSTATE_NORMAL, // everything's fine INPUTSTATE_AGAIN, // re-process last byte INPUTSTATE_SKIPBLANKS, // shrink multiple spaces INPUTSTATE_LF, // send start-of-line after end-of-statement INPUTSTATE_CR, // same, but also remember to skip LF INPUTSTATE_SKIPLF, // skip LF if that's next INPUTSTATE_COMMENT, // skip characters until newline or EOF INPUTSTATE_EOB, // send end-of-block after end-of-statement INPUTSTATE_EOF, // send end-of-file after end-of-statement }; struct input { const char *original_filename; // during RAM reads, too int line_number, // in file (on RAM reads, too) source_is_ram; // TRUE if RAM, FALSE if file enum inputstate state; // state of input union { FILE *fd; // file descriptor char *ram_ptr; // RAM read ptr (loop or macro block) } src; }; // Constants extern const char FILE_READBINARY[]; // Special characters // The program *heavily* relies on CHAR_EOS (end of statement) being 0x00! #define CHAR_EOS (0) // end of statement (in high-level format) #define CHAR_SOB '{' // start of block #define CHAR_EOB '}' // end of block #define CHAR_SOL (10) // start of line (in high-level format) #define CHAR_EOF (13) // end of file (in high-level format) // If the characters above are changed, don't forget to adjust Byte_flags[]! // Variables extern struct input *Input_now; // current input structure // Prototypes // let current input point to start of file extern void Input_new_file(const char *filename, FILE *fd); // get next byte from currently active byte source in shortened high-level // format. When inside quotes, use GetQuotedByte() instead! extern char GetByte(void); // get next byte from currently active byte source in un-shortened high-level // format. Complains if CHAR_EOS (end of statement) is read. extern char GetQuotedByte(void); // Skip remainder of statement, for example on error extern void Input_skip_remainder(void); // Ensure that the remainder of the current statement is empty, for example // after mnemonics using implied addressing. extern void Input_ensure_EOS(void); // Skip or store block (starting with next byte, so call directly after // reading opening brace). // If "Store" is TRUE, the block is read into GlobalDynaBuf, then a copy // is made and a pointer to that is returned. // If "Store" is FALSE, NULL is returned. // After calling this function, GotByte holds '}'. Unless EOF was found first, // but then a serious error would have been thrown. extern char *Input_skip_or_store_block(int store); // Read bytes and add to GlobalDynaBuf until the given terminator (or CHAR_EOS) // is found. Act upon single and double quotes by entering (and leaving) quote // mode as needed (So the terminator does not terminate when inside quotes). extern void Input_until_terminator(char terminator); // Append to GlobalDynaBuf while characters are legal for keywords. // Throws "missing string" error if none. Returns number of characters added. extern int Input_append_keyword_to_global_dynabuf(void); // Check GotByte. // If LOCAL_PREFIX ('.'), store current local scope value and read next byte. // If CHEAP_PREFIX ('@'), store current cheap scope value and read next byte. // Otherwise, store global scope value. // Then jump to Input_read_keyword(), which returns length of keyword. extern int Input_read_scope_and_keyword(scope_t *scope); // Clear dynamic buffer, then append to it until an illegal (for a keyword) // character is read. Zero-terminate the string. Return its length (without // terminator). // Zero lengths will produce a "missing string" error. extern int Input_read_keyword(void); // Clear dynamic buffer, then append to it until an illegal (for a keyword) // character is read. Zero-terminate the string, then convert to lower case. // Return its length (without terminator). // Zero lengths will produce a "missing string" error. extern int Input_read_and_lower_keyword(void); // Try to read a file name. // If "allow_library" is TRUE, library access by using <...> quoting // is possible as well. If "uses_lib" is non-NULL, info about library // usage is stored there. // The file name given in the assembler source code is converted from // UNIX style to platform style. // Returns whether error occurred (TRUE on error). Filename in GlobalDynaBuf. // Errors are handled and reported, but caller should call // Input_skip_remainder() then. extern int Input_read_filename(int library_allowed, int *uses_lib); // Try to read a comma, skipping spaces before and after. Return TRUE if comma // found, otherwise FALSE. extern int Input_accept_comma(void); // read optional info about parameter length extern int Input_get_force_bit(void); // include path stuff - should be moved to its own file: // init list extern void includepaths_init(void); // add entry extern void includepaths_add(const char *path); // open file for reading (trying list entries as prefixes) // "uses_lib" tells whether to access library or to make use of include paths // file name is expected in GlobalDynaBuf extern FILE *includepaths_open_ro(int uses_lib); #endif acme-crossassembler-0.96.4/src/macro.c000066400000000000000000000314701333777125400176210ustar00rootroot00000000000000// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. // Copyright (C) 1998-2017 Marco Baye // Have a look at "acme.c" for further info // // Macro stuff #include "macro.h" #include // needs strlen() + memcpy() #include "config.h" #include "platform.h" #include "acme.h" #include "alu.h" #include "dynabuf.h" #include "global.h" #include "input.h" #include "section.h" #include "symbol.h" #include "tree.h" // Constants #define MACRONAME_DYNABUF_INITIALSIZE 128 #define ARG_SEPARATOR ' ' // separates macro title from arg types #define ARGTYPE_NUM_VAL 'v' #define ARGTYPE_NUM_REF 'V' //#define ARGTYPE_STR_VAL 's' //#define ARGTYPE_STR_REF 'S' #define REFERENCE_CHAR '~' // prefix for call-by-reference #define HALF_INITIAL_ARG_TABLE_SIZE 4 static const char exception_macro_twice[] = "Macro already defined."; // macro struct type definition struct macro { int def_line_number; // line number of definition for error msgs char *def_filename, // file name of definition for error msgs *original_name, // user-supplied name for error msgs *parameter_list, // parameters (whole line) *body; // RAM block containing macro body }; // there's no need to make this a struct and add a type component: // when the macro has been found, accessing its parameter_list component // gives us the possibility to find out which args are call-by-value and // which ones are call-by-reference. union macro_arg_t { struct result result; // value and flags (call by value) struct symbol *symbol; // pointer to symbol struct (call by reference) }; // Variables static struct dynabuf *user_macro_name; // original macro title static struct dynabuf *internal_name; // plus param type chars static struct rwnode *macro_forest[256]; // trees (because of 8b hash) // Dynamic argument table static union macro_arg_t *arg_table = NULL; static int argtable_size = HALF_INITIAL_ARG_TABLE_SIZE; // Functions // Enlarge the argument table static void enlarge_arg_table(void) { argtable_size *= 2; arg_table = realloc(arg_table, argtable_size * sizeof(*arg_table)); if (arg_table == NULL) Throw_serious_error(exception_no_memory_left); } // create dynamic buffers and arg table void Macro_init(void) { user_macro_name = DynaBuf_create(MACRONAME_DYNABUF_INITIALSIZE); internal_name = DynaBuf_create(MACRONAME_DYNABUF_INITIALSIZE); enlarge_arg_table(); } // Read macro scope and title. Title is read to GlobalDynaBuf and then copied // over to internal_name DynaBuf, where ARG_SEPARATOR is added. // In user_macro_name DynaBuf, the original name is reconstructed (even with // LOCAL_PREFIX) so a copy can be linked to the resulting macro struct. static scope_t get_scope_and_title(void) { scope_t macro_scope; Input_read_scope_and_keyword(¯o_scope); // skips spaces before // now GotByte = illegal character after title // copy macro title to private dynabuf and add separator character DYNABUF_CLEAR(user_macro_name); DYNABUF_CLEAR(internal_name); if (macro_scope != SCOPE_GLOBAL) { // TODO - allow "cheap macros"?! DynaBuf_append(user_macro_name, LOCAL_PREFIX); } DynaBuf_add_string(user_macro_name, GLOBALDYNABUF_CURRENT); DynaBuf_add_string(internal_name, GLOBALDYNABUF_CURRENT); DynaBuf_append(user_macro_name, '\0'); DynaBuf_append(internal_name, ARG_SEPARATOR); SKIPSPACE(); // done here once so it's not necessary at two callers return macro_scope; } // Check for comma. If there, append to GlobalDynaBuf. static int pipe_comma(void) { int result; result = Input_accept_comma(); if (result) DYNABUF_APPEND(GlobalDynaBuf, ','); return result; } // Return malloc'd copy of string static char *get_string_copy(const char *original) { size_t size; char *copy; size = strlen(original) + 1; copy = safe_malloc(size); memcpy(copy, original, size); return copy; } // This function is called from both macro definition and macro call. // Terminate macro name and copy from internal_name to GlobalDynaBuf // (because that's where Tree_hard_scan() looks for the search string). // Then try to find macro and return whether it was created. static int search_for_macro(struct rwnode **result, scope_t scope, int create) { DynaBuf_append(internal_name, '\0'); // terminate macro name // now internal_name = macro_title SPC argument_specifiers NUL DYNABUF_CLEAR(GlobalDynaBuf); DynaBuf_add_string(GlobalDynaBuf, internal_name->buffer); DynaBuf_append(GlobalDynaBuf, '\0'); return Tree_hard_scan(result, macro_forest, scope, create); } // This function is called when an already existing macro is re-defined. // It first outputs a warning and then a serious error, stopping assembly. // Showing the first message as a warning guarantees that ACME does not reach // the maximum error limit inbetween. static void report_redefinition(struct rwnode *macro_node) { struct macro *original_macro = macro_node->body; // show warning with location of current definition Throw_warning(exception_macro_twice); // CAUTION, ugly kluge: fiddle with Input_now and section_now // data to generate helpful error messages Input_now->original_filename = original_macro->def_filename; Input_now->line_number = original_macro->def_line_number; section_now->type = "original"; section_now->title = "definition"; // show serious error with location of original definition Throw_serious_error(exception_macro_twice); } // This function is only called during the first pass, so there's no need to // check whether to skip the definition or not. // Return with GotByte = '}' void Macro_parse_definition(void) // Now GotByte = illegal char after "!macro" { char *formal_parameters; struct rwnode *macro_node; struct macro *new_macro; scope_t macro_scope = get_scope_and_title(); // now GotByte = first non-space after title DYNABUF_CLEAR(GlobalDynaBuf); // prepare to hold formal parameters // GlobalDynaBuf = "" (will hold formal parameter list) // user_macro_name = [LOCAL_PREFIX] MacroTitle NUL // internal_name = MacroTitle ARG_SEPARATOR (grows to signature) // Accept n>=0 comma-separated formal parameters before CHAR_SOB ('{'). // Valid argument formats are: // .LOCAL_LABEL_BY_VALUE // ~.LOCAL_LABEL_BY_REFERENCE // @CHEAP_LOCAL_LABEL_BY_VALUE // ~@CHEAP_LOCAL_LABEL_BY_REFERENCE // GLOBAL_LABEL_BY_VALUE global args are very uncommon, // ~GLOBAL_LABEL_BY_REFERENCE but not forbidden // now GotByte = non-space if (GotByte != CHAR_SOB) { // any at all? do { // handle call-by-reference character ('~') if (GotByte != REFERENCE_CHAR) { DynaBuf_append(internal_name, ARGTYPE_NUM_VAL); } else { DynaBuf_append(internal_name, ARGTYPE_NUM_REF); DynaBuf_append(GlobalDynaBuf, REFERENCE_CHAR); GetByte(); } // handle prefix for (cheap) local symbols ('.'/'@') if ((GotByte == LOCAL_PREFIX) || (GotByte == CHEAP_PREFIX)) { DynaBuf_append(GlobalDynaBuf, GotByte); GetByte(); } // handle symbol name Input_append_keyword_to_global_dynabuf(); } while (pipe_comma()); // ensure CHAR_SOB ('{') if (GotByte != CHAR_SOB) Throw_serious_error(exception_no_left_brace); } DynaBuf_append(GlobalDynaBuf, CHAR_EOS); // terminate param list // now GlobalDynaBuf = comma-separated parameter list without spaces, // but terminated with CHAR_EOS. formal_parameters = DynaBuf_get_copy(GlobalDynaBuf); // now GlobalDynaBuf = unused // Reading the macro body would change the line number. To have correct // error messages, we're checking for "macro twice" *now*. // Search for macro. Create if not found. // But if found, complain (macro twice). if (search_for_macro(¯o_node, macro_scope, TRUE) == FALSE) report_redefinition(macro_node); // quits with serious error // Create new macro struct and set it up. Finally we'll read the body. new_macro = safe_malloc(sizeof(*new_macro)); new_macro->def_line_number = Input_now->line_number; new_macro->def_filename = get_string_copy(Input_now->original_filename); new_macro->original_name = get_string_copy(user_macro_name->buffer); new_macro->parameter_list = formal_parameters; new_macro->body = Input_skip_or_store_block(TRUE); // changes LineNumber macro_node->body = new_macro; // link macro struct to tree node // and that about sums it up } // Parse macro call ("+MACROTITLE"). Has to be re-entrant. void Macro_parse_call(void) // Now GotByte = dot or first char of macro name { char local_gotbyte; struct symbol *symbol; struct section new_section, *outer_section; struct input new_input, *outer_input; struct macro *actual_macro; struct rwnode *macro_node, *symbol_node; scope_t macro_scope, symbol_scope; int arg_count = 0; int outer_err_count; // Enter deeper nesting level // Quit program if recursion too deep. if (--macro_recursions_left < 0) Throw_serious_error("Too deeply nested. Recursive macro calls?"); macro_scope = get_scope_and_title(); // now GotByte = first non-space after title // internal_name = MacroTitle ARG_SEPARATOR (grows to signature) // Accept n>=0 comma-separated arguments before CHAR_EOS. // Valid argument formats are: // EXPRESSION (everything that does NOT start with '~' // ~.LOCAL_LABEL_BY_REFERENCE // ~GLOBAL_LABEL_BY_REFERENCE // now GotByte = non-space if (GotByte != CHAR_EOS) { // any at all? do { // if arg table cannot take another element, enlarge if (argtable_size <= arg_count) enlarge_arg_table(); // Decide whether call-by-reference or call-by-value // In both cases, GlobalDynaBuf may be used. if (GotByte == REFERENCE_CHAR) { // read call-by-reference arg DynaBuf_append(internal_name, ARGTYPE_NUM_REF); GetByte(); // skip '~' character Input_read_scope_and_keyword(&symbol_scope); // GotByte = illegal char arg_table[arg_count].symbol = symbol_find(symbol_scope, 0); } else { // read call-by-value arg DynaBuf_append(internal_name, ARGTYPE_NUM_VAL); ALU_any_result(&(arg_table[arg_count].result)); } ++arg_count; } while (Input_accept_comma()); } // now arg_table contains the arguments // now GlobalDynaBuf = unused // check for "unknown macro" // Search for macro. Do not create if not found. search_for_macro(¯o_node, macro_scope, FALSE); if (macro_node == NULL) { Throw_error("Macro not defined (or wrong signature)."); Input_skip_remainder(); } else { // make macro_node point to the macro struct actual_macro = macro_node->body; local_gotbyte = GotByte; // CAUTION - ugly kluge // set up new input new_input.original_filename = actual_macro->def_filename; new_input.line_number = actual_macro->def_line_number; new_input.source_is_ram = TRUE; new_input.state = INPUTSTATE_NORMAL; // FIXME - fix others! new_input.src.ram_ptr = actual_macro->parameter_list; // remember old input outer_input = Input_now; // activate new input Input_now = &new_input; outer_err_count = Throw_get_counter(); // remember error count (for call stack decision) // remember old section outer_section = section_now; // start new section (with new scope) // FALSE = title mustn't be freed section_new(&new_section, "Macro", actual_macro->original_name, FALSE); section_new_cheap_scope(&new_section); GetByte(); // fetch first byte of parameter list // assign arguments if (GotByte != CHAR_EOS) { // any at all? arg_count = 0; do { // Decide whether call-by-reference // or call-by-value // In both cases, GlobalDynaBuf may be used. if (GotByte == REFERENCE_CHAR) { // assign call-by-reference arg GetByte(); // skip '~' character Input_read_scope_and_keyword(&symbol_scope); if ((Tree_hard_scan(&symbol_node, symbols_forest, symbol_scope, TRUE) == FALSE) && (pass_count == 0)) Throw_error("Macro parameter twice."); symbol_node->body = arg_table[arg_count].symbol; } else { // assign call-by-value arg Input_read_scope_and_keyword(&symbol_scope); symbol = symbol_find(symbol_scope, 0); // FIXME - add a possibility to symbol_find to make it possible to find out // whether symbol was just created. Then check for the same error message here // as above ("Macro parameter twice."). symbol->result = arg_table[arg_count].result; } ++arg_count; } while (Input_accept_comma()); } // and now, finally, parse the actual macro body Input_now->state = INPUTSTATE_NORMAL; // FIXME - fix others! // maybe call parse_ram_block(actual_macro->def_line_number, actual_macro->body) Input_now->src.ram_ptr = actual_macro->body; Parse_until_eob_or_eof(); if (GotByte != CHAR_EOB) Bug_found("IllegalBlockTerminator", GotByte); // end section (free title memory, if needed) section_finalize(&new_section); // restore previous section section_now = outer_section; // restore previous input: Input_now = outer_input; // restore old Gotbyte context GotByte = local_gotbyte; // CAUTION - ugly kluge // if needed, output call stack if (Throw_get_counter() != outer_err_count) Throw_warning("...called from here."); Input_ensure_EOS(); } ++macro_recursions_left; // leave this nesting level } acme-crossassembler-0.96.4/src/macro.h000066400000000000000000000010121333777125400176130ustar00rootroot00000000000000// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. // Copyright (C) 1998-2016 Marco Baye // Have a look at "acme.c" for further info // // Macro stuff #ifndef macro_H #define macro_H #include "config.h" // Prototypes // create dynamic buffers and arg table extern void Macro_init(void); // create private dynabuf // only call once (during first pass) extern void Macro_parse_definition(void); // Parse macro call ("+MACROTITLE"). Has to be re-entrant. extern void Macro_parse_call(void); #endif acme-crossassembler-0.96.4/src/mnemo.c000066400000000000000000001565731333777125400176470ustar00rootroot00000000000000// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. // Copyright (C) 1998-2017 Marco Baye // Have a look at "acme.c" for further info // // Mnemonics stuff #include "mnemo.h" #include "config.h" #include "alu.h" #include "cpu.h" #include "dynabuf.h" #include "global.h" #include "input.h" #include "output.h" #include "tree.h" #include "typesystem.h" // Constants #define s_ror (s_error + 2) // Yes, I know I'm sick #define MNEMO_DYNABUF_INITIALSIZE 8 // 4 + terminator should suffice // These values are needed to recognize addressing modes: // indexing: #define INDEX_NONE 0 // no index #define INDEX_S 1 // stack-indexed (",s" or ",sp"), for 65816 and 65ce02 #define INDEX_X 2 // x-indexed (",x") #define INDEX_Y 3 // y-indexed (",y") #define INDEX_Z 4 // z-indexed (",z"), only for 65ce02 // 5..7 are left for future expansion, 8 would need the AMB_INDEX macro below to be adjusted! // adress mode bits: #define AMB_IMPLIED (1u << 0) // no value given #define AMB_IMMEDIATE (1u << 1) // '#' at start #define AMB_INDIRECT (1u << 2) // value has at least one unnecessary pair of "()" #define AMB_LONGINDIRECT (1u << 3) // value is given in [] #define AMB_PREINDEX(idx) ((idx) << 4) // three bits for indexing inside () #define AMB_INDEX(idx) ((idx) << 7) // three bits for external indexing // end values (here, "absolute addressing" always includes zeropage addressing, because they look the same) #define IMPLIED_ADDRESSING AMB_IMPLIED #define IMMEDIATE_ADDRESSING AMB_IMMEDIATE #define ABSOLUTE_ADDRESSING 0 #define X_INDEXED_ADDRESSING AMB_INDEX(INDEX_X) #define Y_INDEXED_ADDRESSING AMB_INDEX(INDEX_Y) #define INDIRECT_ADDRESSING AMB_INDIRECT #define X_INDEXED_INDIRECT_ADDRESSING (AMB_PREINDEX(INDEX_X) | AMB_INDIRECT) #define INDIRECT_Y_INDEXED_ADDRESSING (AMB_INDIRECT | AMB_INDEX(INDEX_Y)) // only for 65ce02: #define INDIRECT_Z_INDEXED_ADDRESSING (AMB_INDIRECT | AMB_INDEX(INDEX_Z)) // for 65816 and 65ce02: #define STACK_INDEXED_INDIRECT_Y_INDEXED_ADDRESSING (AMB_PREINDEX(INDEX_S) | AMB_INDIRECT | AMB_INDEX(INDEX_Y)) // only for 65816: #define STACK_INDEXED_ADDRESSING AMB_INDEX(INDEX_S) #define LONG_INDIRECT_ADDRESSING AMB_LONGINDIRECT #define LONG_INDIRECT_Y_INDEXED_ADDRESSING (AMB_LONGINDIRECT | AMB_INDEX(INDEX_Y)) // Constant values, used to mark the possible parameter lengths of commands. // Not all of the eight values are actually used, however (because of the // supported CPUs). #define MAYBE______ (0) #define MAYBE_1____ (MVALUE_FORCE08) #define MAYBE___2__ (MVALUE_FORCE16) #define MAYBE_1_2__ (MVALUE_FORCE08 | MVALUE_FORCE16) #define MAYBE_____3 (MVALUE_FORCE24) #define MAYBE_1___3 (MVALUE_FORCE08 | MVALUE_FORCE24) #define MAYBE___2_3 (MVALUE_FORCE16 | MVALUE_FORCE24) #define MAYBE_1_2_3 (MVALUE_FORCE08 | MVALUE_FORCE16 | MVALUE_FORCE24) // The mnemonics are split up into groups, each group has its own function to be dealt with: enum mnemogroup { GROUP_ACCU, // main accumulator stuff, plus PEI Byte value = table index GROUP_MISC, // read-modify-write and others Byte value = table index GROUP_ALLJUMPS, // the jump instructions Byte value = table index GROUP_IMPLIEDONLY, // mnemonics using only implied addressing Byte value = opcode GROUP_RELATIVE8, // short branch instructions Byte value = opcode GROUP_BITBRANCH, // bbr0..7 and bbs0..7 Byte value = opcode GROUP_REL16_2, // 16bit relative to pc+2 Byte value = opcode GROUP_REL16_3, // 16bit relative to pc+3 Byte value = opcode GROUP_BOTHMOVES, // the "move" commands MVP and MVN Byte value = opcode GROUP_ZPONLY // rmb0..7 and smb0..7 Byte value = opcode FIXME - use for IDX816COP,IDXeDEW,IDXeINW as well! }; // save some space #define SCB static const unsigned char #define SCS static const unsigned short #define SCL static const unsigned long // Code tables for group GROUP_ACCU: // These tables are used for the main accumulator-related mnemonics. By reading // the mnemonic's byte value (from the mnemotable), the assembler finds out the // column to use here. The row depends on the used addressing mode. A zero // entry in these tables means that the combination of mnemonic and addressing // mode is illegal. // | 6502/65c02/65816/65ce02 | 65816 | 6510 illegals | enum { IDX_ORA,IDXcORA,IDX816ORA,IDXeORA,IDX_AND,IDXcAND,IDX816AND,IDXeAND,IDX_EOR,IDXcEOR,IDX816EOR,IDXeEOR,IDX_ADC,IDXcADC,IDX816ADC,IDXeADC,IDX_STA,IDXcSTA,IDX816STA,IDXeSTA,IDX_LDA,IDXcLDA,IDX816LDA,IDXeLDA,IDX_CMP,IDXcCMP,IDX816CMP,IDXeCMP,IDX_SBC,IDXcSBC,IDX816SBC,IDXeSBC,IDX816PEI,IDX_SLO,IDX_RLA,IDX_SRE,IDX_RRA,IDX_SAX,IDX_LAX,IDX_DCP,IDX_ISC,IDX_SHA}; SCB accu_imm[] = { 0x09, 0x09, 0x09, 0x09, 0x29, 0x29, 0x29, 0x29, 0x49, 0x49, 0x49, 0x49, 0x69, 0x69, 0x69, 0x69, 0, 0, 0, 0, 0xa9, 0xa9, 0xa9, 0xa9, 0xc9, 0xc9, 0xc9, 0xc9, 0xe9, 0xe9, 0xe9, 0xe9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // #$ff #$ffff SCL accu_abs[] = { 0x0d05, 0x0d05, 0x0f0d05, 0x0d05, 0x2d25, 0x2d25, 0x2f2d25, 0x2d25, 0x4d45, 0x4d45, 0x4f4d45, 0x4d45, 0x6d65, 0x6d65, 0x6f6d65, 0x6d65, 0x8d85, 0x8d85, 0x8f8d85, 0x8d85, 0xada5, 0xada5, 0xafada5, 0xada5, 0xcdc5, 0xcdc5, 0xcfcdc5, 0xcdc5, 0xede5, 0xede5, 0xefede5, 0xede5, 0, 0x0f07, 0x2f27, 0x4f47, 0x6f67, 0x8f87, 0xafa7, 0xcfc7, 0xefe7, 0}; // $ff $ffff $ffffff SCL accu_xabs[] = { 0x1d15, 0x1d15, 0x1f1d15, 0x1d15, 0x3d35, 0x3d35, 0x3f3d35, 0x3d35, 0x5d55, 0x5d55, 0x5f5d55, 0x5d55, 0x7d75, 0x7d75, 0x7f7d75, 0x7d75, 0x9d95, 0x9d95, 0x9f9d95, 0x9d95, 0xbdb5, 0xbdb5, 0xbfbdb5, 0xbdb5, 0xddd5, 0xddd5, 0xdfddd5, 0xddd5, 0xfdf5, 0xfdf5, 0xfffdf5, 0xfdf5, 0, 0x1f17, 0x3f37, 0x5f57, 0x7f77, 0, 0, 0xdfd7, 0xfff7, 0}; // $ff,x $ffff,x $ffffff,x SCS accu_yabs[] = { 0x1900, 0x1900, 0x1900, 0x1900, 0x3900, 0x3900, 0x3900, 0x3900, 0x5900, 0x5900, 0x5900, 0x5900, 0x7900, 0x7900, 0x7900, 0x7900, 0x9900, 0x9900, 0x9900, 0x9900, 0xb900, 0xb900, 0xb900, 0xb900, 0xd900, 0xd900, 0xd900, 0xd900, 0xf900, 0xf900, 0xf900, 0xf900, 0, 0x1b00, 0x3b00, 0x5b00, 0x7b00, 0x97, 0xbfb7, 0xdb00, 0xfb00, 0x9f00}; // $ff,y $ffff,y SCB accu_xind8[] = { 0x01, 0x01, 0x01, 0x01, 0x21, 0x21, 0x21, 0x21, 0x41, 0x41, 0x41, 0x41, 0x61, 0x61, 0x61, 0x61, 0x81, 0x81, 0x81, 0x81, 0xa1, 0xa1, 0xa1, 0xa1, 0xc1, 0xc1, 0xc1, 0xc1, 0xe1, 0xe1, 0xe1, 0xe1, 0, 0x03, 0x23, 0x43, 0x63, 0x83, 0xa3, 0xc3, 0xe3, 0}; // ($ff,x) SCB accu_indy8[] = { 0x11, 0x11, 0x11, 0x11, 0x31, 0x31, 0x31, 0x31, 0x51, 0x51, 0x51, 0x51, 0x71, 0x71, 0x71, 0x71, 0x91, 0x91, 0x91, 0x91, 0xb1, 0xb1, 0xb1, 0xb1, 0xd1, 0xd1, 0xd1, 0xd1, 0xf1, 0xf1, 0xf1, 0xf1, 0, 0x13, 0x33, 0x53, 0x73, 0, 0xb3, 0xd3, 0xf3, 0x93}; // ($ff),y SCB accu_ind8[] = { 0, 0x12, 0x12, 0, 0, 0x32, 0x32, 0, 0, 0x52, 0x52, 0, 0, 0x72, 0x72, 0, 0, 0x92, 0x92, 0, 0, 0xb2, 0xb2, 0, 0, 0xd2, 0xd2, 0, 0, 0xf2, 0xf2, 0, 0xd4, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // ($ff) SCB accu_sabs8[] = { 0, 0, 0x03, 0, 0, 0, 0x23, 0, 0, 0, 0x43, 0, 0, 0, 0x63, 0, 0, 0, 0x83, 0, 0, 0, 0xa3, 0, 0, 0, 0xc3, 0, 0, 0, 0xe3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // $ff,s SCB accu_sindy8[] = { 0, 0, 0x13, 0, 0, 0, 0x33, 0, 0, 0, 0x53, 0, 0, 0, 0x73, 0, 0, 0, 0x93, 0x82, 0, 0, 0xb3, 0xe2, 0, 0, 0xd3, 0, 0, 0, 0xf3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // ($ff,s),y SCB accu_lind8[] = { 0, 0, 0x07, 0, 0, 0, 0x27, 0, 0, 0, 0x47, 0, 0, 0, 0x67, 0, 0, 0, 0x87, 0, 0, 0, 0xa7, 0, 0, 0, 0xc7, 0, 0, 0, 0xe7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // [$ff] SCB accu_lindy8[] = { 0, 0, 0x17, 0, 0, 0, 0x37, 0, 0, 0, 0x57, 0, 0, 0, 0x77, 0, 0, 0, 0x97, 0, 0, 0, 0xb7, 0, 0, 0, 0xd7, 0, 0, 0, 0xf7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // [$ff],y SCB accu_indz8[] = { 0, 0, 0, 0x12, 0, 0, 0, 0x32, 0, 0, 0, 0x52, 0, 0, 0, 0x72, 0, 0, 0, 0x92, 0, 0, 0, 0xb2, 0, 0, 0, 0xd2, 0, 0, 0, 0xf2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // ($ff),z // Code tables for group GROUP_MISC: // These tables are needed for finding out the correct code in cases when // there are no general rules. By reading the mnemonic's byte value (from the // mnemotable), the assembler finds out the column to use here. The row // depends on the used addressing mode. A zero entry in these tables means // that the combination of mnemonic and addressing mode is illegal. // | 6502 | 6502/65c02/65ce02 | 65c02 | 65ce02 | 65816 | 6510 illegals | C64DTV2 | enum { IDX_ASL,IDX_ROL,IDX_LSR,IDX_ROR,IDX_LDY,IDX_LDX,IDX_CPY,IDX_CPX,IDX_BIT,IDXcBIT,IDX_STX,IDXeSTX,IDX_STY,IDXeSTY,IDX_DEC,IDXcDEC,IDX_INC,IDXcINC,IDXcTSB,IDXcTRB,IDXcSTZ,IDXeASR,IDXeASW,IDXeCPZ,IDXeDEW,IDXeINW,IDXeLDZ,IDXePHW,IDXeROW,IDXeRTN,IDX816COP,IDX816REP,IDX816SEP,IDX816PEA,IDX_ANC,IDX_ASR,IDX_ARR,IDX_SBX,IDX_DOP,IDX_TOP,IDX_JAM,IDX_LXA,IDX_ANE,IDX_LAS,IDX_TAS,IDX_SHX,IDX_SHY,IDX_SAC,IDX_SIR}; SCB misc_impl[] = { 0x0a, 0x2a, 0x4a, 0x6a, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x3a, 0, 0x1a, 0, 0, 0, 0x43, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x80, 0x0c, 0x02, 0, 0, 0, 0, 0, 0, 0, 0}; // implied/accu SCB misc_imm[] = { 0, 0, 0, 0, 0xa0, 0xa2, 0xc0, 0xe0, 0, 0x89, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xc2, 0, 0, 0xa3, 0xf4, 0, 0x62, 0, 0xc2, 0xe2, 0, 0x0b, 0x4b, 0x6b, 0xcb, 0x80, 0, 0, 0xab, 0x8b, 0, 0, 0, 0, 0x32, 0x42}; // #$ff #$ffff SCS misc_abs[] = { 0x0e06, 0x2e26, 0x4e46, 0x6e66, 0xaca4, 0xaea6, 0xccc4, 0xece4, 0x2c24, 0x2c24, 0x8e86, 0x8e86, 0x8c84, 0x8c84, 0xcec6, 0xcec6, 0xeee6, 0xeee6, 0x0c04, 0x1c14, 0x9c64, 0x44, 0xcb00, 0xdcd4, 0xc3, 0xe3, 0xab00, 0xfc00, 0xeb00, 0, 0x02, 0, 0, 0xf400, 0, 0, 0, 0, 0x04, 0x0c00, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // $ff $ffff SCS misc_xabs[] = { 0x1e16, 0x3e36, 0x5e56, 0x7e76, 0xbcb4, 0, 0, 0, 0, 0x3c34, 0, 0, 0x94, 0x8b94, 0xded6, 0xded6, 0xfef6, 0xfef6, 0, 0, 0x9e74, 0x54, 0, 0, 0, 0, 0xbb00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x14, 0x1c00, 0, 0, 0, 0, 0, 0, 0x9c00, 0, 0}; // $ff,x $ffff,x SCS misc_yabs[] = { 0, 0, 0, 0, 0, 0xbeb6, 0, 0, 0, 0, 0x96, 0x9b96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xbb00, 0x9b00, 0x9e00, 0, 0, 0}; // $ff,y $ffff,y // Code tables for group GROUP_ALLJUMPS: // These tables are needed for finding out the correct code when the mnemonic // is "jmp" or "jsr" (or the long versions "jml" and "jsl"). // By reading the mnemonic's byte value (from the mnemotable), the assembler // finds out the column to use here. The row depends on the used addressing // mode. A zero entry in these tables means that the combination of mnemonic // and addressing mode is illegal. // | 6502/65c02/65816/65ce02 | 65816 | enum { IDX_JMP,IDXcJMP,IDX816JMP,IDX_JSR,IDXeJSR,IDX816JSR,IDX816JML,IDX816JSL}; SCL jump_abs[] = { 0x4c00, 0x4c00, 0x5c4c00, 0x2000, 0x2000, 0x222000, 0x5c0000, 0x220000}; // $ffff $ffffff SCS jump_ind[] = { 0x6c00, 0x6c00, 0x6c00, 0, 0x2200, 0, 0, 0}; // ($ffff) SCS jump_xind[] = { 0, 0x7c00, 0x7c00, 0, 0x2300, 0xfc00, 0, 0}; // ($ffff,x) SCS jump_lind[] = { 0, 0, 0xdc00, 0, 0, 0, 0xdc00, 0}; // [$ffff] #undef SCB #undef SCS #undef SCL // error message strings static const char exception_illegal_combination[] = "Illegal combination of command and addressing mode."; static const char exception_oversized_addrmode[] = "Using oversized addressing mode."; // Variables static struct dynabuf *mnemo_dyna_buf; // dynamic buffer for mnemonics // predefined stuff static struct ronode *mnemo_6502_tree = NULL; // 6502 mnemonics static struct ronode *mnemo_6502undoc1_tree = NULL; // 6502 undocumented ("illegal") opcodes supported by DTV2 static struct ronode *mnemo_6502undoc2_tree = NULL; // remaining 6502 undocumented ("illegal") opcodes (currently ANC only, maybe more will get moved) static struct ronode *mnemo_c64dtv2_tree = NULL; // C64DTV2 extensions (BRA/SAC/SIR) static struct ronode *mnemo_65c02_tree = NULL; // 65c02 extensions static struct ronode *mnemo_bitmanips_tree = NULL; // Rockwell's bit manipulation extensions static struct ronode *mnemo_stp_wai_tree = NULL; // WDC's "stp" and "wai" instructions static struct ronode *mnemo_65816_tree = NULL; // WDC 65816 extensions static struct ronode *mnemo_65ce02_tree = NULL; // CSG 65ce02/4502 extensions static struct ronode *mnemo_aug_tree = NULL; // CSG 65ce02's "aug" instruction static struct ronode *mnemo_map_eom_tree = NULL; // CSG 4502's "map" and "eom" instructions // Command's code and group values are stored together in a single integer. // To extract the code, use "& CODEMASK". // To extract the immediate mode, use "& IMMASK". // To extract the group, use GROUP() #define CODEMASK 0x0ff // opcode or table index // immediate mode: #define IM_FORCE8 0x000 // immediate values are 8 bits (CAUTION - program relies on "no bits set" being the default!) #define IM_FORCE16 0x100 // immediate value is 16 bits (for 65ce02's PHW#) #define IM_ACCUMULATOR 0x200 // immediate value depends on accumulator length #define IM_INDEXREGS 0x300 // immediate value depends on index register length #define IMMASK 0x300 // mask for immediate modes #define MERGE(g, v) (((g) << 10) | (v)) #define GROUP(v) ((v) >> 10) static struct ronode mnemos_6502[] = { PREDEFNODE("ora", MERGE(GROUP_ACCU, IDX_ORA)), PREDEFNODE(s_and, MERGE(GROUP_ACCU, IDX_AND)), PREDEFNODE(s_eor, MERGE(GROUP_ACCU, IDX_EOR)), PREDEFNODE("adc", MERGE(GROUP_ACCU, IDX_ADC)), PREDEFNODE("sta", MERGE(GROUP_ACCU, IDX_STA)), PREDEFNODE("lda", MERGE(GROUP_ACCU, IDX_LDA)), PREDEFNODE("cmp", MERGE(GROUP_ACCU, IDX_CMP)), PREDEFNODE("sbc", MERGE(GROUP_ACCU, IDX_SBC)), PREDEFNODE("bit", MERGE(GROUP_MISC, IDX_BIT)), PREDEFNODE(s_asl, MERGE(GROUP_MISC, IDX_ASL)), PREDEFNODE("rol", MERGE(GROUP_MISC, IDX_ROL)), PREDEFNODE(s_lsr, MERGE(GROUP_MISC, IDX_LSR)), PREDEFNODE(s_ror, MERGE(GROUP_MISC, IDX_ROR)), PREDEFNODE("sty", MERGE(GROUP_MISC, IDX_STY)), PREDEFNODE("stx", MERGE(GROUP_MISC, IDX_STX)), PREDEFNODE("ldy", MERGE(GROUP_MISC, IDX_LDY)), PREDEFNODE("ldx", MERGE(GROUP_MISC, IDX_LDX)), PREDEFNODE("cpy", MERGE(GROUP_MISC, IDX_CPY)), PREDEFNODE("cpx", MERGE(GROUP_MISC, IDX_CPX)), PREDEFNODE("dec", MERGE(GROUP_MISC, IDX_DEC)), PREDEFNODE("inc", MERGE(GROUP_MISC, IDX_INC)), PREDEFNODE("bpl", MERGE(GROUP_RELATIVE8, 0x10)), PREDEFNODE("bmi", MERGE(GROUP_RELATIVE8, 0x30)), PREDEFNODE("bvc", MERGE(GROUP_RELATIVE8, 0x50)), PREDEFNODE("bvs", MERGE(GROUP_RELATIVE8, 0x70)), PREDEFNODE("bcc", MERGE(GROUP_RELATIVE8, 0x90)), PREDEFNODE("bcs", MERGE(GROUP_RELATIVE8, 0xb0)), PREDEFNODE("bne", MERGE(GROUP_RELATIVE8, 0xd0)), PREDEFNODE("beq", MERGE(GROUP_RELATIVE8, 0xf0)), PREDEFNODE("jmp", MERGE(GROUP_ALLJUMPS, IDX_JMP)), PREDEFNODE("jsr", MERGE(GROUP_ALLJUMPS, IDX_JSR)), PREDEFNODE("brk", MERGE(GROUP_IMPLIEDONLY, 0)), PREDEFNODE("php", MERGE(GROUP_IMPLIEDONLY, 8)), PREDEFNODE("clc", MERGE(GROUP_IMPLIEDONLY, 24)), PREDEFNODE("plp", MERGE(GROUP_IMPLIEDONLY, 40)), PREDEFNODE("sec", MERGE(GROUP_IMPLIEDONLY, 56)), PREDEFNODE("rti", MERGE(GROUP_IMPLIEDONLY, 64)), PREDEFNODE("pha", MERGE(GROUP_IMPLIEDONLY, 72)), PREDEFNODE("cli", MERGE(GROUP_IMPLIEDONLY, 88)), PREDEFNODE("rts", MERGE(GROUP_IMPLIEDONLY, 96)), PREDEFNODE("pla", MERGE(GROUP_IMPLIEDONLY, 104)), PREDEFNODE("sei", MERGE(GROUP_IMPLIEDONLY, 120)), PREDEFNODE("dey", MERGE(GROUP_IMPLIEDONLY, 136)), PREDEFNODE("txa", MERGE(GROUP_IMPLIEDONLY, 138)), PREDEFNODE("tya", MERGE(GROUP_IMPLIEDONLY, 152)), PREDEFNODE("txs", MERGE(GROUP_IMPLIEDONLY, 154)), PREDEFNODE("tay", MERGE(GROUP_IMPLIEDONLY, 168)), PREDEFNODE("tax", MERGE(GROUP_IMPLIEDONLY, 170)), PREDEFNODE("clv", MERGE(GROUP_IMPLIEDONLY, 184)), PREDEFNODE("tsx", MERGE(GROUP_IMPLIEDONLY, 186)), PREDEFNODE("iny", MERGE(GROUP_IMPLIEDONLY, 200)), PREDEFNODE("dex", MERGE(GROUP_IMPLIEDONLY, 202)), PREDEFNODE("cld", MERGE(GROUP_IMPLIEDONLY, 216)), PREDEFNODE("inx", MERGE(GROUP_IMPLIEDONLY, 232)), PREDEFNODE("nop", MERGE(GROUP_IMPLIEDONLY, 234)), PREDEFLAST("sed", MERGE(GROUP_IMPLIEDONLY, 248)), // ^^^^ this marks the last element }; // undocumented opcodes of the NMOS 6502 that are also supported by c64dtv2: static struct ronode mnemos_6502undoc1[] = { PREDEFNODE("slo", MERGE(GROUP_ACCU, IDX_SLO)), // ASL + ORA (aka ASO) PREDEFNODE("rla", MERGE(GROUP_ACCU, IDX_RLA)), // ROL + AND PREDEFNODE("sre", MERGE(GROUP_ACCU, IDX_SRE)), // LSR + EOR (aka LSE) PREDEFNODE("rra", MERGE(GROUP_ACCU, IDX_RRA)), // ROR + ADC PREDEFNODE("sax", MERGE(GROUP_ACCU, IDX_SAX)), // STX + STA (aka AXS aka AAX) PREDEFNODE("lax", MERGE(GROUP_ACCU, IDX_LAX)), // LDX + LDA PREDEFNODE("dcp", MERGE(GROUP_ACCU, IDX_DCP)), // DEC + CMP (aka DCM) PREDEFNODE("isc", MERGE(GROUP_ACCU, IDX_ISC)), // INC + SBC (aka ISB aka INS) PREDEFNODE("las", MERGE(GROUP_MISC, IDX_LAS)), // A,X,S = {addr} & S (aka LAR aka LAE) PREDEFNODE("tas", MERGE(GROUP_MISC, IDX_TAS)), // S = A & X {addr} = A&X& {H+1} (aka SHS aka XAS) PREDEFNODE("sha", MERGE(GROUP_ACCU, IDX_SHA)), // {addr} = A & X & {H+1} (aka AXA aka AHX) PREDEFNODE("shx", MERGE(GROUP_MISC, IDX_SHX)), // {addr} = X & {H+1} (aka XAS aka SXA) PREDEFNODE("shy", MERGE(GROUP_MISC, IDX_SHY)), // {addr} = Y & {H+1} (aka SAY aka SYA) PREDEFNODE(s_asr, MERGE(GROUP_MISC, IDX_ASR)), // LSR + EOR (aka ALR) PREDEFNODE("arr", MERGE(GROUP_MISC, IDX_ARR)), // ROR + ADC PREDEFNODE("sbx", MERGE(GROUP_MISC, IDX_SBX)), // DEX + CMP (aka AXS aka SAX) PREDEFNODE("dop", MERGE(GROUP_MISC, IDX_DOP)), // skip next byte PREDEFNODE("top", MERGE(GROUP_MISC, IDX_TOP)), // skip next word PREDEFNODE("jam", MERGE(GROUP_MISC, IDX_JAM)), // jam/crash/kill/halt-and-catch-fire PREDEFNODE("ane", MERGE(GROUP_MISC, IDX_ANE)), // A = (A | ??) & X & arg (aka XAA) PREDEFLAST("lxa", MERGE(GROUP_MISC, IDX_LXA)), // A,X = (A | ??) & arg (aka OAL aka ATX) // ^^^^ this marks the last element }; // undocumented opcodes of the NMOS 6502 that are _not_ supported by c64dtv2: static struct ronode mnemos_6502undoc2[] = { PREDEFLAST("anc", MERGE(GROUP_MISC, IDX_ANC)), // ROL + AND, ASL + ORA (aka AAC) // ^^^^ this marks the last element }; // additional opcodes of c64dtv2: static struct ronode mnemos_c64dtv2[] = { PREDEFNODE(s_bra, MERGE(GROUP_RELATIVE8, 0x12)), // branch always PREDEFNODE("sac", MERGE(GROUP_MISC, IDX_SAC)), // set accumulator mapping PREDEFLAST("sir", MERGE(GROUP_MISC, IDX_SIR)), // set index register mapping // ^^^^ this marks the last element }; // new stuff in CMOS re-design: static struct ronode mnemos_65c02[] = { // more addressing modes for some mnemonics: PREDEFNODE("ora", MERGE(GROUP_ACCU, IDXcORA)), PREDEFNODE(s_and, MERGE(GROUP_ACCU, IDXcAND)), PREDEFNODE(s_eor, MERGE(GROUP_ACCU, IDXcEOR)), PREDEFNODE("adc", MERGE(GROUP_ACCU, IDXcADC)), PREDEFNODE("sta", MERGE(GROUP_ACCU, IDXcSTA)), PREDEFNODE("lda", MERGE(GROUP_ACCU, IDXcLDA)), PREDEFNODE("cmp", MERGE(GROUP_ACCU, IDXcCMP)), PREDEFNODE("sbc", MERGE(GROUP_ACCU, IDXcSBC)), PREDEFNODE("jmp", MERGE(GROUP_ALLJUMPS, IDXcJMP)), PREDEFNODE("bit", MERGE(GROUP_MISC, IDXcBIT)), PREDEFNODE("dec", MERGE(GROUP_MISC, IDXcDEC)), PREDEFNODE("inc", MERGE(GROUP_MISC, IDXcINC)), // and eight new mnemonics: PREDEFNODE(s_bra, MERGE(GROUP_RELATIVE8, 0x80)), PREDEFNODE("phy", MERGE(GROUP_IMPLIEDONLY, 90)), PREDEFNODE("ply", MERGE(GROUP_IMPLIEDONLY, 122)), PREDEFNODE("phx", MERGE(GROUP_IMPLIEDONLY, 218)), PREDEFNODE("plx", MERGE(GROUP_IMPLIEDONLY, 250)), PREDEFNODE("tsb", MERGE(GROUP_MISC, IDXcTSB)), PREDEFNODE("trb", MERGE(GROUP_MISC, IDXcTRB)), PREDEFLAST("stz", MERGE(GROUP_MISC, IDXcSTZ)), // ^^^^ this marks the last element }; // bit-manipulation extensions (by Rockwell?) static struct ronode mnemos_bitmanips[] = { PREDEFNODE("rmb0", MERGE(GROUP_ZPONLY, 0x07)), PREDEFNODE("rmb1", MERGE(GROUP_ZPONLY, 0x17)), PREDEFNODE("rmb2", MERGE(GROUP_ZPONLY, 0x27)), PREDEFNODE("rmb3", MERGE(GROUP_ZPONLY, 0x37)), PREDEFNODE("rmb4", MERGE(GROUP_ZPONLY, 0x47)), PREDEFNODE("rmb5", MERGE(GROUP_ZPONLY, 0x57)), PREDEFNODE("rmb6", MERGE(GROUP_ZPONLY, 0x67)), PREDEFNODE("rmb7", MERGE(GROUP_ZPONLY, 0x77)), PREDEFNODE("smb0", MERGE(GROUP_ZPONLY, 0x87)), PREDEFNODE("smb1", MERGE(GROUP_ZPONLY, 0x97)), PREDEFNODE("smb2", MERGE(GROUP_ZPONLY, 0xa7)), PREDEFNODE("smb3", MERGE(GROUP_ZPONLY, 0xb7)), PREDEFNODE("smb4", MERGE(GROUP_ZPONLY, 0xc7)), PREDEFNODE("smb5", MERGE(GROUP_ZPONLY, 0xd7)), PREDEFNODE("smb6", MERGE(GROUP_ZPONLY, 0xe7)), PREDEFNODE("smb7", MERGE(GROUP_ZPONLY, 0xf7)), PREDEFNODE("bbr0", MERGE(GROUP_BITBRANCH, 0x0f)), PREDEFNODE("bbr1", MERGE(GROUP_BITBRANCH, 0x1f)), PREDEFNODE("bbr2", MERGE(GROUP_BITBRANCH, 0x2f)), PREDEFNODE("bbr3", MERGE(GROUP_BITBRANCH, 0x3f)), PREDEFNODE("bbr4", MERGE(GROUP_BITBRANCH, 0x4f)), PREDEFNODE("bbr5", MERGE(GROUP_BITBRANCH, 0x5f)), PREDEFNODE("bbr6", MERGE(GROUP_BITBRANCH, 0x6f)), PREDEFNODE("bbr7", MERGE(GROUP_BITBRANCH, 0x7f)), PREDEFNODE("bbs0", MERGE(GROUP_BITBRANCH, 0x8f)), PREDEFNODE("bbs1", MERGE(GROUP_BITBRANCH, 0x9f)), PREDEFNODE("bbs2", MERGE(GROUP_BITBRANCH, 0xaf)), PREDEFNODE("bbs3", MERGE(GROUP_BITBRANCH, 0xbf)), PREDEFNODE("bbs4", MERGE(GROUP_BITBRANCH, 0xcf)), PREDEFNODE("bbs5", MERGE(GROUP_BITBRANCH, 0xdf)), PREDEFNODE("bbs6", MERGE(GROUP_BITBRANCH, 0xef)), PREDEFLAST("bbs7", MERGE(GROUP_BITBRANCH, 0xff)), // ^^^^ this marks the last element }; // "stp" and "wai" extensions by WDC: static struct ronode mnemos_stp_wai[] = { PREDEFNODE("stp", MERGE(GROUP_IMPLIEDONLY, 219)), PREDEFLAST("wai", MERGE(GROUP_IMPLIEDONLY, 203)), // ^^^^ this marks the last element }; // most of the 65816 stuff static struct ronode mnemos_65816[] = { // CAUTION - these use 6502/65c02 indices, because the opcodes are the same - but I need flags for immediate mode! PREDEFNODE("ldy", MERGE(GROUP_MISC, IDX_LDY | IM_INDEXREGS)), PREDEFNODE("ldx", MERGE(GROUP_MISC, IDX_LDX | IM_INDEXREGS)), PREDEFNODE("cpy", MERGE(GROUP_MISC, IDX_CPY | IM_INDEXREGS)), PREDEFNODE("cpx", MERGE(GROUP_MISC, IDX_CPX | IM_INDEXREGS)), PREDEFNODE("bit", MERGE(GROUP_MISC, IDXcBIT | IM_ACCUMULATOR)), // more addressing modes for some mnemonics: PREDEFNODE("ora", MERGE(GROUP_ACCU, IDX816ORA | IM_ACCUMULATOR)), PREDEFNODE(s_and, MERGE(GROUP_ACCU, IDX816AND | IM_ACCUMULATOR)), PREDEFNODE(s_eor, MERGE(GROUP_ACCU, IDX816EOR | IM_ACCUMULATOR)), PREDEFNODE("adc", MERGE(GROUP_ACCU, IDX816ADC | IM_ACCUMULATOR)), PREDEFNODE("sta", MERGE(GROUP_ACCU, IDX816STA)), PREDEFNODE("lda", MERGE(GROUP_ACCU, IDX816LDA | IM_ACCUMULATOR)), PREDEFNODE("cmp", MERGE(GROUP_ACCU, IDX816CMP | IM_ACCUMULATOR)), PREDEFNODE("sbc", MERGE(GROUP_ACCU, IDX816SBC | IM_ACCUMULATOR)), PREDEFNODE("jmp", MERGE(GROUP_ALLJUMPS, IDX816JMP)), PREDEFNODE("jsr", MERGE(GROUP_ALLJUMPS, IDX816JSR)), // PREDEFNODE("pei", MERGE(GROUP_ACCU, IDX816PEI)), PREDEFNODE("jml", MERGE(GROUP_ALLJUMPS, IDX816JML)), PREDEFNODE("jsl", MERGE(GROUP_ALLJUMPS, IDX816JSL)), PREDEFNODE("mvp", MERGE(GROUP_BOTHMOVES, 0x44)), PREDEFNODE("mvn", MERGE(GROUP_BOTHMOVES, 0x54)), PREDEFNODE("per", MERGE(GROUP_REL16_3, 98)), PREDEFNODE(s_brl, MERGE(GROUP_REL16_3, 130)), PREDEFNODE("cop", MERGE(GROUP_MISC, IDX816COP)), PREDEFNODE("rep", MERGE(GROUP_MISC, IDX816REP)), PREDEFNODE("sep", MERGE(GROUP_MISC, IDX816SEP)), PREDEFNODE("pea", MERGE(GROUP_MISC, IDX816PEA)), PREDEFNODE("phd", MERGE(GROUP_IMPLIEDONLY, 11)), PREDEFNODE("tcs", MERGE(GROUP_IMPLIEDONLY, 27)), PREDEFNODE("pld", MERGE(GROUP_IMPLIEDONLY, 43)), PREDEFNODE("tsc", MERGE(GROUP_IMPLIEDONLY, 59)), PREDEFNODE("wdm", MERGE(GROUP_IMPLIEDONLY, 66)), PREDEFNODE("phk", MERGE(GROUP_IMPLIEDONLY, 75)), PREDEFNODE("tcd", MERGE(GROUP_IMPLIEDONLY, 91)), PREDEFNODE("rtl", MERGE(GROUP_IMPLIEDONLY, 107)), PREDEFNODE("tdc", MERGE(GROUP_IMPLIEDONLY, 123)), PREDEFNODE("phb", MERGE(GROUP_IMPLIEDONLY, 139)), PREDEFNODE("txy", MERGE(GROUP_IMPLIEDONLY, 155)), PREDEFNODE("plb", MERGE(GROUP_IMPLIEDONLY, 171)), PREDEFNODE("tyx", MERGE(GROUP_IMPLIEDONLY, 187)), PREDEFNODE("xba", MERGE(GROUP_IMPLIEDONLY, 235)), PREDEFLAST("xce", MERGE(GROUP_IMPLIEDONLY, 251)), // ^^^^ this marks the last element }; // 65ce02 has 46 new opcodes and a few changes: static struct ronode mnemos_65ce02[] = { // 65ce02 changes (zp) addressing of 65c02 to (zp),z addressing: PREDEFNODE("ora", MERGE(GROUP_ACCU, IDXeORA)), PREDEFNODE(s_and, MERGE(GROUP_ACCU, IDXeAND)), PREDEFNODE(s_eor, MERGE(GROUP_ACCU, IDXeEOR)), PREDEFNODE("adc", MERGE(GROUP_ACCU, IDXeADC)), PREDEFNODE("sta", MERGE(GROUP_ACCU, IDXeSTA)), // +1 for (8,s),y PREDEFNODE("lda", MERGE(GROUP_ACCU, IDXeLDA)), // +1 for (8,s),y PREDEFNODE("cmp", MERGE(GROUP_ACCU, IDXeCMP)), PREDEFNODE("sbc", MERGE(GROUP_ACCU, IDXeSBC)), // more addressing modes: PREDEFNODE("jsr", MERGE(GROUP_ALLJUMPS, IDXeJSR)), // +2 PREDEFNODE("stx", MERGE(GROUP_MISC, IDXeSTX)), // +1 PREDEFNODE("sty", MERGE(GROUP_MISC, IDXeSTY)), // +1 // +10 long branches (8 normal, 1 unconditional, BSR uncond to subroutine)) PREDEFNODE("lbpl", MERGE(GROUP_REL16_2, 0x13)), PREDEFNODE("lbmi", MERGE(GROUP_REL16_2, 0x33)), PREDEFNODE("lbvc", MERGE(GROUP_REL16_2, 0x53)), PREDEFNODE("lbvs", MERGE(GROUP_REL16_2, 0x73)), PREDEFNODE("lbcc", MERGE(GROUP_REL16_2, 0x93)), PREDEFNODE("lbcs", MERGE(GROUP_REL16_2, 0xb3)), PREDEFNODE("lbne", MERGE(GROUP_REL16_2, 0xd3)), PREDEFNODE("lbeq", MERGE(GROUP_REL16_2, 0xf3)), PREDEFNODE("bru", MERGE(GROUP_RELATIVE8, 0x80)), // alias for 65c02's "bra" PREDEFNODE("bsr", MERGE(GROUP_REL16_2, 0x63)), PREDEFNODE("lbru", MERGE(GROUP_REL16_2, 0x83)), PREDEFNODE("lbra", MERGE(GROUP_REL16_2, 0x83)), // alias // new mnemonics: PREDEFNODE("asr", MERGE(GROUP_MISC, IDXeASR)), PREDEFNODE("asw", MERGE(GROUP_MISC, IDXeASW)), PREDEFNODE("cpz", MERGE(GROUP_MISC, IDXeCPZ)), PREDEFNODE("dew", MERGE(GROUP_MISC, IDXeDEW)), PREDEFNODE("inw", MERGE(GROUP_MISC, IDXeINW)), PREDEFNODE("ldz", MERGE(GROUP_MISC, IDXeLDZ)), PREDEFNODE("phw", MERGE(GROUP_MISC, IDXePHW | IM_FORCE16)), PREDEFNODE("row", MERGE(GROUP_MISC, IDXeROW)), PREDEFNODE("rtn", MERGE(GROUP_MISC, IDXeRTN)), PREDEFNODE("cle", MERGE(GROUP_IMPLIEDONLY, 0x02)), PREDEFNODE("see", MERGE(GROUP_IMPLIEDONLY, 0x03)), PREDEFNODE("inz", MERGE(GROUP_IMPLIEDONLY, 0x1b)), PREDEFNODE("dez", MERGE(GROUP_IMPLIEDONLY, 0x3b)), PREDEFNODE("neg", MERGE(GROUP_IMPLIEDONLY, 0x42)), PREDEFNODE("tsy", MERGE(GROUP_IMPLIEDONLY, 0x0b)), PREDEFNODE("tys", MERGE(GROUP_IMPLIEDONLY, 0x2b)), PREDEFNODE("taz", MERGE(GROUP_IMPLIEDONLY, 0x4b)), PREDEFNODE("tab", MERGE(GROUP_IMPLIEDONLY, 0x5b)), PREDEFNODE("tza", MERGE(GROUP_IMPLIEDONLY, 0x6b)), PREDEFNODE("tba", MERGE(GROUP_IMPLIEDONLY, 0x7b)), PREDEFNODE("phz", MERGE(GROUP_IMPLIEDONLY, 0xdb)), PREDEFLAST("plz", MERGE(GROUP_IMPLIEDONLY, 0xfb)), // ^^^^ this marks the last element }; // 65ce02's "aug" opcode: static struct ronode mnemos_aug[] = { PREDEFLAST("aug", MERGE(GROUP_IMPLIEDONLY, 0x5c)), // actually a "4-byte NOP reserved for future expansion" // ^^^^ this marks the last element }; // 4502's "map" and "eom" opcodes: static struct ronode mnemos_map_eom[] = { PREDEFNODE("map", MERGE(GROUP_IMPLIEDONLY, 0x5c)), // change memory mapping PREDEFLAST("eom", MERGE(GROUP_IMPLIEDONLY, 0xea)), // actually the NOP opcode // ^^^^ this marks the last element }; // Functions // create dynamic buffer, build keyword trees void Mnemo_init(void) { mnemo_dyna_buf = DynaBuf_create(MNEMO_DYNABUF_INITIALSIZE); Tree_add_table(&mnemo_6502_tree, mnemos_6502); Tree_add_table(&mnemo_6502undoc1_tree, mnemos_6502undoc1); Tree_add_table(&mnemo_6502undoc2_tree, mnemos_6502undoc2); Tree_add_table(&mnemo_c64dtv2_tree, mnemos_c64dtv2); Tree_add_table(&mnemo_65c02_tree, mnemos_65c02); Tree_add_table(&mnemo_bitmanips_tree, mnemos_bitmanips); Tree_add_table(&mnemo_stp_wai_tree, mnemos_stp_wai); Tree_add_table(&mnemo_65816_tree, mnemos_65816); Tree_add_table(&mnemo_65ce02_tree, mnemos_65ce02); Tree_add_table(&mnemo_aug_tree, mnemos_aug); Tree_add_table(&mnemo_map_eom_tree, mnemos_map_eom); } // Address mode parsing // utility function for parsing indices. result must be processed via AMB_PREINDEX() or AMB_INDEX() macro! static int get_index(int next) { if (next) GetByte(); if (!Input_accept_comma()) return INDEX_NONE; // there was a comma, so check next character (spaces will have been skipped): switch (GotByte) { case 's': case 'S': // if next character is 'p' or 'P', eat that as well, so ",sp" works just like ",s" GetByte(); if ((GotByte == 'p') || (GotByte == 'P')) GetByte(); SKIPSPACE(); return INDEX_S; case 'x': case 'X': NEXTANDSKIPSPACE(); return INDEX_X; case 'y': case 'Y': NEXTANDSKIPSPACE(); return INDEX_Y; case 'z': case 'Z': NEXTANDSKIPSPACE(); return INDEX_Z; } Throw_error(exception_syntax); return INDEX_NONE; } // This function stores the command's argument in the given result // structure (using the valueparser). The addressing mode is returned. static int get_argument(struct result *result) { int open_paren, address_mode_bits = 0; SKIPSPACE(); switch (GotByte) { case '[': GetByte(); // proceed with next char ALU_int_result(result); typesystem_want_addr(result); if (GotByte == ']') address_mode_bits |= AMB_LONGINDIRECT | AMB_INDEX(get_index(TRUE)); else Throw_error(exception_syntax); break; case '#': GetByte(); // proceed with next char address_mode_bits |= AMB_IMMEDIATE; ALU_int_result(result); typesystem_want_imm(result); // FIXME - this is wrong for 65ce02's PHW# break; default: // liberal, to allow for "(...," open_paren = ALU_liberal_int(result); typesystem_want_addr(result); // check for implied addressing if ((result->flags & MVALUE_EXISTS) == 0) address_mode_bits |= AMB_IMPLIED; // check for indirect addressing if (result->flags & MVALUE_INDIRECT) address_mode_bits |= AMB_INDIRECT; // check for internal index (before closing parenthesis) if (open_paren) { // in case there are still open parentheses, // read internal index address_mode_bits |= AMB_PREINDEX(get_index(FALSE)); if (GotByte == ')') GetByte(); // go on with next char else Throw_error(exception_syntax); } // check for external index (after closing parenthesis) address_mode_bits |= AMB_INDEX(get_index(FALSE)); } // ensure end of line Input_ensure_EOS(); //printf("AM: %x\n", addressing_mode); return address_mode_bits; } // Helper function for calc_arg_size() // Only call with "size_bit = MVALUE_FORCE16" or "size_bit = MVALUE_FORCE24" static int check_oversize(int size_bit, struct result *argument) { // only check if value is *defined* if ((argument->flags & MVALUE_DEFINED) == 0) return size_bit; // pass on result // value is defined, so check if (size_bit == MVALUE_FORCE16) { // check 16-bit argument for high byte zero if ((argument->val.intval <= 255) && (argument->val.intval >= -128)) Throw_warning(exception_oversized_addrmode); } else { // check 24-bit argument for bank byte zero if ((argument->val.intval <= 65535) && (argument->val.intval >= -32768)) Throw_warning(exception_oversized_addrmode); } return size_bit; // pass on result } // Utility function for comparing force bits, argument value, argument size, // "unsure"-flag and possible addressing modes. Returns force bit matching // number of parameter bytes to send. If it returns zero, an error occurred // (and has already been delivered). // force_bit none, 8b, 16b, 24b // argument value and flags of parameter // addressing_modes adressing modes (8b, 16b, 24b or any combination) // Return value = force bit for number of parameter bytes to send (0 = error) static int calc_arg_size(int force_bit, struct result *argument, int addressing_modes) { // if there are no possible addressing modes, complain if (addressing_modes == MAYBE______) { Throw_error(exception_illegal_combination); return 0; } // if command has force bit, act upon it if (force_bit) { // if command allows this force bit, return it if (addressing_modes & force_bit) return force_bit; // if not, complain Throw_error("Illegal combination of command and postfix."); return 0; } // Command has no force bit. Check whether value has one // if value has force bit, act upon it if (argument->flags & MVALUE_FORCEBITS) { // Value has force bit set, so return this or bigger size if (MVALUE_FORCE08 & addressing_modes & argument->flags) return MVALUE_FORCE08; if (MVALUE_FORCE16 & addressing_modes & argument->flags) return MVALUE_FORCE16; if (MVALUE_FORCE24 & addressing_modes) return MVALUE_FORCE24; Throw_error(exception_number_out_of_range); // else error return 0; } // Value has no force bit. Check whether there's only one addr mode // if only one addressing mode, use that if ((addressing_modes == MVALUE_FORCE08) || (addressing_modes == MVALUE_FORCE16) || (addressing_modes == MVALUE_FORCE24)) return addressing_modes; // There's only one, so use it // There's more than one addressing mode. Check whether value is sure // if value is unsure, use default size if (argument->flags & MVALUE_UNSURE) { // if there is an 8-bit addressing mode *and* the value // is sure to fit into 8 bits, use the 8-bit mode if ((addressing_modes & MVALUE_FORCE08) && (argument->flags & MVALUE_ISBYTE)) return MVALUE_FORCE08; // if there is a 16-bit addressing, use that // call helper function for "oversized addr mode" warning if (MVALUE_FORCE16 & addressing_modes) return check_oversize(MVALUE_FORCE16, argument); // if there is a 24-bit addressing, use that // call helper function for "oversized addr mode" warning if (MVALUE_FORCE24 & addressing_modes) return check_oversize(MVALUE_FORCE24, argument); // otherwise, use 8-bit-addressing, which will raise an // error later on if the value won't fit return MVALUE_FORCE08; } // Value is sure, so use its own size // if value is negative, size cannot be chosen. Complain! if (argument->val.intval < 0) { Throw_error("Negative value - cannot choose addressing mode."); return 0; } // Value is positive or zero. Check size ranges // if there is an 8-bit addressing mode and value fits, use 8 bits if ((addressing_modes & MVALUE_FORCE08) && (argument->val.intval < 256)) return MVALUE_FORCE08; // if there is a 16-bit addressing mode and value fits, use 16 bits if ((addressing_modes & MVALUE_FORCE16) && (argument->val.intval < 65536)) return MVALUE_FORCE16; // if there is a 24-bit addressing mode, use that. In case the // value doesn't fit, the output function will do the complaining. if (addressing_modes & MVALUE_FORCE24) return MVALUE_FORCE24; // Value is too big for all possible addressing modes Throw_error(exception_number_out_of_range); return 0; } // Mnemonics using only implied addressing. static void group_only_implied_addressing(int opcode) { // for 65ce02 and 4502, warn about buggy decimal mode if ((opcode == 248) && (CPU_state.type->flags & CPUFLAG_DECIMALSUBTRACTBUGGY)) Throw_first_pass_warning("Found SED instruction for CPU with known decimal SBC bug."); Output_byte(opcode); Input_ensure_EOS(); } // helper function to output "Target not in bank" message static void not_in_bank(intval_t target) { char buffer[60]; // 640K should be enough for anybody sprintf(buffer, "Target not in bank (0x%lx).", (long) target); Throw_error(buffer); } // helper function for branches with 8-bit offset (including bbr0..7/bbs0..7) static void near_branch(int preoffset) { struct result target; intval_t offset = 0; // dummy value, to not throw more errors than necessary ALU_int_result(&target); // FIXME - check for outermost parentheses and raise error! typesystem_want_addr(&target); // FIXME - read pc via function call instead! if (CPU_state.pc.flags & target.flags & MVALUE_DEFINED) { if ((target.val.intval | 0xffff) != 0xffff) { not_in_bank(target.val.intval); } else { offset = (target.val.intval - (CPU_state.pc.val.intval + preoffset)) & 0xffff; // clip to 16 bit offset // fix sign if (offset & 0x8000) offset -= 0x10000; // range check if ((offset < -128) || (offset > 127)) { char buffer[60]; // 640K should be enough for anybody sprintf(buffer, "Target out of range (%ld; %ld too far).", (long) offset, (long) (offset < -128 ? -128 - offset : offset - 127)); Throw_error(buffer); } } } // this fn has its own range check (see above). // No reason to irritate the user with another error message, // so use Output_byte() instead of output_8() //output_8(offset); Output_byte(offset); Input_ensure_EOS(); } // helper function for relative addressing with 16-bit offset static void far_branch(int preoffset) { struct result target; intval_t offset = 0; // dummy value, to not throw more errors than necessary ALU_int_result(&target); // FIXME - check for outermost parentheses and raise error! typesystem_want_addr(&target); // FIXME - read pc via function call instead! if (CPU_state.pc.flags & target.flags & MVALUE_DEFINED) { if ((target.val.intval | 0xffff) != 0xffff) { not_in_bank(target.val.intval); } else { offset = (target.val.intval - (CPU_state.pc.val.intval + preoffset)) & 0xffff; // no further checks necessary, 16-bit branches can access whole bank } } output_le16(offset); Input_ensure_EOS(); } // set addressing mode bits depending on which opcodes exist, then calculate // argument size and output both opcode and argument static void make_command(int force_bit, struct result *result, unsigned long opcodes) { int addressing_modes = MAYBE______; if (opcodes & 0x0000ff) addressing_modes |= MAYBE_1____; if (opcodes & 0x00ff00) addressing_modes |= MAYBE___2__; if (opcodes & 0xff0000) addressing_modes |= MAYBE_____3; switch (calc_arg_size(force_bit, result, addressing_modes)) { case MVALUE_FORCE08: Output_byte(opcodes & 255); output_8(result->val.intval); break; case MVALUE_FORCE16: Output_byte((opcodes >> 8) & 255); output_le16(result->val.intval); break; case MVALUE_FORCE24: Output_byte((opcodes >> 16) & 255); output_le24(result->val.intval); } } // check whether 16bit immediate addressing is allowed. If not, return given // opcode. If it is allowed, set force bits according to CPU register length // and return given opcode for both 8- and 16-bit mode. static unsigned int imm_ops(int *force_bit, unsigned char opcode, int immediate_mode) { int long_register = 0; switch (immediate_mode) { case IM_FORCE8: return opcode; // result in bits 0..7 forces single-byte argument case IM_FORCE16: // currently only for 65ce02's PHW# return ((unsigned int) opcode) << 8; // opcode in bits8.15 forces two-byte argument case IM_ACCUMULATOR: // for 65816 long_register = CPU_state.a_is_long; break; case IM_INDEXREGS: // for 65816 long_register = CPU_state.xy_are_long; break; default: Bug_found("IllegalImmediateMode", immediate_mode); } // if the CPU does not support long registers... if ((CPU_state.type->flags & CPUFLAG_SUPPORTSLONGREGS) == 0) return opcode; // result in bits 0..7 forces single-byte argument // check force bits - if no force bits given, use cpu state and convert to force bit if (*force_bit == 0) *force_bit = long_register ? MVALUE_FORCE16 : MVALUE_FORCE08; // return identical opcodes for single-byte and two-byte arguments: return (((unsigned int) opcode) << 8) | opcode; } // helper function to warn if zp pointer wraps around static void check_zp_wraparound(struct result *result) { if ((result->val.intval == 0xff) && (result->flags & MVALUE_DEFINED)) Throw_warning("Zeropage pointer wraps around from $ff to $00"); } // The main accumulator stuff (ADC, AND, CMP, EOR, LDA, ORA, SBC, STA) // plus PEI. static void group_main(int index, int immediate_mode) { unsigned long immediate_opcodes; struct result result; int force_bit = Input_get_force_bit(); // skips spaces after switch (get_argument(&result)) { case IMMEDIATE_ADDRESSING: // #$ff or #$ffff (depending on accu length) immediate_opcodes = imm_ops(&force_bit, accu_imm[index], immediate_mode); // CAUTION - do not incorporate the line above into the line // below - "force_bit" might be undefined (depends on compiler). make_command(force_bit, &result, immediate_opcodes); break; case ABSOLUTE_ADDRESSING: // $ff, $ffff, $ffffff make_command(force_bit, &result, accu_abs[index]); break; case X_INDEXED_ADDRESSING: // $ff,x, $ffff,x, $ffffff,x make_command(force_bit, &result, accu_xabs[index]); break; case Y_INDEXED_ADDRESSING: // $ffff,y (in theory, "$ff,y" as well) make_command(force_bit, &result, accu_yabs[index]); break; case STACK_INDEXED_ADDRESSING: // $ff,s make_command(force_bit, &result, accu_sabs8[index]); break; case X_INDEXED_INDIRECT_ADDRESSING: // ($ff,x) make_command(force_bit, &result, accu_xind8[index]); break; case INDIRECT_ADDRESSING: // ($ff) make_command(force_bit, &result, accu_ind8[index]); check_zp_wraparound(&result); break; case INDIRECT_Y_INDEXED_ADDRESSING: // ($ff),y make_command(force_bit, &result, accu_indy8[index]); check_zp_wraparound(&result); break; case INDIRECT_Z_INDEXED_ADDRESSING: // ($ff),z make_command(force_bit, &result, accu_indz8[index]); check_zp_wraparound(&result); break; case LONG_INDIRECT_ADDRESSING: // [$ff] make_command(force_bit, &result, accu_lind8[index]); break; case LONG_INDIRECT_Y_INDEXED_ADDRESSING: // [$ff],y make_command(force_bit, &result, accu_lindy8[index]); break; case STACK_INDEXED_INDIRECT_Y_INDEXED_ADDRESSING: // ($ff,s),y make_command(force_bit, &result, accu_sindy8[index]); break; default: // other combinations are illegal Throw_error(exception_illegal_combination); } } // Various mnemonics with different addressing modes. static void group_misc(int index, int immediate_mode) { unsigned long immediate_opcodes; struct result result; int force_bit = Input_get_force_bit(); // skips spaces after switch (get_argument(&result)) { case IMPLIED_ADDRESSING: // implied addressing if (misc_impl[index]) Output_byte(misc_impl[index]); else Throw_error(exception_illegal_combination); break; case IMMEDIATE_ADDRESSING: // #$ff or #$ffff (depending on index register length) immediate_opcodes = imm_ops(&force_bit, misc_imm[index], immediate_mode); // CAUTION - do not incorporate the line above into the line // below - "force_bit" might be undefined (depends on compiler). make_command(force_bit, &result, immediate_opcodes); // check whether to warn about 6510's unstable ANE/LXA if ((CPU_state.type->flags & CPUFLAG_8B_AND_AB_NEED_0_ARG) && ((result.val.intval & 0xff) != 0x00) && (result.flags & MVALUE_DEFINED)) { if (immediate_opcodes == 0x8b) Throw_warning("Assembling unstable ANE #NONZERO instruction"); else if (immediate_opcodes == 0xab) Throw_warning("Assembling unstable LXA #NONZERO instruction"); } break; case ABSOLUTE_ADDRESSING: // $ff or $ffff make_command(force_bit, &result, misc_abs[index]); break; case X_INDEXED_ADDRESSING: // $ff,x or $ffff,x make_command(force_bit, &result, misc_xabs[index]); break; case Y_INDEXED_ADDRESSING: // $ff,y or $ffff,y make_command(force_bit, &result, misc_yabs[index]); break; default: // other combinations are illegal Throw_error(exception_illegal_combination); } } // mnemonics using only 8bit relative addressing (short branch instructions). static void group_std_branches(int opcode) { Output_byte(opcode); near_branch(2); } // "bbr0..7" and "bbs0..7" static void group_bbr_bbs(int opcode) { struct result zpmem; ALU_int_result(&zpmem); // FIXME - check for outermost parentheses and raise error! typesystem_want_addr(&zpmem); if (Input_accept_comma()) { Output_byte(opcode); Output_byte(zpmem.val.intval); near_branch(3); } else { Throw_error(exception_syntax); } } // mnemonics using only 16bit relative addressing (BRL and PER of 65816, and the long branches of 65ce02) static void group_relative16(int opcode, int preoffset) { Output_byte(opcode); far_branch(preoffset); } // "mvn" and "mvp" static void group_mvn_mvp(int opcode) { struct result source, target; // assembler syntax: "mnemonic source, target" ALU_int_result(&source); // FIXME - check for outermost parentheses and raise error! typesystem_want_imm(&source); if (Input_accept_comma()) { ALU_int_result(&target); // FIXME - check for outermost parentheses and raise error! typesystem_want_imm(&target); // machine language order: "opcode target source" Output_byte(opcode); output_8(target.val.intval); output_8(source.val.intval); Input_ensure_EOS(); } else { Throw_error(exception_syntax); } } // "rmb0..7" and "smb0..7" static void group_only_zp(int opcode) { struct result target; ALU_int_result(&target); // FIXME - check for outermost parentheses and raise error! typesystem_want_addr(&target); Output_byte(opcode); output_8(target.val.intval); Input_ensure_EOS(); } // The jump instructions. static void group_jump(int index) { struct result result; int force_bit = Input_get_force_bit(); // skips spaces after switch (get_argument(&result)) { case ABSOLUTE_ADDRESSING: // absolute16 or absolute24 make_command(force_bit, &result, jump_abs[index]); break; case INDIRECT_ADDRESSING: // ($ffff) make_command(force_bit, &result, jump_ind[index]); // check whether to warn about 6502's JMP() bug if (((result.val.intval & 0xff) == 0xff) && (result.flags & MVALUE_DEFINED) && (CPU_state.type->flags & CPUFLAG_INDIRECTJMPBUGGY)) Throw_warning("Assembling buggy JMP($xxff) instruction"); break; case X_INDEXED_INDIRECT_ADDRESSING: // ($ffff,x) make_command(force_bit, &result, jump_xind[index]); break; case LONG_INDIRECT_ADDRESSING: // [$ffff] make_command(force_bit, &result, jump_lind[index]); break; default: // other combinations are illegal Throw_error(exception_illegal_combination); } } // Work function static int check_mnemo_tree(struct ronode *tree, struct dynabuf *dyna_buf) { void *node_body; int code, immediate_mode; // size of immediate argument // search for tree item if (!Tree_easy_scan(tree, &node_body, dyna_buf)) return FALSE; code = ((int) node_body) & CODEMASK; // get opcode or table index immediate_mode = ((int) node_body) & IMMASK; // get immediate mode switch (GROUP((long) node_body)) { case GROUP_ACCU: // main accumulator stuff group_main(code, immediate_mode); break; case GROUP_MISC: // misc group_misc(code, immediate_mode); break; case GROUP_ALLJUMPS: // the jump instructions group_jump(code); break; case GROUP_IMPLIEDONLY: // mnemonics with only implied addressing group_only_implied_addressing(code); break; case GROUP_RELATIVE8: // short relative group_std_branches(code); break; case GROUP_BITBRANCH: // "bbr0..7" and "bbs0..7" group_bbr_bbs(code); break; case GROUP_REL16_2: // long relative to pc+2 group_relative16(code, 2); break; case GROUP_REL16_3: // long relative to pc+3 group_relative16(code, 3); break; case GROUP_BOTHMOVES: // "mvp" and "mvn" group_mvn_mvp(code); break; case GROUP_ZPONLY: // "rmb0..7" and "smb0..7" group_only_zp(code); break; default: // others indicate bugs Bug_found("IllegalGroupIndex", code); } return TRUE; } // check whether mnemonic in GlobalDynaBuf is supported by 6502 cpu. int keyword_is_6502_mnemo(int length) { if (length != 3) return FALSE; // make lower case version of mnemonic in local dynamic buffer DynaBuf_to_lower(mnemo_dyna_buf, GlobalDynaBuf); return check_mnemo_tree(mnemo_6502_tree, mnemo_dyna_buf) ? TRUE : FALSE; } // check whether mnemonic in GlobalDynaBuf is supported by 6510 cpu. int keyword_is_6510_mnemo(int length) { if (length != 3) return FALSE; // make lower case version of mnemonic in local dynamic buffer DynaBuf_to_lower(mnemo_dyna_buf, GlobalDynaBuf); // first check undocumented ("illegal") opcodes... if (check_mnemo_tree(mnemo_6502undoc1_tree, mnemo_dyna_buf)) return TRUE; // then check some more undocumented ("illegal") opcodes... if (check_mnemo_tree(mnemo_6502undoc2_tree, mnemo_dyna_buf)) return TRUE; // ...then check original opcodes return check_mnemo_tree(mnemo_6502_tree, mnemo_dyna_buf) ? TRUE : FALSE; } // check whether mnemonic in GlobalDynaBuf is supported by C64DTV2 cpu. int keyword_is_c64dtv2_mnemo(int length) { if (length != 3) return FALSE; // make lower case version of mnemonic in local dynamic buffer DynaBuf_to_lower(mnemo_dyna_buf, GlobalDynaBuf); // first check C64DTV2 extensions... if (check_mnemo_tree(mnemo_c64dtv2_tree, mnemo_dyna_buf)) return TRUE; // ...then check a few undocumented ("illegal") opcodes... if (check_mnemo_tree(mnemo_6502undoc1_tree, mnemo_dyna_buf)) return TRUE; // ...then check original opcodes return check_mnemo_tree(mnemo_6502_tree, mnemo_dyna_buf) ? TRUE : FALSE; } // check whether mnemonic in GlobalDynaBuf is supported by 65c02 cpu. int keyword_is_65c02_mnemo(int length) { if (length != 3) return FALSE; // make lower case version of mnemonic in local dynamic buffer DynaBuf_to_lower(mnemo_dyna_buf, GlobalDynaBuf); // first check extensions because some mnemonics gained new addressing modes... if (check_mnemo_tree(mnemo_65c02_tree, mnemo_dyna_buf)) return TRUE; // ...then check original opcodes return check_mnemo_tree(mnemo_6502_tree, mnemo_dyna_buf) ? TRUE : FALSE; } // check whether mnemonic in GlobalDynaBuf is supported by Rockwell 65c02 cpu. int keyword_is_r65c02_mnemo(int length) { if ((length != 3) && (length != 4)) return FALSE; // make lower case version of mnemonic in local dynamic buffer DynaBuf_to_lower(mnemo_dyna_buf, GlobalDynaBuf); // first check 65c02 extensions because some mnemonics gained new addressing modes... if (check_mnemo_tree(mnemo_65c02_tree, mnemo_dyna_buf)) return TRUE; // ...then check original opcodes... if (check_mnemo_tree(mnemo_6502_tree, mnemo_dyna_buf)) return TRUE; // ...then check Rockwell extensions (rmb, smb, bbr, bbs) return check_mnemo_tree(mnemo_bitmanips_tree, mnemo_dyna_buf) ? TRUE : FALSE; } // check whether mnemonic in GlobalDynaBuf is supported by WDC w65c02 cpu. int keyword_is_w65c02_mnemo(int length) { if ((length != 3) && (length != 4)) return FALSE; // make lower case version of mnemonic in local dynamic buffer DynaBuf_to_lower(mnemo_dyna_buf, GlobalDynaBuf); // first check 65c02 extensions because some mnemonics gained new addressing modes... if (check_mnemo_tree(mnemo_65c02_tree, mnemo_dyna_buf)) return TRUE; // ...then check original opcodes... if (check_mnemo_tree(mnemo_6502_tree, mnemo_dyna_buf)) return TRUE; // ...then check Rockwell extensions (rmb, smb, bbr, bbs)... if (check_mnemo_tree(mnemo_bitmanips_tree, mnemo_dyna_buf)) return TRUE; // ...then check WDC extensions "stp" and "wai" return check_mnemo_tree(mnemo_stp_wai_tree, mnemo_dyna_buf) ? TRUE : FALSE; } // check whether mnemonic in GlobalDynaBuf is supported by CSG 65CE02 cpu. int keyword_is_65ce02_mnemo(int length) { if ((length != 3) && (length != 4)) return FALSE; // make lower case version of mnemonic in local dynamic buffer DynaBuf_to_lower(mnemo_dyna_buf, GlobalDynaBuf); // first check 65ce02 extensions because some mnemonics gained new addressing modes... if (check_mnemo_tree(mnemo_65ce02_tree, mnemo_dyna_buf)) return TRUE; // ...then check 65c02 extensions because of the same reason... if (check_mnemo_tree(mnemo_65c02_tree, mnemo_dyna_buf)) return TRUE; // ...then check original opcodes... if (check_mnemo_tree(mnemo_6502_tree, mnemo_dyna_buf)) return TRUE; // ...then check Rockwell extensions (rmb, smb, bbr, bbs)... if (check_mnemo_tree(mnemo_bitmanips_tree, mnemo_dyna_buf)) return TRUE; // ...then check "aug" return check_mnemo_tree(mnemo_aug_tree, mnemo_dyna_buf) ? TRUE : FALSE; } // check whether mnemonic in GlobalDynaBuf is supported by CSG 4502 cpu. int keyword_is_4502_mnemo(int length) { if ((length != 3) && (length != 4)) return FALSE; // make lower case version of mnemonic in local dynamic buffer DynaBuf_to_lower(mnemo_dyna_buf, GlobalDynaBuf); // first check 65ce02 extensions because some mnemonics gained new addressing modes... if (check_mnemo_tree(mnemo_65ce02_tree, mnemo_dyna_buf)) return TRUE; // ...then check 65c02 extensions because of the same reason... if (check_mnemo_tree(mnemo_65c02_tree, mnemo_dyna_buf)) return TRUE; // ...then check original opcodes... if (check_mnemo_tree(mnemo_6502_tree, mnemo_dyna_buf)) return TRUE; // ...then check Rockwell extensions (rmb, smb, bbr, bbs)... if (check_mnemo_tree(mnemo_bitmanips_tree, mnemo_dyna_buf)) return TRUE; // ...then check "map" and "eom" return check_mnemo_tree(mnemo_map_eom_tree, mnemo_dyna_buf) ? TRUE : FALSE; } // check whether mnemonic in GlobalDynaBuf is supported by 65816 cpu. int keyword_is_65816_mnemo(int length) { if (length != 3) return FALSE; // make lower case version of mnemonic in local dynamic buffer DynaBuf_to_lower(mnemo_dyna_buf, GlobalDynaBuf); // first check 65816 extensions because some mnemonics gained new addressing modes... if (check_mnemo_tree(mnemo_65816_tree, mnemo_dyna_buf)) return TRUE; // ...then check 65c02 extensions because of the same reason... if (check_mnemo_tree(mnemo_65c02_tree, mnemo_dyna_buf)) return TRUE; // ...then check original opcodes... if (check_mnemo_tree(mnemo_6502_tree, mnemo_dyna_buf)) return TRUE; // ...then check WDC extensions "stp" and "wai" return check_mnemo_tree(mnemo_stp_wai_tree, mnemo_dyna_buf) ? TRUE : FALSE; } acme-crossassembler-0.96.4/src/mnemo.h000066400000000000000000000025371333777125400176420ustar00rootroot00000000000000// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. // Copyright (C) 1998-2016 Marco Baye // Have a look at "acme.c" for further info // // mnemonic definitions #ifndef mnemo_H #define mnemo_H // create dynamic buffer, build keyword trees extern void Mnemo_init(void); // check whether mnemonic in GlobalDynaBuf is supported by 6502 cpu. extern int keyword_is_6502_mnemo(int length); // check whether mnemonic in GlobalDynaBuf is supported by 6510 cpu. extern int keyword_is_6510_mnemo(int length); // check whether mnemonic in GlobalDynaBuf is supported by C64DTV2 cpu. extern int keyword_is_c64dtv2_mnemo(int length); // check whether mnemonic in GlobalDynaBuf is supported by 65c02 cpu. extern int keyword_is_65c02_mnemo(int length); // check whether mnemonic in GlobalDynaBuf is supported by Rockwell 65c02 cpu. extern int keyword_is_r65c02_mnemo(int length); // check whether mnemonic in GlobalDynaBuf is supported by WDC 65c02 cpu. extern int keyword_is_w65c02_mnemo(int length); // check whether mnemonic in GlobalDynaBuf is supported by 65816 cpu. extern int keyword_is_65816_mnemo(int length); // check whether mnemonic in GlobalDynaBuf is supported by CSG 65ce02 cpu. extern int keyword_is_65ce02_mnemo(int length); // check whether mnemonic in GlobalDynaBuf is supported by CSG 4502 cpu. extern int keyword_is_4502_mnemo(int length); #endif acme-crossassembler-0.96.4/src/output.c000066400000000000000000000434671333777125400200710ustar00rootroot00000000000000// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. // Copyright (C) 1998-2017 Marco Baye // Have a look at "acme.c" for further info // // Output stuff // 24 Nov 2007 Added possibility to suppress segment overlap warnings // 25 Sep 2011 Fixed bug in !to (colons in filename could be interpreted as EOS) // 5 Mar 2014 Fixed bug where setting *>0xffff resulted in hangups. // 19 Nov 2014 Merged Johann Klasek's report listing generator patch // 22 Sep 2015 Added big-endian output functions #include "output.h" #include #include // for memset() #include "acme.h" #include "alu.h" #include "config.h" #include "cpu.h" #include "dynabuf.h" #include "global.h" #include "input.h" #include "platform.h" #include "tree.h" // constants #define OUTBUFFERSIZE 65536 #define NO_SEGMENT_START (-1) // invalid value to signal "not in a segment" // structure for linked list of segment data struct segment { struct segment *next, *prev; intval_t start, length; }; // structure for all output stuff: struct output { // output buffer stuff char *buffer; // holds assembled code intval_t write_idx; // index of next write intval_t lowest_written; // smallest address used intval_t highest_written; // largest address used int initvalue_set; // actually bool struct { intval_t start; // start of current segment (or NO_SEGMENT_START) intval_t max; // highest address segment may use int flags; // segment flags ("overlay" and "invisible", see header file) struct segment list_head; // head element of doubly-linked ring list } segment; char xor; // output modifier }; // variables static struct output default_output; static struct output *out = &default_output; // FIXME - make static struct vcpu CPU_state; // current CPU state // FIXME - move output _file_ stuff to some other .c file! // possible file formats enum output_format { OUTPUT_FORMAT_UNSPECIFIED, // default (uses "plain" actually) OUTPUT_FORMAT_APPLE, // load address, length, code OUTPUT_FORMAT_CBM, // load address, code (default for "!to" pseudo opcode) OUTPUT_FORMAT_PLAIN // code only }; // predefined stuff static struct ronode *file_format_tree = NULL; // tree to hold output formats (FIXME - a tree for three items, really?) static struct ronode file_format_list[] = { #define KNOWN_FORMATS "'plain', 'cbm', 'apple'" // shown in CLI error message for unknown formats PREDEFNODE("apple", OUTPUT_FORMAT_APPLE), PREDEFNODE(s_cbm, OUTPUT_FORMAT_CBM), // PREDEFNODE("o65", OUTPUT_FORMAT_O65), PREDEFLAST("plain", OUTPUT_FORMAT_PLAIN), // ^^^^ this marks the last element }; // chosen file format static enum output_format output_format = OUTPUT_FORMAT_UNSPECIFIED; const char outputfile_formats[] = KNOWN_FORMATS; // string to show if outputfile_set_format() returns nonzero // report binary output static void report_binary(char value) { if (report->bin_used == 0) report->bin_address = out->write_idx; // remember address at start of line if (report->bin_used < REPORT_BINBUFSIZE) report->bin_buf[report->bin_used++] = value; } // set up new out->segment.max value according to the given address. // just find the next segment start and subtract 1. static void find_segment_max(intval_t new_pc) { struct segment *test_segment = out->segment.list_head.next; // search for smallest segment start address that // is larger than given address // use list head as sentinel // FIXME - if +1 overflows intval_t, we have an infinite loop! out->segment.list_head.start = new_pc + 1; while (test_segment->start <= new_pc) test_segment = test_segment->next; if (test_segment == &out->segment.list_head) out->segment.max = OUTBUFFERSIZE - 1; else out->segment.max = test_segment->start - 1; // last free address available } // static void border_crossed(int current_offset) { if (current_offset >= OUTBUFFERSIZE) Throw_serious_error("Produced too much code."); if (pass_count == 0) { Throw_warning("Segment reached another one, overwriting it."); find_segment_max(current_offset + 1); // find new (next) limit } } // function ptr to write byte into output buffer (might point to real fn or error trigger) void (*Output_byte)(intval_t byte); // send low byte to output buffer, automatically increasing program counter static void real_output(intval_t byte) { // did we reach segment limit? if (out->write_idx > out->segment.max) border_crossed(out->write_idx); // new minimum address? if (out->write_idx < out->lowest_written) out->lowest_written = out->write_idx; // new maximum address? if (out->write_idx > out->highest_written) out->highest_written = out->write_idx; // write byte and advance ptrs if (report->fd) report_binary(byte & 0xff); // file for reporting, taking also CPU_2add out->buffer[out->write_idx++] = (byte & 0xff) ^ out->xor; ++CPU_state.add_to_pc; } // throw error (pc undefined) and use fake pc from now on static void no_output(intval_t byte) { Throw_error(exception_pc_undefined); // now change fn ptr to not complain again. Output_byte = real_output; Output_byte(byte); // try again } // skip over some bytes in output buffer without starting a new segment // (used by "!skip", and also called by "!binary" if really calling // Output_byte would be a waste of time) void output_skip(int size) { if (size < 1) { // FIXME - ok for zero, but why is there no error message // output for negative values? return; } // check whether ptr undefined if (Output_byte == no_output) { Output_byte(0); // trigger error with a dummy byte --size; // fix amount to cater for dummy byte } // did we reach segment limit? if (out->write_idx + size - 1 > out->segment.max) border_crossed(out->write_idx + size - 1); // new minimum address? if (out->write_idx < out->lowest_written) out->lowest_written = out->write_idx; // new maximum address? if (out->write_idx + size - 1 > out->highest_written) out->highest_written = out->write_idx + size - 1; // advance ptrs out->write_idx += size; CPU_state.add_to_pc += size; } // output 8-bit value with range check void output_8(intval_t value) { if ((value <= 0xff) && (value >= -0x80)) Output_byte(value); else Throw_error(exception_number_out_of_range); } // output 16-bit value with range check big-endian void output_be16(intval_t value) { if ((value <= 0xffff) && (value >= -0x8000)) { Output_byte(value >> 8); Output_byte(value); } else { Throw_error(exception_number_out_of_range); } } // output 16-bit value with range check little-endian void output_le16(intval_t value) { if ((value <= 0xffff) && (value >= -0x8000)) { Output_byte(value); Output_byte(value >> 8); } else { Throw_error(exception_number_out_of_range); } } // output 24-bit value with range check big-endian void output_be24(intval_t value) { if ((value <= 0xffffff) && (value >= -0x800000)) { Output_byte(value >> 16); Output_byte(value >> 8); Output_byte(value); } else { Throw_error(exception_number_out_of_range); } } // output 24-bit value with range check little-endian void output_le24(intval_t value) { if ((value <= 0xffffff) && (value >= -0x800000)) { Output_byte(value); Output_byte(value >> 8); Output_byte(value >> 16); } else { Throw_error(exception_number_out_of_range); } } // output 32-bit value (without range check) big-endian void output_be32(intval_t value) { // if ((Value <= 0x7fffffff) && (Value >= -0x80000000)) { Output_byte(value >> 24); Output_byte(value >> 16); Output_byte(value >> 8); Output_byte(value); // } else { // Throw_error(exception_number_out_of_range); // } } // output 32-bit value (without range check) little-endian void output_le32(intval_t value) { // if ((Value <= 0x7fffffff) && (Value >= -0x80000000)) { Output_byte(value); Output_byte(value >> 8); Output_byte(value >> 16); Output_byte(value >> 24); // } else { // Throw_error(exception_number_out_of_range); // } } // fill output buffer with given byte value static void fill_completely(char value) { memset(out->buffer, value, OUTBUFFERSIZE); } // define default value for empty memory ("!initmem" pseudo opcode) // returns zero if ok, nonzero if already set int output_initmem(char content) { // if MemInit flag is already set, complain if (out->initvalue_set) { Throw_warning("Memory already initialised."); return 1; // failed } // set MemInit flag out->initvalue_set = TRUE; // init memory fill_completely(content); // enforce another pass if (pass_undefined_count == 0) pass_undefined_count = 1; // FIXME - enforcing another pass is not needed if there hasn't been any // output yet. But that's tricky to detect without too much overhead. // The old solution was to add &&(out->lowest_written < out->highest_written+1) to "if" above return 0; // ok } // try to set output format held in DynaBuf. Returns zero on success. int outputfile_set_format(void) { void *node_body; // make sure tree is initialised if (file_format_tree == NULL) Tree_add_table(&file_format_tree, file_format_list); // perform lookup if (!Tree_easy_scan(file_format_tree, &node_body, GlobalDynaBuf)) return 1; output_format = (enum output_format) node_body; return 0; } // if file format was already chosen, returns zero. // if file format isn't set, chooses CBM and returns 1. int outputfile_prefer_cbm_format(void) { if (output_format != OUTPUT_FORMAT_UNSPECIFIED) return 0; output_format = OUTPUT_FORMAT_CBM; return 1; } // select output file ("!to" pseudo opcode) // returns zero on success, nonzero if already set int outputfile_set_filename(void) { // if output file already chosen, complain and exit if (output_filename) { Throw_warning("Output file already chosen."); return 1; // failed } // get malloc'd copy of filename output_filename = DynaBuf_get_copy(GlobalDynaBuf); return 0; // ok } // init output struct (done later) void Output_init(signed long fill_value) { out->buffer = safe_malloc(OUTBUFFERSIZE); if (fill_value == MEMINIT_USE_DEFAULT) { fill_value = FILLVALUE_INITIAL; out->initvalue_set = FALSE; } else { out->initvalue_set = TRUE; } // init output buffer (fill memory with initial value) fill_completely(fill_value & 0xff); // init ring list of segments out->segment.list_head.next = &out->segment.list_head; out->segment.list_head.prev = &out->segment.list_head; } // dump used portion of output buffer into output file void Output_save_file(FILE *fd) { intval_t start, amount; if (out->highest_written < out->lowest_written) { // nothing written start = 0; // I could try to use some segment start, but what for? amount = 0; } else { start = out->lowest_written; amount = out->highest_written - start + 1; } if (config.process_verbosity) printf("Saving %ld (0x%lx) bytes (0x%lx - 0x%lx exclusive).\n", amount, amount, start, start + amount); // output file header according to file format switch (output_format) { case OUTPUT_FORMAT_APPLE: PLATFORM_SETFILETYPE_APPLE(output_filename); // output 16-bit load address in little-endian byte order putc(start & 255, fd); putc(start >> 8, fd); // output 16-bit length in little-endian byte order putc(amount & 255, fd); putc(amount >> 8, fd); break; case OUTPUT_FORMAT_UNSPECIFIED: case OUTPUT_FORMAT_PLAIN: PLATFORM_SETFILETYPE_PLAIN(output_filename); break; case OUTPUT_FORMAT_CBM: PLATFORM_SETFILETYPE_CBM(output_filename); // output 16-bit load address in little-endian byte order putc(start & 255, fd); putc(start >> 8, fd); } // dump output buffer to file fwrite(out->buffer + start, amount, 1, fd); } // link segment data into segment ring static void link_segment(intval_t start, intval_t length) { struct segment *new_segment, *test_segment = out->segment.list_head.next; // init new segment new_segment = safe_malloc(sizeof(*new_segment)); new_segment->start = start; new_segment->length = length; // use ring head as sentinel out->segment.list_head.start = start; out->segment.list_head.length = length + 1; // +1 to make sure sentinel exits loop // walk ring to find correct spot while ((test_segment->start < new_segment->start) || ((test_segment->start == new_segment->start) && (test_segment->length < new_segment->length))) test_segment = test_segment->next; // link into ring new_segment->next = test_segment; new_segment->prev = test_segment->prev; new_segment->next->prev = new_segment; new_segment->prev->next = new_segment; } // check whether given PC is inside segment. // only call in first pass, otherwise too many warnings might be thrown static void check_segment(intval_t new_pc) { struct segment *test_segment = out->segment.list_head.next; // use list head as sentinel out->segment.list_head.start = new_pc + 1; // +1 to make sure sentinel exits loop out->segment.list_head.length = 1; // search ring for matching entry while (test_segment->start <= new_pc) { if ((test_segment->start + test_segment->length) > new_pc) { Throw_warning("Segment starts inside another one, overwriting it."); return; } test_segment = test_segment->next; } } // clear segment list and disable output void Output_passinit(void) { // struct segment *temp; //FIXME - why clear ring list in every pass? // Because later pass shouldn't complain about overwriting the same segment from earlier pass! // Currently this does not happen because segment checks are only done in first pass. FIXME! // delete segment list (and free blocks) // while ((temp = segment_list)) { // segment_list = segment_list->next; // free(temp); // } // invalidate start and end (first byte actually written will fix them) out->lowest_written = OUTBUFFERSIZE - 1; out->highest_written = 0; // deactivate output - any byte written will trigger error: Output_byte = no_output; out->write_idx = 0; // same as pc on pass init! out->segment.start = NO_SEGMENT_START; // TODO - "no active segment" could be made a segment flag! out->segment.max = OUTBUFFERSIZE - 1; out->segment.flags = 0; out->xor = 0; //vcpu stuff: CPU_state.pc.flags = 0; // not defined yet CPU_state.pc.val.intval = 0; // same as output's write_idx on pass init CPU_state.add_to_pc = 0; // increase PC by this at end of statement CPU_state.a_is_long = FALSE; // short accu CPU_state.xy_are_long = FALSE; // short index regs } // show start and end of current segment // called whenever a new segment begins, and at end of pass. void Output_end_segment(void) { intval_t amount; // in later passes, ignore completely if (pass_count) return; // if there is no segment, there is nothing to do if (out->segment.start == NO_SEGMENT_START) return; // ignore "invisible" segments if (out->segment.flags & SEGMENT_FLAG_INVISIBLE) return; // ignore empty segments amount = out->write_idx - out->segment.start; if (amount == 0) return; // link to segment list link_segment(out->segment.start, amount); // announce if (config.process_verbosity > 1) printf("Segment size is %ld (0x%lx) bytes (0x%lx - 0x%lx exclusive).\n", amount, amount, out->segment.start, out->write_idx); } // change output pointer and enable output void Output_start_segment(intval_t address_change, int segment_flags) { // properly finalize previous segment (link to list, announce) Output_end_segment(); // calculate start of new segment out->write_idx = (out->write_idx + address_change) & 0xffff; out->segment.start = out->write_idx; out->segment.flags = segment_flags; // allow writing to output buffer Output_byte = real_output; // in first pass, check for other segments and maybe issue warning if (pass_count == 0) { if (!(segment_flags & SEGMENT_FLAG_OVERLAY)) check_segment(out->segment.start); find_segment_max(out->segment.start); } } char output_get_xor(void) { return out->xor; } void output_set_xor(char xor) { out->xor = xor; } // set program counter to defined value (FIXME - allow for undefined!) // if start address was given on command line, main loop will call this before each pass. // in addition to that, it will be called on each "* = VALUE". void vcpu_set_pc(intval_t new_pc, int segment_flags) { intval_t new_offset; new_offset = (new_pc - CPU_state.pc.val.intval) & 0xffff; CPU_state.pc.val.intval = new_pc; CPU_state.pc.flags |= MVALUE_DEFINED; // FIXME - remove when allowing undefined! CPU_state.pc.addr_refs = 1; // yes, PC counts as address // now tell output buffer to start a new segment Output_start_segment(new_offset, segment_flags); } /* TODO - overhaul program counter and memory pointer stuff: general stuff: PC and mem ptr might be marked as "undefined" via flags field. However, their "value" fields are still updated, so we can calculate differences. on pass init: if value given on command line, set PC and out ptr to that value otherwise, set both to zero and mark as "undefined" when ALU asks for "*": return current PC (value and flags) when encountering "!pseudopc VALUE { BLOCK }": parse new value (NEW: might be undefined!) remember difference between current and new value set PC to new value after BLOCK, use remembered difference to change PC back when encountering "* = VALUE": parse new value (NEW: might be undefined!) calculate difference between current PC and new value set PC to new value tell outbuf to add difference to mem ptr (starting a new segment) - if new value is undefined, tell outbuf to disable output Problem: always check for "undefined"; there are some problematic combinations. I need a way to return the size of a generated code block even if PC undefined. Maybe like this: * = new_address [, invisible] [, overlay] [, &size_symbol_ref {] ...code... [} ; at end of block, size is written to size symbol given above!] */ // get program counter void vcpu_read_pc(struct result *target) { *target = CPU_state.pc; } // get size of current statement (until now) - needed for "!bin" verbose output int vcpu_get_statement_size(void) { return CPU_state.add_to_pc; } // adjust program counter (called at end of each statement) void vcpu_end_statement(void) { CPU_state.pc.val.intval = (CPU_state.pc.val.intval + CPU_state.add_to_pc) & 0xffff; CPU_state.add_to_pc = 0; } acme-crossassembler-0.96.4/src/output.h000066400000000000000000000072521333777125400200660ustar00rootroot00000000000000// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. // Copyright (C) 1998-2016 Marco Baye // Have a look at "acme.c" for further info // // Output stuff (FIXME - split into outbuf, outfile/format and vcpu parts) #ifndef output_H #define output_H #include #include "config.h" // constants #define MEMINIT_USE_DEFAULT 256 // segment flags #define SEGMENT_FLAG_OVERLAY (1u << 0) // do not warn about this segment overwriting another one #define SEGMENT_FLAG_INVISIBLE (1u << 1) // do not warn about other segments overwriting this one // current CPU state // FIXME - move vcpu struct definition to .c file and change other .c files' accesses to fn calls struct vcpu { const struct cpu_type *type; // current CPU type (default 6502) (FIXME - move out of struct again?) struct result pc; // current program counter (pseudo value) int add_to_pc; // add to PC after statement int a_is_long; int xy_are_long; }; // variables extern struct vcpu CPU_state; // current CPU state FIXME - restrict visibility to .c file // Prototypes // clear segment list and disable output //TODO - does this belong to outbuf stuff? extern void Output_passinit(void); // outbuf stuff: // alloc and init mem buffer (done later) extern void Output_init(signed long fill_value); // skip over some bytes in output buffer without starting a new segment // (used by "!skip", and also called by "!binary" if really calling // Output_byte would be a waste of time) extern void output_skip(int size); // Send low byte of arg to output buffer and advance pointer // FIXME - replace by output_sequence(char *src, size_t size) extern void (*Output_byte)(intval_t); // define default value for empty memory ("!initmem" pseudo opcode) // returns zero if ok, nonzero if already set extern int output_initmem(char content); // move elsewhere: // Output 8-bit value with range check extern void output_8(intval_t); // Output 16-bit value with range check big-endian extern void output_be16(intval_t); // Output 16-bit value with range check little-endian extern void output_le16(intval_t); // Output 24-bit value with range check big-endian extern void output_be24(intval_t); // Output 24-bit value with range check little-endian extern void output_le24(intval_t); // Output 32-bit value (without range check) big-endian extern void output_be32(intval_t); // Output 32-bit value (without range check) little-endian extern void output_le32(intval_t); // outfile stuff: // try to set output format held in DynaBuf. Returns zero on success. extern int outputfile_set_format(void); extern const char outputfile_formats[]; // string to show if outputfile_set_format() returns nonzero // if file format was already chosen, returns zero. // if file format isn't set, chooses CBM and returns 1. extern int outputfile_prefer_cbm_format(void); // try to set output file name held in DynaBuf. Returns zero on success. extern int outputfile_set_filename(void); // write smallest-possible part of memory buffer to file extern void Output_save_file(FILE *fd); // change output pointer and enable output extern void Output_start_segment(intval_t address_change, int segment_flags); // Show start and end of current segment extern void Output_end_segment(void); extern char output_get_xor(void); extern void output_set_xor(char xor); // set program counter to defined value (TODO - allow undefined!) extern void vcpu_set_pc(intval_t new_pc, int flags); // get program counter extern void vcpu_read_pc(struct result *target); // get size of current statement (until now) - needed for "!bin" verbose output extern int vcpu_get_statement_size(void); // adjust program counter (called at end of each statement) extern void vcpu_end_statement(void); #endif acme-crossassembler-0.96.4/src/platform.c000066400000000000000000000013551333777125400203430ustar00rootroot00000000000000// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. // Copyright (C) 1998-2016 Marco Baye // Have a look at "acme.c" for further info // // Platform specific stuff #include "platform.h" #include "config.h" // Amiga #ifdef _AMIGA #ifndef platform_C #define platform_C // Nothing here - Amigas don't need no stinkin' platform-specific stuff! #endif #endif // DOS, OS/2 and Windows #if defined(__DJGPP__) || defined(__OS2__) || defined(__Windows__) || defined(WIN32) || defined(_WIN32) || defined(__WIN32__) #include "_dos.c" #endif // RISC OS #ifdef __riscos__ #include "_riscos.c" #endif // add further platform files here // Unix/Linux/others (surprisingly also works on at least some versions of Windows) #include "_std.c" acme-crossassembler-0.96.4/src/platform.h000066400000000000000000000015361333777125400203510ustar00rootroot00000000000000// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. // Copyright (C) 1998-2016 Marco Baye // Have a look at "acme.c" for further info // // Platform specific stuff // Amiga #ifdef _AMIGA #define PLATFORM_VERSION "Ported to AmigaOS by Christoph Mammitzsch." #include "_amiga.h" #endif // DOS, OS/2 and Windows #if defined(__DJGPP__) || defined(__OS2__) || defined(__Windows__) || defined(WIN32) || defined(_WIN32) || defined(__WIN32__) #define PLATFORM_VERSION "DOS/OS2/Win32 version. Compiled by Dirk Hoepfner" #include "_dos.h" #endif // RISC OS #ifdef __riscos__ #define PLATFORM_VERSION "RISC OS version." #include "_riscos.h" #endif // add further platform files here // Unix/Linux/others (surprisingly also works on Windows) #ifndef PLATFORM_VERSION #define PLATFORM_VERSION "Platform independent version." #endif #include "_std.h" acme-crossassembler-0.96.4/src/pseudoopcodes.c000066400000000000000000001013671333777125400213770ustar00rootroot00000000000000// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. // Copyright (C) 1998-2017 Marco Baye // Have a look at "acme.c" for further info // // pseudo opcode stuff #include "pseudoopcodes.h" #include #include #include "acme.h" #include "config.h" #include "cpu.h" #include "alu.h" #include "dynabuf.h" #include "encoding.h" #include "flow.h" #include "input.h" #include "macro.h" #include "global.h" #include "output.h" #include "section.h" #include "symbol.h" #include "tree.h" #include "typesystem.h" // different ways to handle end-of-statement: enum eos { SKIP_REMAINDER, // skip remainder of line - (after errors) ENSURE_EOS, // make sure there's nothing left in statement PARSE_REMAINDER, // parse what's left AT_EOS_ANYWAY // actually, same as PARSE_REMAINDER }; // constants static const char s_08[] = "08"; #define s_8 (s_08 + 1) // Yes, I know I'm sick #define s_sl (s_asl + 1) // Yes, I know I'm sick #define s_rl (s_brl + 1) // Yes, I know I'm sick // variables static struct ronode *pseudo_opcode_tree = NULL; // tree to hold pseudo opcodes // not really a pseudo opcode, but close enough to be put here: // called when "* = EXPRESSION" is parsed // setting program counter via "* = VALUE" void notreallypo_setpc(void) { int segment_flags = 0; struct result intresult; ALU_defined_int(&intresult); // read new address // check for modifiers while (Input_accept_comma()) { // parse modifier. if no keyword given, give up if (Input_read_and_lower_keyword() == 0) return; if (strcmp(GlobalDynaBuf->buffer, "overlay") == 0) { segment_flags |= SEGMENT_FLAG_OVERLAY; } else if (strcmp(GlobalDynaBuf->buffer, "invisible") == 0) { segment_flags |= SEGMENT_FLAG_INVISIBLE; } else { Throw_error("Unknown \"* =\" segment modifier."); return; } } vcpu_set_pc(intresult.val.intval, segment_flags); } // define default value for empty memory ("!initmem" pseudo opcode) static enum eos po_initmem(void) { struct result intresult; // ignore in all passes but in first if (pass_count) return SKIP_REMAINDER; // get value ALU_defined_int(&intresult); if ((intresult.val.intval > 0xff) || (intresult.val.intval < -0x80)) Throw_error(exception_number_out_of_range); if (output_initmem(intresult.val.intval & 0xff)) return SKIP_REMAINDER; return ENSURE_EOS; } // change output "encryption" ("!xor" pseudo opcode) static enum eos po_xor(void) { char old_value; intval_t change; old_value = output_get_xor(); change = ALU_any_int(); if ((change > 0xff) || (change < -0x80)) { Throw_error(exception_number_out_of_range); change = 0; } output_set_xor(old_value ^ change); // if there's a block, parse that and then restore old value! if (Parse_optional_block()) output_set_xor(old_value); return ENSURE_EOS; } // select output file and format ("!to" pseudo opcode) static enum eos po_to(void) { // bugfix: first read filename, *then* check for first pass. // if skipping right away, quoted colons might be misinterpreted as EOS // FIXME - fix the skipping code to handle quotes! :) // "!sl" has been fixed as well // read filename to global dynamic buffer // if no file name given, exit (complaining will have been done) if (Input_read_filename(FALSE, NULL)) return SKIP_REMAINDER; // only act upon this pseudo opcode in first pass if (pass_count) return SKIP_REMAINDER; if (outputfile_set_filename()) return SKIP_REMAINDER; // select output format // if no comma found, use default file format if (Input_accept_comma() == FALSE) { if (outputfile_prefer_cbm_format()) { // output deprecation warning Throw_warning("Used \"!to\" without file format indicator. Defaulting to \"cbm\"."); } return ENSURE_EOS; } // parse output format name // if no keyword given, give up if (Input_read_and_lower_keyword() == 0) return SKIP_REMAINDER; if (outputfile_set_format()) { // error occurred Throw_error("Unknown output format."); return SKIP_REMAINDER; } return ENSURE_EOS; // success } // helper function for !8, !16, !24 and !32 pseudo opcodes static enum eos iterate(void (*fn)(intval_t)) { do fn(ALU_any_int()); while (Input_accept_comma()); return ENSURE_EOS; } // Insert 8-bit values ("!08" / "!8" / "!by" / "!byte" pseudo opcode) static enum eos po_byte(void) { return iterate(output_8); } // Insert 16-bit values ("!16" / "!wo" / "!word" pseudo opcode) static enum eos po_16(void) { return iterate((CPU_state.type->flags & CPUFLAG_ISBIGENDIAN) ? output_be16 : output_le16); } // Insert 16-bit values big-endian ("!be16" pseudo opcode) static enum eos po_be16(void) { return iterate(output_be16); } // Insert 16-bit values little-endian ("!le16" pseudo opcode) static enum eos po_le16(void) { return iterate(output_le16); } // Insert 24-bit values ("!24" pseudo opcode) static enum eos po_24(void) { return iterate((CPU_state.type->flags & CPUFLAG_ISBIGENDIAN) ? output_be24 : output_le24); } // Insert 24-bit values big-endian ("!be24" pseudo opcode) static enum eos po_be24(void) { return iterate(output_be24); } // Insert 24-bit values little-endian ("!le24" pseudo opcode) static enum eos po_le24(void) { return iterate(output_le24); } // Insert 32-bit values ("!32" pseudo opcode) static enum eos po_32(void) { return iterate((CPU_state.type->flags & CPUFLAG_ISBIGENDIAN) ? output_be32 : output_le32); } // Insert 32-bit values big-endian ("!be32" pseudo opcode) static enum eos po_be32(void) { return iterate(output_be32); } // Insert 32-bit values little-endian ("!le32" pseudo opcode) static enum eos po_le32(void) { return iterate(output_le32); } // Insert bytes given as pairs of hex digits (helper for source code generators) static enum eos po_hex(void) // now GotByte = illegal char { int digits = 0; unsigned char byte = 0; for (;;) { if (digits == 2) { Output_byte(byte); digits = 0; byte = 0; } if (GotByte >= '0' && GotByte <= '9') { byte = (byte << 4) | (GotByte - '0'); ++digits; GetByte(); continue; } if (GotByte >= 'a' && GotByte <= 'f') { byte = (byte << 4) | (GotByte - 'a' + 10); ++digits; GetByte(); continue; } if (GotByte >= 'A' && GotByte <= 'F') { byte = (byte << 4) | (GotByte - 'A' + 10); ++digits; GetByte(); continue; } // if we're here, the current character is not a hex digit, // which is only allowed outside of pairs: if (digits == 1) { Throw_error("Hex digits are not given in pairs."); return SKIP_REMAINDER; // error exit } switch (GotByte) { case ' ': case '\t': GetByte(); // spaces and tabs are ignored (maybe add commas, too?) continue; case CHAR_EOS: return AT_EOS_ANYWAY; // normal exit default: Throw_error(exception_syntax); // all other characters are errors return SKIP_REMAINDER; // error exit } } } // "!cbm" pseudo opcode (now obsolete) static enum eos obsolete_po_cbm(void) { Throw_error("\"!cbm\" is obsolete; use \"!ct pet\" instead."); return ENSURE_EOS; } // read encoding table from file static enum eos user_defined_encoding(FILE *stream) { char local_table[256], *buffered_table = encoding_loaded_table; const struct encoder *buffered_encoder = encoder_current; if (stream) { encoding_load_from_file(local_table, stream); fclose(stream); } encoder_current = &encoder_file; // activate new encoding encoding_loaded_table = local_table; // activate local table // If there's a block, parse that and then restore old values if (Parse_optional_block()) { encoder_current = buffered_encoder; } else { // if there's *no* block, the table must be used from now on. // copy the local table to the "outer" table memcpy(buffered_table, local_table, 256); } // re-activate "outer" table (it might have been changed by memcpy()) encoding_loaded_table = buffered_table; return ENSURE_EOS; } // use one of the pre-defined encodings (raw, pet, scr) static enum eos predefined_encoding(void) { char local_table[256], *buffered_table = encoding_loaded_table; const struct encoder *buffered_encoder = encoder_current; if (Input_read_and_lower_keyword()) { const struct encoder *new_encoder = encoding_find(); if (new_encoder) encoder_current = new_encoder; // activate new encoder } encoding_loaded_table = local_table; // activate local table // if there's a block, parse that and then restore old values if (Parse_optional_block()) encoder_current = buffered_encoder; // re-activate "outer" table encoding_loaded_table = buffered_table; return ENSURE_EOS; } // set current encoding ("!convtab" pseudo opcode) static enum eos po_convtab(void) { int uses_lib; FILE *stream; if ((GotByte == '<') || (GotByte == '"')) { // if file name is missing, don't bother continuing if (Input_read_filename(TRUE, &uses_lib)) return SKIP_REMAINDER; stream = includepaths_open_ro(uses_lib); return user_defined_encoding(stream); } else { return predefined_encoding(); } } // insert string(s) static enum eos encode_string(const struct encoder *inner_encoder, char xor) { const struct encoder *outer_encoder = encoder_current; // buffer encoder // make given encoder the current one (for ALU-parsed values) encoder_current = inner_encoder; do { if (GotByte == '"') { // read initial character GetQuotedByte(); // send characters until closing quote is reached while (GotByte && (GotByte != '"')) { output_8(xor ^ encoding_encode_char(GotByte)); GetQuotedByte(); } if (GotByte == CHAR_EOS) return AT_EOS_ANYWAY; // after closing quote, proceed with next char GetByte(); } else { // Parse value. No problems with single characters // because the current encoding is // temporarily set to the given one. output_8(ALU_any_int()); } } while (Input_accept_comma()); encoder_current = outer_encoder; // reactivate buffered encoder return ENSURE_EOS; } // insert text string (default format) static enum eos po_text(void) { return encode_string(encoder_current, 0); } // insert raw string static enum eos po_raw(void) { return encode_string(&encoder_raw, 0); } // insert PetSCII string static enum eos po_pet(void) { return encode_string(&encoder_pet, 0); } // insert screencode string static enum eos po_scr(void) { return encode_string(&encoder_scr, 0); } // insert screencode string, XOR'd static enum eos po_scrxor(void) { intval_t num = ALU_any_int(); if (Input_accept_comma() == FALSE) { Throw_error(exception_syntax); return SKIP_REMAINDER; } return encode_string(&encoder_scr, num); } // Include binary file ("!binary" pseudo opcode) // FIXME - split this into "parser" and "worker" fn and move worker fn somewhere else. static enum eos po_binary(void) { int uses_lib; FILE *stream; int byte; intval_t size = -1, // means "not given" => "until EOF" skip = 0; // if file name is missing, don't bother continuing if (Input_read_filename(TRUE, &uses_lib)) return SKIP_REMAINDER; // try to open file stream = includepaths_open_ro(uses_lib); if (stream == NULL) return SKIP_REMAINDER; // read optional arguments if (Input_accept_comma()) { if (ALU_optional_defined_int(&size) && (size < 0)) Throw_serious_error(exception_negative_size); if (Input_accept_comma()) ALU_optional_defined_int(&skip); // read skip } // check whether including is a waste of time // FIXME - future changes ("several-projects-at-once") // may be incompatible with this! if ((size >= 0) && (pass_undefined_count || pass_real_errors)) { output_skip(size); // really including is useless anyway } else { // really insert file fseek(stream, skip, SEEK_SET); // set read pointer // if "size" non-negative, read "size" bytes. // otherwise, read until EOF. while (size != 0) { byte = getc(stream); if (byte == EOF) break; Output_byte(byte); --size; } // if more should have been read, warn and add padding if (size > 0) { Throw_warning("Padding with zeroes."); do Output_byte(0); while (--size); } } fclose(stream); // if verbose, produce some output if ((pass_count == 0) && (config.process_verbosity > 1)) { int amount = vcpu_get_statement_size(); printf("Loaded %d (0x%04x) bytes from file offset %ld (0x%04lx).\n", amount, amount, skip, skip); } return ENSURE_EOS; } // reserve space by sending bytes of given value ("!fi" / "!fill" pseudo opcode) static enum eos po_fill(void) { struct result sizeresult; intval_t fill = FILLVALUE_FILL; ALU_defined_int(&sizeresult); // FIXME - forbid addresses! if (Input_accept_comma()) fill = ALU_any_int(); // FIXME - forbid addresses! while (sizeresult.val.intval--) output_8(fill); return ENSURE_EOS; } // skip over some bytes in output without starting a new segment. // in contrast to "*=*+AMOUNT", "!skip AMOUNT" does not start a new segment. // (...and it will be needed in future for assemble-to-end-address) static enum eos po_skip(void) // now GotByte = illegal char { struct result amount; ALU_defined_int(&amount); // FIXME - forbid addresses! if (amount.val.intval < 0) Throw_serious_error(exception_negative_size); else output_skip(amount.val.intval); return ENSURE_EOS; } // insert byte until PC fits condition static enum eos po_align(void) { // FIXME - read cpu state via function call! struct result andresult, equalresult; intval_t fill, test = CPU_state.pc.val.intval; // make sure PC is defined. if ((CPU_state.pc.flags & MVALUE_DEFINED) == 0) { Throw_error(exception_pc_undefined); CPU_state.pc.flags |= MVALUE_DEFINED; // do not complain again return SKIP_REMAINDER; } ALU_defined_int(&andresult); // FIXME - forbid addresses! if (!Input_accept_comma()) Throw_error(exception_syntax); ALU_defined_int(&equalresult); // ...allow addresses (unlikely, but possible) if (Input_accept_comma()) fill = ALU_any_int(); else fill = CPU_state.type->default_align_value; while ((test++ & andresult.val.intval) != equalresult.val.intval) output_8(fill); return ENSURE_EOS; } static const char Error_old_offset_assembly[] = "\"!pseudopc/!realpc\" is obsolete; use \"!pseudopc {}\" instead."; // start offset assembly // FIXME - split in two parts and move backend to output.c? // TODO - maybe add a label argument to assign the block size afterwards (for assemble-to-end-address) (or add another pseudo opcode) static enum eos po_pseudopc(void) { // FIXME - read pc using a function call! struct result new_pc_result; intval_t new_offset; int outer_flags = CPU_state.pc.flags; // set new ALU_defined_int(&new_pc_result); // FIXME - allow for undefined! (complaining about non-addresses would be logical, but annoying) new_offset = (new_pc_result.val.intval - CPU_state.pc.val.intval) & 0xffff; CPU_state.pc.val.intval = new_pc_result.val.intval; CPU_state.pc.flags |= MVALUE_DEFINED; // FIXME - remove when allowing undefined! // if there's a block, parse that and then restore old value! if (Parse_optional_block()) { // restore old CPU_state.pc.val.intval = (CPU_state.pc.val.intval - new_offset) & 0xffff; CPU_state.pc.flags = outer_flags; } else { // not using a block is no longer allowed Throw_error(Error_old_offset_assembly); } return ENSURE_EOS; } // "!realpc" pseudo opcode (now obsolete) static enum eos obsolete_po_realpc(void) { Throw_error(Error_old_offset_assembly); return ENSURE_EOS; } // select CPU ("!cpu" pseudo opcode) static enum eos po_cpu(void) { const struct cpu_type *cpu_buffer = CPU_state.type; // remember current cpu const struct cpu_type *new_cpu_type; if (Input_read_and_lower_keyword()) { new_cpu_type = cputype_find(); if (new_cpu_type) CPU_state.type = new_cpu_type; // activate new cpu type else Throw_error("Unknown processor."); } // if there's a block, parse that and then restore old value if (Parse_optional_block()) CPU_state.type = cpu_buffer; return ENSURE_EOS; } // set register length, block-wise if needed. static enum eos set_register_length(int *var, int make_long) { int old_size = *var; // set new register length (or complain - whichever is more fitting) vcpu_check_and_set_reg_length(var, make_long); // if there's a block, parse that and then restore old value! if (Parse_optional_block()) vcpu_check_and_set_reg_length(var, old_size); // restore old length return ENSURE_EOS; } // switch to long accumulator ("!al" pseudo opcode) static enum eos po_al(void) { return set_register_length(&CPU_state.a_is_long, TRUE); } // switch to short accumulator ("!as" pseudo opcode) static enum eos po_as(void) { return set_register_length(&CPU_state.a_is_long, FALSE); } // switch to long index registers ("!rl" pseudo opcode) static enum eos po_rl(void) { return set_register_length(&CPU_state.xy_are_long, TRUE); } // switch to short index registers ("!rs" pseudo opcode) static enum eos po_rs(void) { return set_register_length(&CPU_state.xy_are_long, FALSE); } // force explicit label definitions to set "address" flag ("!addr"). Has to be re-entrant. static enum eos po_address(void) // now GotByte = illegal char { SKIPSPACE(); if (GotByte == CHAR_SOB) { typesystem_force_address_block(); return ENSURE_EOS; } typesystem_force_address_statement(TRUE); return PARSE_REMAINDER; } #if 0 // enumerate constants static enum eos po_enum(void) // now GotByte = illegal char { intval_t step = 1; ALU_optional_defined_int(&step); Throw_serious_error("Not yet"); // FIXME return ENSURE_EOS; } #endif // (re)set symbol static enum eos po_set(void) // now GotByte = illegal char { struct result result; int force_bit; struct symbol *symbol; scope_t scope; if (Input_read_scope_and_keyword(&scope) == 0) // skips spaces before // now GotByte = illegal char return SKIP_REMAINDER; force_bit = Input_get_force_bit(); // skips spaces after symbol = symbol_find(scope, force_bit); if (GotByte != '=') { Throw_error(exception_syntax); return SKIP_REMAINDER; } // symbol = parsed value GetByte(); // proceed with next char ALU_any_result(&result); // clear symbol's force bits and set new ones symbol->result.flags &= ~(MVALUE_FORCEBITS | MVALUE_ISBYTE); if (force_bit) { symbol->result.flags |= force_bit; result.flags &= ~(MVALUE_FORCEBITS | MVALUE_ISBYTE); } symbol_set_value(symbol, &result, TRUE); return ENSURE_EOS; } // set file name for symbol list static enum eos po_symbollist(void) { // bugfix: first read filename, *then* check for first pass. // if skipping right away, quoted colons might be misinterpreted as EOS // FIXME - why not just fix the skipping code to handle quotes? :) // "!to" has been fixed as well // read filename to global dynamic buffer // if no file name given, exit (complaining will have been done) if (Input_read_filename(FALSE, NULL)) return SKIP_REMAINDER; // only process this pseudo opcode in first pass if (pass_count) return SKIP_REMAINDER; // if symbol list file name already set, complain and exit if (symbollist_filename) { Throw_warning("Symbol list file name already chosen."); return SKIP_REMAINDER; } // get malloc'd copy of filename symbollist_filename = DynaBuf_get_copy(GlobalDynaBuf); // ensure there's no garbage at end of line return ENSURE_EOS; } // switch to new zone ("!zone" or "!zn"). has to be re-entrant. static enum eos po_zone(void) { struct section entry_values; // buffer for outer zone char *new_title; int allocated; // remember everything about current structure entry_values = *section_now; // set default values in case there is no valid title new_title = s_untitled; allocated = FALSE; // Check whether a zone title is given. If yes and it can be read, // get copy, remember pointer and remember to free it later on. if (BYTEFLAGS(GotByte) & CONTS_KEYWORD) { // Because we know of one character for sure, // there's no need to check the return value. Input_read_keyword(); new_title = DynaBuf_get_copy(GlobalDynaBuf); allocated = TRUE; } // setup new section // section type is "subzone", just in case a block follows section_new(section_now, "Subzone", new_title, allocated); if (Parse_optional_block()) { // Block has been parsed, so it was a SUBzone. section_finalize(section_now); // end inner zone *section_now = entry_values; // restore entry values } else { // no block found, so it's a normal zone change section_finalize(&entry_values); // end outer zone section_now->type = s_Zone; // change type to "Zone" } return ENSURE_EOS; } // "!subzone" or "!sz" pseudo opcode (now obsolete) static enum eos obsolete_po_subzone(void) { Throw_error("\"!subzone {}\" is obsolete; use \"!zone {}\" instead."); // call "!zone" instead return po_zone(); } // include source file ("!source" or "!src"). has to be re-entrant. static enum eos po_source(void) // now GotByte = illegal char { int uses_lib; FILE *stream; char local_gotbyte; struct input new_input, *outer_input; // enter new nesting level // quit program if recursion too deep if (--source_recursions_left < 0) Throw_serious_error("Too deeply nested. Recursive \"!source\"?"); // read file name. quit function on error if (Input_read_filename(TRUE, &uses_lib)) return SKIP_REMAINDER; // if file could be opened, parse it. otherwise, complain stream = includepaths_open_ro(uses_lib); if (stream) { #ifdef __GNUC__ char filename[GlobalDynaBuf->size]; // GCC can do this #else char *filename = safe_malloc(GlobalDynaBuf->size); // VS can not #endif strcpy(filename, GLOBALDYNABUF_CURRENT); outer_input = Input_now; // remember old input local_gotbyte = GotByte; // CAUTION - ugly kluge Input_now = &new_input; // activate new input flow_parse_and_close_file(stream, filename); Input_now = outer_input; // restore previous input GotByte = local_gotbyte; // CAUTION - ugly kluge #ifndef __GNUC__ free(filename); // GCC auto-frees #endif } // leave nesting level ++source_recursions_left; return ENSURE_EOS; } // conditional assembly ("!if"). has to be re-entrant. static enum eos po_if(void) // now GotByte = illegal char { struct result cond_result; ALU_defined_int(&cond_result); if (GotByte != CHAR_SOB) Throw_serious_error(exception_no_left_brace); flow_parse_block_else_block(!!cond_result.val.intval); return ENSURE_EOS; } // conditional assembly ("!ifdef" and "!ifndef"). has to be re-entrant. static enum eos ifdef_ifndef(int is_ifndef) // now GotByte = illegal char { struct rwnode *node; struct symbol *symbol; scope_t scope; int defined = FALSE; if (Input_read_scope_and_keyword(&scope) == 0) // skips spaces before return SKIP_REMAINDER; Tree_hard_scan(&node, symbols_forest, scope, FALSE); if (node) { symbol = (struct symbol *) node->body; // in first pass, count usage if (pass_count == 0) symbol->usage++; if (symbol->result.flags & MVALUE_DEFINED) defined = TRUE; } SKIPSPACE(); // if "ifndef", invert condition if (is_ifndef) defined = !defined; if (GotByte != CHAR_SOB) return defined ? PARSE_REMAINDER : SKIP_REMAINDER; flow_parse_block_else_block(defined); return ENSURE_EOS; } // conditional assembly ("!ifdef"). has to be re-entrant. static enum eos po_ifdef(void) // now GotByte = illegal char { return ifdef_ifndef(FALSE); } // conditional assembly ("!ifndef"). has to be re-entrant. static enum eos po_ifndef(void) // now GotByte = illegal char { return ifdef_ifndef(TRUE); } // looping assembly ("!for"). has to be re-entrant. // old syntax: !for VAR, END { BLOCK } VAR counts from 1 to END // new syntax: !for VAR, START, END { BLOCK } VAR counts from START to END static enum eos po_for(void) // now GotByte = illegal char { scope_t scope; int force_bit; struct result intresult; struct for_loop loop; if (Input_read_scope_and_keyword(&scope) == 0) // skips spaces before return SKIP_REMAINDER; // now GotByte = illegal char force_bit = Input_get_force_bit(); // skips spaces after loop.symbol = symbol_find(scope, force_bit); if (!Input_accept_comma()) { Throw_error(exception_syntax); return SKIP_REMAINDER; } ALU_defined_int(&intresult); // read first argument loop.counter.addr_refs = intresult.addr_refs; if (Input_accept_comma()) { loop.old_algo = FALSE; // new format - yay! if (!config.warn_on_old_for) Throw_first_pass_warning("Found new \"!for\" syntax."); loop.counter.first = intresult.val.intval; // use first argument ALU_defined_int(&intresult); // read second argument loop.counter.last = intresult.val.intval; // use second argument // compare addr_ref counts and complain if not equal! if (config.warn_on_type_mismatch && (intresult.addr_refs != loop.counter.addr_refs)) { Throw_first_pass_warning("Wrong type for loop's END value - must match type of START value."); } loop.counter.increment = (loop.counter.last < loop.counter.first) ? -1 : 1; } else { loop.old_algo = TRUE; // old format - booo! if (config.warn_on_old_for) Throw_first_pass_warning("Found old \"!for\" syntax."); if (intresult.val.intval < 0) Throw_serious_error("Loop count is negative."); loop.counter.first = 0; // CAUTION - old algo pre-increments and therefore starts with 1! loop.counter.last = intresult.val.intval; // use given argument loop.counter.increment = 1; } if (GotByte != CHAR_SOB) Throw_serious_error(exception_no_left_brace); // remember line number of loop pseudo opcode loop.block.start = Input_now->line_number; // read loop body into DynaBuf and get copy loop.block.body = Input_skip_or_store_block(TRUE); // changes line number! flow_forloop(&loop); // free memory free(loop.block.body); // GotByte of OuterInput would be '}' (if it would still exist) GetByte(); // fetch next byte return ENSURE_EOS; } // looping assembly ("!do"). has to be re-entrant. static enum eos po_do(void) // now GotByte = illegal char { struct do_loop loop; // read head condition to buffer SKIPSPACE(); flow_store_doloop_condition(&loop.head_cond, CHAR_SOB); // must be freed! if (GotByte != CHAR_SOB) Throw_serious_error(exception_no_left_brace); // remember line number of loop body, // then read block and get copy loop.block.start = Input_now->line_number; // reading block changes line number! loop.block.body = Input_skip_or_store_block(TRUE); // must be freed! // now GotByte = '}' NEXTANDSKIPSPACE(); // now GotByte = first non-blank char after block // read tail condition to buffer flow_store_doloop_condition(&loop.tail_cond, CHAR_EOS); // must be freed! // now GotByte = CHAR_EOS flow_doloop(&loop); // free memory free(loop.head_cond.body); free(loop.block.body); free(loop.tail_cond.body); return AT_EOS_ANYWAY; } #if 0 // looping assembly (alternative for people used to c-style loops) static enum eos po_while(void) // now GotByte = illegal char { Throw_serious_error("Not yet"); // FIXME return ENSURE_EOS; } #endif // macro definition ("!macro"). static enum eos po_macro(void) // now GotByte = illegal char { // in first pass, parse. In all other passes, skip. if (pass_count == 0) { Macro_parse_definition(); // now GotByte = '}' } else { // skip until CHAR_SOB ('{') is found. // no need to check for end-of-statement, because such an // error would already have been detected in first pass. // for the same reason, there is no need to check for quotes. while (GotByte != CHAR_SOB) GetByte(); Input_skip_or_store_block(FALSE); // now GotByte = '}' } GetByte(); // Proceed with next character return ENSURE_EOS; } // constants #define USERMSG_DYNABUF_INITIALSIZE 80 // variables static struct dynabuf *user_message; // dynamic buffer (!warn/error/serious) // helper function to show user-defined messages static enum eos throw_string(const char prefix[], void (*fn)(const char *)) { struct result result; DYNABUF_CLEAR(user_message); DynaBuf_add_string(user_message, prefix); do { if (GotByte == '"') { // parse string GetQuotedByte(); // read initial character // send characters until closing quote is reached while (GotByte && (GotByte != '"')) { DYNABUF_APPEND(user_message, GotByte); GetQuotedByte(); } if (GotByte == CHAR_EOS) return AT_EOS_ANYWAY; // after closing quote, proceed with next char GetByte(); } else { // parse value ALU_any_result(&result); if (result.flags & MVALUE_IS_FP) { // floating point if (result.flags & MVALUE_DEFINED) { char buffer[40]; // write up to 30 significant characters. // remaining 10 should suffice for sign, // decimal point, exponent, terminator etc. sprintf(buffer, "%.30g", result.val.fpval); DynaBuf_add_string(user_message, buffer); } else { DynaBuf_add_string(user_message, ""); } } else { // integer if (result.flags & MVALUE_DEFINED) { char buffer[32]; // 11 for dec, 8 for hex sprintf(buffer, "%ld (0x%lx)", (long) result.val.intval, (long) result.val.intval); DynaBuf_add_string(user_message, buffer); } else { DynaBuf_add_string(user_message, ""); } } } } while (Input_accept_comma()); DynaBuf_append(user_message, '\0'); fn(user_message->buffer); return ENSURE_EOS; } #if 0 // show debug data given in source code static enum eos po_debug(void) { // FIXME - make debug output depend on some cli switch return throw_string("!debug: ", throw_message); } // show info given in source code static enum eos po_info(void) { return throw_string("!info: ", throw_message); } #endif // throw warning as given in source code static enum eos po_warn(void) { return throw_string("!warn: ", Throw_warning); } // throw error as given in source code static enum eos po_error(void) { return throw_string("!error: ", Throw_error); } // throw serious error as given in source code static enum eos po_serious(void) { return throw_string("!serious: ", Throw_serious_error); } // end of source file ("!endoffile" or "!eof") static enum eos po_endoffile(void) { // well, it doesn't end right here and now, but at end-of-line! :-) Input_ensure_EOS(); Input_now->state = INPUTSTATE_EOF; return AT_EOS_ANYWAY; } // pseudo opcode table static struct ronode pseudo_opcode_list[] = { PREDEFNODE("initmem", po_initmem), PREDEFNODE("xor", po_xor), PREDEFNODE("to", po_to), PREDEFNODE(s_8, po_byte), PREDEFNODE(s_08, po_byte), PREDEFNODE("by", po_byte), PREDEFNODE("byte", po_byte), PREDEFNODE("wo", po_16), PREDEFNODE("word", po_16), PREDEFNODE("16", po_16), PREDEFNODE("be16", po_be16), PREDEFNODE("le16", po_le16), PREDEFNODE("24", po_24), PREDEFNODE("be24", po_be24), PREDEFNODE("le24", po_le24), PREDEFNODE("32", po_32), PREDEFNODE("be32", po_be32), PREDEFNODE("le32", po_le32), PREDEFNODE("h", po_hex), PREDEFNODE("hex", po_hex), PREDEFNODE(s_cbm, obsolete_po_cbm), PREDEFNODE("ct", po_convtab), PREDEFNODE("convtab", po_convtab), PREDEFNODE("tx", po_text), PREDEFNODE("text", po_text), PREDEFNODE(s_raw, po_raw), PREDEFNODE(s_pet, po_pet), PREDEFNODE(s_scr, po_scr), PREDEFNODE(s_scrxor, po_scrxor), PREDEFNODE("bin", po_binary), PREDEFNODE("binary", po_binary), PREDEFNODE("fi", po_fill), PREDEFNODE("fill", po_fill), PREDEFNODE("skip", po_skip), PREDEFNODE("align", po_align), PREDEFNODE("pseudopc", po_pseudopc), PREDEFNODE("realpc", obsolete_po_realpc), PREDEFNODE("cpu", po_cpu), PREDEFNODE("al", po_al), PREDEFNODE("as", po_as), PREDEFNODE(s_rl, po_rl), PREDEFNODE("rs", po_rs), PREDEFNODE("addr", po_address), PREDEFNODE("address", po_address), // PREDEFNODE("enum", po_enum), PREDEFNODE("set", po_set), PREDEFNODE(s_sl, po_symbollist), PREDEFNODE("symbollist", po_symbollist), PREDEFNODE("zn", po_zone), PREDEFNODE(s_zone, po_zone), PREDEFNODE("sz", obsolete_po_subzone), PREDEFNODE(s_subzone, obsolete_po_subzone), PREDEFNODE("src", po_source), PREDEFNODE("source", po_source), PREDEFNODE("if", po_if), PREDEFNODE("ifdef", po_ifdef), PREDEFNODE("ifndef", po_ifndef), PREDEFNODE("for", po_for), PREDEFNODE("do", po_do), // PREDEFNODE("while", po_while), PREDEFNODE("macro", po_macro), // PREDEFNODE("debug", po_debug), // PREDEFNODE("info", po_info), PREDEFNODE("warn", po_warn), PREDEFNODE(s_error, po_error), PREDEFNODE("serious", po_serious), PREDEFNODE("eof", po_endoffile), PREDEFLAST("endoffile", po_endoffile), // ^^^^ this marks the last element }; // register pseudo opcodes and create dynamic buffer void pseudoopcodes_init(void) { user_message = DynaBuf_create(USERMSG_DYNABUF_INITIALSIZE); Tree_add_table(&pseudo_opcode_tree, pseudo_opcode_list); } // parse a pseudo opcode. has to be re-entrant. void pseudoopcode_parse(void) // now GotByte = "!" { void *node_body; enum eos (*fn)(void), then = SKIP_REMAINDER; // prepare for errors GetByte(); // read next byte // on missing keyword, return (complaining will have been done) if (Input_read_and_lower_keyword()) { // search for tree item if ((Tree_easy_scan(pseudo_opcode_tree, &node_body, GlobalDynaBuf)) && node_body) { fn = (enum eos (*)(void)) node_body; SKIPSPACE(); // call function then = fn(); } else { Throw_error("Unknown pseudo opcode."); } } if (then == SKIP_REMAINDER) Input_skip_remainder(); else if (then == ENSURE_EOS) Input_ensure_EOS(); // the other two possibilities (PARSE_REMAINDER and AT_EOS_ANYWAY) // will lead to the remainder of the line being parsed by the mainloop. } acme-crossassembler-0.96.4/src/pseudoopcodes.h000066400000000000000000000007201333777125400213730ustar00rootroot00000000000000// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. // Copyright (C) 1998-2016 Marco Baye // Have a look at "acme.c" for further info // // pseudo opcode stuff #ifndef pseudoopcodes_H #define pseudoopcodes_H // call when "* = EXPRESSION" is parsed extern void notreallypo_setpc(void); // register pseudo opcodes extern void pseudoopcodes_init(void); // parse pseudo opcode. has to be re-entrant. extern void pseudoopcode_parse(void); #endif acme-crossassembler-0.96.4/src/section.c000066400000000000000000000046611333777125400201660ustar00rootroot00000000000000// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. // Copyright (C) 1998-2017 Marco Baye // Have a look at "acme.c" for further info // // section stuff (move to symbol.h?) #include "section.h" #include "config.h" #include "dynabuf.h" #include "global.h" // FIXME - remove when no longer needed #include "input.h" #include "symbol.h" #include "tree.h" #define SCOPE_INCREMENT 2 // inc by 2 so locals are even and cheaps are odd // fake section structure (for error msgs before any real section is in use) static struct section initial_section = { 0, // local scope value 1, // cheap scope value "during", // "type" => normally "zone Title" or "init", // "title" => "macro test", now "during init" FALSE, // no, title was not malloc'd }; // variables struct section *section_now = &initial_section; // current section static struct section outer_section; // outermost section struct static scope_t local_scope_max; // highest scope number yet static scope_t cheap_scope_max; // highest scope number yet // write given info into given structure and activate it void section_new(struct section *section, const char *type, char *title, int allocated) { // new scope for locals local_scope_max += SCOPE_INCREMENT; section->local_scope = local_scope_max; // keep scope for cheap locals section->cheap_scope = section_now->cheap_scope; // copy other data section->type = type; section->title = title; section->allocated = allocated; // activate new section section_now = section; //printf("[new section %d: %s, %s]\n", section->local_scope, section->type, section->title); } // change scope of cheap locals in given section void section_new_cheap_scope(struct section *section) { // new scope for cheap locals cheap_scope_max += SCOPE_INCREMENT; section->cheap_scope = cheap_scope_max; } // Tidy up: If necessary, release section title. // Warning - the state of the component "Allocd" may have // changed in the meantime, so don't rely on a local variable. void section_finalize(struct section *section) { if (section->allocated) free(section->title); } // setup outermost section void section_passinit(void) { //printf("[old maxima: locals=%d, cheap=%d]\n", local_scope_max, cheap_scope_max); local_scope_max = 0; // will be incremented by 2 by next line section_new(&outer_section, s_Zone, s_untitled, FALSE); cheap_scope_max = -1; // will be incremented by 2 by next line section_new_cheap_scope(&outer_section); } acme-crossassembler-0.96.4/src/section.h000066400000000000000000000020671333777125400201710ustar00rootroot00000000000000// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. // Copyright (C) 1998-2017 Marco Baye // Have a look at "acme.c" for further info // // section stuff #ifndef section_H #define section_H #include "config.h" // "section" structure type definition struct section { scope_t local_scope; // section's scope for local symbols scope_t cheap_scope; // section's scope for cheap locals const char *type; // "Zone", "Subzone" or "Macro" char *title; // zone title, subzone title or macro title int allocated; // whether title was malloc()'d }; // current section structure extern struct section *section_now; // write given info into given structure and activate it extern void section_new(struct section *section, const char *type, char *title, int allocated); // change scope of cheap locals in given section extern void section_new_cheap_scope(struct section *section); // setup outermost section extern void section_passinit(void); // tidy up: if necessary, release section title. extern void section_finalize(struct section *section); #endif acme-crossassembler-0.96.4/src/symbol.c000066400000000000000000000216451333777125400200300ustar00rootroot00000000000000// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. // Copyright (C) 1998-2017 Marco Baye // Have a look at "acme.c" for further info // // symbol stuff // // 22 Nov 2007 "warn on indented labels" is now a CLI switch // 25 Sep 2011 Fixed bug in !sl (colons in filename could be interpreted as EOS) // 23 Nov 2014 Added label output in VICE format #include "symbol.h" #include #include "acme.h" #include "alu.h" #include "dynabuf.h" #include "global.h" // FIXME - remove when no longer needed #include "input.h" #include "output.h" #include "platform.h" #include "section.h" #include "tree.h" #include "typesystem.h" // variables struct rwnode *symbols_forest[256] = { NULL }; // because of 8-bit hash - must be (at least partially) pre-defined so array will be zeroed! // Dump symbol value and flags to dump file static void dump_one_symbol(struct rwnode *node, FILE *fd) { struct symbol *symbol = node->body; // output name if (config.warn_on_type_mismatch && symbol->result.addr_refs == 1) fprintf(fd, "!addr"); fprintf(fd, "\t%s", node->id_string); switch (symbol->result.flags & MVALUE_FORCEBITS) { case MVALUE_FORCE16: fprintf(fd, "+2\t= "); break; case MVALUE_FORCE16 | MVALUE_FORCE24: /*FALLTHROUGH*/ case MVALUE_FORCE24: fprintf(fd, "+3\t= "); break; default: fprintf(fd, "\t= "); } if (symbol->result.flags & MVALUE_DEFINED) { if (symbol->result.flags & MVALUE_IS_FP) fprintf(fd, "%.30f", symbol->result.val.fpval); //FIXME %g else fprintf(fd, "$%x", (unsigned) symbol->result.val.intval); } else { fprintf(fd, " ?"); // TODO - maybe write "UNDEFINED" instead? then the file could at least be parsed without errors } if (symbol->result.flags & MVALUE_UNSURE) fprintf(fd, "\t; ?"); // TODO - write "forward" instead? if (symbol->usage == 0) fprintf(fd, "\t; unused"); fprintf(fd, "\n"); } // output symbols in VICE format (example: "al C:09ae .nmi1") static void dump_vice_address(struct rwnode *node, FILE *fd) { struct symbol *symbol = node->body; // dump address symbols even if they are not used if ((symbol->result.flags & MVALUE_DEFINED) && !(symbol->result.flags & MVALUE_IS_FP) && (symbol->result.addr_refs == 1)) fprintf(fd, "al C:%04x .%s\n", (unsigned) symbol->result.val.intval, node->id_string); } static void dump_vice_usednonaddress(struct rwnode *node, FILE *fd) { struct symbol *symbol = node->body; // dump non-addresses that are used if (symbol->usage && (symbol->result.flags & MVALUE_DEFINED) && !(symbol->result.flags & MVALUE_IS_FP) && (symbol->result.addr_refs != 1)) fprintf(fd, "al C:%04x .%s\n", (unsigned) symbol->result.val.intval, node->id_string); } static void dump_vice_unusednonaddress(struct rwnode *node, FILE *fd) { struct symbol *symbol = node->body; // dump non-addresses that are unused if (!symbol->usage && (symbol->result.flags & MVALUE_DEFINED) && !(symbol->result.flags & MVALUE_IS_FP) && (symbol->result.addr_refs != 1)) fprintf(fd, "al C:%04x .%s\n", (unsigned) symbol->result.val.intval, node->id_string); } // search for symbol. create if nonexistant. if created, give it flags "flags". // the symbol name must be held in GlobalDynaBuf. struct symbol *symbol_find(scope_t scope, int flags) { struct rwnode *node; struct symbol *symbol; int node_created, force_bits = flags & MVALUE_FORCEBITS; node_created = Tree_hard_scan(&node, symbols_forest, scope, TRUE); // if node has just been created, create symbol as well if (node_created) { // create new symbol structure symbol = safe_malloc(sizeof(*symbol)); // finish empty symbol item symbol->result.flags = flags; symbol->result.addr_refs = 0; if (flags & MVALUE_IS_FP) symbol->result.val.fpval = 0; else symbol->result.val.intval = 0; symbol->usage = 0; // usage count symbol->pass = pass_count; node->body = symbol; } else { symbol = node->body; } // make sure the force bits don't clash if ((node_created == FALSE) && force_bits) if ((symbol->result.flags & MVALUE_FORCEBITS) != force_bits) Throw_error("Too late for postfix."); return symbol; } // assign value to symbol. the function acts upon the symbol's flag bits and // produces an error if needed. void symbol_set_value(struct symbol *symbol, struct result *new_value, int change_allowed) { int oldflags = symbol->result.flags; // value stuff if ((oldflags & MVALUE_DEFINED) && (change_allowed == FALSE)) { // symbol is already defined, so compare new and old values // if different type OR same type but different value, complain if (((oldflags ^ new_value->flags) & MVALUE_IS_FP) || ((oldflags & MVALUE_IS_FP) ? (symbol->result.val.fpval != new_value->val.fpval) : (symbol->result.val.intval != new_value->val.intval))) Throw_error("Symbol already defined."); } else { // symbol is not defined yet OR redefinitions are allowed symbol->result = *new_value; } // flags stuff // Ensure that "unsure" symbols without "isByte" state don't get that if ((oldflags & (MVALUE_UNSURE | MVALUE_ISBYTE)) == MVALUE_UNSURE) new_value->flags &= ~MVALUE_ISBYTE; if (change_allowed) { oldflags = (oldflags & MVALUE_UNSURE) | new_value->flags; } else { if ((oldflags & MVALUE_FORCEBITS) == 0) if ((oldflags & (MVALUE_UNSURE | MVALUE_DEFINED)) == 0) oldflags |= new_value->flags & MVALUE_FORCEBITS; oldflags |= new_value->flags & ~MVALUE_FORCEBITS; } symbol->result.flags = oldflags; } // parse label definition (can be either global or local). // name must be held in GlobalDynaBuf. void symbol_set_label(scope_t scope, int stat_flags, int force_bit, int change_allowed) { struct result pc, result; struct symbol *symbol; symbol = symbol_find(scope, force_bit); // label definition if ((stat_flags & SF_FOUND_BLANK) && config.warn_on_indented_labels) Throw_first_pass_warning("Label name not in leftmost column."); vcpu_read_pc(&pc); result.flags = pc.flags & MVALUE_DEFINED; result.val.intval = pc.val.intval; result.addr_refs = pc.addr_refs; symbol_set_value(symbol, &result, change_allowed); // global labels must open new scope for cheap locals if (scope == SCOPE_GLOBAL) section_new_cheap_scope(section_now); } // parse symbol definition (can be either global or local, may turn out to be a label). // name must be held in GlobalDynaBuf. void symbol_parse_definition(scope_t scope, int stat_flags) { struct result result; struct symbol *symbol; int force_bit = Input_get_force_bit(); // skips spaces after // FIXME - force bit is allowed for label definitions?! if (GotByte == '=') { // explicit symbol definition (symbol = ) symbol = symbol_find(scope, force_bit); // symbol = parsed value GetByte(); // skip '=' ALU_any_result(&result); // if wanted, mark as address reference if (typesystem_says_address()) result.addr_refs = 1; symbol_set_value(symbol, &result, FALSE); Input_ensure_EOS(); } else { symbol_set_label(scope, stat_flags, force_bit, FALSE); } } // set global symbol to value, no questions asked (for "-D" switch) // Name must be held in GlobalDynaBuf. void symbol_define(intval_t value) { struct result result; struct symbol *symbol; result.flags = MVALUE_GIVEN; result.val.intval = value; symbol = symbol_find(SCOPE_GLOBAL, 0); symbol_set_value(symbol, &result, TRUE); } // dump global symbols to file void symbols_list(FILE *fd) { Tree_dump_forest(symbols_forest, SCOPE_GLOBAL, dump_one_symbol, fd); } void symbols_vicelabels(FILE *fd) { // FIXME - if type checking is enabled, maybe only output addresses? // the order of dumped labels is important because VICE will prefer later defined labels // dump unused labels Tree_dump_forest(symbols_forest, SCOPE_GLOBAL, dump_vice_unusednonaddress, fd); fputc('\n', fd); // dump other used labels Tree_dump_forest(symbols_forest, SCOPE_GLOBAL, dump_vice_usednonaddress, fd); fputc('\n', fd); // dump address symbols Tree_dump_forest(symbols_forest, SCOPE_GLOBAL, dump_vice_address, fd); } // fix name of anonymous forward label (held in DynaBuf, NOT TERMINATED!) so it // references the *next* anonymous forward label definition. The tricky bit is, // each name length would need its own counter. But hey, ACME's real quick in // finding symbols, so I'll just abuse the symbol system to store those counters. void symbol_fix_forward_anon_name(int increment) { struct symbol *counter_symbol; unsigned long number; // terminate name, find "counter" symbol and read value DynaBuf_append(GlobalDynaBuf, '\0'); counter_symbol = symbol_find(section_now->local_scope, 0); // make sure it gets reset to zero in each new pass if (counter_symbol->pass != pass_count) { counter_symbol->pass = pass_count; counter_symbol->result.val.intval = 0; } number = (unsigned long) counter_symbol->result.val.intval; // now append to the name to make it unique GlobalDynaBuf->size--; // forget terminator, we want to append do { DYNABUF_APPEND(GlobalDynaBuf, 'a' + (number & 15)); number >>= 4; } while (number); DynaBuf_append(GlobalDynaBuf, '\0'); if (increment) counter_symbol->result.val.intval++; } acme-crossassembler-0.96.4/src/symbol.h000066400000000000000000000035751333777125400200370ustar00rootroot00000000000000// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. // Copyright (C) 1998-2017 Marco Baye // Have a look at "acme.c" for further info // // symbol stuff #ifndef symbol_H #define symbol_H #include #include "config.h" struct symbol { struct result result; // expression flags and value int usage; // usage count int pass; // pass of creation (for anon counters) // add flag to indicate "has already been reported as undefined" // add file ref + line num of last definition }; // Constants #define SCOPE_GLOBAL 0 // number of "global zone" // variables extern struct rwnode *symbols_forest[]; // trees (because of 8-bit hash) // function acts upon the symbol's flag bits and produces an error if needed. extern void symbol_set_value(struct symbol *symbol, struct result *new_value, int change_allowed); // parse label definition (can be either global or local). // name must be held in GlobalDynaBuf. extern void symbol_set_label(scope_t scope, int stat_flags, int force_bit, int change_allowed); // parse symbol definition (can be either global or local, may turn out to be a label). // name must be held in GlobalDynaBuf. extern void symbol_parse_definition(scope_t scope, int stat_flags); // search for symbol. create if nonexistant. if created, assign flags. // name must be held in GlobalDynaBuf. extern struct symbol *symbol_find(scope_t, int flags); // set global symbol to value, no questions asked (for "-D" switch) // name must be held in GlobalDynaBuf. extern void symbol_define(intval_t value); // dump global symbols to file extern void symbols_list(FILE *fd); // dump global labels to file in VICE format extern void symbols_vicelabels(FILE *fd); // fix name of anonymous forward label (held in GlobalDynaBuf, NOT TERMINATED!) // so it references the *next* anonymous forward label definition. extern void symbol_fix_forward_anon_name(int increment); #endif acme-crossassembler-0.96.4/src/tree.c000066400000000000000000000140621333777125400174550ustar00rootroot00000000000000// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. // Copyright (C) 1998-2016 Marco Baye // Have a look at "acme.c" for further info // // tree stuff #include "tree.h" #include "config.h" #include "dynabuf.h" #include "global.h" #include "platform.h" // Functions // Compute hash value by exclusive ORing the node's ID string and write // output to struct. // This function is not allowed to change GlobalDynaBuf! hash_t make_hash(struct ronode *node) { register char byte; register const char *read; register hash_t tmp = 0; read = node->id_string; while ((byte = *read++)) tmp = ((tmp << 7) | (tmp >> (8 * sizeof(hash_t) - 7))) ^ byte; node->hash_value = tmp; return tmp; } // Link a predefined data set to a tree void add_node_to_tree(struct ronode **tree, struct ronode *node_to_add) { hash_t hash; // compute hash value hash = make_hash(node_to_add); while (*tree) { // compare HashValue if (hash > (*tree)->hash_value) tree = &((*tree)->greater_than); else tree = &((*tree)->less_than_or_equal); } *tree = node_to_add; // add new leaf to tree // New nodes are always added as leaves, so there's no need to copy a second // pointer. And because the PREDEF* macros contain NULL as init values, it is // not necessary to clear the new node's greater_than and less_than_or_equal // fields. } // Add predefined tree items to given tree. The PREDEF* macros set HashValue // to 1 in all entries but the last. The last entry contains 0. void Tree_add_table(struct ronode **tree, struct ronode *table_to_add) { // Caution when trying to optimise this. :) while (table_to_add->hash_value) add_node_to_tree(tree, table_to_add++); add_node_to_tree(tree, table_to_add); } // Search for a given ID string in a given tree. // Compute the hash of the given string and then use that to try to find a // tree item that matches the given data (HashValue and DynaBuf-String). // Store "body" component in node_body and return TRUE. // Return FALSE if no matching item found. int Tree_easy_scan(struct ronode *tree, void **node_body, struct dynabuf *dyna_buf) { struct ronode wanted; // temporary storage const char *p1, *p2; char b1, b2; hash_t hash; wanted.id_string = dyna_buf->buffer; hash = make_hash(&wanted); while (tree) { // compare HashValue if (hash > tree->hash_value) { // wanted hash is bigger than current, so go // to tree branch with bigger hashes tree = tree->greater_than; continue; } if (hash == tree->hash_value) { p1 = wanted.id_string; p2 = tree->id_string; do { b1 = *p1++; b2 = *p2++; } while ((b1 == b2) && b1); if (b1 == b2) { // store body data *node_body = tree->body; return TRUE; } } // either the wanted hash is smaller or // it was exact but didn't match tree = tree->less_than_or_equal; } return FALSE ; // indicate failure } // Search for a "RAM tree" item. Compute the hash of string in GlobalDynaBuf // and then use that to try to find a tree item that matches the given data // (HashValue, ID_Number, GlobalDynaBuf-String). Save pointer to found tree // item in given location. // If no matching item is found, check the "create" flag. If it is set, create // a new tree item, link to tree, fill with data and store its pointer. If the // "create" flag is zero, store NULL as result. // Returns whether item was created. int Tree_hard_scan(struct rwnode **result, struct rwnode **forest, int id_number, int create) { struct ronode wanted; // temporary storage struct rwnode **current_node; struct rwnode *new_leaf_node; const char *p1, *p2; char b1, b2; hash_t byte_hash; wanted.id_string = GLOBALDYNABUF_CURRENT; // incorporate ID number into hash value byte_hash = make_hash(&wanted) ^ id_number; wanted.hash_value = byte_hash; // correct struct's hash PLATFORM_UINT2CHAR(byte_hash); // transform into byte current_node = &(forest[byte_hash]); // point into table while (*current_node) { // compare HashValue if (wanted.hash_value > (*current_node)->hash_value) { // wanted hash is bigger than current, so go // to tree branch with bigger hashes current_node = &((*current_node)->greater_than); continue; } if (wanted.hash_value == (*current_node)->hash_value) { if (id_number == (*current_node)->id_number) { p1 = wanted.id_string; p2 = (*current_node)->id_string; do { b1 = *p1++; b2 = *p2++; } while ((b1 == b2) && b1); if (b1 == b2) { // store node pointer *result = *current_node; // return FALSE because node // was not created return FALSE; } } } // either the wanted hash is smaller or // it was exact but didn't match current_node = &((*current_node)->less_than_or_equal); } // node wasn't found. Check whether to create it if (create == FALSE) { *result = NULL; // indicate failure return FALSE; // return FALSE because node was not created } // create new node new_leaf_node = safe_malloc(sizeof(*new_leaf_node)); new_leaf_node->greater_than = NULL; new_leaf_node->less_than_or_equal = NULL; new_leaf_node->hash_value = wanted.hash_value; new_leaf_node->id_number = id_number; new_leaf_node->id_string = DynaBuf_get_copy(GlobalDynaBuf); // make permanent copy // add new leaf to tree *current_node = new_leaf_node; // store pointer to new node in result location *result = new_leaf_node; return TRUE; // return TRUE because node was created } // Call given function for each object of matching type in the given tree. // Calls itself recursively. void dump_tree(struct rwnode *node, int id_number, void (*fn)(struct rwnode *, FILE *), FILE *env) { if (node->id_number == id_number) fn(node, env); if (node->greater_than) dump_tree(node->greater_than, id_number, fn, env); if (node->less_than_or_equal) dump_tree(node->less_than_or_equal, id_number, fn, env); } // Calls Tree_dump_tree for each non-zero entry of the given tree table. void Tree_dump_forest(struct rwnode **forest, int id_number, void (*fn)(struct rwnode *, FILE *), FILE *env) { int ii; for (ii = 255; ii >= 0; --ii) { if (*forest) dump_tree(*forest, id_number, fn, env); ++forest; } } acme-crossassembler-0.96.4/src/tree.h000066400000000000000000000042071333777125400174620ustar00rootroot00000000000000// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. // Copyright (C) 1998-2016 Marco Baye // Have a look at "acme.c" for further info // // tree stuff #ifndef tree_H #define tree_H #include // for FILE // macros for pre-defining tree node tables #define PREDEFNODE(s, v) {NULL, NULL, 1, s, (void *) (v)} #define PREDEFLAST(s, v) {NULL, NULL, 0, s, (void *) (v)} // type definitions typedef unsigned int hash_t; // must be unsigned, otherwise the hash algorithm won't be very useful! // tree node structure type definition for lookups in "read-only" (i.e. keyword) trees struct ronode { struct ronode *greater_than; // pointer to sub-tree struct ronode *less_than_or_equal; // pointer to sub-tree hash_t hash_value; const char *id_string; // name, zero-terminated void *body; // bytes, handles or handler function }; // tree node structure type definition for "read/write" items, i.e. macros/symbols struct rwnode { struct rwnode *greater_than; // pointer to sub-tree struct rwnode *less_than_or_equal; // pointer to sub-tree hash_t hash_value; char *id_string; // name, zero-terminated void *body; // macro/symbol body unsigned int id_number; // scope number }; // prototypes // Add predefined tree items to given tree. extern void Tree_add_table(struct ronode **tree, struct ronode *table_to_add); // Search for a given ID string in a given tree. Store "body" component in // node_body and return TRUE. Return FALSE if no matching item found. struct dynabuf; extern int Tree_easy_scan(struct ronode *tree, void **node_body, struct dynabuf *dyna_buf); // Search for a "RAM tree" item. Save pointer to found tree item in given // location. If no matching item is found, check the "create" flag: If set, // create new tree item, link to tree, fill with data and store its pointer. // If "create" is zero, store NULL. Returns whether item was created. extern int Tree_hard_scan(struct rwnode **result, struct rwnode **forest, int id_number, int create); // Calls given function for each node of each tree of given forest. extern void Tree_dump_forest(struct rwnode **, int, void (*)(struct rwnode *, FILE *), FILE *); #endif acme-crossassembler-0.96.4/src/typesystem.c000066400000000000000000000024121333777125400207400ustar00rootroot00000000000000// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. // Copyright (C) 1998-2017 Marco Baye // Have a look at "acme.c" for further info // // type system stuff #include "typesystem.h" #include "config.h" #include "alu.h" #include "global.h" static int in_address_block = FALSE; static int in_address_statement = FALSE; // Functions int typesystem_says_address(void) { return in_address_block | in_address_statement; } void typesystem_force_address_block(void) { int buffer = in_address_block; in_address_block = TRUE; Parse_optional_block(); in_address_block = buffer; } void typesystem_force_address_statement(int value) { in_address_statement = value; } void typesystem_want_imm(struct result *result) { if (!config.warn_on_type_mismatch) return; if (!(result->flags & MVALUE_DEFINED)) return; if (result->addr_refs != 0) { Throw_warning("Wrong type - expected integer."); //printf("refcount should be 0, but is %d\n", result->addr_refs); } } void typesystem_want_addr(struct result *result) { if (!config.warn_on_type_mismatch) return; if (!(result->flags & MVALUE_DEFINED)) return; if (result->addr_refs != 1) { Throw_warning("Wrong type - expected address."); //printf("refcount should be 1, but is %d\n", result->addr_refs); } } acme-crossassembler-0.96.4/src/typesystem.h000066400000000000000000000013631333777125400207510ustar00rootroot00000000000000// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. // Copyright (C) 1998-2016 Marco Baye // Have a look at "acme.c" for further info // // Type system stuff #ifndef typesystem_H #define typesystem_H #include "config.h" // return whether explicit symbol definitions should force "address" mode extern int typesystem_says_address(void); // parse a block while forcing address mode extern void typesystem_force_address_block(void); // force address mode on or off for the next statement extern void typesystem_force_address_statement(int value); // warn if result is not integer extern void typesystem_want_imm(struct result *result); // warn if result is not address extern void typesystem_want_addr(struct result *result); #endif acme-crossassembler-0.96.4/src/version.h000066400000000000000000000011051333777125400202020ustar00rootroot00000000000000// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code. // Copyright (C) 1998-2017 Marco Baye // Have a look at "acme.c" for further info // // version info #ifndef version_H #define version_H #define RELEASE "0.96.4" // update before release FIXME #define CODENAME "Fenchurch" // update before release #define CHANGE_DATE "22 Dec" // update before release FIXME #define CHANGE_YEAR "2017" // update before release //#define HOME_PAGE "http://home.pages.de/~mac_bacon/smorbrod/acme/" #define HOME_PAGE "http://sourceforge.net/p/acme-crossass/" // FIXME #endif acme-crossassembler-0.96.4/src/win/000077500000000000000000000000001333777125400171445ustar00rootroot00000000000000acme-crossassembler-0.96.4/src/win/logo.ico000066400000000000000000003554031333777125400206120ustar00rootroot00000000000000(h h    & !  )@@h ^:@@(D@@ (BZr[ W(  @ !I 93wȀ!S{wʐ5t9( ;szS<FzLOlqu K%1:rs M ---///3334&4555666888999:::;;;<<<>>>???@@@ABBBBCCCDDDEEEF >JJJJJKIJMNOOOPPPRSRRRT?{TTTUUOUUUWWWZZZ[[cccff!fffiiijjjnnnooopppqqqrrruvuu9yzzz'zzz{{{|||}}}~~~54 24}"-YA,'>dC.0&3fYC7-+]`np[syWA/-*_~k2_F|{`?..[|QXA_g5u}~L8#PfoMm,IN?Z}|{%KetRdh(o1jv;Olz|wRGs6L_s|bJT >>JJJQ|uuuooorrrZZZCCClqFzM???%iiiff!uu9[<KIJ M VUUU4&4F >zz'Qz;K%[[  RSuv JJsSuMNyzT?{4UUOGQAB245rs}ICL1:LOPP{!(0 D B@ "dU50UݐU0cޠ0@@ _ @  (0/e: N$&'(@C_ddhJxGLOS ! C: !!!!!"""###$$$$:6***+++---...0011123333444445556667778888899-:::;;;;;<<<??4???C3DDDEEEFFFGGGIIIJJJKKKNONNNNOJOOOOOPPPQQQRRRS'{STTTTTUHUFUUUVWVVVWWWXXXZZZ\\\^^^____\bbbdddeffffggghhhkkknnnoooqrBqrqrrrrrssstttuvuuuwwwyyyzzz{{{{{|||}}}̆)^K" S@GuY{J@d;)/)rQ);cQ訙OZU}T=zoX4@/V¸ۯ_{S|?t̩dj令VSLfbSqT||N׏P]V׋s˖sÆ͓pӴƧMwJh͊ka?‘؟؟[m؞׻mך4#$x`g&!/*EaH %<,j:n> IB6C\^eK 190Dvi 78ѢWlA392F-.(~'5+"Ry _ @  (0 H TTT- KKK+++PPPpV 777TTTUUU "gggGGGgggXXX TTT666~J wwwrrrrrr---###RRR~"""l OOODDDfff***VVV UUU&$$$333WWWPPP!!!QQQJJJsssO777;;;UUU|||$$$rrr!!!TTTqqq[888555!!!FFF___"""XXX3pppRQQQQQQkkkWWWddd111TTT$$$Wwwwˡ^^^IIIoooIIIVVVzzzdddhhhEEEUxxxbbbtttꚚNNNggg:::Ejjjwwwkhhh\\\<<<444fffyyywww|&&&HHHjmmm~{{{ʣwww...???nnnuuu{ayyyZZZ}}}쬬???rrs{ADFiiiyyyh𘘘NOJ4499-j/:_d$& 444wwwǏqrq(OS̆JWVW";;;uvC3Se N_\ss  0023UH886' ! NOVW;;UFSO{{K$:6)??4 1~ "S C:qrBHx;S'{GLdh!!TTef^OO%HJ5>@C~G?DDN) ( @ ڪ ٙ ڪ ` "d""$ P̀"UU  !U3w ̠" UP "UP `US `Uv```?7#??7#`@  ( @4F   ',12=@[I    #    ! !!""" "$$$$$%)%&&&&&***---..0,,000111444666667787888899:::;<<<BBBDDDHHHIHJJJNNNQQQUUUWWWYYY]]]ccceeeeeggiiilllnnnppprrruuuxxxxxz{{{|||~~~*  FVVVebLVyJQVũyLV]ʯ|~TZJCXؽ|V__XRCjؽX~rQQQTŨٴ_RLXRCTƩ˫bnXQQQDz˷VRV]V䵤nVƪegXTƿLbظXVnjrgXrVղeeZƾeƻbÞbgƬeZl_Dz]ƿǘQôb|Ǿb~]rǻ|ǺǿmQl~ǻmƽeXƻgƵbǸg13ƼåTQ5-Ƿy4! #2/X|DW\ޱ .0,b{L Y؜khHG(UaPut:  )F}N+*;6BB[ov?HE'Bdp AH=^swqcl>HH7IOf%򓓓ooo ***nnn"""eee;YYY2 ccc}}}~~~666QQQooommm iiiBl3333 卍mmmfff eeeCWWWyyyuuuܣ{{{===666<<=? IABBC C9DDDEKEFDJJKKFKLKKKNQSRSfSST[Z\\\\\^_,bbdddjlmmmmpqsssyyzz{{{}}VI! !ܽl *} >%ttjoojjtdttddodj˼odoƺod~etøoetjtdø~ojteo˩eeotjedÅ`totoj~ש~ejtdjeojeư˴dtojdjdǰ˴~djjjtjưˮjojojjtj~ưۺjojj`tjeưдetúojdojd~~ǸǺǰ``jjjtoǸƼǰƺ~jej~oøôǰoojejootoຩƢo~tîjooeooüǰjeǴ~ooejoǴo~d~deojjoǴoeetƼte׮ejoǸtee~otàoeeƮeooǍd•eoojj`״dǼtdǼƼd~ëǺǦdt©ۼ~~Ƽ~eoǰddoǫð~joǼojotoǩиtƺƀj«eeǫjt~ǩjejtư~jjjÅǦƦ`toojoeǢjdüjotǢé~ǸejjjtǢǼjtǠǫeddtǢƺeeƠǼ d4<<;ƺǺ3deb?9 ,HHH5k~nn뷙& 1CH7|V"t`{rgdd +@2ތ]W$uwg `ʯmx]]]\.``wbb I]]]]]]]]_Lwhnugbba "S^l]V"$S]]Y)`h`wn̾}Q]]]]]ULP$hurS]]]]]O{ͽ`rr\]]]]Qwhiצ Y]]]]Mhhp Z]]]][ؗfB}}t[]]]]]UhTDR]]]]]]RqEyN^]]]][`ណ`sHcEagKX]]]Xu܉HHH-0==6vg(KJazHHG#ae'AF0bbbba*/? ?    ?  ?????<<0?00000<<(@ 0fffggg:*92egggggg; .. "hhhggg ;() BBBhhhggg0$ MMMnnnnnn555ffffff#222F"'''ooo555dddeee]fffkkk .. ****ooo555cccfff___iiiy(I  < ooo444uuuxxx  \\\gggW v"mmm```3aaauuuooo555###111ooojjj]]]fffM*!<< @j "ccc{{{555oooooobbbcccfffkkkiii=(*82%%%eee|||ooo$$$QQQoooooo\\\ uuuxxx... hhhggg0+ &;;;ooooooYYY,,,oooiii@@@ffffff#<< ..  111aaajjj}}}[[[  nnnoooooocccfffkkkiii=.V """oooqqqwwwjjjPPP]]]ooooooooouuuxxxhhhggg0]ttt '''qqqqqqooooooooojjj kkkQQQffffff#666PPP*"###!!!vvvooosssooommm)))fffSSSooolll dddeeeYykkkkkk=||||||*xxxaaaWWWtttooowwwbbbaaaxxxyyy000%%%ooooooooo cccfff kkkiii<uuuvvv=qqq߈uuuwwwGGGgggoooooollluuuxxxhhhggg4vvvwww>>888:uuuvvv=rrrڜKKK}}}LLLDDDRRR"""uuuooo)))000ooo::::wwwxxx*wwwqqq:::>>>lllRRRbbbpppkkk((("""hhhooo$$$:uuuzzz"spppooo!!! KKK'''NNNdddttt???rrrooo!!!:||| }}}lllfffooo|||888EEE (((]]]BBBnnnzzz< ===l***⚚cccoooppp)))vvv cccrrr ???\\\=ppp "%%%VVV~~~sssoooyyyppp dddxxx :::000**=UUU<|||4uuu0|||vsssooosss EEEDDD iii333666aaa111"TTTzzzsss!}}}mmm[[[cccrrrbbbpppBBB  ^^^ !<<#[///vvvqqqoooJLJ @:9nWko676['j\\\nnnzzzppp @:={.-.~c`cWSWEKE - ^;;;bbb$$$67KKG999):(]"yy#```vvvh121&&67  :2bg;;'6\!'+- ]:{~}}HH66GL %(*'':-[NNVr$$^66 (+LLX:88\\"1  ,1pq""*:)D T# lm-1%,}^_,8:k{'',12<  >< 25bbbh#=,^Y'f  j<8BC +*]-01FD4/ C9bb ""--LLF=&( 8-l<&-+!I! )<*=<:::94j`,.[Z R;NV? I>>Sf2.*>=0A/6B?W[zz *!,?89kQS! g^ //(IIAB|"-!+-W[  t*=<::<=- !j13RU )@@) )=<::::::<=*88??????PNG  IHDR\rfIDATxi6 ͓unffd4UT-c4mQ$GpX(jMsP(l @`c ;Pey<5e_T`s_T$wy{Oqt 6ҋ PۮvjRp:PۯbGZ}R*%TlsF~Cϲ P" @1n{R-},{دթ-ڐUVM8ȯwP`u{lhrͧߟLD *;,}>zUFy>O; (-3LJ u12<ۇTYDo}? !It#?Nşm&/A+[X_]S񿖗6Pc,OQן? [a(~  OKGzQFg3`<3?ɋR52įZ~pTHBs.Bc3)v:@~{u#k}?c O G[*r8mY $@agydtUP xS@gO</vz9>Y#ӖDׯ?'p؉vo?'?Y/r}3{#ϏA=9@%}[j^e2 7gz$G de,pﵧyZUMYfu1 pT?}^_ b81MVcᰒkK oa5~Bp/hm.wxٌtOݕRğ`A`x$M!|'/_o5H3/֥=TwTO8D @ l D ]v'!60@:@0wZ? .|d?N%.n>zJ 롢c93.t/p]0JqhI*,ی%8x6 .7G_y|Ǖ׷^' ;xazw5\> {wҖ*s7[l@'G 4USx*W; -W§zоՒT\Tkm/>Sӆ9n/PB3(h9^'0>@g V[~ZZ82%&~g]g]@g;XNY _:6V'׻gK>Z`+9=A.h#`苊6nۛm, \JGg\KHRĿ 8|Np 5\Lm`+WkoȽ86T!a-8G*j-OL` xKOZo)E­ǰs`@VW[@ہ.n-ۋֆr-o{.nvE@PU4]}M`*)@Ԅ BGMuw"&M@ @r;"D>-P- GtgğE b>anſ#>L ߱r?  A' e]x?Wu} |Fy3#_# ,J6+KG"3nt$#N}Cy. |:KG<I@0CkVvSzLJUCi=.ӂ.\|Vϰ?,[]_ʚx~> WT6=fYaM4WJ; y{5za3`{,ů=opgW@hyPAGḣgF@ 9?w>[-~0 05Az_'/s۩j^!0} ovޖL$~nqķx g_ `R#"=bv^< @/񇌌 p<=2BEB?Ll=@ LV][!e?4t¼Q!Pf%"ſ_4ΒuH摳j f@QkQ} `w 뤝Vlju/@#xX`u/ Y<)p H_=3 F x4|Ky`Lu/*S`PV 복3`9" 0$\C[3jq{Wz?R5@D).v13FoiO9A*_e"յ`GH,`4X/`f.]pv&\m1X,mgB 48!\D O]-׆U=/` x[Mgj:8Po5UY߰#:,ztcٜsw:jwl~/O;s x ?Kj_Sg;8б,n)wW3HFʱ;(yjs4Kşq>ʹ#[].- GFsnEJQs{sl3gM‘W ɬ7+lXct Wtcv۟އQ+X7G+@GYJA@T$S IX Ex|M1KyKĞ64󎦋%&FŨ"/ XC@+4k,?%tG4+F8B[;fFըO.tw^@H ˻XoQ_([67F}* t'`98s8.ޓx ާgH 3嵚R{@ѾZiGN, !~;u: jT^4jwx +1HB55u? e]-sVlYyw=QzB.K~^z5 eo6@\yΨIѬ,|Ǣ6G<\w.u:JN= C!=C&]F!Sz@译WF(M@44F6R>Z+Jкqߟ\@Q&<(9~R<>UHp=8ξEVЄE>),0㈟=$0P:x wg\寳u6!]w~f"Z.Ws#څrY8.u n54`h'|v}p\k!X{H* ^hVNc8,$`.ΈK◬k4ܺ5lP,\#r5䖡Ľ wika=k K5Ϟ ^o%|I5Ky'~ 6_Iݙj OJ8zq? _@B zr#` 0j<=@[lbwCm.[zҕRq9MR3+8۸}fq=y2TlaXcْdZw2֩@@&~c뱓@$>Հ߿o $,lj=xFȳ3[ޯp\K4(ژʪ7 ~s{x|6GmB@Q_*PI<~SCdo:H/ ċ2٪X>($PUl޴h_ІnqW`^klࣸuzt0uNw?_5[3#@kOk 0^@B8Bvɝ=35p$յallOv+d TþN4T#YljvRK U@GQy76V p7zY鸨m1+M8@lQ(DzVy8{hB ַvHp Lcon*+(ht-L/` -h@ S:g$ !d1sn@Dl>W\x@-x|T<2hO8:ҸV@KKgha+$#x2@γ' -9;GvqrNSlBcD*b7XeAB3O4`9iqX?C(.0텪4 ϾkZ 0pj4nmw/W5/$n o5–wZR yu=vR_g ,IqCx@04 Mq&Zrdtbop yԷhX{@QfbXZh7gP!GI  7m|ְ@TZ F q ʸ[    ~;8h@`-Au? pAe_H $ hL}@ %`|\ xh/z砷– -p< ^^t\ U  ~>{a3 @CZna/@Y:l`# z?b c6kPv\`b(3B@P@.8P< IENDB`PNG  IHDR\rf[9IDATx]|TU>{`@ + `Wv-ˊ EʮbGQD)+bU&E!^rƛWk3Eb2y==; K4G[>PB 5@(!k,PB`Qh޼ XV+jUlツ A P !DjU+?>DFjѻS Z \k!Rw~(te !ϛBa{B(S/}D_Buon5p}р_mɽ0"t C.u"H(Ofp}ԌF(M(FtOgV@),k~tV]gm-[&u}IMHUvF7F)*L9D?Z3Jb_`9Wǁ Bumf '+ʟ .|9]t%R};>CE&M(aCIתETD ,()P[0yS03_EMӌ|?s {=:ѧ+ @*T*DJԪIT(uJ+2]3iyRo?qZb} ņ#x(V!wrǷ9}F|dߞ/;ڷՁN H w@0iaM C4G EOO'SND;:%}'?O}ԇ񒇩ͯQG8?ꔽ}nzl&Zml@߁э׸P=)]Z-F(~T)+.*nMJ95@;jO|j5ٟS{\7e?t>Tnw# gjT)B : 8NHZ=˵y;f@<虓C(@R)9<l\wJg eAAzLmY)~մ6~TrT@\/pDM)gP/>&@ve:ni[7:aMX>5WO3{}#O7`' >r46z =tAvw{//5_Ckiw*ັgTպ J؆iH(@W.%tG9 s*;} {UqR?UPfFU?pge'.)uߨq5*.=Tovk<` 7@-v`pP0A{Z`ʛ4cRj{yL0|0h _7A T& =`q>^G3(=`}E+hYѬxvܝ a#e,YJ9]& B f&+e`Sk`We;Ex] 3S^~n`vvګq)*t22D/Toz|IwiY[R*]:;ӎ~ U lA 4bgRЋ΢FFMW-MՄrG1DX#F^]< xBmաcTVeY+W0Œ~H˗ ߧI,0\vlDٹ{?(XK9K#K *FUgOj_h9|UHZ<#npJgs}+ձ;iaҹځ18ZME^TbX27n)GbWng/~ICZ~ROAJ eb -"u:2`4HDUdzZT5veaܧx&>^h ҫ s"o=&]{uq {8Rz[?.eB=+a 5+Um)+Վ2X=~F823 6mQX U*WbpYqMr{& PV0ڄΰ%82Tb#jCL=Y{ rmodr 4 1`r=y][׈[ξʰ PtWs+Կ Tp;` M=XܣH@Fnʯ3Q5w`lAA_XriAm4[ B}vѮ#g{XAR4DA?VnB3L#A+ጀi,@L@NPiVմ`o$+}p`mrjK>fZ{*票yJ@MU b|CI<+J*E;\%-770))yN6mʉ*KWW\ \&n GJ5(b F07y,߆L?ښz.sj}ZRm4؊c "qLM#P8Ji37aڵԵEocuʳu*7G̳Zj1`N\)G@b(f.9Än Y$v&~KO}cg`t+4Q\6NQ?J: $;vw304JǒiNJ=XLnG}u0| }hnH-sݓc_͡ʩ{H\Ap 1ڋ*lOp$Pԭ 7_-Y5f ch1cJ#]pIzpI1,[`VBH"tc|{Nh&dVJdQФ&y PrSy@)-?_^IIJlӓRǦXQ҂\@N; Sw)Y´̒@fvkȑ#ƿ?vRv,}{%~ \&}ݺ|Y-,=U@*TFEܬ7 2{>EwS37Anb5V>;T!LUJuӖMh^̤= D>X%%=D',:ǓRq @xK#x@OpS|<#S>gӝ^N:v4#I00:*`8`S0P. hߔ_c-A!vGQ";͂Nfzl:.E%P ),7FqY4UzC`>=AUqVͻ҈5w^kq9h|}ўt ˓Rk6'h?@%f7PӒkע.B76Zht|Xo994;:E";+"IncB xLhr`@ϼH? ' w'}ha ۭM.Π+H[ތ^^4ҔN,q*a4? .x&1C-["llĨIȩuO G9BcDPYӜhs9Mݣ-0FMWVEffԆvrk(P"0EvMA2_yiD.0Z2@\?n`! zD%'*ϟm'x dddiFzk@@%XLNK]JinkJtYTZgIF`$m̤uL%>ʯ2 -]RR锎1O~D}ofrD\nA`T꽶,Dq:$N)R@2?:g .gyƍbTfezc^5+r>ȡhwi+B.SF]m 1K`e캩=Ƒߴb_!$f)'{  8|qDGZMnJ)i5NѬP>=MSPn~i^o[(~IFX}}d/=+hQJJa]-vAoDʬBG?G4pG:wr_rMEYr0 ~Pd_]aT b߁K77B]x!ċ/г'5WmJy2nڙ)ʰPG6T./) v5]xd7`Z#>spuߓ@%'5Hszj? +@F2w~Ęˌo]Nћ SX>bf 4F嫕UJ5?҂ \er  -' L&Htd$YG^Eҙt! zKb 7=;<ʸ"U) {n\ژo8~fgъо>g?xe9R>(}Z,=z KSL\X|͙?gsv6cUґ@bycx( Ϗ< xz#Ղ1,r),R圏? zW.?=G3Lmbٴi-amڸ)/s>z, +Rd4pU۷m Oo6hIp-P ,v)>݄<7xTB\%'GpI, @Iīk7 |ԣGڽc}0e $87SoeAuM0; :(dSOjr E,pܼU5VEHT ԩS5iNͤTN j:~Ϟe[&TYro Em,5DI7k,V@@j(+M.hЁ}Cx¸ܷ8@^n:%$xi&գ; (E n]˖Ѿ{jjT}{"\1߿_'s#h=#)4֢->@5go71H/0" ToDF@Wv;y#޽D5~[\?`ucyjjLO?*H> } _Pv!oFCGUف}DO<]w"Tr-5'Iy-prH,pUgSFaڷiC˕Hª3ŋ9Х+EWM?!XO RONj$VA}`^Gd/?T50P zV~O2 /ڹ2|CӧMM гo]Ol/^g@[P 8.}(|.`gwzRf5L2%i_h׳];ǔӱ/ kwOD '\z_5`w-[VJ%ҨyŊ$>Xn}駴f~4w]N֦S4!So?nS-TL~>MV C3~T;oLTy1Cw*+ߎ;{._۳#WJ*wlLq7d f͚u x֝Eaw%pED(F?ujrloAӾtnb$BuBkD\#,i ]?I5hDGb.#F>~vNd&m`_u`Mw⵴x|ZJTBT(^ \4.HDTNV\`|nfOɡAvyrxX~BF.Joݪ^a!͜31Ia {N^чƼzp"S|`C{|G-@~\De?G{H~> [V 6oެ M|JCz'K/>q}jܼyB-qYi!~d!Kf[>9I׳_f ?FtΙw1D/(wNAT_Hm<0صeKcŠ6pDmeT vS=/^CNYQ߹vLm`n}:qc_n-.%v|;۴%hB=P gAO#/^}_oݣEw:@k/׊|u_n3Y۶V,YG0k'-7SV]-7 |頸{Y"r8An fM;i96ی6]̡{XINCⳠ~J`e棄\rLDd.ۤ VL;>ϥK_YBnclq0dnH3qjk4_#/Ӎv)/^}&L͵I9J*R*唕`ѪvKJk(:T~ޕ(q_O;6o2THwq/,D2G ¤^!A'ǿRlZ&͝9ƽװa !GϏW/(pϻ?7 Zw%_ї hƹ}Zm8U{-5Kڮ;]/\'TyJW_}E֭/.@ {Ox$5mMFs\ [)v: :4KA`w˵F1d_!$b"'sQ ,57\aۤA@i#A(?ZnJ200W 9{l ,7$Ͻhp`Ǟ|cY̭WjU*!܃ s~YyGqW;bZ83ρ2-Nm=ٔG;iA܆7 @}ڳon7/}SڜX.m*?NsqӐbܫ-[RVL82nܸBw!$\ b^ds|ge&i<sjS NhL;~MIW^5ri&Zʴ].q[ kJQ$"2XF|ց6wr#[n@m+\N+U3~x^HXV<yv2*1O;c!(B4]` q ;Z0_eL^z'U^y׮cMt}v23dVi*y̕3*[lF ={Vcr=afu֨j*K%*dST d CպbPdHqT~'8 E,*V_er>v̍1e(=gu#䡟|b1thZ{~Yo74 P 9mOBɓ闭4@ȨGUI7a+1E(ZU6mV'S>?W\o޼~޸KOwnĉ4vvk4K p-\zC'wޡƾL[MC5`֓w}OmX3!)>,F:Ұ^GQu؇_VEhqmyZyC_ 6܁Ԥɮ&YnׅIF;6i*4 G^r}-MYx46 #3_}2Zae JƯwjps ѱuÅz}v LA,!bl&E`#EЎ˕g WVaa#j +`uWi|;2\ &%QNZ-0 ];/KH܏ig@e4G/ԯվ}9QUh53vap&\"}\r۠{ou^~bip*-gvFM[ Jw. ~Dž:\g|㍎[&Mp1А.3J +x'Tc򛺦՞|VztgO?Mp'npSRH|\smu]>s}c&SAhׇcO_H')?\4r,|7|ñ:rs,V×-(?%HhѢŘDA ~|4 GlZ$%h `z h`Ry; p-1`RЫ^S5sWفBN[rE<~x1ytV~Us߮GrпOJvaW}C 1bFR^W(v<ʆ#]*>Rz=yo|Nk(~0qӄ6VF'Y)OZMbZòFO?t-8Dn Sw.nm7k|wW;>oK -q>z!so}zŃ\A`!EϑՇ:1Cfb_h>ԡCg e"hŇuq;pTEO@Ѵqz![p! 1Y?;vc5PEJS>燥KSmnt:aAdy&NytA5il@4@ɂ`[ZUV5*p>wpJq9Q \\΂+ƨN$les&x+A_Eٷ균r`RUcw* >H;x4; ddAb;Sf|U~mQ(FVaԨ\؃r.btȄiw/2$_[GfQ~|MQoW^:fbDKdqϫʬ:0m6nqXuv-H`sG}kot,:v\yWe("uX|@o} oԩSS&F`=5^u4} 9trES^g}>}Tٗ I]vb|ѬTW@Ze5s OA?tW+7*!aԭY96x/< T,jOfݷ/v*5LJeC#Qb+jW_x--^6_۴b?[Ύ2X+S". "o[ZPK.u̳'0s@6T@ pĚ*vwvxvTb hʮ$; ?r!M=W-G<_QҌ)3\V,h ^7s #cp(sΧMbkIsI>aoV;`0 P6 zD"4rSg `P~9CytYl;v+%3sЧMZrudU*$Q;V] Be8nEkQ=7;>"ڄU&q&8۬g[U UczZ|ztHHxҬlۮl 3w<%@IӐ룂PO8)ګ4DCq\3@ЏS}&nxFJ ͛4 p@"|YeGafڙl]*}]؅1$[&";wE`e[ 1. qN>%ʯ᠋E['EAX9-VW ~vyJn<7䝔A.Dy>naGhD< nsM o4#>0c ׳2q,4Y*)nhO^p.*)*od77M ar(AQ;3/ʯi>hX0yg 2%By `pڇIA˻7>ǪȎrޛ;bq_E8 A7lؠYƲ˧D]9++ R8 IC|2; 7ܪV6=ez 0KQl h)Z2<͸\NOvld0 :4):^AAA%,SG&7SV] @J@}X k!U~f~4dmŗה>WD3$ MEn3IAfP,r}i{[qP~<_VS xE;AxіM,e pvA  `fK;k@>vл-؋~$ r)cYz_GTs-slLx1H&+?&o.ۏ…mmő,8`ōrMͲմ_A>M,Rlj$ @g!3"h6]t`a(mS|;hj$1Z(cn?ògϞ>tfñuGJvښ ;gqkߞ&hzLzcј\8AOq_pێo5h<_XMa mH#Uw|~`iP?a5 ?-j@l P-(3I;CnA:IN7ꩱ&z-_~yfIk[",s|> p^&&>jRݟ~3" 펨g a-43V^Via@mJPuV:k/$(fs3Ep׉?rǣ J\`tDbL Y2#ʎusѨX0U}YA~2[8sMƍ 5_@̫X-Eg[ Pjcjڬmz[P|=+xb-Nyb胀\b5mߨsNt~Feר~Q!8馄wn,#|NڹAFtyHvuE ʄ"F\7 \P''s |[/% 7n(gEq? %@{o3dА36@rLܤIKT AQ8"`Z9#ᔘ땃h,02 Lo1 t;Hn HVbG(y/"{~Cc^lMۛhAs8AN&"B:0pO?&M!sC>mv|N-Dp;1oQ#"g qpCjF0cƌqļKS53,(s>f`t"eJ;ur)4߭ ~FCI xOj戠9]nggKd VA>Nמr=Eb&g4-FϽ]j 0UPATу:vP04,B„\DV**7HbAbҍy*;"@sߍpDs윆 2/%p_/Z JCArpD|N|}7i vB1𰪘 %e%S |r*Jj0/݃$mL;Pe?,3Eu.o~~*/WaWb#pVM7EHD$=ܰw_ѢYޟg?8XfA)wi?75Pc(d$(ĀXM}- 3e+ɇ :X$UV .BQ'{?Ga~XٲD pUm߫4w03GU,ڀE(+JPB=S_hExe;C&A!>~y)bL@ F(&iƏ`<2{O煗duWQ~nt"Ӕa`*>:>}|ogpq^.XQ^)U|>X1:H OhgDs1 @4\$fAU^,=G”nl15|v# $-@`lϻЃFwZ9e2(i.xp[7UxΕ2;Vz0#rKLvEel[?S$@*pJ!G1i>gyz (v9dY=}6=SU[,SPsc[SDjSk@fFve_nLb 4D= lyX$Y$QJ/rsSE;yS!FLR_~E2@ė7zP#l5 qqRs\l}`t*~P Xo񆥏ǩK_jqqT|cEt[t[h C%RFiaÆٞxKeGp<Ǖ 9N=d 0-2 Y7Rrl j%vcoN0[BJA)7 6ڶkW8ƿ)x7bU;bGJ[nwFZCq_8Pʴ[V `z><zQ?'oYȻDq/thUny`>FfLn@UiNAU뗯1450p8\cXyt^}]ic[>$eBl_pv5D ,=k߶]񶡆gVo8ƭ7 3z/#xW`ep&ăXZsնsIRsΦzammSx5㏸>I_[$~j՞,yzؽK~nLNh={|z6C7Ud@QRbP/r `s߬F;t)-O?- T`73 @顱/>Xf%a*TŻf׷LZ`Xh*n0M!P`t[*\2lL?>Fb}*Uo3 ;͂jFt/b1n@*|\Ip7s< ݍ0]" }̧hE{l3h F6M<\J}x]IU`].1}q]8*<}OK cԨI]HJ_J Ϗ͘z{3A ׮c;~|G# ?X@cFJ8(UF_2 ၕoZ$T\#7C |8gN]]O<7P}/ s9쯄[%p3nRFRe GdH.0*wa6ݘŌ IpU!>I}<ք]ɰ$"H$#Ti*=l#M{~|'j92߉Q P||6ǝ.f4J7rd)[=p 4f&HM6-j%_PSUeysS*E7fbg]Ű @89 % yd::\y~3 x!v@i`A?UVuFbUk' 0h`Z4`SN5q#[3|G>}Fx)>R8 -[,mn|4TM~S_Tq"%XV =o_Td\3Jo>Y=]APCߪQ(ՂIMIUЂê)TQ+3Yp@3K Y8HMTnD6|6;ƴƮ Or\UN>0ӣZhԸQ.B{niڎm}pj$~QaW<4cZeH0qG1+gO7\CI@ہyEt z췡 i@3+nlg.2c8r{i6K=6r+U| ₍+0ۉ'_I*xh%klC|f~w]}`ܯK0<7n<F+Zn=X)nH+Rd%<3?U Cn hs2v~(I#3'*v`dӧ!'sWPnFz q^(Ю>ZP>|~#+ Lf=Es~olC ҏxF {Jvn6M*Pvm[iDv<pÎ^TZwRx/QO(W%nj)3 w0LK?~ ۥy]3`|-@bX&#SAȽXHcxDJj+ʕfb5 ^񹯾bʪJsATq}K` sI hll~- ȴ坧T}}t (/ygqRa@ ܗx(~<B%Pn Ydf2+>1ݼ$ Ղ Gm53Mk?AA?Pr4C@؅j(9` NS,;@@U[X}r߼Qx?]7^@|H֨Ecb1X55:TA@%"g#RoVqWX'%@ 7> ~B k<B*ʀRLAP!*@`ɦ*=(XTx'@`z QMG 1?UeFJv;9Ī~[p)S,c3*uӎ 3 *R?~`h@처AXSa݀@Pq<|Qg֮kD\`Ʌ fTڈ#'R('`gIJGeҤɖC38E%v.[HjƧʄ>uo,"WS97|ƍD3v8@B$Q'(nv`sZ+Ijf FfPS,fY*jp0,#r4+ 7aϛEu 8&#c,ğ#aZNgGqKp) HĎ2: ￳cW~ /1Fqj5\gA`[{ &(QI/0ʭpJEǔWE aULCY_u]rdGq EeI s* _.v&fc!psڨåv4͸  `>~|XA@!0*@N]'"q95  ̔Mx~" nAH(tZ<7ҼUdؼ4pAnx!=/jdQ"`YqXovmZ H3ᡜVA$߮/I~"E¢Fkwn) h X>3#UōgS I؏_^F#rT-\-M@v (CC;4TDG>0jqgpԏ/ӂ۵93",bqGRF RUY,D"w>3cB|=_u3䛐˵OxNjYN[w1T"b{[6(֬Yt-Q/PQ3 qj@]xoMaBvՆ-vM7_w~g e @fX#Xl!Hs!dPU42lذ0,ݟ˰Í3(5bdF b;Н[6S%A@#O&7] @7nV+* wfÇcHmWjxp'аW5]f%}Vx@ 3QUd!Fb v~(V|?:v!]F R-3W @xfoAI.ʁ|+25Jkק*U2ϯy gtD=;Ku~;@?sg}=Ry CvAEQ`?G ` (%[話9ҏoCS~B1X5HyX_t.@zjf H\RǷ"SZAx]` \¢<?Gb':[S 8Oo׏o,U,N fqPC\qNԆ5*9 5bD1E_ da;ygw;oلt>p0A'ܫ`֏k)Ȫ/,@P qn,(Xȉ 7+Y/a/0ك& `Au}+ KL ſn/h@ Z  [j AE@sY ƙ K 넿n X7`lB @h1=V)*Ayi͛7' DX=L2Jڶ %"LݻcV1qcBA@B!}v-0Cї /@rd3JEaB(r \h @NҀrGԈÄ f@v µ@w83 T9L}k4CA0k) hMoFRJf n(췦}@ZZzPGQ{Y/@@:pP|@fscb΍` znb ^_cءұ ,軇x;M׳BwL! 89q=_}X8%v}.ڈ5@d$ @IqGFET WJ<˞xzgvG% 07B7vVB 8X$`tm IJ<@҈3 %b}%p$T3 % tޝ[wZ50 8-@(!a˕ d[w b*O(!Rb6_ Cq-0@IENDB`PNG  IHDR\rfsIDATx]`~Wջd˽w j PB(!?$@HBR!!i$ z ;lɲl۝խNw8 }Alk[>ZXYh{G-͛qch}hh;V-WwZ'`']?0m< !Jנ@[-ɭG>f/{^I@@VNqLJWFz>Be#O_a^i)r;3='a>~Fs4D`i`&-|_"L7X7y8D,fGAݣŅ^ :AdH׵$+hk,V9?6q'ZZo,F N{Eqk˘up=xУI)fϫM)>`( GbD@:OaO e![7>JC=AB|/U*`8zq^%ࡾ8xz> a},S$!]swW8 uh7vQ8JEΨWץxq!ZC.BK(5 u}x3':E)92"5h1Fe  "JQob~d@1Z5$%Ѫ%hyC'@w'~4AT `Hz@I)s{>/oDkFkwW]4y\S8*O( h q >wk$$c-'({qjb 9B G+L!aJm K1BwCYvqh(ʂ|~?l[]t˱G=geˉ{h:{H$iLz lT*a/F+Rˀjބ{jg]hD(0bU @`??8qV CC`@*;˪ | ٝLZqo7gbcEu屽P_JU+ olq~-sEo&W>i6QݖJ`- GfG 8\ت4UT{P8`k|:Кvy *RVAKpnxs*`2;ͯ%aY+0 {z)c'0WnҒc`,u6]7{U/zTsj ұ*F(RBXR]+al|JG|~f[G؎ nWT}eXfR@RWI~*37Gk!q|6Ͳ}=R/lVhp[:z)#'STe+RSaڃF:Z#Ks &65pn:SHźDR~xx|xdaQ&@$p>gRY 3OwvDeg AOv)AmO\Q P;!?vnU_m@?Uz:IzLΐMךHJi} d$Gi 2uUZy m Z+:3s3@|?z5brT$A7hآJ8lքsؤoR,$|C}h7 <7Ht4 9#l- Dț%:鳐d)^#,@{Cp}-S{ }T)u4fLLlEg9LJH!Zf10p|)gcR)+fc"0͌R6|D (܎!;N>]?}ǃm,K D xb @Uo.N&M3\XQ}ۿMQgBgom݇nfR4]!z7t֐/eɔ_h|+&-hGet˼p ]N8z yd9p4!Эs*WCtH.ē':G009Vαde`~bd,9W`բM/ rӔ5FgҭQw'X(;t=)Ҝ@yE͙%讟IA_:*KR?0S>?4t8E!ՈOOӝhL<˕ g;y@40[Lr#R:0Н@fbmj2,}8m!vELa?P4A[;t? PNzY`*0CU zOlNĕؿ[ Kț9qc \2\j׏߉ ɟOyF;o( `OgG" Q_ f00q6~`3eƖ{ɇ{-nyU?/ŦIA<<0"kwء? RPϴ?|JTJi1ʉ6Yy˭kMǘI=Zut S}B JE4xl-G݃T݋~ QiA Kq`N 3Cvr7*oCGq@ H=A C帽Y;7/eo3!cȴi HyU4AaBlFdC\Q:H$0A1ʨ4zr9]irҔZ hïiONchvlF@n[ rq"n x iF\f#ଣZ;P LűN#K@sJo93!?Mǣ^7v,vit*{à+ˏ4NNb&Ə )s{5N?mDa^8]#;>>%UJ@j7CTnD]JΝkgXy׏*8k)W+cee>gy]=7q։a{{ 赉h疔fu>J3"[>bx,1SnmHjXսRNTZNBsp"-9]kj>V:C.l$Nǜ%iA@Z,AWSYEV*`ΰ Ꮺ<2qJ%vs. 6^Wi?W-Fv8onR |oD>IhM;t@#3"+YШHj0PSmrLnL @S c=\ǡ02DǬ_ qƿPNbҭGQCv$"M*o TLyҁdŖ78 bwe? $W Z@7 23 3ӯZ[l9mpT9e@G^u#4{dm|ɂ-dC뫃 zII3 O"U/?aM-!!:,HKCB?xpA`&`C⫝̸*YѤ v(sW"w|\oN.~#E tZ_{Lwq-)A+seȒqY2yt>Hceg 7~,\~dH`xǏ.([Et) }`- 0|~V5i9}AܻzoE4Asg"Γ)Y2!걉]#9ҀFiiϥ^`q_}+8{~RIf܃۬x lp|NQ ~eiOё 'c^ϻ[lj.GBf} O8#3]$;dh*)tDW^є)5}lUGc40 ϯˣq@98OH;v4"瑴) +1@ ۿ(!|3:내=`G'׺P-ReˎSߞIgϡ=1Zq8l+\>>u Oʿfޙ'Sll}(ES~xZQd?O8%H'ǟ<<^)>I}}IN H݇DصGWTftޮQeҼyJ+A0J0*1>>>- gXG?BA5&h,UҼi겧͓xndE̒@'UGmAcF60lOʆS3\,ߋEK@ds8H e##1>^Og8k@9B{6Qvb /'ٽ%Oə ӁP:D䋼D(DFm#n2/^ H6DG)GVsz޾*FЃ;2S~H>AnC (P/CyG۝;_$}k-E"2t@эe&RG#Btf8C{[Vv)! K)E{}2WRUM˱/S^1]#1>S@p.pDYyծЄm4éݞ*_L&El%2/@q 4_V'D!خ&H@ȅ~}nc,O4K< ,| ! uA a5^Rz8oxIKkG|3Mǐ!:z%Os̅fVbiKձ d4i.G3Lf#nd;~MGiZtjZ @5MLZGXC " N kM8 M<Yڟ*JQ[/a !d*N]6t$ԓOH7%BwU/MH i,+N?{ 'Eێ/;e/`@#Y4d%(RDra,݇cN4n'OXJH ϞDfhּR|U2|I):+WH8̊B1uA]g_sNzg(*Wx] !/jw@]'݊8I4&Jz`XR>53Ϛi!KDPar1z#k iYFПi α}M1vY'x@ +pQF2A @-*8tjaGjIvae؍BWB%hGT@č'@v @`B%!O]/Hu6W0J? n=E.SAiY_#jOw hiZc3` qf[/'kjp c׶ e_U{9:~+7:Ƞ;3^~HaWLr:`6n.NZQ]͊#ڔY鉏 f<6P&u쥴0[Չ}~9Q/B!"c‰qzh i(ٓ=>3S5x #ױ_DxAZŝH^v VJ:L)\.:Kgz;}s(Ѵ~vZ:i͚5dIZs]L^\o=Gjܝ|zkޭ_Ҿ?|E瓕.\2?~WN $Z[a(nnkJ۹Rua٪xد qz1` ! O;}>-fjXh(O†:>-Jgžt bk m-ܫaS`Diʷ?H򪯺J׈pHv9T d=!8q0ԝ^}h6 Xx7W3G13g=ǏQee4q8eṯR8Ng7)D  7Ł/%y ~"\֡2 wcCzP4*%ZNg߉9fAhnF:Hd#ZFyt3=ӫѸ$^#ofKnu:y@P>p4hqGP\B^OBtՅɵ4EQ5ɩ趫N, u?h"9ӯ~|iU"?=Q5XLD!Öe%7R'٥E)h(ƫJM_1#DY,:[˭߇vt;i14]\AJg)}!|N(l. }Q9xe$0@7agyq3c* c>uuy|;~Ote.P 0eM ~wÍdz`+0bN:qVvv9g`c_FV~ 5g5':"k\WW]]]1t(CX a$(W;t:it[:y,yܶb5Mq-~,nXf;onO+g?hwcDW\*``8i @ӟLralO2C`CȇsmF^X.Ҧd83͔ @>8*Gʫ#)P `/ەo@Mtb/2L%P/V^ݪ̈fW*c{^.ʲ]a||qdq0VBPLұt"'ytlsw{ϓ -mE''t,]J[}sH~<CL朦D_oQ1$Xҍ P s`LȶsX#X_Rfz c_CM D/:!F?7IWcԼ :C_%mHLPs]e][Jl{kQ/չ9;_\NڿMB5 cHb& `WĔ,Q.OT Nԭf)nP;e5sfE`x^HTY(&P@ ) ~اaLJpG& ɺK%C:YGW zL}6Z9WC83`d^]0NV/wtk(b}\rfhя%i} sHP8Ρ#J SXВPe C 0[ϔ&٦~BpX~v0أsЩ8Sd\W)S]]ҹB-i1;  Rc؊LGc $lt,WꔝU:_RBdc`mx3Ov!> (^h..gZ3C_b/6Z=O*x"rI;1!8Q̟7y-]]]b zwjNyq"] uuw=nWB?P7zz= 9jk"~3Շ8q.sTsANWE|~_F8 hO%#2vРbhNSma/^ r8m):T);K=H&h1O0:*{"( pg}Bzt_Rh}Lk_Q'Ɩz?@b:_җ}[ö+he?Z7m&D񜛙X ; 0{lg_~cȥhOr@fI.(-bڅY3(Sێ( p0t#bBh1iT(9 `C_6ugGt Lꕦ9pXw7"#_F" ӱ8x*" B]ig(B6mi7iiiҼϨQf%>?0 W&334[ɍ*v}`OZ/Pb+ wg@s DHbt L3k*U Yn2`A`"? 8PsfM1){CHBhUKbӌt [MC!3`͙@t GBv0ÜL6헬b1+߷ ?k~B \`DO96~(c>͸+<#@uju8k"R aUP#  Iy_GPGޡwaM[]g2Q%:$m"&&aq{椠H e+tZ"Wr+K8 ѭ.?ڊLgSJQ`.6 ##c.)Ɣ\Hl鲱x'MF<6yd{._$E\O& µ=[$0#'`^MT}.J{^ѝ$CA^TS M/bf*`Cgi(@(,sp9}qc0Su7.m?@pxY/ |F7 PY1;g0Ժ@R&Wju,TId{36y|>$ݣTTT H(hXseUHX:Ff"2z NRH&AG7"Թy `(Wza%$|[w RO_Ni 2pr9OAGQ|BK]{0tfG\| %+UyDg8F8]CK7p)~7<_yAQŕV `jiV]^ Lx 1D0 {ў p.:1[M}s[ BQ5)8 d+0Td\Mm( vv>] zF їh81kz]oH)VU$x2or3#9`OV8p(yq#$kl~ Q7(OqYڌA@ 7!v̯w1`LҤZ#\a5 -GY|2ǼqpyqI > ̐f x_,L dJ+fo 0;Sv3Bj1Uw2 =|%az; @5WsMpF݊ł3xʯ`u׬Yɓlas^xܕ`2FNX,udǒ[d#.=USa8ǡ' *@4"41͞=JJ'ʌp.Lv.v-Н+Ja#fM;۱M tIꎖ41'h; R >B% R+H{]E.ZD_uqZifl.[Fu}n? 9e8.g͓.p%m r=t39pLw2 2=e)X <c ߂{H669ZSt,q:pa,nb$v%ܗ"agk}t-e+0/qPNebr.~ qP= vۅ=P>)]Npb&3׬!]wSw* `p[p<k-&`f:{Ac~zAGm@&( j!A}l~@j~Ln2Bc_?lc V_Xy=op\L6lMܚR%.rp}%Sq4nw٦$^}GR< 5h?HsPfAfoo&gwY ?^ udݶ@Zq`VnV v=:U6>SҦ0S& 22|JZBwbe$ BHj\ 䡃āi:oMR*( "n"!@ڸXkI'U۞n]LUK5TL!%;- ="Y3emȹ7K`ś!Ƒsl`t:>{Ca>) *E/^GZ 7^M-ZKI|ڂ=fzh5XԀf( Fzp4m^#d*2YC5 fe`?k ` ǑA7F*De}3sJX:]e8'%6LY)uS¿Vq&[381GdÉӿT *UA".z>vB k?N_}oNՅAc[bUpRc.#O5mY^& ihSZUGPZQҜ`9VjQ,)?+)/@m3L-'&~p>-誘x":L$13,}M%WqF MX?j| ~(z+ s8ab%/'+ O8U Ha<3$%7hOjDHmf&CȨ xa?RKy-v lC?qUXnJIضdmAB*%TcE35YOc5})<)M"P]ѕis("/,̉C\&͊]ݖ6iA/d swR|]ā.s8BbUbQtye40frJI1 AUQɌY~5vqlѰ?ASlXTˍ 2og5Q/=h ](VDEb*.|?6Ki\%K+sA;}DM)tvR`+#r~(@r ȥ_jlvKȹ?k -l÷ Fsu y&\~돍8`7\Ov}oo VEK8K_]@yեT=f5yセDz'·۩Uom`jD_~/@85@Z^ Vtv+jx?IǑ<;{%Cp"دWpM>zD'!邁? =`"zhfK;I9R|ve%G>朒hzt})(r(b=@rT G94bTOQҼi_G˫Ә=i:6JftFc^^ Qc>~ry"n, `*0ws?\3&ZwGc:-s#75|Yg^&voͫmu%9gSH%ialD^k!"y!H& )hgyf/q8(SdMyiitࡇїN>>~TXr9썴Fe^7}3W&&ɳ@߇@rµL6? wlX*R t FbT@`@g obA\zPJ8Kvm>o^dWKt3F@;|[ \V@ף5z#pgTc]BSyٸ|XinQybXOb0(e{s9t_<kP;؇K{v;K!g1`$8j tC(uh`B}7 5?L,?w  s.Gtu,2x^ KRTԑU$5t]Mϡ7$Ɠr<=oV`Qh>4;{<#B04]"8F8{><&(Sb,6X+W@`̹t.8y4ߏGiia`e1= 9sT6 g9-/M0ρC8"Y§M%#iU{}b餺Ic˚At I_H %9s);f=M/$3tL]Ot]nqTFE}B{^Co%d:zW?U 8ϱӇq"8Y&NL.#cLJ^`*%etY%W]FƏK㺵kNS=!Er/pbQt? 3;xZn3$w|*j լf-7M .STvR{ >AΧfʼnt8I!][= vה{`/~)DJ1[VL_R-BJ L)RC?ІC?Vʼr!{/l<8t7]ZZ&yZ@ğ{F\ImRV˧;tOog=eSo9ן<ŝ[fc1[fًyt*r3-7oRkwwzEACVFG ˉJ|J((Ë |`dM;.%n2Ep#}/RQa@-[R09%+` 3/+!/S_Zժg  #s8R 'a?NIw1~@XP0𜀼͝N@o1.1 ׅoWk/pC Nk*?j;|W'w/WXKW۫Wk/IģkR5H PK0;NB<9u@ˮ&zlMC'x2`WΡQFvgx? ;hl6+x1nK靪ݻu#@kz"ȈKqQF DJ6W[AJmNM.&**w]C0V<{Cv@`Ou!TfM0hoC~\278X|*$'Le4vo)'ue.]DE_R>Qݨ辥 V] 5hfi^EҒԩSp2D"Ц,C70C|ƛB DWI 0pBAn>em{Ӓ/1X]DJ]LmR)|Sx2,赮LUsqirkK] kd~4U(q5^Jy#z_@v^("w.O6UZ5fO "~Z `Gy$}<&՟6m:Z-Z7<}֬Y⢋.|3 L _PP*7778:0ߏ=}gx":6~x BdFDEaWkpZ :P2d zi7+c<ҹJ})h#pX[Rh Gbs@KUuUGeEewIII> -!eh{ཙ3K  #˅Fz'in& * 3cp8Px<1TZ5~xYS]#Xj2neMzꉧ!~mu-LxDhI6p͜VHOD矚-.:1v<9 O]ݤ\6:>pJ` ]U& !ܫHi|6&+1Q]+m4 ?w̙̔ qD37 _QQAcǎeee' ͭʹ5aG³ֆFd+f rr^GB#49McL1NˑVZa -]^ujޑ3=g. {RIv!.<$/Y~øeO|3pne]8v4 f/HKsYb ozh~oooyOOOMWWXigb0^pW_}87ߤncڟSwg4>8ÏzŕfU}(&CLy=9C&>C34= #Gބ0hժUhҘh!ԋE(>S*Tٲ?EGޑ?=6bQtFsڵk%9<֫ni~==bۖ|s2xr?NL흝 P++#EQl}1oAUUV^^ ]" \Ѫc11 ?/:&U8΢? 814я*Ti6 j~&2뉶gx[Crr\uDi0q ÕvSW{;ߣ Y#TUf@fAm׫5|pENj/6 _߾}{B^ _V sQxotGGGw1f@ 衇27Sz7]~tڶ#U(=쳴qzpA0ҳiO>Yӭd_1_[ X@1QZ|>;r|`\z} :Q#ٻ`Yrf@E#ɋ,vwV>Od r\#0!`񶲍(VcS@& pـe_3f־>#^(ga9/V9 rؼXGɓK8{ﭱ6d`KT*h>v챽ύCZ:v'%[Ξޮ.z`0(YN?øt^78F)@dĉT[SCE@6RU^zO;iS#{L¯S`o@ACٳ'aPtq6Edfm,kn2NY936 wBKs{[=Khs5r0\SGߠ6MPq6`h"+nm 7访6,d)F%QLjU ̀H fpDA)Ɉ9Uca%jjF8|4kVPD>轼{̛}뭷jsJ:sd x||<P; hk{9igK-o j5;7`0p?\AÅ=~#(̀kw>NFQ閳D e}}Š vW_C\+,1:>)^2(DiP0ƧG#s_k`ChQϹ F ZC 3?vX/lo/krCkF鸼Gu>@4i$0a >$d)nN~ m-UOKyYa8᳂_#rbLMYX2B&=w U#J?!DDF &K?{?%D25+!lG:L70;@9i@2uΚcGx`W@Ãx LS,l*x  >kzxNDa5v LRo%đh0Q|7&y2Ou;:^G}t䋓 o`= +~4[+`BϹLbzh|8BIn!yWIDDm!:瞲bM1R%W)蠁9kcR<+T~΅t 20"; 2|UqL >叄 }Ҡ~w2Bio6[JJJn8C0z2gS& K =?@[.L%S=S$*& ;Ym+-zٵZbY>pZ)dz2nfmŊb^ |h";mOc o|#0uTw߭¶B[ ۶Tt*̂uY՜՛c/M`-ςςi&8?tHvx^ve&#bS@ʲjcöo_YֹW3ӵAAOV nz|~ Ty.8v7ToTgkm,^?ٷ|`CCC Пq]wgF)`F@Uc vW-ģ'JLm5NuVmedgA߾};ՙUomʔ)t7S#k܁Zr%mY=]{&嵲ݷߦ5W+hs} e7'5LT>B 0U9.f `_b|{c}WOEēn8gŽ컑v|SOHMMMU c@vttwﳗ0]0L;5=/idyUU)dB*#B(!RTx  (<,TA4yF!PPJ(!9=ם=}ʽ$ϝL枲ZײmrU]OH~> )-<^cʔ)vm}Lk|8#;E@`59' Nω ! ƈ4%ڡcq]d+t J 00=\#x=cՃ:^4˄k?KM}?.DnaĨr4V@1{݁ I J`]BU,?bB ih7ki= bj";Yf@W/_#fy  B=mROρO30? 8d{կ|] !x4\oɫB/}+tX 0|v[*&%%sٟi5w (.jzv=<)CN5ĘtI3}ӟ_[6 `C3xi=z|*rޝL7Y#pslR|s/~2h*|O aU;쐆I Fz-CFr '7 o;GOG' P}g; 0}CgLf 6) !^Q> v&+:o3QE"kV]S"Gw~E y`|z >ʃH}>l6^_K: $D~Т7_@K{  }4t"tL:a)a45#O_?[o* !r'P_=xIImHmʫcHz-eأ>ڛ(R0?4WkT8,B2@οi@@VdML ^gy(5Xv=>8r2{c٨dסh'S%Μ9-oHCPVK`|iCϭǗsu݆ېU?y^OPka*'TO3S¨pzz-L _ό O" wy== у>餓YYQqc& ̻m@;`aa-Z/ı|UEu-8!o+#=mZ;.!=waZmaG `0@3$ w}L?J%*& IYP4\M\K@f|\ ZI>΋Pq:I54/e|S/<|"@ e4䀁<ؠy+re@C|"G#T+Sî C0?9hHA8@uaHгgF?SsF 0/HO+5kO +̘~`84@Ũ CVFSsg ~NB|/{h (H-6=<kGSl" bfyZ-4]@k=N5#κ>;3Z`A:kW}NRr-kZ|ߍ,@ Q~Wq>Q;͸H?&E{t0I^veHI_Vpi uZ;:K3Ά!Uf$Lko-` z>; SOJu]@!L7k֬kP~ԿҚV!>3MsID/wAӯx 4pM7u A F+*B$ӄɿtI'CR*!/N;e믿kv=6}~+[{+̕@0P#':8PewijH6` )PL9U ${FH-[p5LV{lA TaPz?,O:5oxy:0dYO=Et Z@Z/u]ס>D^{!D,;zD LB)/R.#Ʉl}r u.}NgL S!a#j8^Y/b^90{ {Tw cD[oj8hԡ$ZhU@dI$4?3 iyGc0z u=x_pk8 3+CPoӓya#pHfMt9 IfLO[+۹]z!`\,O`bIz/drR/Lk C^ƉF7āJ A@HCcUh.E0)RYTk^ T^:hڸuB`CO)HE { Oߡ<HsŸ6qT݌g<Ӳ T'B@V=>9^zUE+3RQDJ CdwATw}wtWD-*p c}U:s m|FZi`M: @l;دkh2P{+ ae@_j>M4@#'+=gG ˮTŪ 1= ċ: dq&R-3%x3U __Gz]GG3 ڧO#`891e.@Gj NY$T YǩI$,xrb!#0r;t0?H3TM9眓e$>L;@s-d4zZ.Z;4XI*" ;Sw^ʫznB5.~"g6F#y-`97_a/Ăy0 '%k~ 7ߋf+/"A+*4v4~ }hVEl}U Rx|$?@95k(ca  Ds͛W~38# ~% Key Lz|t6(KObfTįa45ضH8Ĥvaɳ TA?x~\f"!(.3P_Bځ@@_,Q,Ŏ&`7*6z|*ʨs &I V&&&YQ$rjΣ*hI%_$A;+0PguB]_r:7(FVEK2s5Y|;i#R7OTys<#b tƠK[f夢o},97 DI扇rJxՉ#D7.#QQgTRBʸͼ * (‰ i &h"j/=B `F0zR W$tw>`0k/rZj6T +VKh=yր@l)d)-ǗMg়`f_-@ ·ڹԚtah'\zwgXj+WՆx: !ݍ`rMkؚ ed!4=y_<@=c*k"S?ثtS`|hd@|0f.=uT v0KEgˋ8Đv#V0>הc!5LcPm%ˢ&_Ք[=I(di-:;:"!ju\_gAsrM'N2=Smua~CMT;+D4-?mٴ[4pt5c9c…"E[E"ǗJBJ6q1+\&H}@ =r5iH򷑎Z]fZ-64ƧJ# )@ME֞vyso̙i »: aq] ,=yfi 7v qE5wcaiBt Qt;K+GE!"fv*6zm.P-JB{A|Y1ifv%VN\!CP8ȅ`d*TY7GqB:(KopXS59F[p9ob`2@HY7Uuڮoy72n햚t]OYV6`kg 9,d0k7`"R탽 a@ J^G@vT!ȵhe0"AQ1[fM,gl(QH !QfS wH"IB8'1m3"50pbֽy:*u3b:RyzY=#aF[vo^$޶&Pl8|mR]lNhu9( > 4-{:EN9y P/ȴHiUʃqp VNZ:a+"."R7s,d+2r+ h0GZ'61=̆4@Uck6 @7M*t [Hz48 >яvFKQGgg, 6Bт`Qd|GXP~`.$ީmnBjDm$*sj_l hイfJ}f4gΓsLO"GydcJUV;Veje*4&4EF/3~34}fp \o9?M^0g52\a1L3 yBSq&Q *ֲf|9dvhIr R.lcub`7.09f Xq6$8P@ Z4Ԓ^U 4 'C>x'sO?<تY5Ӵ8FAT3H!=@B`$224MF 1@ YfȖYXFBr3H6~C:s\&!?z櫍fQK"г$n}AhZhM<|e~|>?YC)G pC|C UaH~>&b4y 0I Z9 H}z(};F7)icM$3HKX/a;뿼1늆{Z٨c(h:`$ Z9`|蓪Q@B(آUSriW>ב\KmuyЍ[ɧ\bѵh,vgUe4e~Br"MQKSMD|m L7wwh@|&L_=gDzd _x&4#Zh43)ͭ`&-t-"\F +59a~-3 h&+}@&yziCzSfw|axxefNJXծĻ9 .Ԑbѫ$J4炙` i|fGGq( % B㹫}ό:מ}V(3WBJkCZE>gɪ̳av"վW)Jߢ5І&&;qٖ؂$|H˿S:!D0b?. !$A)AĪ ^uƢl7ٵƩ &#CE$[gnEѵ)rܘJ9 R֌ 3IGM8ŌR`ǹi6&o25\G(r1Ud8#ɿ7~-ABMX|YF" wTy{ꊚ <0hyرH3~?dQW{M8-`H_40$kJbpN^z H@'~%8}E4N=BҲx6<95-u x6Mн4g|3ͥ?vosS!-#~B$7gaLv]2:hW4^NlXm€gcQ]!Q 4iu Q C[LE\x\ՎvcڨZcm N}m}CgYEUC}Xc/}>.2>Y>O# 0h !j΢PB BLd oJ)-vn8½`| pq0N}|{p_G3ǐ6$T\"`h)ųhC+߱@xs3KȉV5[C_|Μ9iL7p1ǤmRਿM#CgDtT9y3$/& 0O 5 &[yiÁS) ,:G F >lF&hViTMIsl&>*BPIA{S]77,W mSzx,@c-i@ S| AhکC8^C^K] '?w\^p,N oj0I. Y"帏ַ *@C"|ޗ ZIڌd4+[̣1_6bsa2!>5a&Ekn1W$z9> Y* @uΪd5ķ9Aߎ>D%X쏎Rsk0BO|q=nsدTrВZ`HX^:ue ɋ"|jAj= :qM $ǛF'˽Qa~ P @ @9CEFvɫ >x2UnE~'TWE;.YF;'}1dŜ)!tƀDP;`!nDhG GFEy@- Q|A̦QS!ԐԖKtQ;v{q&9s#96. i=PІ vyuEOVD h<(!u(h11|#5Ӥ hOs1ڟ? K}e~5GkEΞ=n~uꩧ&BR,QQr`KSI@@T&0x0|t7LG9/UɯgA5.EhH\j?sP+뭩l\oV] @0m z߅qhCҢ,QKvVC j*_ ?}K7tSB Zex\֢D &ɿ7{Gmh1j+ jKL8_PXQj56:?v.6w=|%ׯ]>8Ю/@@EێSHEY,Χ/| )ٝw}!;^Y?B^%hveBvfNdG H)ƧdașF0oz j V8eӆd4)hL YH5J;΄T< u"dC蟮RPtB/":Ē',#.NG!]|6,6,6#u| Ui8³hĊdfgFlvZ~d)fI}~Em,?dW |niap`_oL@ @W!ę̯qhJdcHD eYAdž ώC4aP3Tq]Q#Ӹ/NY6Ogw<3H4-_U{wBݥ7"/Y zÏm`MU R:jb>$hk| 7}& HWc!:t0}&KhIS[C]OY$Q֨*[T9Km3O*] B^ZGhPG4Qg@W(Lm/{ҘITN@L- (j &fTp*Ĩ>ۼ#YYf{mƾe&"IO6m)e kց&Ii4>~3… c(7"t@~69 9N8!Qުr>Hf߽ FEVfڊU I}}ep)7Go^bPh9W %t2W;8#/b3AkSG3<{w.V h4a{'Ym]oQ`BR˲%zsEi_< 17dDB.o !p=P(R i&{` led}q}$ s=luP>f}lb&aTbAM5 EU4mݶ+Dpl h*/Y$SPʬ3wrf.s$n$#P lמY@ S=i% dBU@-䅯 ͥq2U ꈄ1OEHLbPy Py#ܻ+ZdB;հxY!=[F5X",&C35[@%&aO~ҹW4Y=l?'f&FR~h9+|lwKOIH"gLO@BT[wEZ>k֬L`AyI$t yYQ|PRObV[ѧ؇H ,>F n-H2G*)(i0::PR̯*j$>_4;\B҃iǿ]mNΝ|Lk4E{0$5/]] ]ђz;_;&bs>oaekhl Pw0aBN;ԙqJ$hBA@'6T5m Cøvb#=5=+rOv?(b`L]zW!`)袋{68C?AbReUyb|ez?yׇ)a?cvHX?(8N}ѐva6$ϺB!;Es BH3#pPs0rQ5Ɋۆ[C!|'xbc#<  PI^5qb)e.1NGic *&XED}{gHjuA0TWǜӪѡ |¡A@zd9a}s5m 9?iACP91B p{H BEӿ7M6HeYG4@L*O'de,ZVa`lW`J2 d y]٠7ha5ZQ>'IF (1@DVwFa7@"I*3>b#p1 u?H *PcV p$7oҙ!Zv"!,ovNeKs,o0q/[KGi &:q d`؆0_3d4fDHS6+~H~D}&qc'_:x?LbD&AHy0+ HC2HnGF C4)"M/~񋩤=3?fph]<PBzgcFYM8WZĊYk`kazaXc0-kD7ҷz|LzYR}v44X>ZeCZh@j0K5ZM9 0=8t.t*syvR< @m`V>9Q5ENN; Q肎br@e}D ᩽ɦ *,LE ` qP4Rhгf_XM;2<5?`4hX$r̓_}GHC$B B89}R?N: [x1|Kȹ,&>PADR;4KH`و8y*S5&:E-6]:&P3&cM)?YtH/;/H7ģ{)=į\݌0 q`#xǧ?a2dղu۾@n ^#$.Q@@;j6r}\ 7: ruM727#`վ>z~w X@|rῆ@Ԇl+ 3( ۤ)nuo 1۾̓aaVmEvASul M;r㫍V`Mi(ºV*:ּ.HIξHfFƏCWdiuӂ |/NAI7O5N3;}4!ׇjRb<< rNX0~B"0T5XAg!푒,b^zvן S] !P94$jP9VZvb=FFk"jEʱV~7. po6e-99[땳ChdO3@j F50~Ϩ0; PW[ϟa4Qʹ{y{ g3U y-|RGm#|WD2h;n51| hķ&42Dt8S9Ut][oVEgOa2-:=UɊڠQ5`vcѶ}/02yFYw ͍q8z4ȧhC% q 3@@:rN'0Щ,Z KLk[A[*5|j̏ Wۭ@bw^ښUGљ\TfY:00=(p@eeB6,>БNE@V55exׄB2E'@-cv$ $P=>]x;ëN{xc%h]O'A  wPǟ/|eS?McP@`-ea6FY[~-'(ynBsM M4uli IᢎJ~`VTP;<^&֋>=w'6O.2[@-4erK:\WM ?R^_,0S&xD]fa@M]@S `ICկ~|Fh ғ&$ 3Q +r[QW3/vv[q&gcQ!E}u(沱H W@%$3lE:x~̯ ,;,SrI>EJ[oM{OV=~n?b@`~cy@֥} 4&ey& h7|!83bQOQ]]aJB]@Z*{䪏0Ej@45WpF͇@մ8|\B_OƷ߻ѣLUq|=H ^,iف6^{2Ssz 4\z0L?Yy&((O~k9);,ݲ<0A?aqy~_}XԹEqD&/vGze1#bSvyjnOؤ`UI-|zE+Q"~$7I:YuhaMEG>Ƙ.*sj,7B(aDzv<`G7aBpK AO0a:_+5ua H?TuDyص^;;_ LlСX.N5u!BڥG"k%q-8X<ds,$xrk5k֬H[ic$.ğl>8uЖkE#0~z Zs= ̌}>}YVQ4{o&IV=~Ҩ{ 0ӥƯVGDU8,L p^!},/xrc &Mzy]wTa=xc9& @)Hh6 &U A>CRuYN'}H0N6@){{&6zt֗-"Ŗ ;6i)FCZĮ޼&HO -:ɪGb|9d/-Gv5r``!o, wvm\p0Q(="IW&0ֲ|h׾:&@PGw/|y%^ŞASvaNcq(*FnW ^rɔD `7P'55Hѳwz|hCЄS J‘{eK ~d"#'#ßL'O>Ƣth]D A d;>t,\S[$h"חeN6d;\>񏧱{LxIJJGhqYTb0}᪚DF َ9y5ZGcc:68k P}S.L>mLz|j Tϧuzsc966?#0}<w<,$0zڵZfBp@(B}k7Q.%וs=9S瞵Ao>QOd!b̸ (󇪲xdu׍:-(~␇_eu#Ҝ8h6S_R'_Ura@ NPe!ϡ|/"؇3 _JP1vb$F BОd!]JMjDA^ ],D|l LǸq`~2*o7Շ=̳JI]T;7SOF(װ7O1o!kg;J9`#*ﵩTn;ϻD;~I@G1ſu}[U60=k #"Y: ׎@ 1Cm a: 9ػQ׎ $w~*DTZPHҪ(/sazU3J)$Co =z0. yyNKgY?Cy4!F*萝'{ {xG h Q7j!H#f#c`j³dC>l℀H*F ( j=D$H"G㬡EGi P;-suD6#ϧ]Rh`kSf~ozƄr\TI+l'ɢ'XO> @`eH@TG룁9ˣ@V~_k@`:B8 l/H풴JWi߂l^݄E*"Hg3Tdܹ69}>N;z!B!o6lf+B|sȄ\$4/AL $|Cmf.dVUTalh]\0z8j%W# X 7uh  :6Fm'DvC[ Vb.#{WQgS@^uUcTSI |@@y0s@WqV>o:%n6(,0 'cc$U??LFOf:/ׂ"]yN9c Ɋ  0wS@QqZHK!D62CT;fvu2BC!Ĭ5T< w|d.Ġh93Һ|1fdps=w0DVLY2GPT.E9_ҞSd<\ b;Og[8hyXu߻[wZ7L/P$CQjip5:-terԓke"3 Yk|xm˵`n]c8,Ix΃ք Y>u7O fdh5D<1!+EaӧO/]/ʢE E>5YzWgaIGA30^!iu,*FAJTJvaTG 5CH!>TqpV z@DBIBQtRb'sm$1P_uקRz|@uh7&ދ [W`+I-Ǘu|AYuLr@d=4QcqL0Qͳ-4W_q{Q$@B(GLF5h'pM ``}4ʌYr9x'F31 U=~v&8!b*yWd_g@Z+D!IkT`aaN8dP%'čdS>y)D-e $wwLkp:g$ĈUR x>yQYT]ve5 #A ۔gRN-AF@ $$nY6od)bч1򹃀 4Ch^Вw`d=:a>'_bi͜ `E[d¾e7B}ڕ_5x) `F LQY D28di EzF@!?vfs '7h CTde7g}|{hߊFT껱=U\*D ȹw(2svpL2{W1J'11u`e5 C ,HvBz})\i0/ PX^==kvx0ɡhIx߯ǧQa6;EPNĀϞ&B|XZyQFQ o!P=0Q L a:GqtJ l<xGZX* i7ڬz|\51= 1ٷW:V?0 Q0[ȹNg_@3h%873ĞgJ~4x͜9siӺ8Iڿ!PL2)!c 'uQfiNBE'(ɫ7id@m?RÌaU=)_RǏC!@u9 ,M.3Gu #L`a|6r' 4\ӧ 9tLQ@ڠ$ lV,k$بElx*c_;3Jqy@4xx5<Lz@Cƨz|h!_m !mh670ŀ CKlS:-jzK6 @BmRLjÅ37TzMٵ E]VZ %";@܄gZ8(W3]4c)ROcz`S XU=>6OÍ-o@=2(1&pw@u ~| :8@]s5d0ɼy޼U0L( &rqn 0zCz.@? Ey1Vz6Ttl6 3_U"C0J lnM QS@vr@emO;:i}޻qADf{5hXc45z| !0Qv=Z6BYVʿ0f ]wݝmY0aXS  ~\oɁOOfߞr\V(dwgz|pJjfzi=W_]O4;-Zx5M74& Lbk: %4pZ/Dlho@HλkEz{)B3w^.Rs^lHG h88 qg^ ip½oMdۥį^wuuILndSƦ Q FQv=>>} (dȐɾE ] :G?.D0[B#!6/@@5jHiH$ NLJ2x7gq ̫ǧ^@$bP뮻R1qP%@V=~@5zk ͐k k;ފYͲO|)#Wz6TtčF *T$s~cG1@ײaQ`@~'0eD|盦y\pЀUo: !9cKj샂ٳg'Y |W=7I)|wO?%yFfWz6TtS2g_d &ǍWzЁnHS* `>!31\iC0oV=~X`/ w*R `Μ9IV=03*PO|&SsmY߭;gV4Gfw K X,y0 ]%QFб~Ġ7+ΐ `1Tjj (+8hD@gy?'st1"SUo<͊ 3Osm97g)$8~JC <#rd1 [f 쨿`8Si#Pf41 dܹp7Ơ>oش=5%{&Y #<sT_> 7i#>"<_193e~9bM^oΝ&א] 1sv(е5 W0?ۧeQFz|evD{ERr/2c X.{d;//;mp@k0#[׀zHJhÜ^uӜ.'@Âv=q|R_ǒT\o=>Lgu88Bwmz|K|NEcǧhhȩ0R>%Tweo@PkWi>Յ'i ~ȯ5վ8 TSw^~P0~xl bH_aaȬ7z=^i3jzҘqj .v`!('mf$ NhPL_t] ?u7:! H8Me h=>j0h^zY&H`'8|&pMǵ0(\OI\/SGJrb wwM>dmw韏Vz6[Sda5$%>sf_MC`Rj@()ZT`qzqD3iuE_tQI/xlzRl,7Юo :::&! 5/`N*#:`@rK ih!_zM䞴Խ{j#>V>,1>v=~ްG yGUkum ,4Vɮ)0nP>=gp"dAP3x [zBmx0 wZ[b:(1cF- I@z2Um)S( ġz HcϪG AB:u4E h?&ɬv=~@LT$ p6(FSf5H7QP=~\exP=rϵ住[hOq,W,~.Iv=~VȣHI@\kf2c'{p9 5قv*.n+qKhEiۙ' QhrmX{U60{_ ̛7/hk4$9kM(+묳N&xl_=>68xZ#, sϖg<E9QQ +a' BQf YE5v8Gw${$C} !+@l3+05z:P=I;D6fèXAQ"߽Amy'mXY?!"{=.꼳BSE2ӳT~?)Ld=81lXwuS$,<a@/o0_mX|Fڧy(Z#!L"'ՅW?&*^yMcB6L>@2`X]@_ǣa^) =1^G.l`e=jyBc\]ZD9 Ck&ٖ&H&g~T4xtvzL6-Q -{セ՘Yh=>R˿F:\)p[Ԯo@ H#w>rN̑px៕Rc#Ohf0$8.\{^P`m:f~e^K*jO(J--;6L1W'=9#@}5=kXF4fڙ{n0cpӛ7ه >" 9wi WF X[Tө@}K,qaOX&@Yhv6ئ}6eZ1xÆ #0r̘1]0~QXxqs=GYu֫}@MmݖIENDB`acme-crossassembler-0.96.4/src/win/setRelease.sh000066400000000000000000000025771333777125400216070ustar00rootroot00000000000000#/bin/bash # # Get release and create RC-File # function DEBUG() { [ "$_DEBUG" == "on" ] && $@ } function pause() { read -p "Weiter mit Eingabe" $a } FILE="resource.rc" RELEASE=`grep "define RELEASE" ../version.h | cut -f3` RELEASE=${RELEASE//\"/} DEBUG echo $RELEASE VERSION=${RELEASE//./,},0 DEBUG echo $VERSION CODENAME=`grep "define CODENAME" ../version.h | cut -f2` DEBUG echo $CODENAME FILEVERSION=\""$RELEASE ${CODENAME//\"/}"\" DEBUG echo $FILEVERSION cat << EndOfFile > $FILE // Iconfile (64/32/16) ID ICON "Logo.ico" // Infos for windows 1 VERSIONINFO FILEVERSION $VERSION PRODUCTVERSION $VERSION FILEFLAGSMASK 0x3fL FILEFLAGS 0x0L FILEOS 0x40004L FILETYPE 0x2L FILESUBTYPE 0x0L BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904E4" BEGIN VALUE "CompanyName", "Smrbrd Software" VALUE "FileDescription", "Acme crossassembler" VALUE "FileVersion", $FILEVERSION VALUE "InternalName", "ACME crossassembler" VALUE "LegalCopyright", "Copyright 2015 Marco Baye" VALUE "OriginalFilename", "acme.exe" VALUE "ProductName", "ACME Crossassembler" VALUE "ProductVersion", $FILEVERSION VALUE "PorductLicence","GNU General Public License" VALUE "WindowsPort","Dirk Hpfner hoeppie@gmx.de" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x409, 1252 END END EndOfFile