+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:
+
+
+speed and/or
+size.
+
+
+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:
+
+
+smc.mac
+opcodes.inc
+
+
+The latter is only needed if you also plan to modify opcodes and not only data
+within your code.
+
+Usage
+The use of the macros is quite simple:
+
+Original:
+
+
+ PHA
+ JSR SUBROUTINE
+ PLA
+
+
+By applying SMC, the speed will now be increased by once cycle:
+
+SMC:
+
+
+ SMC_StoreValue RestoreAccu
+ JSR SUBROUTINE
+SMC RestoreAccu, { LDA #SMC_Value }
+
+
+The first line stores the value of the accu into the 'RestoreAccu'
+labeled SMC target.
+
+Please note:
+
+ for all SMC store or transfer operations, a second argument can be
+ given. This determines the register for the operation:
+ 'SMC_StoreValue Label, y' will store the value of the
+ Y-register.
+
+ If the second argument is missing, the accu will be used automatically.
+
+ The label targets a 'special SMC namespace'. It fits only to
+ destinations which are introduced with the macro 'SMC'. A
+ normal label 'RestoreAccu' wouldn't match and could even
+ coexist (even if you should abstain from doing so).
+
+ The macro 'SMC_StoreValue' 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!
+
+
+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 'SMC' 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:
+
+
+ The code is better documented. It is clearly visible that the given
+ value is about to be changed.
+ 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.
+
+
+Argument placeholders
+
+There are four kinds of placeholders:
+
+
+
+
+
+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:
+
+SMC Example, { SMC_Opcode SMC_AbsAdr }
+
+Since the opcode is 'SMC Example, { sta SMC_AbsAdr } ; Note: Opcode will be modified too!
+
+Accessing opcodes
+
+Some macros are designed to access the instruction of a code line. To increase
+readability, please use the opcodes as defined in the 'opcodes.inc'
+file.
+
+
+
+
+ SMC_TransferOpcode label, opcode (, register)
+ Loads and store an opcode to given SMC instruction.
+
+ Example:
+
+
+SMC SumRegister, { LDA #10 }
+ JSR OUTPUT
+ SMC_TransferOpcode SumRegister, OPC_ADC_imm, x
+
+
+The macro above will load the opcode 'ADC #' into the x - register
+and stores it at the place of the 'LDA #'.
+
+
+ SMC_LoadOpcode label (, register)
+ Loads the opcode of a SMC line to the given register.
+
+ Example:
+
+SMC ShiftOrNothing, { LSL }
+ SMC_LoadOpcode ShiftOrNothing, y
+ CPY #OPC_NOP
+ BEQ Exit
+
+
+
+ SMC_StoreOpcode label (, register)
+ Stores the value of the given register at the opcode place of a SMC line.
+
+ Example:
+
+SetBoldMode:
+ LDA #OPC_INX
+ SMC_StoreOpcode AdaptCharWidth
+ SMC_StoreOpcode AdaptUnderlineWidth
+ RTS
+ ...
+SMC AdaptCharWidth, { NOP }
+ ...
+SMC AdaptUnderlineWidth, { NOP }
+
+
+
+
+Accessing arguments
+
+These marcos are determined to get, set and change arguments of instructions:
+
+
+
+
+ SMC_ChangeBranch label, destination (, register)
+
+ 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:
+
+Disable Handler:
+ SMC_ChangeBranch BranchToHandler, Exit
+ RTS
+ ...
+ LDA warning
+SMC BranchToHandler, { BNE Handler }
+Exit:
+ RTS
+
+
+
+
+ SMC_TransferValue label, value (, register)
+
+ Changes the value of a SMC line.
+
+ Example:
+
+ClearDefault:
+ SMC_TransferValue LoadDefault, 0
+ RTS
+ ...
+SMC LoadDefault, { LDX #25 }
+
+
+
+
+ SMC_LoadValue label (, register)
+
+ Retreives the value of a SMC line.
+
+ Example:
+
+ShowDefault:
+ SMC_LoadValue LoadDefault
+ JSR PrintValue
+ RTS
+ ...
+SMC LoadDefault, { LDX #25 }
+
+
+
+
+ SMC_StoreValue label (, register)
+
+ Stores the value in the register to given SMC line.
+
+ Example:
+
+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 }
+
+
+
+
+ SMC_TransferLowByte label, value (, register)
+
+ Does the same as 'SMC_TransferValue' but should be used for
+ low-bytes of adresses for better readability.
+
+ Example:
+
+ActivateSecondDataSet:
+ SMC_TransferLowByte LoadData, $40
+ RTS
+ ...
+SMC LoadData, { LDA $2000 }
+
+
+
+
+ SMC_LoadLowByte label (, register)
+
+ Does the same as 'SMC_LoadValue' but should be used for low-bytes
+ of adresses for better readability.
+
+ Example:
+
+IsSecondDataSetActive:
+ SMC_LoadLowByte LoadData, y
+ CPY #$40
+ BNE NotActive
+ ...
+SMC LoadData, { LDA $2000 }
+
+
+
+
+ SMC_StoreLowByte label (, register)
+
+ Does the same as 'SMC_StoreValue' but should be used for low-bytes
+ of adresses for better readability.
+
+ Example:
+
+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 }
+
+
+
+
+ SMC_TransferHighByte label, value (, register)
+
+ Loads and stores the given value via the named register to the high-byte
+ adress portion of an SMC-instruction.
+
+ Example:
+
+PlaySFX:
+SMC GetVolume { LDA $3200,x }
+ STA SoundOut
+ INX
+ BNE PlaySFX
+ ...
+PlayOtherSound:
+ SMC_TransferHighByte GetVolume, $34
+
+
+
+
+ SMC_LoadHighByte label (, register)
+
+ Loads the high-byte part of an SMC-instruction adress to the given register.
+
+ Example:
+
+PlaySFX:
+SMC GetVolume { LDA $3200,x }
+ ...
+ SMC_LoadHighByte GetVolume
+ cmp #$34
+ beq OtherSoundPlaying
+ ...
+
+
+
+
+ SMC_StoreHighByte label (, register)
+
+ Stores the high-byte adress part of an SMC-instruction from the given
+ register.
+
+ Example:
+
+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 }
+
+
+
+
+ SMC_TransferAddressSingle label, address (, register)
+
+ Transfers the contents of the given address via the given register to the
+ designated SMC instruction.
+
+ Example:
+
+PrintHello:
+ SMC_TransferAddressSingle GetChar, #HelloMsg
+ ...
+ LDX #0
+NextChar:
+SMC GetChar, { LDA SMC_AbsAdr, x }
+ BEQ leave
+ JSR CharOut
+ INX
+ BNE NextChar
+
+
+
+
+ SMC_TransferAddress label, address
+
+ Loads contents of given address to A/X and stores the result to SMC
+ instruction. Allows reuse of register contents by using
+ 'SMC_StoreAddress' for multiple SMC instruction modifications.
+
+ Example:
+
+ SMC_TransferAddress JumpTo, #CloseChannel, Y
+ ...
+SMC JumpTo, { JMP OpenChannel }
+
+
+
+
+ SMC_StoreAddress label
+
+ Stores the address value in a/x to a SMC instruction address position.
+
+ Example:
+
+ SMC_StoreAddress GetData
+ ...
+SMC GetData, { LDA SMC_AbsAdr }
+
+
+
+
+Operational macros
+
+These marcos are determined to let read/modify/write opcodes work on parts of
+SMC instructions.
+
+
+
+
+ SMC_OperateOnValue opcode, label
+
+ Let given opcode work on the value part of a SMC instruction.
+
+ Example:
+
+ SMC_OperateOnValue ASL, LoadMask ; shift mask to left
+ ...
+SMC LoadMask, { LDA #$20 }
+
+
+
+ SMC_OperateOnLowByte opcode, label
+
+ Same as '
+ SMC_OperateOnLowByte DEC, AccessData
+ ...
+SMC AccessData, { LDX Data }
+
+
+
+ SMC_OperateOnHighByte opcode, label
+
+ Let the given opcode work on the high-byte part on a SMC-instruction.
+
+ Example:
+
+NextPage:
+ SMC_OperateOnHighByte INC, GetPageData
+ ...
+SMC GetPageData, { LDA SourceData, X }
+
+
+
+Scope macros
+
+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)!
+
+
+
+ SMC_Export alias, label
+
+ SMC label will be exported under given alias.
+
+ Example:
+
+.proc GetValue
+SMC LoadValue, { LDA #12 }
+ rts
+.endproc
+
+SMC_Export GetValueLoader, GetValue::LoadValue
+
+
+
+ SMC_Import alias
+
+ SMC line is made accessible under given alias.
+
+ Example:
+
+SMC_Import GetValueLoader
+ ...
+ SMC_TransferValue GetValueLoader, #47
+ ...
+
+
+
+A complex example
+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:
+
+
+
+ 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: ...
+
+
+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.
+
+