summaryrefslogtreecommitdiff
path: root/doc/www_webmail_supervisord_org_tutorials_register_preservation.txt
blob: b8833ffc1cab6d76efa3b0cbddaa4a4eb91ace6d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
  Register Preservation Using The Stack (and a BRK handler)

   by Bruce Clark, 7 Jun 2004
     __________________________________________________________________

   On the 65C02, a subroutine can save the A, X, and Y registers without
   altering them by using:
PHA
PHX
PHY

   The subroutine then restores A, X, and Y by exiting with:
PLY
PLX
PLA
RTS

   The PHX/PLX and PHY/PLY instructions are not available on the 6502. On
   the 6502, A, X, and Y are often saved using:
PHA
TXA
PHA
TYA
PHA

   which preserves X and Y, but overwrites A. The subroutine then restores
   A, X, and Y by exiting with:
PLA
TAY
PLA
TAX
PLA
RTS

   One way to preserve A (to allow it to be passed as a parameter to the
   subroutine) -- as well as X and Y -- is to use:
STA TEMP
PHA
TXA
PHA
TYA
PHA
LDA TEMP

   which takes 4 additional bytes and 6 additional cycles if TEMP is a
   zero page location, and 6 additional bytes and 8 additional cycles if
   TEMP is an absolute memory location.

   This is usually sufficient, but if, for example, the subroutine is used
   by the main program and by an interrupt routine, interrupts must be
   disabled so that TEMP will not be in use when the interrupt occurs.

   An alternative is to use:
PHA
TXA
TSX
PHA
TYA
PHA
INX
LDA $100,X ; get A from the stack

   which takes 5 additional bytes and 8 additional cycles (as compared to
   the the first 6502 example, not the previous example) but interrupts
   need not be disabled since it gets A from the stack rather than
   requiring a special memory location. This could be used in a subroutine
   called by an NMI (since an NMI can't be disabled). Remember, NMIs are
   dangerous, and should be used with extreme caution if at all.

   Notice that the idea is to get the stack pointer into X as soon as
   possible so that the number INX instructions needed can be minimized.

   The code above works correctly regardless of the value of the stack
   pointer. As long as the stack pointer is not $FF, the last two
   instructions could be replaced with a LDA $101,X instruction, which
   would save 1 byte and two cycles. Ordinarily, this is not a problem
   since the stack pointer is usually initialized to $FF (and will
   therefore be less than $FF after the PHA). It goes without saying that
   the software should be fully debugged before even considering the LDA
   $101,X change.

   The code above preserves A, but overwrites X. To preserve A, X, and Y,
   use:
PHA
TXA
TSX
PHA
TYA
PHA
INX
LDA $100,X ; get A from the stack
PHA
DEX
LDA $100,X ; get X from the stack
TAX
PLA

   which is 7 bytes and 15 cycles longer than the previous example. This
   may be more trouble than it is worth, but it could come in handy for
   subroutines that are used for debugging (where the idea is to be as
   independent as possible and to stay as far out of the way of the main
   program as possible).

   The preceding techniques can also be applied to the BRK/IRQ interrupt
   handler. For example, on the 6502,
PHA
TXA
TSX
PHA
INX
INX
LDA $100,X ; get the status register from the stack
AND #$10   ; mask B flag
BNE BREAK
BEQ IRQ

   can be used to determine whether the interrupt was caused by a BRK
   instruction or an IRQ. Ordinarily, of course, the code would simply
   fall through to the IRQ handler, eliminating the BEQ instruction. The
   BRK and IRQ handlers would then exit with:
PLA
TAX
PLA
RTI

   Alternatively, A and X could be restored immediately after branching
   (or falling through) to the BRK or IRQ interrupt handler.

   On the 65C02, with PHX and PLX available,
PHX
TSX
PHA
INX
INX
LDA $100,X ; get the status register from the stack
AND #$10   ; mask B flag
BNE BREAK
BEQ IRQ

   can be used, which takes 1 fewer byte and 2 fewer cycles than the 6502
   example. The BRK and IRQ handlers would then exit with:
PLA
PLX
RTI

   which also takes 1 fewer byte and 2 fewer cycles than the 6502 example.

   Note that A and X are saved in the opposite order that they were in the
   6502 example!

   The B (Break) flag, bit 4 of the P (Processor status) register, is a
   frequent source of confusion on the 6502. The sole purpose of this flag
   is to distinguish a BRK from a IRQ. However, the behavior of BRK and
   IRQ (and how to distinguish between the two) can be described without
   even mentioning the B flag.

   After the return address has been pushed onto the stack, a BRK
   instruction pushes the value of the P register ORed with $10, then
   transfers control to the BRK/IRQ interrupt handler.

   After the return address has been pushed onto the stack, an IRQ
   interrupt pushes the value of the P register ANDed with $EF, then
   transfers control to the BRK/IRQ interrupt handler.

   This means that the value of the P register that was pushed onto the
   stack must be used to distinguish a BRK from a IRQ, not the value of
   the P register upon entry to the BRK/IRQ interrupt handler.
   Specifically,
PHA
PHP
PLA
AND #$10  ; mask B flag
BNE BREAK
BEQ IRQ

   does NOT properly distinguish a BRK from an IRQ!

   Chelly adds: At the end proper checking of the B flag is discussed. My
   feedback is that it would be simpler to explain that there is no B flag
   in the processor status register; that bit is simply unused. When
   pushing the status register on the stack, that bit is set to a fixed
   value based on the instruction/event (set for PHP and BRK, clear for
   NMI). This is much simpler to explain and leads to no incorrect
   assumption that there is a B flag in the status register that can be
   checked.
     __________________________________________________________________

   Last page update: November 11, 2006.