1 <!doctype linuxdoc system>
4 <title>ca65 Macros for Self Modifying Code
5 <author>Christian Krüger
8 The 'smc.inc' macro package for ca65 eases the use, increases the safeness and
9 self-explanation of 'self-modifying-code' (SMC).
12 <!-- Table of contents -->
15 <!-- Begin the document -->
18 When reading assembler sources, self modifying code is often hard to identify
19 and applying it needs a lot of discipline.
21 Since the cacheless 6502 is a thankful target of such kind of code, the macro
22 package will not only reduce this complexness, but also document the use. The
23 resulting source is more self-explanatory and so easier to maintain.
25 While for general purposes SMC is not a desired form for implementations, it
26 can be quite useful for a small range of scenarios. Normally SMC will be
27 introduced when optimizing code in respect to:
34 Please mind that SMC can only be applied for code in RAM, which means that a
35 general purpose library with SMC excludes ROM targets!
37 The ca65 SMC macro package consists of two files:
40 <item><tt>smc.inc</tt>
41 <item><tt>opcodes.inc</tt>
44 The latter is only needed if you also plan to modify opcodes and not only data
48 The use of the macros is quite simple:
58 By applying SMC, the speed will now be increased by once cycle:
63 SMC_StoreValue RestoreAccu
65 SMC RestoreAccu, { LDA #SMC_Value }
68 The first line stores the value of the accu into the '<tt>RestoreAccu</tt>'
73 <item> for all SMC store or transfer operations, a second argument can be
74 given. This determines the register for the operation:
75 '<tt>SMC_StoreValue Label, y</tt>' will store the value of the
78 If the second argument is missing, the accu will be used automatically.
80 <item> The label targets a 'special SMC namespace'. It fits only to
81 destinations which are introduced with the macro '<tt>SMC</tt>'. A
82 normal label '<tt>RestoreAccu</tt>' wouldn't match and could even
83 coexist (even if you should abstain from doing so).
85 <item> The macro '<tt>SMC_StoreValue</tt>' takes care, that the store
86 operation will occur on the value-position of a SMC-instruction. As
87 you will see, other macros influence other instruction part positions.
88 There is no consistency check, if the targeted SMC instruction acually
89 contains a value. Storing a 'value' on an immplied SMC instruction
90 would corrupt the following memory cell!
93 The second line needs no further explanation, this is just a placeholder for
94 some code in the example.
96 The third line is the code line which is about to be modified. It has to start
97 with the '<tt>SMC</tt>' macro and must be labeled, so that the modification
98 can be designated. Then the unmodified code is given in curly braces.
100 Please note the usage of the value placeholder 'SMC_Value'. Using such a
101 placeholder has two advantages:
104 <item> The code is better documented. It is clearly visible that the given
105 value is about to be changed.
106 <item> When examining an (initial) disassembly (e.g. in a debugger), these
107 placegolders can be better identified: They are fixed and, you may
108 notice that below, quite eye catching defined.
111 <sect1>Argument placeholders<p>
113 There are four kinds of placeholders:
117 <label id="Address placeholder">
118 <tag><tt>SMC_AbsAdr</tt></tag>
120 Used to indicate an address. The value is '<tt>$FADE</tt>'.
122 Example: <tt>STA SMC_AbsAdr</tt>
125 <label id="Zero-Page-Address placeholder">
126 <tag><tt>SMC_ZpAdr</tt></tag>
128 Used to indicate a zero-page-address. The value is '<tt>$00</tt>'.
130 Example: <tt>LDA SMC_ZpAdr</tt>
133 <label id="Opcode placeholder">
134 <tag><tt>SMC_Opcode</tt></tag>
136 Used to indicate an instruction. The value is '<tt>NOP</tt>'.
138 Example: <tt>SMC_Opcode</tt>
141 <label id="Immediate value placeholder">
142 <tag><tt>SMC_Value</tt></tag>
144 Used to indicate a value. The value is '<tt>$42</tt>'.
146 Example: <tt>LDX #SMC_Value</tt>
149 Attention: Often code is modified after the initial use - where using the
150 placeholders does not makes sense. Please mind also, that in very variable
151 expressions (e.g. opcode and argument is about to be changed), placeholders
152 can lead to unidentifyable code for a debugger/disassembler:
154 <tt>SMC Example, { SMC_Opcode SMC_AbsAdr } </tt>
156 Since the opcode is '<tt/NOP/', the value '<tt/$DE/' from '<tt/$FADE/' will
157 interpreted as opcode in a disassembler too. This breaks the correct
158 disassembly, because '<tt/$DE/' is interpreted as '<tt/DEC abx/'. Establishing
159 a valid placeholder instruction may be better:
161 <tt>SMC Example, { sta SMC_AbsAdr } ; Note: Opcode will be modified too!</tt>
163 <sect1>Accessing opcodes<p>
165 Some macros are designed to access the instruction of a code line. To increase
166 readability, please use the opcodes as defined in the '<tt>opcodes.inc</tt>'
171 <label id="Transfer opcode">
172 <tag><tt>SMC_TransferOpcode label, opcode (, register)</tt></tag>
173 Loads and store an opcode to given SMC instruction.
178 SMC SumRegister, { LDA #10 }
180 SMC_TransferOpcode SumRegister, OPC_ADC_imm, x
183 The macro above will load the opcode '<tt>ADC #</tt>' into the x - register
184 and stores it at the place of the '<tt>LDA #</tt>'.
186 <label id="Load opcode">
187 <tag><tt>SMC_LoadOpcode label (, register)</tt></tag>
188 Loads the opcode of a SMC line to the given register.
192 SMC ShiftOrNothing, { LSL }
193 SMC_LoadOpcode ShiftOrNothing, y
198 <label id="Store opcode">
199 <tag><tt>SMC_StoreOpcode label (, register)</tt></tag>
200 Stores the value of the given register at the opcode place of a SMC line.
206 SMC_StoreOpcode AdaptCharWidth
207 SMC_StoreOpcode AdaptUnderlineWidth
210 SMC AdaptCharWidth, { NOP }
212 SMC AdaptUnderlineWidth, { NOP }
217 <sect1>Accessing arguments<p>
219 These marcos are determined to get, set and change arguments of instructions:
223 <label id="Change branch">
224 <tag><tt>SMC_ChangeBranch label, destination (, register)</tt></tag>
226 Used to modify the destination of a branch instruction. If the address offset
227 exceeds the supported range of 8-bit of the 6502, a error will be thrown.
232 SMC_ChangeBranch BranchToHandler, Exit
236 SMC BranchToHandler, { BNE Handler }
242 <label id="Transfer value">
243 <tag><tt>SMC_TransferValue label, value (, register)</tt></tag>
245 Changes the value of a SMC line.
250 SMC_TransferValue LoadDefault, 0
253 SMC LoadDefault, { LDX #25 }
257 <label id="Load value">
258 <tag><tt>SMC_LoadValue label (, register)</tt></tag>
260 Retreives the value of a SMC line.
265 SMC_LoadValue LoadDefault
269 SMC LoadDefault, { LDX #25 }
273 <label id="Store value">
274 <tag><tt>SMC_StoreValue label (, register)</tt></tag>
276 Stores the value in the register to given SMC line.
282 SMC_StoreValue GetI, y
283 SMC_StoreValue GetJ, y
284 SMC_StoreValue GetK, y
286 SMC GetI, { LDX #SMC_Value }
288 SMC GetJ, { LDX #SMC_Value }
290 SMC GetK, { LDX #SMC_Value }
294 <label id="Transfer low-byte">
295 <tag><tt>SMC_TransferLowByte label, value (, register)</tt></tag>
297 Does the same as '<tt>SMC_TransferValue</tt>' but should be used for
298 low-bytes of addresses for better readability.
302 ActivateSecondDataSet:
303 SMC_TransferLowByte LoadData, $40
306 SMC LoadData, { LDA $2000 }
310 <label id="Load low-byte">
311 <tag><tt>SMC_LoadLowByte label (, register)</tt></tag>
313 Does the same as '<tt>SMC_LoadValue</tt>' but should be used for low-bytes
314 of addresses for better readability.
318 IsSecondDataSetActive:
319 SMC_LoadLowByte LoadData, y
323 SMC LoadData, { LDA $2000 }
327 <label id="Store low-byte">
328 <tag><tt>SMC_StoreLowByte label (, register)</tt></tag>
330 Does the same as '<tt>SMC_StoreValue</tt>' but should be used for low-bytes
331 of addresses for better readability.
335 InitStructureBaseAddresses:
337 SMC_StoreLowByte GetPlayerGraphic, x
338 SMC_StoreLowByte GetObjectGraphic, x
339 SMC_StoreLowByte StoreCollisionData, x
342 SMC GetPlayerGraphic, { LDX $2000 }
344 SMC GetObjectGraphic, { LDA $2100,x }
346 SMC StoreCollisionData, { STY $2200 }
350 <label id="Transfer high-byte">
351 <tag><tt>SMC_TransferHighByte label, value (, register)</tt></tag>
353 Loads and stores the given value via the named register to the high-byte
354 address portion of an SMC-instruction.
359 SMC GetVolume { LDA $3200,x }
365 SMC_TransferHighByte GetVolume, $34
369 <label id="Load high-byte">
370 <tag><tt>SMC_LoadHighByte label (, register)</tt></tag>
372 Loads the high-byte part of an SMC-instruction address to the given register.
377 SMC GetVolume { LDA $3200,x }
379 SMC_LoadHighByte GetVolume
381 beq OtherSoundPlaying
386 <label id="Store high-byte">
387 <tag><tt>SMC_StoreHighByte label (, register)</tt></tag>
389 Stores the high-byte address part of an SMC-instruction from the given
396 SMC_StoreHighByte GetLevelData, x
397 SMC_StoreHighByte GetScreenData, x
398 SMC_StoreHighByte GetSoundData, x
401 SMC GetLevelData, { LDA Level1Base+Data }
403 SMC GetScreenData, { LDA Level1Base+Screen, x }
405 SMC GetSoundData, { LDA Level1Base+Sound, y }
409 <label id="Transfer single address">
410 <tag><tt>SMC_TransferAddressSingle label, address (, register)</tt></tag>
412 Transfers the contents of the given address via the given register to the
413 designated SMC instruction.
418 SMC_TransferAddressSingle GetChar, #HelloMsg
422 SMC GetChar, { LDA SMC_AbsAdr, x }
430 <label id="Transfer address">
431 <tag><tt>SMC_TransferAddress label, address</tt></tag>
433 Loads contents of given address to A/X and stores the result to SMC
434 instruction. Allows reuse of register contents by using
435 '<tt>SMC_StoreAddress</tt>' for multiple SMC instruction modifications.
439 SMC_TransferAddress JumpTo, #CloseChannel
441 SMC JumpTo, { JMP OpenChannel }
445 <label id="Store address">
446 <tag><tt>SMC_StoreAddress label</tt></tag>
448 Stores the address value in a/x to a SMC instruction address position.
452 SMC_StoreAddress GetData
454 SMC GetData, { LDA SMC_AbsAdr }
459 <sect1>Operational macros<p>
461 These marcos are determined to let read/modify/write opcodes work on parts of
466 <label id="Operate on value">
467 <tag><tt>SMC_OperateOnValue opcode, label</tt></tag>
469 Let given opcode work on the value part of a SMC instruction.
473 SMC_OperateOnValue ASL, LoadMask ; shift mask to left
475 SMC LoadMask, { LDA #$20 }
478 <label id="Operate on low-byte">
479 <tag><tt>SMC_OperateOnLowByte opcode, label</tt></tag>
481 Same as '<tt/SMC_OperateOnValue/' but renamed for better readability when
482 accessing low-bytes of address.
486 SMC_OperateOnLowByte DEC, AccessData
488 SMC AccessData, { LDX Data }
491 <label id="Operate on high-byte">
492 <tag><tt>SMC_OperateOnHighByte opcode, label</tt></tag>
494 Let the given opcode work on the high-byte part on a SMC-instruction.
499 SMC_OperateOnHighByte INC, GetPageData
501 SMC GetPageData, { LDA SourceData, X }
505 <sect1>Scope macros<p>
507 These marcos are determined to export and import SMC labels out of the current
508 file scope. Please handle with care! If you cannot abstain from leaving the
509 file scope, you should at least document the exported SMC lines very well. On
510 import side no checking is available if the SMC line is correct accessed (e.g.
511 invalid access to the value of an implied instruction)!
514 <label id="Export SMC line under given name">
515 <tag><tt>SMC_Export alias, label</tt></tag>
517 SMC label will be exported under given alias.
522 SMC LoadValue, { LDA #12 }
526 SMC_Export GetValueLoader, GetValue::LoadValue
529 <label id="Import SMC alias">
530 <tag><tt>SMC_Import alias</tt></tag>
532 SMC line is made accessible under given alias.
536 SMC_Import GetValueLoader
538 SMC_TransferValue GetValueLoader, #47
543 <sect>A complex example<p>
544 Let's have a look on a quite sophisticated example for the usage of SMC. It
545 not only modifies code, but also the modification of the code is modified -
546 allowing reuse of some instructions.
549 <tag/The code is from my 'memset()'implementation:/
552 2: SMC_StoreAddress StoreAccuFirstSection
554 4: StoreToFirstSection:
555 5: SMC StoreAccuFirstSection, { sta SMC_AbsAdr, Y }
557 7: RestoreCodeBranchBaseAdr:
558 8: SMC FirstIncHighByte, { SMC_OperateOnHighByte inc, StoreAccuFirstSection } ; code will be overwritten to 'beq RestoreCode' (*)
560 10: SMC_TransferOpcode FirstIncHighByte, OPC_BEQ , x ; change code marked above with (*)
561 11: SMC_TransferValue FirstIncHighByte, #(restoreCode - RestoreCodeBranchBaseAdr-2), x ; set relative address to 'RestoreCode'
564 14: SMC_TransferOpcode FirstIncHighByte, OPC_INC_abs , x ; restore original code...
565 15: SMC_TransferValue FirstIncHighByte, #(<(StoreToFirstSection+2)), x ; (second byte of inc contained low-byte of address)
569 <tag/Some explanation:/
570 Line 2: The register pair A/X contains an address, which is stored on the
571 address location of a SMC line called 'StoreAccuFirstSection'. According to
572 cc65's calling convention, the low-byte is in accu while the high-byte is in
575 Line 5: The (modified) address is accessed.
577 Line 8: We have a line here, which is about to be modified (it begins with
578 SMC), but itself modifies code. Please note: Contrary to the rest of SMC-line
579 modifying macros, the 'OperateOn'-macros just expand their given arguments
580 into a single instruction line. These can be changed of course too.
582 Line 10,11: These lines construct a branch operation for line 8: The
583 X-register will be used to change it from 'inc StoreAccuFirstSection+2'
584 (high-byte operation) to 'beq restoreCode'. Please note: To calculate the
585 relaive branch offset, we introduced a second label
586 ('RestoreCodeBranchBaseAdr') for to calculate it. Some could also use the
587 internal name of the SMC label, but you should abstain to do so - it may be
588 changed in the future...
590 Line 14,15: The original code from line 8 is reestablished.