1 <!doctype linuxdoc system>
4 <title>ca65 Macros for Self Modifying Code
5 <author>Christian Krüger
9 The 'smc.inc' macro package for ca65 eases the use, increases the safeness and
10 self-explanation of 'self-modifying-code' (SMC).
13 <!-- Table of contents -->
16 <!-- Begin the document -->
19 When reading assembler sources, self modifying code is often hard to identify
20 and applying it needs a lot of discipline.
22 Since the cacheless 6502 is a thankful target of such kind of code, the macro
23 package will not only reduce this complexness, but also document the use. The
24 resulting source is more self-explanatory and so easier to maintain.
26 While for general purposes SMC is not a desired form for implementations, it
27 can be quite useful for a small range of scenarios. Normally SMC will be
28 introduced when optimizing code in respect to:
35 Please mind that SMC can only be applied for code in RAM, which means that a
36 general purpose library with SMC excludes ROM targets!
38 The ca65 SMC macro package consists of two files:
41 <item><tt>smc.inc</tt>
42 <item><tt>opcodes.inc</tt>
45 The latter is only needed if you also plan to modify opcodes and not only data
49 The use of the macros is quite simple:
59 By applying SMC, the speed will now be increased by once cycle:
64 SMC_StoreValue RestoreAccu
66 SMC RestoreAccu, { LDA #SMC_Value }
69 The first line stores the value of the accu into the '<tt>RestoreAccu</tt>'
74 <item> for all SMC store or transfer operations, a second argument can be
75 given. This determines the register for the operation:
76 '<tt>SMC_StoreValue Label, y</tt>' will store the value of the
79 If the second argument is missing, the accu will be used automatically.
81 <item> The label targets a 'special SMC namespace'. It fits only to
82 destinations which are introduced with the macro '<tt>SMC</tt>'. A
83 normal label '<tt>RestoreAccu</tt>' wouldn't match and could even
84 coexist (even if you should abstain from doing so).
86 <item> The macro '<tt>SMC_StoreValue</tt>' takes care, that the store
87 operation will occur on the value-position of a SMC-instruction. As
88 you will see, other macros influence other instruction part positions.
89 There is no consistency check, if the targeted SMC instruction acually
90 contains a value. Storing a 'value' on an immplied SMC instruction
91 would corrupt the following memory cell!
94 The second line needs no further explanation, this is just a placeholder for
95 some code in the example.
97 The third line is the code line which is about to be modified. It has to start
98 with the '<tt>SMC</tt>' macro and must be labeled, so that the modification
99 can be designated. Then the unmodified code is given in curly braces.
101 Please note the usage of the value placeholder 'SMC_Value'. Using such a
102 placeholder has two advantages:
105 <item> The code is better documented. It is clearly visible that the given
106 value is about to be changed.
107 <item> When examining an (initial) disassembly (e.g. in a debugger), these
108 placegolders can be better identified: They are fixed and, you may
109 notice that below, quite eye catching defined.
112 <sect1>Argument placeholders<p>
114 There are four kinds of placeholders:
118 <label id="Address placeholder">
119 <tag><tt>SMC_AbsAdr</tt></tag>
121 Used to indicate an address. The value is '<tt>$FADE</tt>'.
123 Example: <tt>STA SMC_AbsAdr</tt>
126 <label id="Zero-Page-Address placeholder">
127 <tag><tt>SMC_ZpAdr</tt></tag>
129 Used to indicate a zero-page-address. The value is '<tt>$00</tt>'.
131 Example: <tt>LDA SMC_ZpAdr</tt>
134 <label id="Opcode placeholder">
135 <tag><tt>SMC_Opcode</tt></tag>
137 Used to indicate an instruction. The value is '<tt>NOP</tt>'.
139 Example: <tt>SMC_Opcode</tt>
142 <label id="Immediate value placeholder">
143 <tag><tt>SMC_Value</tt></tag>
145 Used to indicate a value. The value is '<tt>$42</tt>'.
147 Example: <tt>LDX #SMC_Value</tt>
150 Attention: Often code is modified after the initial use - where using the
151 placeholders does not makes sense. Please mind also, that in very variable
152 expressions (e.g. opcode and argument is about to be changed), placeholders
153 can lead to unidentifyable code for a debugger/disassembler:
155 <tt>SMC Example, { SMC_Opcode SMC_AbsAdr } </tt>
157 Since the opcode is '<tt/NOP/', the value '<tt/$DE/' from '<tt/$FADE/' will
158 interpreted as opcode in a disassembler too. This breaks the correct
159 disassembly, because '<tt/$DE/' is interpreted as '<tt/DEC abx/'. Establishing
160 a valid placeholder instruction may be better:
162 <tt>SMC Example, { sta SMC_AbsAdr } ; Note: Opcode will be modified too!</tt>
164 <sect1>Accessing opcodes<p>
166 Some macros are designed to access the instruction of a code line. To increase
167 readability, please use the opcodes as defined in the '<tt>opcodes.inc</tt>'
172 <label id="Transfer opcode">
173 <tag><tt>SMC_TransferOpcode label, opcode (, register)</tt></tag>
174 Loads and store an opcode to given SMC instruction.
179 SMC SumRegister, { LDA #10 }
181 SMC_TransferOpcode SumRegister, OPC_ADC_imm, x
184 The macro above will load the opcode '<tt>ADC #</tt>' into the x - register
185 and stores it at the place of the '<tt>LDA #</tt>'.
187 <label id="Load opcode">
188 <tag><tt>SMC_LoadOpcode label (, register)</tt></tag>
189 Loads the opcode of a SMC line to the given register.
193 SMC ShiftOrNothing, { LSL }
194 SMC_LoadOpcode ShiftOrNothing, y
199 <label id="Store opcode">
200 <tag><tt>SMC_StoreOpcode label (, register)</tt></tag>
201 Stores the value of the given register at the opcode place of a SMC line.
207 SMC_StoreOpcode AdaptCharWidth
208 SMC_StoreOpcode AdaptUnderlineWidth
211 SMC AdaptCharWidth, { NOP }
213 SMC AdaptUnderlineWidth, { NOP }
218 <sect1>Accessing arguments<p>
220 These marcos are determined to get, set and change arguments of instructions:
224 <label id="Change branch">
225 <tag><tt>SMC_ChangeBranch label, destination (, register)</tt></tag>
227 Used to modify the destination of a branch instruction. If the address offset
228 exceeds the supported range of 8-bit of the 6502, a error will be thrown.
233 SMC_ChangeBranch BranchToHandler, Exit
237 SMC BranchToHandler, { BNE Handler }
243 <label id="Transfer value">
244 <tag><tt>SMC_TransferValue label, value (, register)</tt></tag>
246 Changes the value of a SMC line.
251 SMC_TransferValue LoadDefault, 0
254 SMC LoadDefault, { LDX #25 }
258 <label id="Load value">
259 <tag><tt>SMC_LoadValue label (, register)</tt></tag>
261 Retreives the value of a SMC line.
266 SMC_LoadValue LoadDefault
270 SMC LoadDefault, { LDX #25 }
274 <label id="Store value">
275 <tag><tt>SMC_StoreValue label (, register)</tt></tag>
277 Stores the value in the register to given SMC line.
283 SMC_StoreValue GetI, y
284 SMC_StoreValue GetJ, y
285 SMC_StoreValue GetK, y
287 SMC GetI, { LDX #SMC_Value }
289 SMC GetJ, { LDX #SMC_Value }
291 SMC GetK, { LDX #SMC_Value }
295 <label id="Transfer low-byte">
296 <tag><tt>SMC_TransferLowByte label, value (, register)</tt></tag>
298 Does the same as '<tt>SMC_TransferValue</tt>' but should be used for
299 low-bytes of addresses for better readability.
303 ActivateSecondDataSet:
304 SMC_TransferLowByte LoadData, $40
307 SMC LoadData, { LDA $2000 }
311 <label id="Load low-byte">
312 <tag><tt>SMC_LoadLowByte label (, register)</tt></tag>
314 Does the same as '<tt>SMC_LoadValue</tt>' but should be used for low-bytes
315 of addresses for better readability.
319 IsSecondDataSetActive:
320 SMC_LoadLowByte LoadData, y
324 SMC LoadData, { LDA $2000 }
328 <label id="Store low-byte">
329 <tag><tt>SMC_StoreLowByte label (, register)</tt></tag>
331 Does the same as '<tt>SMC_StoreValue</tt>' but should be used for low-bytes
332 of addresses for better readability.
336 InitStructureBaseAddresses:
338 SMC_StoreLowByte GetPlayerGraphic, x
339 SMC_StoreLowByte GetObjectGraphic, x
340 SMC_StoreLowByte StoreCollisionData, x
343 SMC GetPlayerGraphic, { LDX $2000 }
345 SMC GetObjectGraphic, { LDA $2100,x }
347 SMC StoreCollisionData, { STY $2200 }
351 <label id="Transfer high-byte">
352 <tag><tt>SMC_TransferHighByte label, value (, register)</tt></tag>
354 Loads and stores the given value via the named register to the high-byte
355 address portion of an SMC-instruction.
360 SMC GetVolume { LDA $3200,x }
366 SMC_TransferHighByte GetVolume, $34
370 <label id="Load high-byte">
371 <tag><tt>SMC_LoadHighByte label (, register)</tt></tag>
373 Loads the high-byte part of an SMC-instruction address to the given register.
378 SMC GetVolume { LDA $3200,x }
380 SMC_LoadHighByte GetVolume
382 beq OtherSoundPlaying
387 <label id="Store high-byte">
388 <tag><tt>SMC_StoreHighByte label (, register)</tt></tag>
390 Stores the high-byte address part of an SMC-instruction from the given
397 SMC_StoreHighByte GetLevelData, x
398 SMC_StoreHighByte GetScreenData, x
399 SMC_StoreHighByte GetSoundData, x
402 SMC GetLevelData, { LDA Level1Base+Data }
404 SMC GetScreenData, { LDA Level1Base+Screen, x }
406 SMC GetSoundData, { LDA Level1Base+Sound, y }
410 <label id="Transfer single address">
411 <tag><tt>SMC_TransferAddressSingle label, address (, register)</tt></tag>
413 Transfers the contents of the given address via the given register to the
414 designated SMC instruction.
419 SMC_TransferAddressSingle GetChar, #HelloMsg
423 SMC GetChar, { LDA SMC_AbsAdr, x }
431 <label id="Transfer address">
432 <tag><tt>SMC_TransferAddress label, address</tt></tag>
434 Loads contents of given address to A/X and stores the result to SMC
435 instruction. Allows reuse of register contents by using
436 '<tt>SMC_StoreAddress</tt>' for multiple SMC instruction modifications.
440 SMC_TransferAddress JumpTo, #CloseChannel
442 SMC JumpTo, { JMP OpenChannel }
446 <label id="Store address">
447 <tag><tt>SMC_StoreAddress label</tt></tag>
449 Stores the address value in a/x to a SMC instruction address position.
453 SMC_StoreAddress GetData
455 SMC GetData, { LDA SMC_AbsAdr }
460 <sect1>Operational macros<p>
462 These marcos are determined to let read/modify/write opcodes work on parts of
467 <label id="Operate on value">
468 <tag><tt>SMC_OperateOnValue opcode, label</tt></tag>
470 Let given opcode work on the value part of a SMC instruction.
474 SMC_OperateOnValue ASL, LoadMask ; shift mask to left
476 SMC LoadMask, { LDA #$20 }
479 <label id="Operate on low-byte">
480 <tag><tt>SMC_OperateOnLowByte opcode, label</tt></tag>
482 Same as '<tt/SMC_OperateOnValue/' but renamed for better readability when
483 accessing low-bytes of address.
487 SMC_OperateOnLowByte DEC, AccessData
489 SMC AccessData, { LDX Data }
492 <label id="Operate on high-byte">
493 <tag><tt>SMC_OperateOnHighByte opcode, label</tt></tag>
495 Let the given opcode work on the high-byte part on a SMC-instruction.
500 SMC_OperateOnHighByte INC, GetPageData
502 SMC GetPageData, { LDA SourceData, X }
506 <sect1>Scope macros<p>
508 These marcos are determined to export and import SMC labels out of the current
509 file scope. Please handle with care! If you cannot abstain from leaving the
510 file scope, you should at least document the exported SMC lines very well. On
511 import side no checking is available if the SMC line is correct accessed (e.g.
512 invalid access to the value of an implied instruction)!
515 <label id="Export SMC line under given name">
516 <tag><tt>SMC_Export alias, label</tt></tag>
518 SMC label will be exported under given alias.
523 SMC LoadValue, { LDA #12 }
527 SMC_Export GetValueLoader, GetValue::LoadValue
530 <label id="Import SMC alias">
531 <tag><tt>SMC_Import alias</tt></tag>
533 SMC line is made accessible under given alias.
537 SMC_Import GetValueLoader
539 SMC_TransferValue GetValueLoader, #47
544 <sect>A complex example<p>
545 Let's have a look on a quite sophisticated example for the usage of SMC. It
546 not only modifies code, but also the modification of the code is modified -
547 allowing reuse of some instructions.
550 <tag/The code is from my 'memset()'implementation:/
553 2: SMC_StoreAddress StoreAccuFirstSection
555 4: StoreToFirstSection:
556 5: SMC StoreAccuFirstSection, { sta SMC_AbsAdr, Y }
558 7: RestoreCodeBranchBaseAdr:
559 8: SMC FirstIncHighByte, { SMC_OperateOnHighByte inc, StoreAccuFirstSection } ; code will be overwritten to 'beq RestoreCode' (*)
561 10: SMC_TransferOpcode FirstIncHighByte, OPC_BEQ , x ; change code marked above with (*)
562 11: SMC_TransferValue FirstIncHighByte, #(restoreCode - RestoreCodeBranchBaseAdr-2), x ; set relative address to 'RestoreCode'
565 14: SMC_TransferOpcode FirstIncHighByte, OPC_INC_abs , x ; restore original code...
566 15: SMC_TransferValue FirstIncHighByte, #(<(StoreToFirstSection+2)), x ; (second byte of inc contained low-byte of address)
570 <tag/Some explanation:/
571 Line 2: The register pair A/X contains an address, which is stored on the
572 address location of a SMC line called 'StoreAccuFirstSection'. According to
573 cc65's calling convention, the low-byte is in accu while the high-byte is in
576 Line 5: The (modified) address is accessed.
578 Line 8: We have a line here, which is about to be modified (it begins with
579 SMC), but itself modifies code. Please note: Contrary to the rest of SMC-line
580 modifying macros, the 'OperateOn'-macros just expand their given arguments
581 into a single instruction line. These can be changed of course too.
583 Line 10,11: These lines construct a branch operation for line 8: The
584 X-register will be used to change it from 'inc StoreAccuFirstSection+2'
585 (high-byte operation) to 'beq restoreCode'. Please note: To calculate the
586 relaive branch offset, we introduced a second label
587 ('RestoreCodeBranchBaseAdr') for to calculate it. Some could also use the
588 internal name of the SMC label, but you should abstain to do so - it may be
589 changed in the future...
591 Line 14,15: The original code from line 8 is reestablished.