From: uz Date: Tue, 21 Feb 2012 20:02:20 +0000 (+0000) Subject: Added a macro package for writing self modyfying code. By Christian Krüger. X-Git-Tag: V2.14~527 X-Git-Url: https://git.sur5r.net/?a=commitdiff_plain;h=0d4b9c59f38aabe14ef3bb1ede2190b64c651db1;p=cc65 Added a macro package for writing self modyfying code. By Christian Krüger. git-svn-id: svn://svn.cc65.org/cc65/trunk@5536 b7a2c559-68d2-44c3-8de9-860c34a00d81 --- diff --git a/asminc/opcodes.inc b/asminc/opcodes.inc new file mode 100644 index 000000000..7c52871d2 --- /dev/null +++ b/asminc/opcodes.inc @@ -0,0 +1,509 @@ +; opcodes.inc +; ca65 6502 - opcode definitions, mainly for self modifying code +; +; Christian Krüger, latest change: 18-Sep-2010 +; +; This software is provided 'as-is', without any expressed or implied +; warranty. In no event will the authors be held liable for any damages +; arising from the use of this software. +; +; Permission is granted to anyone to use this software for any purpose, +; including commercial applications, and to alter it and redistribute it +; freely, subject to the following restrictions: +; +; 1. The origin of this software must not be misrepresented; you must not +; claim that you wrote the original software. If you use this software +; in a product, an acknowledgment in the product documentation would be +; appreciated but is not required. +; 2. Altered source versions must be plainly marked as such, and must not +; be misrepresented as being the original software. +; 3. This notice may not be removed or altered from any source +; distribution. +; + +; Opcode-Table +; ------------ +; Post fix explanation: +; imm = #$00 +; zp = $00 +; zpx = $00,X +; zpy = $00,Y +; izp = ($00) +; izx = ($00,X) +; izy = ($00),Y +; abs = $0000 +; abx = $0000,X +; aby = $0000,Y +; ind = ($0000) +; iax = ($0000,X) +; rel = $0000 (PC-relative) (supressed here) + +.macpack cpu + +OPC_BRK = $00 +OPC_ORA_izx = $01 +OPC_ORA_zp = $05 +OPC_ASL_zp = $06 +OPC_PHP = $08 +OPC_ORA_imm = $09 +OPC_ASL = $0A +OPC_ORA_abs = $0D +OPC_ASL_abs = $0E + +OPC_BPL = $10 +OPC_ORA_izy = $11 +OPC_ORA_zpx = $15 +OPC_ASL_zpx = $16 +OPC_CLC = $18 +OPC_ORA_aby = $19 +OPC_ORA_abx = $1D +OPC_ASL_abx = $1E + +OPC_JSR_abs = $20 +OPC_AND_izx = $21 +OPC_BIT_zp = $24 +OPC_AND_zp = $25 +OPC_ROL_zp = $26 +OPC_PLP = $28 +OPC_AND_imm = $29 +OPC_ROL = $2A +OPC_BIT_abs = $2C +OPC_AND_abs = $2D +OPC_ROL_abs = $2E + +OPC_BMI = $30 +OPC_AND_izy = $31 +OPC_AND_zpx = $35 +OPC_ROL_zpx = $36 +OPC_SEC = $38 +OPC_AND_aby = $39 +OPC_AND_abx = $3D +OPC_ROL_abx = $3E + + +OPC_RTI = $40 +OPC_EOR_izx = $41 +OPC_EOR_zp = $45 +OPC_LSR_zp = $46 +OPC_PHA = $48 +OPC_EOR_imm = $49 +OPC_LSR = $4A +OPC_JMP_abs = $4C +OPC_EOR_abs = $4D +OPC_LSR_abs = $4E + +OPC_BVC = $50 +OPC_EOR_izy = $51 +OPC_EOR_zpx = $55 +OPC_LSR_zpx = $56 +OPC_CLI = $58 +OPC_EOR_aby = $59 +OPC_EOR_abx = $5D +OPC_LSR_abx = $5E + +OPC_RTS = $60 +OPC_ADC_izx = $61 +OPC_ADC_zp = $65 +OPC_ROR_zp = $66 +OPC_PLA = $68 +OPC_ADC_imm = $69 +OPC_ROR = $6A +OPC_JMP_ind = $6C +OPC_ADC_abs = $6D +OPC_ROR_abs = $6E + +OPC_BVS = $70 +OPC_ADC_izy = $71 +OPC_ADC_zpx = $75 +OPC_ROR_zpx = $76 +OPC_SEI = $78 +OPC_ADC_aby = $79 +OPC_ADC_abx = $7D +OPC_ROR_abx = $7E + +OPC_STA_izx = $81 +OPC_STY_zp = $84 +OPC_STA_zp = $85 +OPC_STX_zp = $86 +OPC_DEY = $88 +OPC_TXA = $8A +OPC_STY_abs = $8C +OPC_STA_abs = $8D +OPC_STX_abs = $8E + +OPC_BCC = $90 +OPC_STA_izy = $91 +OPC_STY_zpx = $94 +OPC_STA_zpx = $95 +OPC_STX_zpy = $96 +OPC_TYA = $98 +OPC_STA_aby = $99 +OPC_TXS = $9A +OPC_STA_abx = $9D + +OPC_LDY_imm = $A0 +OPC_LDA_izx = $A1 +OPC_LDX_imm = $A2 +OPC_LDY_zp = $A4 +OPC_LDA_zp = $A5 +OPC_LDX_zp = $A6 +OPC_TAY = $A8 +OPC_LDA_imm = $A9 +OPC_TAX = $AA +OPC_LDY_abs = $AC +OPC_LDA_abs = $AD +OPC_LDX_abs = $AE + +OPC_BCS = $B0 +OPC_LDA_izy = $B1 +OPC_LDY_zpx = $B4 +OPC_LDA_zpx = $B5 +OPC_LDX_zpy = $B6 +OPC_CLV = $B8 +OPC_LDA_aby = $B9 +OPC_TSX = $BA +OPC_LDY_abx = $BC +OPC_LDA_abx = $BD +OPC_LDX_aby = $BE + +OPC_CPY_imm = $C0 +OPC_CMP_izx = $C1 +OPC_CPY_zp = $C4 +OPC_CMP_zp = $C5 +OPC_DEC_zp = $C6 +OPC_INY = $C8 +OPC_CMP_imm = $C9 +OPC_DEX = $CA +OPC_CPY_abs = $CC +OPC_CMP_abs = $CD +OPC_DEC_abs = $CE + +OPC_BNE = $D0 +OPC_CMP_izy = $D1 +OPC_CMP_zpx = $D5 +OPC_DEC_zpx = $D6 +OPC_CLD = $D8 +OPC_CMP_aby = $D9 +OPC_CMP_abx = $DD +OPC_DEC_abx = $DE + +OPC_CPX_imm = $E0 +OPC_SBC_izx = $E1 +OPC_CPX_zp = $E4 +OPC_SBC_zp = $E5 +OPC_INC_zp = $E6 +OPC_INX = $E8 +OPC_SBC_imm = $E9 +OPC_NOP = $EA +OPC_CPX_abs = $EC +OPC_SBC_abs = $ED +OPC_INC_abs = $EE + + +OPC_BEQ = $F0 +OPC_SBC_izy = $F1 +OPC_SBC_zpx = $F5 +OPC_INC_zpx = $F6 +OPC_SED = $F8 +OPC_SBC_aby = $F9 +OPC_SBC_abx = $FD +OPC_INC_abx = $FE + + +.if (.cpu .bitand ::CPU_ISET_65SC02) + +; OPC_NOP = $02 ; doublet +; OPC_NOP = $03 ; doublet +OPC_TSB_zp = $04 +; OPC_NOP = $0B ; doublet +OPC_TSB_abs = $0C + +OPC_ORA_izp = $12 +; OPC_NOP = $13 ; doublet +OPC_TRB_zp = $14 +OPC_INC = $1A +; OPC_NOP = $1B ; doublet +OPC_TRB_abs = $1C + +; OPC_NOP = $22 ; doublet +; OPC_NOP = $23 ; doublet +; OPC_NOP = $2B ; doublet + +OPC_AND_izp = $32 +; OPC_NOP = $33 ; doublet +OPC_BIT_zpx = $34 +OPC_DEC = $3A +; OPC_NOP = $3B ; doublet +OPC_BIT_abx = $3C + +; OPC_NOP = $42 ; doublet +; OPC_NOP = $43 ; doublet +; OPC_NOP = $44 ; doublet +; OPC_NOP = $4B ; doublet + +OPC_EOR_izp = $52 +; OPC_NOP = $53 ; doublet +; OPC_NOP = $54 ; doublet +; OPC_NOP = $5A ; doublet +; OPC_NOP = $5B ; doublet +OPC_EOR_abx = $5C + +; OPC_NOP = $62 ; doublet +; OPC_NOP = $63 ; doublet +OPC_STZ_zp = $64 +; OPC_NOP = $6B ; doublet + +OPC_ADC_izp = $72 +; OPC_NOP = $73 ; doublet +OPC_STZ_zpx = $74 +OPC_PLY = $7A +; OPC_NOP = $7B ; doublet +OPC_JMP_iax = $7C + +OPC_BRA = $80 +; OPC_NOP = $82 ; doublet +; OPC_NOP = $83 ; doublet +OPC_BIT_imm = $89 +; OPC_NOP = $8B ; doublet + +OPC_STA_izp = $92 +; OPC_NOP = $93 ; doublet +; OPC_NOP = $9B ; doublet +OPC_STZ_abs = $9C +OPC_STZ_abx = $9E + +; OPC_NOP = $A3 ; doublet +; OPC_NOP = $AB ; doublet + +OPC_LDA_izp = $B2 +; OPC_NOP = $B3 ; doublet +; OPC_NOP = $BB ; doublet + +; OPC_NOP = $C2 ; doublet +; OPC_NOP = $C3 ; doublet +; OPC_NOP = $CB ; doublet + +OPC_CMP_izp = $D2 +; OPC_NOP = $D3 ; doublet +; OPC_NOP = $D4 ; doublet +OPC_PHX = $DA +; OPC_NOP = $DB ; doublet +; OPC_NOP = $DC ; doublet + +; OPC_NOP = $E2 ; doublet +; OPC_NOP = $E3 ; doublet +; OPC_NOP = $EB ; doublet + +OPC_SBC_izp = $F2 +; OPC_NOP = $F3 ; doublet +; OPC_NOP = $F4 ; doublet +OPC_PLX = $FA +; OPC_NOP = $FB ; doublet +; OPC_NOP = $FC ; doublet + + +.if (.cpu .bitand ::CPU_ISET_65C02) + +; bit instructions for 65C02 + +OPC_RMB0 = $07 +OPC_RMB1 = $17 +OPC_RMB2 = $27 +OPC_RMB3 = $37 +OPC_RMB4 = $47 +OPC_RMB5 = $57 +OPC_RMB6 = $67 +OPC_RMB7 = $77 + +OPC_SMB0 = $87 +OPC_SMB1 = $97 +OPC_SMB2 = $A7 +OPC_SMB3 = $B7 +OPC_SMB4 = $C7 +OPC_SMB5 = $D7 +OPC_SMB6 = $E7 +OPC_SMB7 = $F7 + +OPC_BBR0 = $0F +OPC_BBR1 = $1F +OPC_BBR2 = $2F +OPC_BBR3 = $3F +OPC_BBR4 = $4F +OPC_BBR5 = $5F +OPC_BBR6 = $6F +OPC_BBR7 = $7F + +OPC_BBS0 = $8F +OPC_BBS1 = $9F +OPC_BBS2 = $AF +OPC_BBS3 = $BF +OPC_BBS4 = $CF +OPC_BBS5 = $DF +OPC_BBS6 = $EF +OPC_BBS7 = $FF + +.else + +; no bit instructions for 65SC02 + +; OPC_NOP = $07 ; doublet +; OPC_NOP = $17 ; doublet +; OPC_NOP = $27 ; doublet +; OPC_NOP = $37 ; doublet +; OPC_NOP = $47 ; doublet +; OPC_NOP = $57 ; doublet +; OPC_NOP = $67 ; doublet +; OPC_NOP = $77 ; doublet +; OPC_NOP = $87 ; doublet +; OPC_NOP = $97 ; doublet +; OPC_NOP = $A7 ; doublet +; OPC_NOP = $B7 ; doublet +; OPC_NOP = $C7 ; doublet +; OPC_NOP = $D7 ; doublet +; OPC_NOP = $E7 ; doublet +; OPC_NOP = $F7 ; doublet +; OPC_NOP = $0F ; doublet +; OPC_NOP = $1F ; doublet +; OPC_NOP = $2F ; doublet +; OPC_NOP = $3F ; doublet +; OPC_NOP = $4F ; doublet +; OPC_NOP = $5F ; doublet +; OPC_NOP = $6F ; doublet +; OPC_NOP = $7F ; doublet +; OPC_NOP = $8F ; doublet +; OPC_NOP = $9F ; doublet +; OPC_NOP = $AF ; doublet +; OPC_NOP = $BF ; doublet +; OPC_NOP = $CF ; doublet +; OPC_NOP = $DF ; doublet +; OPC_NOP = $EF ; doublet +; OPC_NOP = $FF ; doublet + +.endif + +.elseif (.cpu .bitand ::CPU_ISET_6502X) + +; stable, undocumented opcodes + +; OPC_KIL = $02 ; unstable +OPC_SLO_izx = $03 +OPC_NOP_zp = $04 +OPC_SLO_zp = $07 +OPC_ANC_imm = $0B +OPC_NOP_abs = $0C +OPC_SLO_abs = $0F + +; OPC_KIL = $12 ; unstable +OPC_SLO_izy = $13 +OPC_NOP_zpx = $14 +OPC_SLO_zpx = $17 +;OPC_NOP = $1A +OPC_SLO_aby = $1B +OPC_NOP_abx = $1C +OPC_SLO_abx = $1F + +; OPC_KIL = $22 ; unstable +OPC_RLA_izx = $23 +OPC_RLA_zp = $27 +OPC_ANC_imm = $2B +OPC_RLA_abs = $2F + +; OPC_KIL = $32 ; unstable +OPC_RLA_izy = $33 +OPC_NOP_zpx = $34 +OPC_RLA_zpx = $37 +; OPC_NOP = $3A ; doublet +OPC_RLA_aby = $3B +OPC_NOP_abx = $3C +OPC_RLA_abx = $3F + +; OPC_KIL = $42 ; unstable +OPC_SRE_izx = $43 +OPC_NOP_zp = $44 +OPC_SRE_zp = $47 +OPC_ALR_imm = $4B +OPC_SRE_abs = $4F + +; OPC_KIL = $52 ; unstable +OPC_SRE_izy = $53 +OPC_NOP_zpx = $54 +OPC_SRE_zpx = $57 +; OPC_NOP = $5A ; doublet +OPC_SRE_aby = $5B +OPC_NOP_abx = $5C +OPC_SRE_abx = $5F + +; OPC_KIL = $62 +OPC_RRA_izx = $63 +OPC_NOP_zp = $64 +OPC_RRA_zp = $67 +OPC_ARR_imm = $6B +OPC_RRA_abs = $6F + +; OPC_KIL = $72 +OPC_RRA_izy = $73 +OPC_NOP_zpx = $74 +OPC_RRA_zpx = $77 +; OPC_NOP = $7A ; doublet +OPC_RRA_aby = $7B +OPC_NOP_abx = $7C +OPC_RRA_abx = $7F + +OPC_NOP_imm = $80 +; OPC_NOP_imm = $82 ; doublet +OPC_SAX_izx = $83 +OPC_SAX_zp = $87 +; OPC_NOP_imm = $89 ; doublet +; OPC_XAA = $8B ; unstable +OPC_SAX_abs = $8F + +; OPC_KIL = $92 ; unstable +; OPC_AHX_izy = $93 ; unstable +OPC_SAX_zpy = $97 +; OPC_TAS_aby = $9B ; unstable +; OPC_SHY_abx = $9C ; unstable +; OPC_SHX_aby = $9E ; unstable +; OPC_AHX_aby = $9F ; unstable + +OPC_LAX_izx = $A3 +OPC_LAX_zp = $A7 +; OPC_LAX_imm = $AB ; unstable +OPC_LAX_abs = $AF + +; OPC_KIL = $B2 ; unstable +OPC_LAX_izy = $B3 +OPC_LAX_zpy = $B7 +OPC_LAS_aby = $BB +OPC_LAX_aby = $BF + +; OPC_NOP_imm = $C2 ; doublet +OPC_DCP_izx = $C3 +OPC_DCP_zp = $C7 +OPC_AXS_imm = $CB +OPC_DCP_abs = $CF + +; OPC_KIL = $D2 ; unstable +OPC_DCP_izy = $D3 +OPC_NOP_zpx = $D4 +OPC_DCP_zpx = $D7 +OPC_NOP_DA = $DA +OPC_DCP_aby = $DB +OPC_NOP_abx = $DC +OPC_DCP_abx = $DF + +; OPC_NOP_imm = $E2 ; doublet +OPC_ISC_izx = $E3 +OPC_ISC_zp = $E7 +; OPC_SBC_imm = $EB ; doublet +OPC_ISC_abs = $EF + +; OPC_KIL = $F2 ; unstable +OPC_ISC_izy = $F3 +OPC_NOP_zpx = $F4 +OPC_ISC_zpx = $F7 +OPC_NOP_FA = $FA +OPC_ISC_aby = $FB +OPC_NOP_abx = $FC +OPC_ISC_abx = $FF + +.endif diff --git a/asminc/smc.mac b/asminc/smc.mac new file mode 100644 index 000000000..42dcb1055 --- /dev/null +++ b/asminc/smc.mac @@ -0,0 +1,249 @@ +; smc.mac +; ca65 Macro-Pack for Self Modifying Code (SMC) +; +; (c) Christian Krüger, latest change: 09-Nov-2011 +; +; This software is provided 'as-is', without any expressed or implied +; warranty. In no event will the authors be held liable for any damages +; arising from the use of this software. +; +; Permission is granted to anyone to use this software for any purpose, +; including commercial applications, and to alter it and redistribute it +; freely, subject to the following restrictions: +; +; 1. The origin of this software must not be misrepresented; you must not +; claim that you wrote the original software. If you use this software +; in a product, an acknowledgment in the product documentation would be +; appreciated but is not required. +; 2. Altered source versions must be plainly marked as such, and must not +; be misrepresented as being the original software. +; 3. This notice may not be removed or altered from any source +; distribution. +; + +.define _SMCDesignator .mid(0, .tcount(label) - 1, label) .ident(.concat(.string(.right(1, label)), "_SMC")) +.define _SMCAlias .mid(0, .tcount(alias) - 1, alias) .ident(.concat(.string(.right(1, alias)), "_SMC")) +.define SMC_AbsAdr $FADE +.define SMC_ZpAdr $00 +.define SMC_Opcode nop +.define SMC_Value $42 + +.macro SMC_OperateOnValue opcode, label + opcode _SMCDesignator+1 +.endmacro + +.macro SMC_OperateOnLowByte opcode, label + SMC_OperateOnValue opcode, label +.endmacro + +.macro SMC_OperateOnHighByte opcode, label + opcode _SMCDesignator + 2 +.endmacro + +.macro SMC_Import alias +.import _SMCAlias +.endmacro + +.macro SMC_Export alias, label +.export _SMCAlias := _SMCDesignator +.endmacro + +.macro SMC label, statement +_SMCDesignator: statement +.endmacro + +.macro SMC_TransferOpcode label, opcode, register +.if .paramcount = 2 .or .match ({register}, a) + lda #opcode + sta _SMCDesignator +.elseif .match ({register}, x) + ldx #opcode + stx _SMCDesignator +.elseif .match ({register}, y) + ldy #opcode + sty _SMCDesignator +.endif +.endmacro + +.macro SMC_LoadOpcode label, register +.if .paramcount = 1 .or .match ({register}, a) + lda _SMCDesignator +.elseif .match ({register}, x) + ldx _SMCDesignator +.elseif .match ({register}, y) + ldy _SMCDesignator +.endif +.endmacro + +.macro SMC_StoreOpcode label, register +.if .paramcount = 1 .or .match ({register}, a) + sta _SMCDesignator +.elseif .match ({register}, x) + stx _SMCDesignator +.elseif .match ({register}, y) + sty _SMCDesignator +.endif +.endmacro + +.macro SMC_ChangeBranch label, destination, register +.if .paramcount = 2 .or .match ({register}, a) + lda #(destination - _SMCDesignator -2) + sta _SMCDesignator+1 +.elseif .match ({register}, x) + ldx #(destination - _SMCDesignator - 2) + stx _SMCDesignator+1 +.elseif .match ({register}, y) + ldy #(destination - _SMCDesignator - 2) + sty _SMCDesignator+1 +.endif +.endmacro + +.macro SMC_TransferValue label, value, register +.if .paramcount = 2 .or .match ({register}, a) + lda value + sta _SMCDesignator+1 +.elseif .match ({register}, x) + ldx value + stx _SMCDesignator+1 +.elseif .match ({register}, y) + ldy value + sty _SMCDesignator+1 +.endif +.endmacro + +.macro SMC_LoadValue label, register +.if .paramcount = 1 .or .match ({register}, a) + lda _SMCDesignator+1 +.elseif .match ({register}, x) + ldx _SMCDesignator+1 +.elseif .match ({register}, y) + ldy _SMCDesignator+1 +.endif +.endmacro + +.macro SMC_StoreValue label, register +.if .paramcount = 1 .or .match ({register}, a) + sta _SMCDesignator+1 +.elseif .match ({register}, x) + stx _SMCDesignator+1 +.elseif .match ({register}, y) + sty _SMCDesignator+1 +.endif +.endmacro + + +.macro SMC_TransferLowByte label, value, register +SMC_TransferValue label, value, register +.endmacro + +.macro SMC_LoadLowByte label, register +SMC_LoadValue label, register +.endmacro + +.macro SMC_StoreLowByte label, register +SMC_StoreValue label, register +.endmacro + +.macro SMC_TransferHighByte label, value, register +.if .paramcount = 2 .or .match ({register}, a) + lda value + sta _SMCDesignator+2 +.elseif .match ({register}, x) + ldx value + stx _SMCDesignator+2 +.elseif .match ({register}, y) + ldy value + sty _SMCDesignator+2 +.endif +.endmacro + +.macro SMC_LoadHighByte label, register +.if .paramcount = 1 .or .match ({register}, a) + lda _SMCDesignator+2 +.elseif .match ({register}, x) + ldx _SMCDesignator+2 +.elseif .match ({register}, y) + ldy _SMCDesignator+2 +.endif +.endmacro + +.macro SMC_StoreHighByte label, register +.if .paramcount = 1 .or .match ({register}, a) + sta _SMCDesignator+2 +.elseif .match ({register}, x) + stx _SMCDesignator+2 +.elseif .match ({register}, y) + sty _SMCDesignator+2 +.endif +.endmacro + + +.macro SMC_TransferAddressSingle label, address, register +.if .paramcount = 2 .or .match ((register), a) + .if (.match (.left (1, {adress}), #)) + ; immediate mode + lda #<(.right (.tcount ({adress})-1, {adress})) + sta _SMCDesignator+1 + lda #>(.right (.tcount ({adress})-1, {adress})) + sta _SMCDesignator+2 + .else + ; assume absolute or zero page + lda address + sta _SMCDesignator+1 + lda 1+(address) + sta _SMCDesignator+2 + .endif +.elseif .match ((register), x) + .if (.match (.left (1, {adress}), #)) + ; immediate mode + ldx #<(.right (.tcount ({adress})-1, {adress})) + stx _SMCDesignator+1 + ldx #>(.right (.tcount ({adress})-1, {adress})) + stx _SMCDesignator+2 + .else + ; assume absolute or zero page + ldx address + stx _SMCDesignator+1 + ldx 1+(address) + stx _SMCDesignator+2 + .endif +.elseif .match ((register), y) + .if (.match (.left (1, {adress}), #)) + ; immediate mode + ldy #<(.right (.tcount ({adress})-1, {adress})) + sty _SMCDesignator+1 + ldy #>(.right (.tcount ({adress})-1, {adress})) + sty _SMCDesignator+2 + .else + ; assume absolute or zero page + ldy address + sty _SMCDesignator+1 + ldy 1+(address) + sty _SMCDesignator+2 + .endif +.endif +.endmacro + + +.macro SMC_TransferAddress label, address +.if (.match (.left (1, {adress}), #)) + ; immediate mode + lda #<(.right (.tcount ({adress})-1, {adress})) + sta _SMCDesignator+1 + ldx #>(.right (.tcount ({adress})-1, {adress})) + stx _SMCDesignator+2 +.else + ; assume absolute or zero page + lda {address} + sta _SMCDesignator+1 + ldx 1+{address} + stx _SMCDesignator)+2 +.endif +.endmacro + +.macro SMC_StoreAddress label + sta _SMCDesignator+1 + stx _SMCDesignator+2 +.endmacro + + diff --git a/doc/Makefile b/doc/Makefile index 5534baadd..169e64381 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -49,6 +49,7 @@ SGML = apple2.sgml \ od65.sgml \ pet.sgml \ plus4.sgml \ + smc.sgml \ supervision.sgml\ vic20.sgml diff --git a/doc/index.sgml b/doc/index.sgml index 0d84a367c..868a4e859 100644 --- a/doc/index.sgml +++ b/doc/index.sgml @@ -94,6 +94,10 @@ Main documentation page, contains links to other available stuff. An overview over the cc65 runtime and C libraries. + + Describes Christian Krügers macro package for writing self modifying + assembler code. + Describes the o65 file format that is used for dynamically loadable modules diff --git a/doc/smc.sgml b/doc/smc.sgml new file mode 100644 index 000000000..01a977715 --- /dev/null +++ b/doc/smc.sgml @@ -0,0 +1,596 @@ + + +
+ca65 Macros for Self Modifying Code +<author>Christian Krüger +<date>2012-02-19 + +<abstract> +The 'smc.mac' macro package for ca65 eases the use, increases the safeness and +self-explanation of 'self-modifying-code' (SMC). +</abstract> + +<!-- Table of contents --> +<toc> + +<!-- Begin the document --> + +<sect>Overview<p> +When reading assembler sources, self modifying code is often hard to identify +and applying it needs a lot of discipline. + +Since the cacheless 6502 is a thankful target of such kind of code, the macro +package will not only reduce this complexness, but also document the use. The +resulting source is more self-explanatory and so easier to maintain. + +While for general purposes SMC is not a desired form for implementations, it +can be quite useful for a small range of scenarios. Normally SMC will be +introduced when optimizing code in respect to: + +<itemize> +<item>speed and/or +<item>size. +</itemize> + +Please mind that SMC can only be applied for code in RAM, which means that a +general purpose library with SMC excludes ROM targets! + +The ca65 SMC macro package consists of two files: + +<itemize> +<item><tt>smc.mac</tt> +<item><tt>opcodes.inc</tt> +</itemize> + +The latter is only needed if you also plan to modify opcodes and not only data +within your code. + +<sect>Usage<p> +The use of the macros is quite simple: + +Original: + +<tscreen><verb> + PHA + JSR SUBROUTINE + PLA +</verb></tscreen> + +By applying SMC, the speed will now be increased by once cycle: + +SMC: + +<tscreen><verb> + SMC_StoreValue RestoreAccu + JSR SUBROUTINE +SMC RestoreAccu, { LDA #SMC_Value } +</verb></tscreen> + +The first line stores the value of the accu into the '<tt>RestoreAccu</tt>' +labeled SMC target. + +Please note: +<enum> +<item> for all SMC store or transfer operations, a second argument can be + given. This determines the register for the operation: + '<tt>SMC_StoreValue Label, y</tt>' will store the value of the + Y-register. + + If the second argument is missing, the accu will be used automatically. + +<item> The label targets a 'special SMC namespace'. It fits only to + destinations which are introduced with the macro '<tt>SMC</tt>'. A + normal label '<tt>RestoreAccu</tt>' wouldn't match and could even + coexist (even if you should abstain from doing so). + +<item> The macro '<tt>SMC_StoreValue</tt>' takes care, that the store + operation will occur on the value-position of a SMC-instruction. As + you will see, other macros influence other instruction part positions. + There is no consistency check, if the targeted SMC instruction acually + contains a value. Storing a 'value' on an immplied SMC instruction + would corrupt the following memory cell! +</enum> + +The second line needs no further explanation, this is just a placeholder for +some code in the example. + +The third line is the code line which is about to be modified. It has to start +with the '<tt>SMC</tt>' macro and must be labeled, so that the modification +can be designated. Then the unmodified code is given in curly braces. + +Please note the usage of the value placeholder 'SMC_Value'. Using such a +placeholder has two advantages: + +<enum> +<item> The code is better documented. It is clearly visible that the given + value is about to be changed. +<item> When examining an (initial) disassembly (e.g. in a debugger), these + placegolders can be better identified: They are fixed and, you may + notice that below, quite eye catching defined. +</enum> + +<sect1>Argument placeholders<p> + +There are four kinds of placeholders: + +<descrip> + + <label id="Address placeholder"> + <tag><tt>SMC_AbsAdr</tt></tag> + + Used to indicate an address. The value is '<tt>$FADE</tt>'. + + Example: <tt>STA SMC_AbsAdr</tt> + + + <label id="Zero-Page-Address placeholder"> + <tag><tt>SMC_ZpAdr</tt></tag> + + Used to indicate a zero-page-address. The value is '<tt>$00</tt>'. + + Example: <tt>LDA SMC_ZpAdr</tt> + + + <label id="Opcode placeholder"> + <tag><tt>SMC_Opcode</tt></tag> + + Used to indicate an instruction. The value is '<tt>NOP</tt>'. + + Example: <tt>SMC_Opcode</tt> + + + <label id="Immediate value placeholder"> + <tag><tt>SMC_Value</tt></tag> + + Used to indicate a value. The value is '<tt>$42</tt>'. + + Example: <tt>LDX #SMC_Value</tt> +</descrip> + +Attention: Often code is modified after the initial use - where using the +placeholders does not makes sense. Please mind also, that in very variable +expressions (e.g. opcode and argument is about to be changed), placeholders +can lead to unidentifyable code for a debugger/disassembler: + +<tt>SMC Example, { SMC_Opcode SMC_AbsAdr } </tt> + +Since the opcode is '<tt/NOP/', the value '<tt/$DE/' from '<tt/$FADE/' will +interpreted as opcode in a disassembler too. This breaks the correct +disassembly, because '<tt/$DE/' is interpreted as '<tt/DEC abx/'. Establishing +a valid placeholder instruction may be better: + +<tt>SMC Example, { sta SMC_AbsAdr } ; Note: Opcode will be modified too!</tt> + +<sect1>Accessing opcodes<p> + +Some macros are designed to access the instruction of a code line. To increase +readability, please use the opcodes as defined in the '<tt>opcodes.inc</tt>' +file. + +<descrip> + + <label id="Transfer opcode"> + <tag><tt>SMC_TransferOpcode label, opcode (, register)</tt></tag> + Loads and store an opcode to given SMC instruction. + + Example: + +<tscreen><verb> +SMC SumRegister, { LDA #10 } + JSR OUTPUT + SMC_TransferOpcode SumRegister, OPC_ADC_imm, x +</verb></tscreen> + +The macro above will load the opcode '<tt>ADC #</tt>' into the x - register +and stores it at the place of the '<tt>LDA #</tt>'. + + <label id="Load opcode"> + <tag><tt>SMC_LoadOpcode label (, register)</tt></tag> + Loads the opcode of a SMC line to the given register. + + Example: +<tscreen><verb> +SMC ShiftOrNothing, { LSL } + SMC_LoadOpcode ShiftOrNothing, y + CPY #OPC_NOP + BEQ Exit +</verb></tscreen> + + <label id="Store opcode"> + <tag><tt>SMC_StoreOpcode label (, register)</tt></tag> + Stores the value of the given register at the opcode place of a SMC line. + + Example: +<tscreen><verb> +SetBoldMode: + LDA #OPC_INX + SMC_StoreOpcode AdaptCharWidth + SMC_StoreOpcode AdaptUnderlineWidth + RTS + ... +SMC AdaptCharWidth, { NOP } + ... +SMC AdaptUnderlineWidth, { NOP } +</verb></tscreen> + +</descrip> + +<sect1>Accessing arguments<p> + +These marcos are determined to get, set and change arguments of instructions: + +<descrip> + + <label id="Change branch"> + <tag><tt>SMC_ChangeBranch label, destination (, register)</tt></tag> + + Used to modify the destination of a branch instruction. If the adress offset + exceeds the supported range of 8-bit of the 6502, a error will be thrown. + + Example: +<tscreen><verb> +Disable Handler: + SMC_ChangeBranch BranchToHandler, Exit + RTS + ... + LDA warning +SMC BranchToHandler, { BNE Handler } +Exit: + RTS +</verb></tscreen> + + + <label id="Transfer value"> + <tag><tt>SMC_TransferValue label, value (, register)</tt></tag> + + Changes the value of a SMC line. + + Example: +<tscreen><verb> +ClearDefault: + SMC_TransferValue LoadDefault, 0 + RTS + ... +SMC LoadDefault, { LDX #25 } +</verb></tscreen> + + + <label id="Load value"> + <tag><tt>SMC_LoadValue label (, register)</tt></tag> + + Retreives the value of a SMC line. + + Example: +<tscreen><verb> +ShowDefault: + SMC_LoadValue LoadDefault + JSR PrintValue + RTS + ... +SMC LoadDefault, { LDX #25 } +</verb></tscreen> + + + <label id="Store value"> + <tag><tt>SMC_StoreValue label (, register)</tt></tag> + + Stores the value in the register to given SMC line. + + Example: +<tscreen><verb> +InitCounters: + LDY #0 + SMC_StoreValue GetI, y + SMC_StoreValue GetJ, y + SMC_StoreValue GetK, y + ... +SMC GetI, { LDX #SMC_Value } + ... +SMC GetJ, { LDX #SMC_Value } + ... +SMC GetK, { LDX #SMC_Value } +</verb></tscreen> + + + <label id="Transfer low-byte"> + <tag><tt>SMC_TransferLowByte label, value (, register)</tt></tag> + + Does the same as '<tt>SMC_TransferValue</tt>' but should be used for + low-bytes of adresses for better readability. + + Example: +<tscreen><verb> +ActivateSecondDataSet: + SMC_TransferLowByte LoadData, $40 + RTS + ... +SMC LoadData, { LDA $2000 } +</verb></tscreen> + + + <label id="Load low-byte"> + <tag><tt>SMC_LoadLowByte label (, register)</tt></tag> + + Does the same as '<tt>SMC_LoadValue</tt>' but should be used for low-bytes + of adresses for better readability. + + Example: +<tscreen><verb> +IsSecondDataSetActive: + SMC_LoadLowByte LoadData, y + CPY #$40 + BNE NotActive + ... +SMC LoadData, { LDA $2000 } +</verb></tscreen> + + + <label id="Store low-byte"> + <tag><tt>SMC_StoreLowByte label (, register)</tt></tag> + + Does the same as '<tt>SMC_StoreValue</tt>' but should be used for low-bytes + of adresses for better readability. + + Example: +<tscreen><verb> +InitStructureBaseAddresses: + LDX #0 + SMC_StoreLowByte GetPlayerGraphic, x + SMC_StoreLowByte GetObjectGraphic, x + SMC_StoreLowByte StoreCollisionData, x + RTS + ... +SMC GetPlayerGraphic, { LDX $2000 } + ... +SMC GetObjectGraphic, { LDA $2100,x } + ... +SMC StoreCollisionData, { STY $2200 } +</verb></tscreen> + + + <label id="Transfer high-byte"> + <tag><tt>SMC_TransferHighByte label, value (, register)</tt></tag> + + Loads and stores the given value via the named register to the high-byte + adress portion of an SMC-instruction. + + Example: +<tscreen><verb> +PlaySFX: +SMC GetVolume { LDA $3200,x } + STA SoundOut + INX + BNE PlaySFX + ... +PlayOtherSound: + SMC_TransferHighByte GetVolume, $34 +</verb></tscreen> + + + <label id="Load high-byte"> + <tag><tt>SMC_LoadHighByte label (, register)</tt></tag> + + Loads the high-byte part of an SMC-instruction adress to the given register. + + Example: +<tscreen><verb> +PlaySFX: +SMC GetVolume { LDA $3200,x } + ... + SMC_LoadHighByte GetVolume + cmp #$34 + beq OtherSoundPlaying + ... +</verb></tscreen> + + + <label id="Store high-byte"> + <tag><tt>SMC_StoreHighByte label (, register)</tt></tag> + + Stores the high-byte adress part of an SMC-instruction from the given + register. + + Example: +<tscreen><verb> +SetupLevel2: + LDX #(>Level2Base) + SMC_StoreHighByte GetLevelData, x + SMC_StoreHighByte GetScreenData, x + SMC_StoreHighByte GetSoundData, x + RTS + ... +SMC GetLevelData, { LDA Level1Base+Data } + ... +SMC GetScreenData, { LDA Level1Base+Screen, x } + ... +SMC GetSoundData, { LDA Level1Base+Sound, y } +</verb></tscreen> + + + <label id="Transfer single adress"> + <tag><tt>SMC_TransferAddressSingle label, address (, register)</tt></tag> + + Transfers the contents of the given address via the given register to the + designated SMC instruction. + + Example: +<tscreen><verb> +PrintHello: + SMC_TransferAddressSingle GetChar, #HelloMsg + ... + LDX #0 +NextChar: +SMC GetChar, { LDA SMC_AbsAdr, x } + BEQ leave + JSR CharOut + INX + BNE NextChar +</verb></tscreen> + + + <label id="Transfer adress"> + <tag><tt>SMC_TransferAddress label, address</tt></tag> + + Loads contents of given address to A/X and stores the result to SMC + instruction. Allows reuse of register contents by using + '<tt>SMC_StoreAddress</tt>' for multiple SMC instruction modifications. + + Example: +<tscreen><verb> + SMC_TransferAddress JumpTo, #CloseChannel, Y + ... +SMC JumpTo, { JMP OpenChannel } +</verb></tscreen> + + + <label id="Store address"> + <tag><tt>SMC_StoreAddress label</tt></tag> + + Stores the address value in a/x to a SMC instruction address position. + + Example: +<tscreen><verb> + SMC_StoreAddress GetData + ... +SMC GetData, { LDA SMC_AbsAdr } +</verb></tscreen> + +</descrip> + +<sect1>Operational macros<p> + +These marcos are determined to let read/modify/write opcodes work on parts of +SMC instructions. + +<descrip> + + <label id="Operate on value"> + <tag><tt>SMC_OperateOnValue opcode, label</tt></tag> + + Let given opcode work on the value part of a SMC instruction. + + Example: +<tscreen><verb> + SMC_OperateOnValue ASL, LoadMask ; shift mask to left + ... +SMC LoadMask, { LDA #$20 } +</verb></tscreen> + + <label id="Operate on low-byte"> + <tag><tt>SMC_OperateOnLowByte opcode, label</tt></tag> + + Same as '<tt/SMC_OperateOnValue/' but renamed for better readability when + accessing low-bytes of address. + + Example: +<tscreen><verb> + SMC_OperateOnLowByte DEC, AccessData + ... +SMC AccessData, { LDX Data } +</verb></tscreen> + + <label id="Operate on high-byte"> + <tag><tt>SMC_OperateOnHighByte opcode, label</tt></tag> + + Let the given opcode work on the high-byte part on a SMC-instruction. + + Example: +<tscreen><verb> +NextPage: + SMC_OperateOnHighByte INC, GetPageData + ... +SMC GetPageData, { LDA SourceData, X } +</verb></tscreen> +</descrip> + +<sect1>Scope macros<p> + +These marcos are determined to export and import SMC labels out of the current +file scope. Please handle with care! If you cannot abstain from leaving the +file scope, you should at least document the exported SMC lines very well. On +import side no checking is available if the SMC line is correct accessed (e.g. +invalid access to the value of an implied instruction)! + +<descrip> + <label id="Export SMC line under given name"> + <tag><tt>SMC_Export alias, label</tt></tag> + + SMC label will be exported under given alias. + + Example: +<tscreen><verb> +.proc GetValue +SMC LoadValue, { LDA #12 } + rts +.endproc + +SMC_Export GetValueLoader, GetValue::LoadValue +</verb></tscreen> + + <label id="Import SMC alias"> + <tag><tt>SMC_Import alias</tt></tag> + + SMC line is made accessible under given alias. + + Example: +<tscreen><verb> +SMC_Import GetValueLoader + ... + SMC_TransferValue GetValueLoader, #47 + ... +</verb></tscreen> +</descrip> + +<sect>A complex example<p> +Let's have a look on a quite sophisticated example for the usage of SMC. It +not only modifies code, but also the modification of the code is modified - +allowing reuse of some instructions. + +The code is from my 'memset()'implementation: + +<descrip> +<tscreen><verb> + 1: ... + 2: SMC_StoreAddress StoreAccuFirstSection + 3: + 4: StoreToFirstSection: + 5: SMC StoreAccuFirstSection, { sta SMC_AbsAdr, Y } + 6: ... + 7: RestoreCodeBranchBaseAdr: + 8: SMC FirstIncHighByte, { SMC_OperateOnHighByte inc, StoreAccuFirstSection } ; code will be overwritten to 'beq RestoreCode' (*) + 9: ... +10: SMC_TransferOpcode FirstIncHighByte, OPC_BEQ , x ; change code marked above with (*) +11: SMC_TransferValue FirstIncHighByte, #(restoreCode - RestoreCodeBranchBaseAdr-2), x ; set relative adress to 'RestoreCode' +12: ... +13: restoreCode: +14: SMC_TransferOpcode FirstIncHighByte, OPC_INC_abs , x ; restore original code... +15: SMC_TransferValue FirstIncHighByte, #(<(StoreToFirstSection+2)), x ; (second byte of inc contained low-byte of adress) +16: ... +</verb></tscreen> + +Some explanation: + +Line 2: The register pair A/X contains an address, which is stored on the +address location of a SMC line called 'StoreAccuFirstSection'. According to +cc65's calling convention, the low-byte is in accu while the high-byte is in +the X-register. + +Line 5: The (modified) address is accessed. + +Line 8: We have a line here, which is about to be modified (it begins with +SMC), but itself modifies code. Please note: Contrary to the rest of SMC-line +modifying macros, the 'OperateOn'-macros just expand their given arguments +into a single instruction line. These can be changed of course too. + +Line 10,11: These lines construct a branch operation for line 8: The +X-register will be used to change it from 'inc StoreAccuFirstSection+2' +(high-byte operation) to 'beq restoreCode'. Please note: To calculate the +relaive branch offset, we introduced a second label +('RestoreCodeBranchBaseAdr') for to calculate it. Some could also use the +internal name of the SMC label, but you should abstain to do so - it may be +changed in the future... + +Line 14,15: The original code from line 8 is reestablished. +</descrip> +</article> +