1 /*****************************************************************************
2 
3 	h6280.c - Portable HuC6280 emulator
4 
5 	Copyright (c) 1999, 2000 Bryan McPhail, mish@tendril.co.uk
6 
7 	This source code is based (with permission!) on the 6502 emulator by
8 	Juergen Buchmueller.  It is released as part of the Mame emulator project.
9 	Let me know if you intend to use this code in any other project.
10 
11 
12 	NOTICE:
13 
14 	This code is around 99% complete!  Several things are unimplemented,
15 	some due to lack of time, some due to lack of documentation, mainly
16 	due to lack of programs using these features.
17 
18 	csh, csl opcodes are not supported.
19 	set opcode and T flag behaviour are not supported.
20 
21 	I am unsure if instructions like SBC take an extra cycle when used in
22 	decimal mode.  I am unsure if flag B is set upon execution of rti.
23 
24 	Cycle counts should be quite accurate, illegal instructions are assumed
25 	to take two cycles.
26 
27 
28 	Changelog, version 1.02:
29 		JMP + indirect X (0x7c) opcode fixed.
30 		SMB + RMB opcodes fixed in disassembler.
31 		change_pc function calls removed.
32 		TSB & TRB now set flags properly.
33 		BIT opcode altered.
34 
35 	Changelog, version 1.03:
36 		Swapped IRQ mask for IRQ1 & IRQ2 (thanks Yasuhiro)
37 
38 	Changelog, version 1.04, 28/9/99-22/10/99:
39 		Adjusted RTI (thanks Karl)
40  		TST opcodes fixed in disassembler (missing break statements in a case!).
41 		TST behaviour fixed.
42 		SMB/RMB/BBS/BBR fixed in disassembler.
43 
44 	Changelog, version 1.05, 8/12/99-16/12/99:
45 		Added CAB's timer implementation (note: irq ack & timer reload are changed).
46 		Fixed STA IDX.
47 		Fixed B flag setting on BRK.
48 		Assumed CSH & CSL to take 2 cycles each.
49 
50 		Todo:  Performance could be improved by precalculating timer fire position.
51 
52 	Changelog, version 1.06, 4/5/00 - last opcode bug found?
53 		JMP indirect was doing a EAL++; instead of EAD++; - Obviously causing
54 		a corrupt read when L = 0xff!  This fixes Bloody Wolf and Trio The Punch!
55 
56 	Changelog, version 1.07, 3/9/00:
57 		Changed timer to be single shot - fixes Crude Buster music in level 1.
58 
59 ******************************************************************************/
60 
61 #include "memory.h"
62 #include "cpuintrf.h"
63 #include "mamedbg.h"
64 #include "h6280.h"
65 
66 #include <stdio.h>
67 #include <string.h>
68 
69 #include <retro_inline.h>
70 
71 extern FILE * errorlog;
72 
73 static UINT8 reg_layout[] = {
74 	H6280_PC, H6280_S, H6280_P, H6280_A, H6280_X, H6280_Y, -1,
75 	H6280_IRQ_MASK, H6280_TIMER_STATE, H6280_NMI_STATE, H6280_IRQ1_STATE, H6280_IRQ2_STATE, H6280_IRQT_STATE,
76 #ifdef MAME_DEBUG
77 	-1,
78 	H6280_M1, H6280_M2, H6280_M3, H6280_M4, -1,
79 	H6280_M5, H6280_M6, H6280_M7, H6280_M8,
80 #endif
81 	0
82 };
83 
84 /* Layout of the debugger windows x,y,w,h */
85 static UINT8 win_layout[] = {
86 	25, 0,55, 4,	/* register window (top rows) */
87 	 0, 0,24,22,	/* disassembler window (left colums) */
88 	25, 5,55, 8,	/* memory #1 window (right, upper middle) */
89 	25,14,55, 8,	/* memory #2 window (right, lower middle) */
90 	 0,23,80, 1,	/* command line window (bottom rows) */
91 };
92 
93 int 	h6280_ICount = 0;
94 
95 /****************************************************************************
96  * The 6280 registers.
97  ****************************************************************************/
98 typedef struct
99 {
100 	PAIR  ppc;			/* previous program counter */
101     PAIR  pc;           /* program counter */
102     PAIR  sp;           /* stack pointer (always 100 - 1FF) */
103     PAIR  zp;           /* zero page address */
104     PAIR  ea;           /* effective address */
105     UINT8 a;            /* Accumulator */
106     UINT8 x;            /* X index register */
107     UINT8 y;            /* Y index register */
108     UINT8 p;            /* Processor status */
109     UINT8 mmr[8];       /* Hu6280 memory mapper registers */
110     UINT8 irq_mask;     /* interrupt enable/disable */
111     UINT8 timer_status; /* timer status */
112 	UINT8 timer_ack;	/* timer acknowledge */
113     int timer_value;    /* timer interrupt */
114     int timer_load;		/* reload value */
115 	int extra_cycles;	/* cycles used taking an interrupt */
116     int nmi_state;
117     int irq_state[3];
118     int (*irq_callback)(int irqline);
119 
120 #if LAZY_FLAGS
121     int NZ;             /* last value (lazy N and Z flag) */
122 #endif
123 
124 }   h6280_Regs;
125 
126 static  h6280_Regs  h6280;
127 
128 #ifdef  MAME_DEBUG /* Need some public segmentation registers for debugger */
129 UINT8	H6280_debug_mmr[8];
130 #endif
131 
132 /* include the macros */
133 #include "h6280ops.h"
134 
135 /* include the opcode macros, functions and function pointer tables */
136 #include "tblh6280.c"
137 
138 /*****************************************************************************/
h6280_init(void)139 void h6280_init(void)
140 {
141 }
142 
h6280_reset(void * param)143 void h6280_reset(void *param)
144 {
145 	int i;
146 
147 	/* wipe out the h6280 structure */
148 	memset(&h6280, 0, sizeof(h6280_Regs));
149 
150 	/* set I and Z flags */
151 	P = _fI | _fZ;
152 
153     /* stack starts at 0x01ff */
154 	h6280.sp.d = 0x1ff;
155 
156     /* read the reset vector into PC */
157 	PCL = RDMEM(H6280_RESET_VEC);
158 	PCH = RDMEM((H6280_RESET_VEC+1));
159 
160 	/* timer off by default */
161 	h6280.timer_status=0;
162 	h6280.timer_ack=1;
163 
164     /* clear pending interrupts */
165 	for (i = 0; i < 3; i++)
166 		h6280.irq_state[i] = CLEAR_LINE;
167 }
168 
h6280_exit(void)169 void h6280_exit(void)
170 {
171 	/* nothing */
172 }
173 
h6280_execute(int cycles)174 int h6280_execute(int cycles)
175 {
176 	int in,lastcycle,deltacycle;
177 	h6280_ICount = cycles;
178 
179     /* Subtract cycles used for taking an interrupt */
180     h6280_ICount -= h6280.extra_cycles;
181 	h6280.extra_cycles = 0;
182 	lastcycle = h6280_ICount;
183 
184 	/* Execute instructions */
185 	do
186     {
187 		h6280.ppc = h6280.pc;
188 
189 #ifdef  MAME_DEBUG
190 	 	{
191 			if (mame_debug)
192 			{
193 				/* Copy the segmentation registers for debugger to use */
194 				int i;
195 				for (i=0; i<8; i++)
196 					H6280_debug_mmr[i]=h6280.mmr[i];
197 
198 				MAME_Debug();
199 			}
200 		}
201 #endif
202 
203 		/* Execute 1 instruction */
204 		in=RDOP();
205 		PCW++;
206 		insnh6280[in]();
207 
208 		/* Check internal timer */
209 		if(h6280.timer_status)
210 		{
211 			deltacycle = lastcycle - h6280_ICount;
212 			h6280.timer_value -= deltacycle;
213 			if(h6280.timer_value<=0 && h6280.timer_ack==1)
214 			{
215 				h6280.timer_ack=h6280.timer_status=0;
216 				h6280_set_irq_line(2,ASSERT_LINE);
217 			}
218 		}
219 		lastcycle = h6280_ICount;
220 
221 		/* If PC has not changed we are stuck in a tight loop, may as well finish */
222 		if( h6280.pc.d == h6280.ppc.d )
223 		{
224 			if (h6280_ICount > 0) h6280_ICount=0;
225 			h6280.extra_cycles = 0;
226 			return cycles;
227 		}
228 
229 	} while (h6280_ICount > 0);
230 
231 	/* Subtract cycles used for taking an interrupt */
232     h6280_ICount -= h6280.extra_cycles;
233     h6280.extra_cycles = 0;
234 
235     return cycles - h6280_ICount;
236 }
237 
h6280_get_context(void * dst)238 unsigned h6280_get_context (void *dst)
239 {
240 	if( dst )
241 		*(h6280_Regs*)dst = h6280;
242 	return sizeof(h6280_Regs);
243 }
244 
h6280_set_context(void * src)245 void h6280_set_context (void *src)
246 {
247 	if( src )
248 		h6280 = *(h6280_Regs*)src;
249 }
250 
h6280_get_reg(int regnum)251 unsigned h6280_get_reg (int regnum)
252 {
253 	switch( regnum )
254 	{
255 		case REG_PC:
256 		case H6280_PC: return PCD;
257 		case REG_SP:
258 		case H6280_S: return S;
259 		case H6280_P: return P;
260 		case H6280_A: return A;
261 		case H6280_X: return X;
262 		case H6280_Y: return Y;
263 		case H6280_IRQ_MASK: return h6280.irq_mask;
264 		case H6280_TIMER_STATE: return h6280.timer_status;
265 		case H6280_NMI_STATE: return h6280.nmi_state;
266 		case H6280_IRQ1_STATE: return h6280.irq_state[0];
267 		case H6280_IRQ2_STATE: return h6280.irq_state[1];
268 		case H6280_IRQT_STATE: return h6280.irq_state[2];
269 #ifdef MAME_DEBUG
270 		case H6280_M1: return h6280.mmr[0];
271 		case H6280_M2: return h6280.mmr[1];
272 		case H6280_M3: return h6280.mmr[2];
273 		case H6280_M4: return h6280.mmr[3];
274 		case H6280_M5: return h6280.mmr[4];
275 		case H6280_M6: return h6280.mmr[5];
276 		case H6280_M7: return h6280.mmr[6];
277 		case H6280_M8: return h6280.mmr[7];
278 #endif
279 		case REG_PREVIOUSPC: return h6280.ppc.d;
280 		default:
281 			if( regnum <= REG_SP_CONTENTS )
282 			{
283 				unsigned offset = S + 2 * (REG_SP_CONTENTS - regnum);
284 				if( offset < 0x1ff )
285 					return RDMEM( offset ) | ( RDMEM( offset+1 ) << 8 );
286 			}
287 	}
288 	return 0;
289 }
290 
h6280_set_reg(int regnum,unsigned val)291 void h6280_set_reg (int regnum, unsigned val)
292 {
293 	switch( regnum )
294 	{
295 		case REG_PC:
296 		case H6280_PC: PCW = val; break;
297 		case REG_SP:
298 		case H6280_S: S = val; break;
299 		case H6280_P: P = val; break;
300 		case H6280_A: A = val; break;
301 		case H6280_X: X = val; break;
302 		case H6280_Y: Y = val; break;
303 		case H6280_IRQ_MASK: h6280.irq_mask = val; CHECK_IRQ_LINES; break;
304 		case H6280_TIMER_STATE: h6280.timer_status = val; break;
305 		case H6280_NMI_STATE: h6280_set_irq_line( IRQ_LINE_NMI, val ); break;
306 		case H6280_IRQ1_STATE: h6280_set_irq_line( 0, val ); break;
307 		case H6280_IRQ2_STATE: h6280_set_irq_line( 1, val ); break;
308 		case H6280_IRQT_STATE: h6280_set_irq_line( 2, val ); break;
309 #ifdef MAME_DEBUG
310 		case H6280_M1: h6280.mmr[0] = val; break;
311 		case H6280_M2: h6280.mmr[1] = val; break;
312 		case H6280_M3: h6280.mmr[2] = val; break;
313 		case H6280_M4: h6280.mmr[3] = val; break;
314 		case H6280_M5: h6280.mmr[4] = val; break;
315 		case H6280_M6: h6280.mmr[5] = val; break;
316 		case H6280_M7: h6280.mmr[6] = val; break;
317 		case H6280_M8: h6280.mmr[7] = val; break;
318 #endif
319 		default:
320 			if( regnum <= REG_SP_CONTENTS )
321 			{
322 				unsigned offset = S + 2 * (REG_SP_CONTENTS - regnum);
323 				if( offset < 0x1ff )
324 				{
325 					WRMEM( offset, val & 0xff );
326 					WRMEM( offset+1, (val >> 8) & 0xff );
327 				}
328 			}
329     }
330 }
331 
332 /*****************************************************************************/
333 
h6280_set_irq_line(int irqline,int state)334 void h6280_set_irq_line(int irqline, int state)
335 {
336 	if (irqline == IRQ_LINE_NMI)
337 	{
338 		if (h6280.nmi_state == state) return;
339 		h6280.nmi_state = state;
340 		if (state != CLEAR_LINE)
341 	    {
342 			DO_INTERRUPT(H6280_NMI_VEC);
343 		}
344 	}
345 	else if (irqline < 3)
346 	{
347 	    h6280.irq_state[irqline] = state;
348 
349 		/* If line is cleared, just exit */
350 		if (state == CLEAR_LINE) return;
351 
352 		/* Check if interrupts are enabled and the IRQ mask is clear */
353 		CHECK_IRQ_LINES;
354 	}
355 }
356 
h6280_set_irq_callback(int (* callback)(int irqline))357 void h6280_set_irq_callback(int (*callback)(int irqline))
358 {
359 	h6280.irq_callback = callback;
360 }
361 
362 /****************************************************************************
363  * Return a formatted string for a register
364  ****************************************************************************/
h6280_info(void * context,int regnum)365 const char *h6280_info(void *context, int regnum)
366 {
367 	static char buffer[32][47+1];
368 	static int which = 0;
369 	h6280_Regs *r = context;
370 
371 	which = (which+1) % 32;
372 	buffer[which][0] = '\0';
373 	if( !context )
374 		r = &h6280;
375 
376 	switch( regnum )
377 	{
378 		case CPU_INFO_REG+H6280_PC: sprintf(buffer[which], "PC:%04X", r->pc.w.l); break;
379         case CPU_INFO_REG+H6280_S: sprintf(buffer[which], "S:%02X", r->sp.b.l); break;
380         case CPU_INFO_REG+H6280_P: sprintf(buffer[which], "P:%02X", r->p); break;
381         case CPU_INFO_REG+H6280_A: sprintf(buffer[which], "A:%02X", r->a); break;
382 		case CPU_INFO_REG+H6280_X: sprintf(buffer[which], "X:%02X", r->x); break;
383 		case CPU_INFO_REG+H6280_Y: sprintf(buffer[which], "Y:%02X", r->y); break;
384 		case CPU_INFO_REG+H6280_IRQ_MASK: sprintf(buffer[which], "IM:%02X", r->irq_mask); break;
385 		case CPU_INFO_REG+H6280_TIMER_STATE: sprintf(buffer[which], "TMR:%02X", r->timer_status); break;
386 		case CPU_INFO_REG+H6280_NMI_STATE: sprintf(buffer[which], "NMI:%X", r->nmi_state); break;
387 		case CPU_INFO_REG+H6280_IRQ1_STATE: sprintf(buffer[which], "IRQ1:%X", r->irq_state[0]); break;
388 		case CPU_INFO_REG+H6280_IRQ2_STATE: sprintf(buffer[which], "IRQ2:%X", r->irq_state[1]); break;
389 		case CPU_INFO_REG+H6280_IRQT_STATE: sprintf(buffer[which], "IRQT:%X", r->irq_state[2]); break;
390 #ifdef MAME_DEBUG
391 		case CPU_INFO_REG+H6280_M1: sprintf(buffer[which], "M1:%02X", r->mmr[0]); break;
392 		case CPU_INFO_REG+H6280_M2: sprintf(buffer[which], "M2:%02X", r->mmr[1]); break;
393 		case CPU_INFO_REG+H6280_M3: sprintf(buffer[which], "M3:%02X", r->mmr[2]); break;
394 		case CPU_INFO_REG+H6280_M4: sprintf(buffer[which], "M4:%02X", r->mmr[3]); break;
395 		case CPU_INFO_REG+H6280_M5: sprintf(buffer[which], "M5:%02X", r->mmr[4]); break;
396 		case CPU_INFO_REG+H6280_M6: sprintf(buffer[which], "M6:%02X", r->mmr[5]); break;
397 		case CPU_INFO_REG+H6280_M7: sprintf(buffer[which], "M7:%02X", r->mmr[6]); break;
398 		case CPU_INFO_REG+H6280_M8: sprintf(buffer[which], "M8:%02X", r->mmr[7]); break;
399 #endif
400 		case CPU_INFO_FLAGS:
401 			sprintf(buffer[which], "%c%c%c%c%c%c%c%c",
402 				r->p & 0x80 ? 'N':'.',
403 				r->p & 0x40 ? 'V':'.',
404 				r->p & 0x20 ? 'R':'.',
405 				r->p & 0x10 ? 'B':'.',
406 				r->p & 0x08 ? 'D':'.',
407 				r->p & 0x04 ? 'I':'.',
408 				r->p & 0x02 ? 'Z':'.',
409 				r->p & 0x01 ? 'C':'.');
410 			break;
411 		case CPU_INFO_NAME: return "HuC6280";
412 		case CPU_INFO_FAMILY: return "Hudsonsoft 6280";
413 		case CPU_INFO_VERSION: return "1.07";
414 		case CPU_INFO_FILE: return __FILE__;
415 		case CPU_INFO_CREDITS: return "Copyright (c) 1999, 2000 Bryan McPhail, mish@tendril.co.uk";
416 		case CPU_INFO_REG_LAYOUT: return (const char*)reg_layout;
417 		case CPU_INFO_WIN_LAYOUT: return (const char*)win_layout;
418     }
419 	return buffer[which];
420 }
421 
h6280_dasm(char * buffer,unsigned pc)422 unsigned h6280_dasm(char *buffer, unsigned pc)
423 {
424 #ifdef MAME_DEBUG
425     return Dasm6280(buffer,pc);
426 #else
427 	sprintf( buffer, "$%02X", cpu_readop(pc) );
428 	return 1;
429 #endif
430 }
431 
432 /*****************************************************************************/
433 
READ_HANDLER(H6280_irq_status_r)434 READ_HANDLER( H6280_irq_status_r )
435 {
436 	int status;
437 
438 	switch (offset)
439 	{
440 		case 0: /* Read irq mask */
441 			return h6280.irq_mask;
442 
443 		case 1: /* Read irq status */
444 			status=0;
445 			if(h6280.irq_state[1]!=CLEAR_LINE) status|=1; /* IRQ 2 */
446 			if(h6280.irq_state[0]!=CLEAR_LINE) status|=2; /* IRQ 1 */
447 			if(h6280.irq_state[2]!=CLEAR_LINE) status|=4; /* TIMER */
448 			return status;
449 	}
450 
451 	return 0;
452 }
453 
WRITE_HANDLER(H6280_irq_status_w)454 WRITE_HANDLER( H6280_irq_status_w )
455 {
456 	switch (offset)
457 	{
458 		case 0: /* Write irq mask */
459 			h6280.irq_mask=data&0x7;
460 			CHECK_IRQ_LINES;
461 			break;
462 
463 		case 1: /* Timer irq ack - timer is reloaded here */
464 			h6280.timer_value = h6280.timer_load;
465 			h6280.timer_ack=1; /* Timer can't refire until ack'd */
466 			break;
467 	}
468 }
469 
READ_HANDLER(H6280_timer_r)470 READ_HANDLER( H6280_timer_r )
471 {
472 	switch (offset) {
473 		case 0: /* Counter value */
474 			return (h6280.timer_value/1024)&127;
475 
476 		case 1: /* Read counter status */
477 			return h6280.timer_status;
478 	}
479 
480 	return 0;
481 }
482 
WRITE_HANDLER(H6280_timer_w)483 WRITE_HANDLER( H6280_timer_w )
484 {
485 	switch (offset) {
486 		case 0: /* Counter preload */
487 			h6280.timer_load=h6280.timer_value=((data&127)+1)*1024;
488 			return;
489 
490 		case 1: /* Counter enable */
491 			if(data&1)
492 			{	/* stop -> start causes reload */
493 				if(h6280.timer_status==0) h6280.timer_value=h6280.timer_load;
494 			}
495 			h6280.timer_status=data&1;
496 			return;
497 	}
498 }
499 
500 /*****************************************************************************/
501