1 /*****************************************************************************
\r
3 h6280.c - Portable HuC6280 emulator
\r
5 Copyright Bryan McPhail, mish@tendril.co.uk
\r
7 This source code is based (with permission!) on the 6502 emulator by
\r
8 Juergen Buchmueller. It is released as part of the Mame emulator project.
\r
9 Let me know if you intend to use this code in any other project.
\r
14 This code is around 99% complete! Several things are unimplemented,
\r
15 some due to lack of time, some due to lack of documentation, mainly
\r
16 due to lack of programs using these features.
\r
18 csh, csl opcodes are not supported.
\r
20 I am unsure if flag B is set upon execution of rti.
\r
22 Cycle counts should be quite accurate.
\r
25 Changelog, version 1.02:
\r
26 JMP + indirect X (0x7c) opcode fixed.
\r
27 SMB + RMB opcodes fixed in disassembler.
\r
28 change_pc function calls removed.
\r
29 TSB & TRB now set flags properly.
\r
32 Changelog, version 1.03:
\r
33 Swapped IRQ mask for IRQ1 & IRQ2 (thanks Yasuhiro)
\r
35 Changelog, version 1.04, 28/9/99-22/10/99:
\r
36 Adjusted RTI (thanks Karl)
\r
37 TST opcodes fixed in disassembler (missing break statements in a case!).
\r
38 TST behaviour fixed.
\r
39 SMB/RMB/BBS/BBR fixed in disassembler.
\r
41 Changelog, version 1.05, 8/12/99-16/12/99:
\r
42 Added CAB's timer implementation (note: irq ack & timer reload are changed).
\r
44 Fixed B flag setting on BRK.
\r
45 Assumed CSH & CSL to take 2 cycles each.
\r
47 Todo: Performance could be improved by precalculating timer fire position.
\r
49 Changelog, version 1.06, 4/5/00 - last opcode bug found?
\r
50 JMP indirect was doing a EAL++; instead of EAD++; - Obviously causing
\r
51 a corrupt read when L = 0xff! This fixes Bloody Wolf and Trio The Punch!
\r
53 Changelog, version 1.07, 3/9/00:
\r
54 Changed timer to be single shot - fixes Crude Buster music in level 1.
\r
56 Changelog, version 1.08, 8/11/05: (Charles MacDonald)
\r
58 Changed timer implementation, no longer single shot and reading the
\r
59 timer registers returns the count only. Fixes the following:
\r
60 - Mesopotamia: Music tempo & in-game timer
\r
61 - Dragon Saber: DDA effects
\r
62 - Magical Chase: Music tempo and speed regulation
\r
63 - Cadash: Allows the first level to start
\r
64 - Turrican: Allows the game to start
\r
66 Changed PLX and PLY to set NZ flags. Fixes:
\r
67 - Afterburner: Graphics unpacking
\r
68 - Aoi Blink: Collision detection with background
\r
70 Fixed the decimal version of ADC/SBC to *not* update the V flag,
\r
71 only the binary ones do.
\r
73 Fixed B flag handling so it is always set outside of an interrupt;
\r
74 even after being set by PLP and RTI.
\r
76 Fixed P state after reset to set I and B, leaving T, D cleared and
\r
77 NVZC randomized (cleared in this case).
\r
79 Fixed interrupt processing order (Timer has highest priority followed
\r
80 by IRQ1 and finally IRQ2).
\r
82 Changelog, version 1.09, 1/07/06: (Rob Bohms)
\r
84 Added emulation of the T flag, fixes PCE Ankuku Densetsu title screen
\r
86 Changelog, version 1.10, 5/09/07: (Wilbert Pol)
\r
88 - Taking of interrupts is delayed to respect a pending instruction already
\r
89 in the instruction pipeline; fixes After Burner.
\r
90 - Added 1 cycle for decimal mode ADC and SBC instructions.
\r
91 - Changed cycle counts for CSH and CSL instructions to 3.
\r
92 - Added T flag support to the SBC instruction.
\r
93 - Fixed ADC T flag to set the Z flag based on the value read.
\r
94 - Added 3 cycle penalty to ADC, AND, EOR, ORA, and SBC instructions
\r
95 when the T flag is set.
\r
96 - Fixed cycle count and support for 65536 byte blocks for the TAI, TDD,
\r
97 TIA, TII, and TIN instructions.
\r
98 - Fixed RDWORD macro in the disassembler.
\r
99 - Fixed setting of N and V flags in the TST instructions.
\r
100 - Removed unneeded debug_mmr code.
\r
101 - Fixed TSB and TRB instructions.
\r
102 - Added 1 delay when accessing the VDC or VCE areas.
\r
103 - Implemented low and high speed cpu modes.
\r
105 Changelog, version 1.11, 18/09/07: (Wilbert Pol)
\r
107 - Improvements to the handling of taking of delayed interrupts.
\r
109 ******************************************************************************/
\r
112 //#include "debugger.h"
\r
115 static void set_irq_line(h6280_Regs* cpustate, int irqline, int state);
\r
117 /* include the macros */
\r
118 #include "h6280ops.h"
\r
120 /* include the opcode macros, functions and function pointer tables */
\r
121 #include "tblh6280.c"
\r
123 /*****************************************************************************/
\r
124 static CPU_INIT( h6280 )
\r
126 h6280_Regs *cpustate = (h6280_Regs *)calloc(1, sizeof(h6280_Regs));
\r
131 static CPU_RESET( h6280 )
\r
135 /* wipe out the h6280 structure */
\r
136 DEVICE *save_program = cpustate->program;
\r
137 DEVICE *save_io = cpustate->io;
\r
138 memset(cpustate, 0, sizeof(h6280_Regs));
\r
139 cpustate->program = save_program;
\r
140 cpustate->io = save_io;
\r
142 /* set I and B flags */
\r
145 /* stack starts at 0x01ff */
\r
146 cpustate->sp.d = 0x1ff;
\r
148 /* read the reset vector into PC */
\r
149 PCL = RDMEM(cpustate, H6280_RESET_VEC);
\r
150 PCH = RDMEM(cpustate, (H6280_RESET_VEC+1));
\r
152 /* CPU starts in low speed mode */
\r
153 cpustate->clocks_per_cycle = 4;
\r
155 /* timer off by default */
\r
156 cpustate->timer_status=0;
\r
157 cpustate->timer_load = 128 * 1024;
\r
159 /* clear pending interrupts */
\r
160 for (i = 0; i < 3; i++)
\r
161 cpustate->irq_state[i] = CLEAR_LINE;
\r
162 cpustate->nmi_state = CLEAR_LINE;
\r
164 cpustate->irq_pending = 0;
\r
167 static CPU_EXECUTE( h6280 )
\r
171 if (ICount == -1) {
\r
172 cpustate->ICount = 1;
\r
174 cpustate->ICount += ICount;
\r
176 int Base_ICount = cpustate->ICount;
\r
178 if ( cpustate->irq_pending == 2 ) {
\r
179 cpustate->irq_pending--;
\r
182 /* Execute instructions */
\r
185 cpustate->ppc = cpustate->pc;
\r
187 /* Execute 1 instruction */
\r
190 insnh6280[in](cpustate);
\r
192 if ( cpustate->irq_pending ) {
\r
193 if ( cpustate->irq_pending == 1 ) {
\r
194 if ( !(P & _fI) ) {
\r
195 cpustate->irq_pending--;
\r
196 CHECK_AND_TAKE_IRQ_LINES;
\r
199 cpustate->irq_pending--;
\r
203 /* Check internal timer */
\r
204 if(cpustate->timer_status)
\r
206 if(cpustate->timer_value<=0)
\r
208 if ( ! cpustate->irq_pending )
\r
209 cpustate->irq_pending = 1;
\r
210 while( cpustate->timer_value <= 0 )
\r
211 cpustate->timer_value += cpustate->timer_load;
\r
212 set_irq_line(cpustate, 2,ASSERT_LINE);
\r
215 } while (cpustate->ICount > 0);
\r
217 return Base_ICount - cpustate->ICount;
\r
220 /*****************************************************************************/
\r
222 static void set_irq_line(h6280_Regs* cpustate, int irqline, int state)
\r
224 if (irqline == INPUT_LINE_NMI)
\r
226 if ( state != ASSERT_LINE ) return;
\r
227 cpustate->nmi_state = state;
\r
230 else if (irqline < 3)
\r
232 /* If the state has not changed, just return */
\r
233 if ( cpustate->irq_state[irqline] == state )
\r
236 cpustate->irq_state[irqline] = state;
\r
244 /*****************************************************************************/
\r
246 READ8_HANDLER( h6280_irq_status_r )
\r
256 if(cpustate->irq_state[1]!=CLEAR_LINE) status|=1; /* IRQ 2 */
\r
257 if(cpustate->irq_state[0]!=CLEAR_LINE) status|=2; /* IRQ 1 */
\r
258 if(cpustate->irq_state[2]!=CLEAR_LINE) status|=4; /* TIMER */
\r
261 case 2: return cpustate->irq_mask;
\r
265 WRITE8_HANDLER( h6280_irq_status_w )
\r
269 case 2: /* Write irq mask */
\r
270 cpustate->irq_mask=data&0x7;
\r
274 case 3: /* Timer irq ack */
\r
275 set_irq_line(cpustate, 2, CLEAR_LINE);
\r
280 READ8_HANDLER( h6280_timer_r )
\r
282 /* only returns countdown */
\r
283 return ((cpustate->timer_value >> 10)&0x7F);
\r
286 WRITE8_HANDLER( h6280_timer_w )
\r
288 switch (offset & 1) {
\r
289 case 0: /* Counter preload */
\r
290 cpustate->timer_load=cpustate->timer_value=((data&127)+1)*1024;
\r
293 case 1: /* Counter enable */
\r
295 { /* stop -> start causes reload */
\r
296 if(cpustate->timer_status==0) cpustate->timer_value=cpustate->timer_load;
\r
298 cpustate->timer_status=data&1;
\r
303 /*****************************************************************************/
\r