]> git.sur5r.net Git - cc65/blob - doc/smc.sgml
b58fc51abe512edefcfd606ede3a6d8bbecd9d65
[cc65] / doc / smc.sgml
1 <!doctype linuxdoc system>
2
3 <article>
4 <title>ca65 Macros for Self Modifying Code
5 <author>Christian Kr&uuml;ger
6 <date>2012-02-19
7
8 <abstract>
9 The 'smc.inc' macro package for ca65 eases the use, increases the safeness and
10 self-explanation of 'self-modifying-code' (SMC).
11 </abstract>
12
13 <!-- Table of contents -->
14 <toc>
15
16 <!-- Begin the document -->
17
18 <sect>Overview<p>
19 When reading assembler sources, self modifying code is often hard to identify
20 and applying it needs a lot of discipline.
21
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.
25
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:
29
30 <itemize>
31 <item>speed and/or
32 <item>size.
33 </itemize>
34
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!
37
38 The ca65 SMC macro package consists of two files:
39
40 <itemize>
41 <item><tt>smc.inc</tt>
42 <item><tt>opcodes.inc</tt>
43 </itemize>
44
45 The latter is only needed if you also plan to modify opcodes and not only data
46 within your code.
47
48 <sect>Usage<p>
49 The use of the macros is quite simple:
50
51 Original:
52
53 <tscreen><verb>
54     PHA
55     JSR SUBROUTINE
56     PLA
57 </verb></tscreen>
58
59 By applying SMC, the speed will now be increased by once cycle:
60
61 SMC:
62
63 <tscreen><verb>
64     SMC_StoreValue RestoreAccu
65     JSR SUBROUTINE
66 SMC RestoreAccu, { LDA #SMC_Value }
67 </verb></tscreen>
68
69 The first line stores the value of the accu into the '<tt>RestoreAccu</tt>'
70 labeled SMC target.
71
72 Please note:
73 <enum>
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
77         Y-register.
78
79         If the second argument is missing, the accu will be used automatically.
80
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).
85
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!
92 </enum>
93
94 The second line needs no further explanation, this is just a placeholder for
95 some code in the example.
96
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.
100
101 Please note the usage of the value placeholder 'SMC_Value'. Using such a
102 placeholder has two advantages:
103
104 <enum>
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.
110 </enum>
111
112 <sect1>Argument placeholders<p>
113
114 There are four kinds of placeholders:
115
116 <descrip>
117
118   <label id="Address placeholder">
119   <tag><tt>SMC_AbsAdr</tt></tag>
120
121   Used to indicate an address. The value is '<tt>$FADE</tt>'.
122
123   Example: <tt>STA SMC_AbsAdr</tt>
124
125
126   <label id="Zero-Page-Address placeholder">
127   <tag><tt>SMC_ZpAdr</tt></tag>
128
129   Used to indicate a zero-page-address. The value is '<tt>$00</tt>'.
130
131   Example: <tt>LDA SMC_ZpAdr</tt>
132
133
134   <label id="Opcode placeholder">
135   <tag><tt>SMC_Opcode</tt></tag>
136
137   Used to indicate an instruction. The value is '<tt>NOP</tt>'.
138
139   Example: <tt>SMC_Opcode</tt>
140
141
142   <label id="Immediate value placeholder">
143   <tag><tt>SMC_Value</tt></tag>
144
145   Used to indicate a value. The value is '<tt>$42</tt>'.
146
147   Example: <tt>LDX #SMC_Value</tt>
148 </descrip>
149
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:
154
155 <tt>SMC Example, { SMC_Opcode SMC_AbsAdr } </tt>
156
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:
161
162 <tt>SMC Example, { sta SMC_AbsAdr }     ; Note: Opcode will be modified too!</tt>
163
164 <sect1>Accessing opcodes<p>
165
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>'
168 file.
169
170 <descrip>
171
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.
175
176   Example:
177
178 <tscreen><verb>
179 SMC SumRegister, { LDA #10 }
180     JSR OUTPUT
181     SMC_TransferOpcode SumRegister, OPC_ADC_imm, x
182 </verb></tscreen>
183
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>'.
186
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.
190
191   Example:
192 <tscreen><verb>
193 SMC ShiftOrNothing, { LSL }
194     SMC_LoadOpcode ShiftOrNothing, y
195     CPY #OPC_NOP
196     BEQ Exit
197 </verb></tscreen>
198
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.
202
203   Example:
204 <tscreen><verb>
205 SetBoldMode:
206     LDA #OPC_INX
207     SMC_StoreOpcode AdaptCharWidth
208     SMC_StoreOpcode AdaptUnderlineWidth
209     RTS
210     ...
211 SMC AdaptCharWidth, { NOP }
212     ...
213 SMC AdaptUnderlineWidth, { NOP }
214 </verb></tscreen>
215
216 </descrip>
217
218 <sect1>Accessing arguments<p>
219
220 These marcos are determined to get, set and change arguments of instructions:
221
222 <descrip>
223
224   <label id="Change branch">
225   <tag><tt>SMC_ChangeBranch label, destination (, register)</tt></tag>
226
227   Used to modify the destination of a branch instruction. If the adress offset
228   exceeds the supported range of 8-bit of the 6502, a error will be thrown.
229
230   Example:
231 <tscreen><verb>
232 Disable Handler:
233     SMC_ChangeBranch BranchToHandler, Exit
234     RTS
235     ...
236     LDA warning
237 SMC BranchToHandler, { BNE Handler }
238 Exit:
239     RTS
240 </verb></tscreen>
241
242
243   <label id="Transfer value">
244   <tag><tt>SMC_TransferValue label, value (, register)</tt></tag>
245
246   Changes the value of a SMC line.
247
248   Example:
249 <tscreen><verb>
250 ClearDefault:
251     SMC_TransferValue LoadDefault, 0
252     RTS
253     ...
254 SMC LoadDefault, { LDX #25 }
255 </verb></tscreen>
256
257
258   <label id="Load value">
259   <tag><tt>SMC_LoadValue label (, register)</tt></tag>
260
261   Retreives the value of a SMC line.
262
263   Example:
264 <tscreen><verb>
265 ShowDefault:
266     SMC_LoadValue LoadDefault
267     JSR PrintValue
268     RTS
269     ...
270 SMC LoadDefault, { LDX #25 }
271 </verb></tscreen>
272
273
274   <label id="Store value">
275   <tag><tt>SMC_StoreValue label (, register)</tt></tag>
276
277   Stores the value in the register to given SMC line.
278
279   Example:
280 <tscreen><verb>
281 InitCounters:
282     LDY #0
283     SMC_StoreValue GetI, y
284     SMC_StoreValue GetJ, y
285     SMC_StoreValue GetK, y
286     ...
287 SMC GetI, { LDX #SMC_Value      }
288     ...
289 SMC GetJ, { LDX #SMC_Value      }
290     ...
291 SMC GetK, { LDX #SMC_Value      }
292 </verb></tscreen>
293
294
295   <label id="Transfer low-byte">
296   <tag><tt>SMC_TransferLowByte label, value (, register)</tt></tag>
297
298   Does the same as '<tt>SMC_TransferValue</tt>' but should be used for
299   low-bytes of adresses for better readability.
300
301   Example:
302 <tscreen><verb>
303 ActivateSecondDataSet:
304     SMC_TransferLowByte LoadData, $40
305         RTS
306     ...
307 SMC LoadData, { LDA $2000 }
308 </verb></tscreen>
309
310
311   <label id="Load low-byte">
312   <tag><tt>SMC_LoadLowByte label (, register)</tt></tag>
313
314   Does the same as '<tt>SMC_LoadValue</tt>' but should be used for low-bytes
315   of adresses for better readability.
316
317   Example:
318 <tscreen><verb>
319 IsSecondDataSetActive:
320         SMC_LoadLowByte LoadData, y
321         CPY #$40
322         BNE NotActive
323     ...
324 SMC LoadData, { LDA $2000 }
325 </verb></tscreen>
326
327
328   <label id="Store low-byte">
329   <tag><tt>SMC_StoreLowByte label (, register)</tt></tag>
330
331   Does the same as '<tt>SMC_StoreValue</tt>' but should be used for low-bytes
332   of adresses for better readability.
333
334   Example:
335 <tscreen><verb>
336 InitStructureBaseAddresses:
337     LDX #0
338     SMC_StoreLowByte GetPlayerGraphic, x
339     SMC_StoreLowByte GetObjectGraphic, x
340     SMC_StoreLowByte StoreCollisionData, x
341     RTS
342     ...
343 SMC GetPlayerGraphic, { LDX $2000 }
344     ...
345 SMC GetObjectGraphic, { LDA $2100,x }
346     ...
347 SMC StoreCollisionData, { STY $2200 }
348 </verb></tscreen>
349
350
351   <label id="Transfer high-byte">
352   <tag><tt>SMC_TransferHighByte label, value (, register)</tt></tag>
353
354   Loads and stores the given value via the named register to the high-byte
355   adress portion of an SMC-instruction.
356
357   Example:
358 <tscreen><verb>
359 PlaySFX:
360 SMC GetVolume { LDA $3200,x }
361     STA SoundOut
362     INX
363     BNE PlaySFX
364     ...
365 PlayOtherSound:
366     SMC_TransferHighByte GetVolume, $34
367 </verb></tscreen>
368
369
370   <label id="Load high-byte">
371   <tag><tt>SMC_LoadHighByte label (, register)</tt></tag>
372
373   Loads the high-byte part of an SMC-instruction adress to the given register.
374
375   Example:
376 <tscreen><verb>
377 PlaySFX:
378 SMC GetVolume { LDA $3200,x }
379     ...
380     SMC_LoadHighByte GetVolume
381     cmp #$34
382     beq OtherSoundPlaying
383     ...
384 </verb></tscreen>
385
386
387   <label id="Store high-byte">
388   <tag><tt>SMC_StoreHighByte label (, register)</tt></tag>
389
390   Stores the high-byte adress part of an SMC-instruction from the given
391   register.
392
393   Example:
394 <tscreen><verb>
395 SetupLevel2:
396     LDX #(>Level2Base)
397     SMC_StoreHighByte GetLevelData, x
398     SMC_StoreHighByte GetScreenData, x
399     SMC_StoreHighByte GetSoundData, x
400     RTS
401     ...
402 SMC GetLevelData, { LDA Level1Base+Data }
403     ...
404 SMC GetScreenData, { LDA Level1Base+Screen, x }
405     ...
406 SMC GetSoundData, { LDA Level1Base+Sound, y }
407 </verb></tscreen>
408
409
410   <label id="Transfer single adress">
411   <tag><tt>SMC_TransferAddressSingle label, address (, register)</tt></tag>
412
413   Transfers the contents of the given address via the given register to the
414   designated SMC instruction.
415
416   Example:
417 <tscreen><verb>
418 PrintHello:
419     SMC_TransferAddressSingle GetChar, #HelloMsg
420     ...
421     LDX #0
422 NextChar:
423 SMC GetChar, { LDA  SMC_AbsAdr, x }
424     BEQ leave
425     JSR CharOut
426     INX
427     BNE NextChar
428 </verb></tscreen>
429
430
431   <label id="Transfer adress">
432   <tag><tt>SMC_TransferAddress label, address</tt></tag>
433
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.
437
438   Example:
439 <tscreen><verb>
440     SMC_TransferAddress JumpTo, #CloseChannel
441     ...
442 SMC JumpTo, { JMP OpenChannel }
443 </verb></tscreen>
444
445
446   <label id="Store address">
447   <tag><tt>SMC_StoreAddress label</tt></tag>
448
449   Stores the address value in a/x to a SMC instruction address position.
450
451   Example:
452 <tscreen><verb>
453     SMC_StoreAddress GetData
454     ...
455 SMC GetData, { LDA SMC_AbsAdr }
456 </verb></tscreen>
457
458 </descrip>
459
460 <sect1>Operational macros<p>
461
462 These marcos are determined to let read/modify/write opcodes work on parts of
463 SMC instructions.
464
465 <descrip>
466
467  <label id="Operate on value">
468   <tag><tt>SMC_OperateOnValue opcode, label</tt></tag>
469
470   Let given opcode work on the value part of a SMC instruction.
471
472   Example:
473 <tscreen><verb>
474     SMC_OperateOnValue ASL, LoadMask    ; shift mask to left
475     ...
476 SMC LoadMask, { LDA #$20 }
477 </verb></tscreen>
478
479   <label id="Operate on low-byte">
480   <tag><tt>SMC_OperateOnLowByte opcode, label</tt></tag>
481
482   Same as '<tt/SMC_OperateOnValue/' but renamed for better readability when
483   accessing low-bytes of address.
484
485   Example:
486 <tscreen><verb>
487     SMC_OperateOnLowByte DEC, AccessData
488     ...
489 SMC AccessData, { LDX Data }
490 </verb></tscreen>
491
492   <label id="Operate on high-byte">
493   <tag><tt>SMC_OperateOnHighByte opcode, label</tt></tag>
494
495   Let the given opcode work on the high-byte part on a SMC-instruction.
496
497   Example:
498 <tscreen><verb>
499 NextPage:
500     SMC_OperateOnHighByte INC, GetPageData
501     ...
502 SMC GetPageData, { LDA SourceData, X }
503 </verb></tscreen>
504 </descrip>
505
506 <sect1>Scope macros<p>
507
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)!
513
514 <descrip>
515   <label id="Export SMC line under given name">
516   <tag><tt>SMC_Export alias, label</tt></tag>
517
518   SMC label will be exported under given alias.
519
520   Example:
521 <tscreen><verb>
522 .proc GetValue
523 SMC LoadValue, { LDA #12 }
524     rts
525 .endproc
526
527 SMC_Export GetValueLoader, GetValue::LoadValue
528 </verb></tscreen>
529
530   <label id="Import SMC alias">
531   <tag><tt>SMC_Import alias</tt></tag>
532
533   SMC line is made accessible under given alias.
534
535   Example:
536 <tscreen><verb>
537 SMC_Import GetValueLoader
538     ...
539     SMC_TransferValue GetValueLoader, #47
540     ...
541 </verb></tscreen>
542 </descrip>
543
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.
548
549 The code is from my 'memset()'implementation:
550
551 <descrip>
552 <tscreen><verb>
553  1:     ...
554  2:     SMC_StoreAddress StoreAccuFirstSection
555  3:
556  4: StoreToFirstSection:
557  5:     SMC StoreAccuFirstSection, { sta SMC_AbsAdr, Y }
558  6:             ...
559  7: RestoreCodeBranchBaseAdr:
560  8:     SMC FirstIncHighByte, { SMC_OperateOnHighByte inc, StoreAccuFirstSection }              ; code will be overwritten to 'beq RestoreCode' (*)
561  9:     ...
562 10:     SMC_TransferOpcode FirstIncHighByte, OPC_BEQ , x                                        ; change code marked above with (*)
563 11:     SMC_TransferValue FirstIncHighByte, #(restoreCode - RestoreCodeBranchBaseAdr-2), x      ; set relative adress to 'RestoreCode'
564 12:     ...
565 13: restoreCode:
566 14:     SMC_TransferOpcode FirstIncHighByte, OPC_INC_abs , x                                    ; restore original code...
567 15:     SMC_TransferValue FirstIncHighByte, #(<(StoreToFirstSection+2)), x                      ; (second byte of inc contained low-byte of adress)
568 16:             ...
569 </verb></tscreen>
570
571 Some explanation:
572
573 Line 2: The register pair A/X contains an address, which is stored on the
574 address location of a SMC line called 'StoreAccuFirstSection'. According to
575 cc65's calling convention, the low-byte is in accu while the high-byte is in
576 the X-register.
577
578 Line 5: The (modified) address is accessed.
579
580 Line 8: We have a line here, which is about to be modified (it begins with
581 SMC), but itself modifies code. Please note: Contrary to the rest of SMC-line
582 modifying macros, the 'OperateOn'-macros just expand their given arguments
583 into a single instruction line. These can be changed of course too.
584
585 Line 10,11: These lines construct a branch operation for line 8: The
586 X-register will be used to change it from 'inc StoreAccuFirstSection+2'
587 (high-byte operation) to 'beq restoreCode'. Please note: To calculate the
588 relaive branch offset, we introduced a second label
589 ('RestoreCodeBranchBaseAdr') for to calculate it. Some could also use the
590 internal name of the SMC label, but you should abstain to do so - it may be
591 changed in the future...
592
593 Line 14,15: The original code from line 8 is reestablished.
594 </descrip>
595 </article>
596