1 /*
2  *  cpu_macros.h - Macro definitions for 6510 CPU emulation opcode routines
3  *
4  *  Frodo (C) Copyright 1994-2004 Christian Bauer
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20 
21 
22 /*
23  *  Status register flag bits
24  */
25 
26 #define PFLAG_N 0x80
27 #define PFLAG_V 0x40
28 #define PFLAG_B 0x10
29 #define PFLAG_D 0x08
30 #define PFLAG_I 0x04
31 #define PFLAG_Z 0x02
32 #define PFLAG_C 0x01
33 
34 
35 /*
36  *  Opcode flag bits
37  */
38 
39 #define OPFLAG_IRQ_DISABLED 0x100
40 #define OPFLAG_IRQ_ENABLED 0x200
41 #define OPFLAG_INT_DELAYED 0x400
42 
43 
44 /*
45  *  Address calculation macros
46  */
47 
48 // Read zero page operand address
49 #define read_adr_zero \
50 	ADR = read_opcode; inc_pc; next_cycle
51 
52 // Read zero page x-indexed operand address
53 #define read_adr_zero_x \
54 	ADR = read_opcode; inc_pc; next_cycle; \
55 	read_idle_zp(ADR); next_cycle; \
56 	ADR = (ADR + RX) & 0xff
57 
58 // Read zero page y-indexed operand address
59 #define read_adr_zero_y \
60 	ADR = read_opcode; inc_pc; next_cycle; \
61 	read_idle_zp(ADR); next_cycle; \
62 	ADR = (ADR + RY) & 0xff
63 
64 // Read absolute operand address
65 #define read_adr_abs \
66 	ADR = read_opcode; inc_pc; next_cycle; \
67 	ADR |= read_opcode << 8; inc_pc; next_cycle
68 
69 // Read absolute x-indexed operand address (no extra cycle for page crossing)
70 #define read_adr_abs_x \
71 { \
72 	ADR = read_opcode; inc_pc; next_cycle; \
73 	unsigned int tmp = ADR + RX; \
74 	ADR = tmp + (read_opcode << 8); inc_pc; next_cycle; \
75 	if (tmp >= 0x100) { \
76 		read_idle((ADR - 0x100) & 0xffff); \
77 	} else { \
78 		read_idle(ADR); \
79 	} \
80 	next_cycle; \
81 }
82 
83 // Read absolute y-indexed operand address (no extra cycle for page crossing)
84 #define read_adr_abs_y \
85 { \
86 	ADR = read_opcode; inc_pc; next_cycle; \
87 	unsigned int tmp = ADR + RY; \
88 	ADR = tmp + (read_opcode << 8); inc_pc; next_cycle; \
89 	if (tmp >= 0x100) { \
90 		read_idle((ADR - 0x100) & 0xffff); \
91 	} else { \
92 		read_idle(ADR); \
93 	} \
94 	next_cycle; \
95 }
96 
97 // Read indexed indirect operand address (no extra cycle for page crossing)
98 #define read_adr_ind_x \
99 { \
100 	unsigned int tmp = read_opcode; inc_pc; next_cycle; \
101 	read_idle_zp(tmp); next_cycle; \
102 	tmp += RX; \
103 	ADR = read_zp(tmp & 0xff); next_cycle; \
104 	ADR |= read_zp((tmp + 1) & 0xff) << 8; next_cycle; \
105 }
106 
107 // Read indirect indexed operand address (no extra cycle for page crossing)
108 #define read_adr_ind_y \
109 { \
110 	unsigned int tmp = read_opcode; inc_pc; next_cycle; \
111 	unsigned int tmp2 = read_zp(tmp) + RY; next_cycle; \
112 	ADR = tmp2 + (read_zp((tmp + 1) & 0xff) << 8); next_cycle; \
113 	if (tmp2 >= 0x100) { \
114 		read_idle((ADR - 0x100) & 0xffff); \
115 	} else { \
116 		read_idle(ADR); \
117 	} \
118 	next_cycle; \
119 }
120 
121 
122 /*
123  *  Operand fetch macros
124  */
125 
126 // Read immediate operand
127 #define read_byte_imm(to) \
128 	to = read_opcode; inc_pc; next_cycle
129 
130 // Read zero page operand
131 #define read_byte_zero(to) \
132 	read_adr_zero; \
133 	to = read_zp(ADR); next_cycle
134 
135 // Read zero page x-indexed operand
136 #define read_byte_zero_x(to) \
137 	read_adr_zero_x; \
138 	to = read_zp(ADR); next_cycle
139 
140 // Read zero page y-indexed operand
141 #define read_byte_zero_y(to) \
142 	read_adr_zero_y; \
143 	to = read_zp(ADR); next_cycle
144 
145 // Read absolute operand
146 #define read_byte_abs(to) \
147 	read_adr_abs; \
148 	to = read_byte(ADR); next_cycle;
149 
150 // Read absolute x-indexed operand (extra cycle for page crossing)
151 #define read_byte_abs_x(to) \
152 { \
153 	ADR = read_opcode; inc_pc; next_cycle; \
154 	unsigned int tmp = ADR + RX; \
155 	ADR = tmp + (read_opcode << 8); inc_pc; next_cycle; \
156 	if (tmp >= 0x100) { \
157 		/* Page crossed */ \
158 		read_idle((ADR - 0x100) & 0xffff); next_cycle; \
159 	} \
160 	to = read_byte(ADR); next_cycle; \
161 }
162 
163 // Read absolute y-indexed operand (extra cycle for page crossing)
164 #define read_byte_abs_y(to) \
165 { \
166 	ADR = read_opcode; inc_pc; next_cycle; \
167 	unsigned int tmp = ADR + RY; \
168 	ADR = tmp + (read_opcode << 8); inc_pc; next_cycle; \
169 	if (tmp >= 0x100) { \
170 		/* Page crossed */ \
171 		read_idle((ADR - 0x100) & 0xffff); next_cycle; \
172 	} \
173 	to = read_byte(ADR); next_cycle; \
174 }
175 
176 // Read indexed indirect operand (no extra cycle for page crossing)
177 #define read_byte_ind_x(to) \
178 	read_adr_ind_x; \
179 	to = read_byte(ADR); next_cycle;
180 
181 // Read indirect indexed operand (extra cycle for page crossing)
182 #define read_byte_ind_y(to) \
183 { \
184 	unsigned int tmp = read_opcode; inc_pc; next_cycle; \
185 	unsigned int tmp2 = read_zp(tmp) + RY; next_cycle; \
186 	ADR = tmp2 + (read_zp((tmp + 1) & 0xff) << 8); next_cycle; \
187 	if (tmp2 >= 0x100) { \
188 		/* Page crossed */ \
189 		read_idle((ADR - 0x100) & 0xffff); next_cycle; \
190 	} \
191 	to = read_byte(ADR); next_cycle; \
192 }
193 
194 
195 /*
196  *  Other macros
197  */
198 
199 // Set N and Z flags
200 #define set_nz(val) \
201 	N_FLAG = Z_FLAG = val
202 
203 // Pop status flags
204 #define pop_flags \
205 	N_FLAG = PFLAGS = pop_byte & 0xcf; next_cycle; \
206 	Z_FLAG = !(PFLAGS & PFLAG_Z)
207 
208 // Push status flags
209 #ifdef DRIVE_CPU
210 #define push_flags(b_flag) \
211 { \
212 	gcr_drive->Update(current_cycle); \
213 	if (gcr_drive->ByteReady()) \
214 		PFLAGS |= PFLAG_V; \
215 	(N_FLAG & 0x80) ? (PFLAGS |= PFLAG_N) : (PFLAGS &= ~PFLAG_N); \
216 	Z_FLAG ? (PFLAGS &= ~PFLAG_Z) : (PFLAGS |= PFLAG_Z); \
217 	push_byte(PFLAGS | 0x20 | b_flag); next_cycle; \
218 }
219 #else
220 #define push_flags(b_flag) \
221 	(N_FLAG & 0x80) ? (PFLAGS |= PFLAG_N) : (PFLAGS &= ~PFLAG_N); \
222 	Z_FLAG ? (PFLAGS &= ~PFLAG_Z) : (PFLAGS |= PFLAG_Z); \
223 	push_byte(PFLAGS | 0x20 | b_flag); next_cycle
224 #endif
225 
226 // ADC operation
227 #define do_adc(byte) \
228 	if (PFLAGS & PFLAG_D) { \
229 		unsigned int tmp, tmp2; \
230 		tmp = (RA & 0x0f) + (byte & 0x0f) + (PFLAGS & PFLAG_C); \
231 		if (tmp > 9) tmp += 6; \
232 		tmp2 = (tmp & 0x0f) + (RA & 0xf0) + (byte & 0xf0); \
233 		if (tmp > 0x0f) tmp2 += 0x10; \
234 		Z_FLAG = RA + byte + (PFLAGS & PFLAG_C); \
235 		N_FLAG = tmp2; \
236 		(!((RA ^ byte) & 0x80) && ((RA ^ tmp2) & 0x80)) ? (PFLAGS |= PFLAG_V) : (PFLAGS &= ~PFLAG_V); \
237 		if ((tmp2 & 0x1f0) > 0x90) tmp2 += 0x60; \
238 		(tmp2 & 0xf00) ? (PFLAGS |= PFLAG_C) : (PFLAGS &= ~PFLAG_C); \
239 		RA = tmp2; \
240 	} else { \
241 		unsigned int tmp = RA + byte + (PFLAGS & PFLAG_C); \
242 		(tmp > 0xff) ? (PFLAGS |= PFLAG_C) : (PFLAGS &= ~PFLAG_C); \
243 		(!((RA ^ byte) & 0x80) && ((RA ^ tmp) & 0x80)) ? (PFLAGS |= PFLAG_V) : (PFLAGS &= ~PFLAG_V); \
244 		set_nz(RA = tmp); \
245 	} \
246 	break
247 
248 // SBC operation
249 #define do_sbc(byte) \
250 { \
251 	unsigned int tmp = RA - byte - ((PFLAGS & PFLAG_C) ? 0 : 1); \
252 	(((RA ^ tmp) & 0x80) && ((RA ^ byte) & 0x80)) ? (PFLAGS |= PFLAG_V) : (PFLAGS &= ~PFLAG_V); \
253 	if (PFLAGS & PFLAG_D) { \
254 		unsigned int tmp2 = (RA & 0x0f) - (byte & 0x0f) - ((PFLAGS & PFLAG_C) ? 0 : 1); \
255 		if (tmp2 & 0x10) \
256 			tmp2 = ((tmp2 - 6) & 0x0f) | ((RA & 0xf0) - (byte & 0xf0) - 0x10); \
257 		else \
258 			tmp2 = (tmp2 & 0x0f) | ((RA & 0xf0) - (byte & 0xf0)); \
259 		if (tmp2 & 0x100) tmp2 -= 0x60; \
260 		RA = tmp2; \
261 	} else { \
262 		RA = tmp; \
263 	} \
264 	(tmp < 0x100) ? (PFLAGS |= PFLAG_C) : (PFLAGS &= ~PFLAG_C); \
265 	set_nz(tmp); \
266 	break; \
267 }
268 
269 // CMP operation
270 #define do_cmp \
271 	unsigned int tmp = RA - t; \
272 	set_nz(tmp); \
273 	(tmp < 0x100) ? (PFLAGS |= PFLAG_C) : (PFLAGS &= ~PFLAG_C); \
274 	break
275 
276 // CPX operation
277 #define do_cpx \
278 	unsigned int tmp = RX - t; \
279 	set_nz(tmp); \
280 	(tmp < 0x100) ? (PFLAGS |= PFLAG_C) : (PFLAGS &= ~PFLAG_C); \
281 	break
282 
283 // CPY operation
284 #define do_cpy \
285 	unsigned int tmp = RY - t; \
286 	set_nz(tmp); \
287 	(tmp < 0x100) ? (PFLAGS |= PFLAG_C) : (PFLAGS &= ~PFLAG_C); \
288 	break
289 
290 // BIT operation
291 #define do_bit \
292 	Z_FLAG = RA & t; \
293 	N_FLAG = t; \
294 	(t & 0x40) ? (PFLAGS |= PFLAG_V) : (PFLAGS &= ~PFLAG_V); \
295 	break
296 
297 // ASL operation
298 #define do_asl(write_cmd) \
299 	(t & 0x80) ? (PFLAGS |= PFLAG_C) : (PFLAGS &= ~PFLAG_C); \
300 	next_cycle; write_cmd(ADR, set_nz(t << 1)); next_cycle; \
301 	break
302 
303 // LSR operation
304 #define do_lsr(write_cmd) \
305 	(t & 0x01) ? (PFLAGS |= PFLAG_C) : (PFLAGS &= ~PFLAG_C); \
306 	next_cycle; write_cmd(ADR, set_nz(t >> 1)); next_cycle; \
307 	break
308 
309 // ROL operation
310 #define do_rol(write_cmd) \
311 	next_cycle; write_cmd(ADR, set_nz((t << 1) | (PFLAGS & PFLAG_C))); next_cycle; \
312 	(t & 0x80) ? (PFLAGS |= PFLAG_C) : (PFLAGS &= ~PFLAG_C); \
313 	break
314 
315 // ROR operation
316 #define do_ror(write_cmd) \
317 	next_cycle; write_cmd(ADR, set_nz((PFLAGS & PFLAG_C) ? (t >> 1) | 0x80 : (t >> 1))); next_cycle; \
318 	(t & 0x01) ? (PFLAGS |= PFLAG_C) : (PFLAGS &= ~PFLAG_C); \
319 	break
320 
321 // Branch operation
322 #define branch(flag) \
323 { \
324 	int8 tmp = read_opcode; inc_pc; next_cycle; \
325 	if (flag) { \
326 		/* Branch taken */ \
327 		ADR = RPC + (int8)tmp; \
328 		if ((ADR ^ RPC) & 0xff00) { \
329 			/* Page crossed */ \
330 			read_idle_opcode; next_cycle; \
331 			if (tmp & 0x80) { \
332 				read_idle(ADR + 0x100); next_cycle; \
333 			} else { \
334 				read_idle(ADR - 0x100); next_cycle; \
335 			} \
336 			jump(ADR); \
337 		} else { \
338 			/* No page crossed */ \
339 			opcode |= OPFLAG_INT_DELAYED; \
340 			read_idle_opcode; next_cycle; \
341 			jump(ADR); \
342 		} \
343 	} \
344 	break; \
345 }
346 
347 // SLO operation
348 #define do_slo(write_cmd) \
349 	(t & 0x80) ? (PFLAGS |= PFLAG_C) : (PFLAGS &= ~PFLAG_C); \
350 	next_cycle; write_cmd(ADR, t <<= 1); next_cycle; \
351 	set_nz(RA |= t); \
352 	break
353 
354 // RLA operation
355 #define do_rla(write_cmd) \
356 	unsigned int t2 = (t << 1) | (PFLAGS & PFLAG_C); \
357 	next_cycle; write_cmd(ADR, t2); next_cycle; \
358 	(t & 0x80) ? (PFLAGS |= PFLAG_C) : (PFLAGS &= ~PFLAG_C); \
359 	set_nz(RA &= t2); \
360 	break
361 
362 // SRE operation
363 #define do_sre(write_cmd) \
364 	(t & 0x01) ? (PFLAGS |= PFLAG_C) : (PFLAGS &= ~PFLAG_C); \
365 	next_cycle; write_cmd(ADR, t >>= 1); next_cycle; \
366 	set_nz(RA ^= t); \
367 	break
368 
369 // RRA operation
370 #define do_rra(write_cmd) \
371 	unsigned int t2 = (PFLAGS & PFLAG_C) ? (t >> 1) | 0x80 : (t >> 1); \
372 	next_cycle; write_cmd(ADR, t2); next_cycle; \
373 	(t & 0x01) ? (PFLAGS |= PFLAG_C) : (PFLAGS &= ~PFLAG_C); \
374 	do_adc(t2);
375 
376 // DCP operation
377 #define do_dcp(write_cmd) \
378 	t = (t - 1) & 0xff; \
379 	next_cycle; write_cmd(ADR, t); next_cycle; \
380 	t = RA - t; \
381 	set_nz(t); \
382 	(t < 0x100) ? (PFLAGS |= PFLAG_C) : (PFLAGS &= ~PFLAG_C); \
383 	break
384 
385 // ISB operation
386 #define do_isb(write_cmd) \
387 	t = (t + 1) & 0xff; \
388 	next_cycle; write_cmd(ADR, t); next_cycle; \
389 	do_sbc(t);
390