1 /*****************************************************************************
2  *
3  *   m6502ops.h
4  *   Addressing mode and opcode macros for 6502,65c02,65sc02,6510,n2a03 CPUs
5  *
6  *   Copyright Juergen Buchmueller, all rights reserved.
7  *   65sc02 core Copyright Peter Trauner, all rights reserved.
8  *
9  *   - This source code is released as freeware for non-commercial purposes.
10  *   - You are free to use and redistribute this code in modified or
11  *     unmodified form, provided you list me in the credits.
12  *   - If you modify this source code, you must add a notice to each modified
13  *     source file that it has been changed.  If you're a nice person, you
14  *     will clearly mark each change too.  :)
15  *   - If you wish to use this for commercial purposes, please contact me at
16  *     pullmoll@t-online.de
17  *   - The author of this copywritten work reserves the right to change the
18  *     terms of its usage and license at any time, including retroactively
19  *   - This entire notice must remain in the source code.
20  *
21  *****************************************************************************/
22 
23 
24 /***************************************************************
25  ***************************************************************
26  *          Macros to emulate the 65C02 opcodes
27  ***************************************************************
28  ***************************************************************/
29 
30 /* 65C02 *******************************************************
31  *  EA = absolute address + X
32  * one additional read if page boundary is crossed
33  ***************************************************************/
34 #define EA_ABX_C02_P											\
35 	EA_ABS;														\
36 	if ( EAL + X > 0xff ) {										\
37 		RDMEM( PCW - 1 );										\
38 	}															\
39 	EAW += X;
40 
41 /* 65C02 *******************************************************
42  *  EA = absolute address + X
43  ***************************************************************/
44 #define EA_ABX_C02_NP											\
45 	EA_ABS;														\
46 	RDMEM( PCW - 1 );											\
47 	EAW += X;
48 
49 /***************************************************************
50  *  EA = absolute address + Y
51  * one additional read if page boundary is crossed
52  ***************************************************************/
53 #define EA_ABY_C02_P											\
54 	EA_ABS;														\
55 	if ( EAL + Y > 0xff ) {										\
56 		RDMEM( PCW - 1 );										\
57 	}															\
58 	EAW += Y;
59 
60 /* 65C02 *******************************************************
61  *  EA = absolute address + Y
62  ***************************************************************/
63 #define EA_ABY_C02_NP											\
64 	EA_ABS;														\
65 	RDMEM( PCW - 1 );											\
66 	EAW += Y
67 
68 /* 65C02 *******************************************************
69  *  EA = zero page indirect + Y (post indexed)
70  *  subtract 1 cycle if page boundary is crossed
71  ***************************************************************/
72 #define EA_IDY_C02_P											\
73 	ZPL = RDOPARG();											\
74 	EAL = RDMEM(ZPD);											\
75 	ZPL++;														\
76 	EAH = RDMEM(ZPD);											\
77 	if (EAL + Y > 0xff) {										\
78 		RDMEM( PCW - 1 );										\
79 	}															\
80 	EAW += Y;
81 
82 /* 65C02 *******************************************************
83  *  EA = zero page indirect + Y
84  ***************************************************************/
85 #define EA_IDY_C02_NP											\
86 	ZPL = RDOPARG();											\
87 	EAL = RDMEM(ZPD);											\
88 	ZPL++;														\
89 	EAH = RDMEM(ZPD);											\
90 	RDMEM( PCW - 1 );											\
91 	EAW += Y
92 
93 /* 65C02 *******************************************************
94  *  EA = indirect (only used by JMP)
95  * correct overflow handling
96  ***************************************************************/
97 #define EA_IND_C02												\
98 	EA_ABS;														\
99 	tmp = RDMEM(EAD);											\
100 	RDMEM(PCW-1);												\
101 	EAD++;														\
102 	EAH = RDMEM(EAD);											\
103 	EAL = tmp
104 
105 /* 65C02 *******************************************************
106  *  EA = indirect plus x (only used by 65c02 JMP)
107  ***************************************************************/
108 #define EA_IAX													\
109 	EA_ABS;														\
110 	RDMEM( PCW - 1 );											\
111 	if (EAL + X > 0xff) {										\
112 		RDMEM( PCW - 1 );										\
113 	}															\
114 	EAW += X;													\
115 	tmp = RDMEM(EAD);											\
116 	EAD++;														\
117 	EAH = RDMEM(EAD);											\
118 	EAL = tmp
119 
120 /* read a value into tmp */
121 /* Base number of cycles taken for each mode (including reading of opcode):
122    RD_ABX_C02_P         4/5
123    RD_ABX_C02_NP/WR_ABX_C02_NP  5
124    RD_ABY_C02_P         4/5
125    RD_IDY_C02_P         5/6
126    WR_IDY_C02_NP        6
127  */
128 #define RD_ABX_C02_P	EA_ABX_C02_P; tmp = RDMEM(EAD)
129 #define RD_ABX_C02_NP	EA_ABX_C02_NP; tmp = RDMEM(EAD)
130 #define RD_ABX_C02_NP_DISCARD	EA_ABX_C02_NP; RDMEM(EAD)
131 #define RD_ABY_C02_P	EA_ABY_C02_P; tmp = RDMEM(EAD)
132 #define RD_IDY_C02_P	EA_IDY_C02_P; tmp = RDMEM_ID(EAD); m6502_ICount -= 1
133 
134 #define WR_ABX_C02_NP	EA_ABX_C02_NP; WRMEM(EAD, tmp)
135 #define WR_ABY_C02_NP	EA_ABY_C02_NP; WRMEM(EAD, tmp)
136 #define WR_IDY_C02_NP	EA_IDY_C02_NP; WRMEM_ID(EAD, tmp); m6502_ICount -= 1
137 
138 
139 /* 65C02********************************************************
140  *  BRA  branch relative
141  *  extra cycle if page boundary is crossed
142  ***************************************************************/
143 #define BRA_C02(cond)											\
144 	tmp = RDOPARG();											\
145 	if (cond)													\
146 	{															\
147 		RDMEM(PCW);												\
148 		EAW = PCW + (signed char)tmp;							\
149 		if ( EAH != PCH ) {										\
150 			RDMEM( PCW - 1 );									\
151 		}														\
152 		PCD = EAD;												\
153 		CHANGE_PC;												\
154 	}
155 
156 /* 65C02 ********************************************************
157  *  ADC Add with carry
158  * different setting of flags in decimal mode
159  ***************************************************************/
160 #define ADC_C02 												\
161 	if (P & F_D)												\
162 	{															\
163 		int c = (P & F_C);										\
164 		int lo = (A & 0x0f) + (tmp & 0x0f) + c; 				\
165 		int hi = (A & 0xf0) + (tmp & 0xf0); 					\
166 		P &= ~(F_V | F_C);										\
167 		if( lo > 0x09 ) 										\
168 		{														\
169 			hi += 0x10; 										\
170 			lo += 0x06; 										\
171 		}														\
172 		if( ~(A^tmp) & (A^hi) & F_N )							\
173 			P |= F_V;											\
174 		if( hi > 0x90 ) 										\
175 			hi += 0x60; 										\
176 		if( hi & 0xff00 )										\
177 			P |= F_C;											\
178 		A = (lo & 0x0f) + (hi & 0xf0);							\
179 		RDMEM( PCW - 1 );										\
180 	}															\
181 	else														\
182 	{															\
183 		int c = (P & F_C);										\
184 		int sum = A + tmp + c;									\
185 		P &= ~(F_V | F_C);										\
186 		if( ~(A^tmp) & (A^sum) & F_N )							\
187 			P |= F_V;											\
188 		if( sum & 0xff00 )										\
189 			P |= F_C;											\
190 		A = (UINT8) sum;										\
191 	}															\
192 	SET_NZ(A)
193 
194 /* 65C02 ********************************************************
195  *  SBC Subtract with carry
196  * different setting of flags in decimal mode
197  ***************************************************************/
198 #define SBC_C02													\
199 	if (P & F_D)												\
200 	{															\
201 		int c = (P & F_C) ^ F_C;								\
202 		int sum = A - tmp - c;									\
203 		int lo = (A & 0x0f) - (tmp & 0x0f) - c; 				\
204 		int hi = (A & 0xf0) - (tmp & 0xf0); 					\
205 		P &= ~(F_V | F_C);										\
206 		if( (A^tmp) & (A^sum) & F_N )							\
207 			P |= F_V;											\
208 		if( lo & 0xf0 ) 										\
209 			lo -= 6;											\
210 		if( lo & 0x80 ) 										\
211 			hi -= 0x10; 										\
212 		if( hi & 0x0f00 )										\
213 			hi -= 0x60; 										\
214 		if( (sum & 0xff00) == 0 )								\
215 			P |= F_C;											\
216 		A = (lo & 0x0f) + (hi & 0xf0);							\
217 		RDMEM( PCW - 1 );										\
218 	}															\
219 	else														\
220 	{															\
221 		int c = (P & F_C) ^ F_C;								\
222 		int sum = A - tmp - c;									\
223 		P &= ~(F_V | F_C);										\
224 		if( (A^tmp) & (A^sum) & F_N )							\
225 			P |= F_V;											\
226 		if( (sum & 0xff00) == 0 )								\
227 			P |= F_C;											\
228 		A = (UINT8) sum;										\
229 	}															\
230 	SET_NZ(A)
231 
232 /* 65C02 *******************************************************
233  *  BBR Branch if bit is reset
234  ***************************************************************/
235 #define BBR(bit)												\
236 	BRA(!(tmp & (1<<bit)))
237 
238 /* 65C02 *******************************************************
239  *  BBS Branch if bit is set
240  ***************************************************************/
241 #define BBS(bit)												\
242 	BRA(tmp & (1<<bit))
243 
244 /* 65c02 ********************************************************
245  *  BRK Break
246  *  increment PC, push PC hi, PC lo, flags (with B bit set),
247  *  set I flag, reset D flag and jump via IRQ vector
248  ***************************************************************/
249 #define BRK_C02 												\
250 	RDOPARG();													\
251 	PUSH(PCH);													\
252 	PUSH(PCL);													\
253 	PUSH(P | F_B);												\
254 	P = (P | F_I) & ~F_D;										\
255 	PCL = RDMEM(M6502_IRQ_VEC); 								\
256 	PCH = RDMEM(M6502_IRQ_VEC+1);								\
257 	CHANGE_PC
258 
259 
260 /* 65C02 *******************************************************
261  *  DEA Decrement accumulator
262  ***************************************************************/
263 #define DEA 													\
264 	A = (UINT8)(A - 1);											\
265 	SET_NZ(A)
266 
267 /* 65C02 *******************************************************
268  *  INA Increment accumulator
269  ***************************************************************/
270 #define INA 													\
271 	A = (UINT8)(A + 1);											\
272 	SET_NZ(A)
273 
274 /* 65C02 *******************************************************
275  *  PHX Push index X
276  ***************************************************************/
277 #define PHX 													\
278 	PUSH(X)
279 
280 /* 65C02 *******************************************************
281  *  PHY Push index Y
282  ***************************************************************/
283 #define PHY 													\
284 	PUSH(Y)
285 
286 /* 65C02 *******************************************************
287  *  PLX Pull index X
288  ***************************************************************/
289 #define PLX 													\
290 	RDMEM(SPD);													\
291 	PULL(X);													\
292 	SET_NZ(X)
293 
294 /* 65C02 *******************************************************
295  *  PLY Pull index Y
296  ***************************************************************/
297 #define PLY 													\
298 	RDMEM(SPD);													\
299 	PULL(Y);													\
300 	SET_NZ(Y)
301 
302 /* 65C02 *******************************************************
303  *  RMB Reset memory bit
304  ***************************************************************/
305 #define RMB(bit)												\
306 	tmp &= ~(1<<bit)
307 
308 /* 65C02 *******************************************************
309  *  SMB Set memory bit
310  ***************************************************************/
311 #define SMB(bit)												\
312 	tmp |= (1<<bit)
313 
314 /* 65C02 *******************************************************
315  * STZ  Store zero
316  ***************************************************************/
317 #define STZ 													\
318 	tmp = 0
319 
320 /* 65C02 *******************************************************
321  * TRB  Test and reset bits
322  ***************************************************************/
323 #define TRB 													\
324 	SET_Z(tmp&A);												\
325 	tmp &= ~A
326 
327 /* 65C02 *******************************************************
328  * TSB  Test and set bits
329  ***************************************************************/
330 #define TSB 													\
331 	SET_Z(tmp&A);												\
332 	tmp |= A
333 
334 /* 6502 ********************************************************
335  *  BIT Bit test Immediate, only Z affected
336  ***************************************************************/
337 #undef BIT_IMM_C02
338 #define BIT_IMM_C02												\
339 	P &= ~(F_Z);												\
340 	if ((tmp & A) == 0) 										\
341 		P |= F_Z
342 
343 
344 /***************************************************************
345  ***************************************************************
346  *          Macros to emulate the 65sc02 opcodes
347  ***************************************************************
348  ***************************************************************/
349 
350 
351 /* 65sc02 ********************************************************
352  *  BSR Branch to subroutine
353  ***************************************************************/
354 #define BSR 													\
355 	EAL = RDOPARG();											\
356 	RDMEM(SPD);													\
357 	PUSH(PCH);													\
358 	PUSH(PCL);													\
359 	EAH = RDOPARG();											\
360 	EAW = PCW + (INT16)(EAW-1); 								\
361 	PCD = EAD;													\
362 	CHANGE_PC
363