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