-/*****************************************************************************\r
-\r
- h6280.c - Portable HuC6280 emulator\r
-\r
- Copyright Bryan McPhail, mish@tendril.co.uk\r
-\r
- This source code is based (with permission!) on the 6502 emulator by\r
- Juergen Buchmueller. It is released as part of the Mame emulator project.\r
- Let me know if you intend to use this code in any other project.\r
-\r
-\r
- NOTICE:\r
-\r
- This code is around 99% complete! Several things are unimplemented,\r
- some due to lack of time, some due to lack of documentation, mainly\r
- due to lack of programs using these features.\r
-\r
- csh, csl opcodes are not supported.\r
-\r
- I am unsure if flag B is set upon execution of rti.\r
-\r
- Cycle counts should be quite accurate.\r
-\r
-\r
- Changelog, version 1.02:\r
- JMP + indirect X (0x7c) opcode fixed.\r
- SMB + RMB opcodes fixed in disassembler.\r
- change_pc function calls removed.\r
- TSB & TRB now set flags properly.\r
- BIT opcode altered.\r
-\r
- Changelog, version 1.03:\r
- Swapped IRQ mask for IRQ1 & IRQ2 (thanks Yasuhiro)\r
-\r
- Changelog, version 1.04, 28/9/99-22/10/99:\r
- Adjusted RTI (thanks Karl)\r
- TST opcodes fixed in disassembler (missing break statements in a case!).\r
- TST behaviour fixed.\r
- SMB/RMB/BBS/BBR fixed in disassembler.\r
-\r
- Changelog, version 1.05, 8/12/99-16/12/99:\r
- Added CAB's timer implementation (note: irq ack & timer reload are changed).\r
- Fixed STA IDX.\r
- Fixed B flag setting on BRK.\r
- Assumed CSH & CSL to take 2 cycles each.\r
-\r
- Todo: Performance could be improved by precalculating timer fire position.\r
-\r
- Changelog, version 1.06, 4/5/00 - last opcode bug found?\r
- JMP indirect was doing a EAL++; instead of EAD++; - Obviously causing\r
- a corrupt read when L = 0xff! This fixes Bloody Wolf and Trio The Punch!\r
-\r
- Changelog, version 1.07, 3/9/00:\r
- Changed timer to be single shot - fixes Crude Buster music in level 1.\r
-\r
- Changelog, version 1.08, 8/11/05: (Charles MacDonald)\r
-\r
- Changed timer implementation, no longer single shot and reading the\r
- timer registers returns the count only. Fixes the following:\r
- - Mesopotamia: Music tempo & in-game timer\r
- - Dragon Saber: DDA effects\r
- - Magical Chase: Music tempo and speed regulation\r
- - Cadash: Allows the first level to start\r
- - Turrican: Allows the game to start\r
-\r
- Changed PLX and PLY to set NZ flags. Fixes:\r
- - Afterburner: Graphics unpacking\r
- - Aoi Blink: Collision detection with background\r
-\r
- Fixed the decimal version of ADC/SBC to *not* update the V flag,\r
- only the binary ones do.\r
-\r
- Fixed B flag handling so it is always set outside of an interrupt;\r
- even after being set by PLP and RTI.\r
-\r
- Fixed P state after reset to set I and B, leaving T, D cleared and\r
- NVZC randomized (cleared in this case).\r
-\r
- Fixed interrupt processing order (Timer has highest priority followed\r
- by IRQ1 and finally IRQ2).\r
-\r
- Changelog, version 1.09, 1/07/06: (Rob Bohms)\r
-\r
- Added emulation of the T flag, fixes PCE Ankuku Densetsu title screen\r
-\r
- Changelog, version 1.10, 5/09/07: (Wilbert Pol)\r
-\r
- - Taking of interrupts is delayed to respect a pending instruction already\r
- in the instruction pipeline; fixes After Burner.\r
- - Added 1 cycle for decimal mode ADC and SBC instructions.\r
- - Changed cycle counts for CSH and CSL instructions to 3.\r
- - Added T flag support to the SBC instruction.\r
- - Fixed ADC T flag to set the Z flag based on the value read.\r
- - Added 3 cycle penalty to ADC, AND, EOR, ORA, and SBC instructions\r
- when the T flag is set.\r
- - Fixed cycle count and support for 65536 byte blocks for the TAI, TDD,\r
- TIA, TII, and TIN instructions.\r
- - Fixed RDWORD macro in the disassembler.\r
- - Fixed setting of N and V flags in the TST instructions.\r
- - Removed unneeded debug_mmr code.\r
- - Fixed TSB and TRB instructions.\r
- - Added 1 delay when accessing the VDC or VCE areas.\r
- - Implemented low and high speed cpu modes.\r
-\r
- Changelog, version 1.11, 18/09/07: (Wilbert Pol)\r
-\r
- - Improvements to the handling of taking of delayed interrupts.\r
-\r
-******************************************************************************/\r
-\r
-//#include "emu.h"\r
-//#include "debugger.h"\r
-#include "h6280.h"\r
-\r
-static void set_irq_line(h6280_Regs* cpustate, int irqline, int state);\r
-\r
-/* include the macros */\r
-#include "h6280ops.h"\r
-\r
-/* include the opcode macros, functions and function pointer tables */\r
-#include "tblh6280.c"\r
-\r
-/*****************************************************************************/\r
-static CPU_INIT( h6280 )\r
-{\r
- h6280_Regs *cpustate = (h6280_Regs *)calloc(1, sizeof(h6280_Regs));\r
-\r
- return cpustate;\r
-}\r
-\r
-static CPU_RESET( h6280 )\r
-{\r
- int i;\r
-\r
- /* wipe out the h6280 structure */\r
- DEVICE *save_program = cpustate->program;\r
- DEVICE *save_io = cpustate->io;\r
- memset(cpustate, 0, sizeof(h6280_Regs));\r
- cpustate->program = save_program;\r
- cpustate->io = save_io;\r
-\r
- /* set I and B flags */\r
- P = _fI | _fB;\r
-\r
- /* stack starts at 0x01ff */\r
- cpustate->sp.d = 0x1ff;\r
-\r
- /* read the reset vector into PC */\r
- PCL = RDMEM(cpustate, H6280_RESET_VEC);\r
- PCH = RDMEM(cpustate, (H6280_RESET_VEC+1));\r
-\r
- /* CPU starts in low speed mode */\r
- cpustate->clocks_per_cycle = 4;\r
-\r
- /* timer off by default */\r
- cpustate->timer_status=0;\r
- cpustate->timer_load = 128 * 1024;\r
-\r
- /* clear pending interrupts */\r
- for (i = 0; i < 3; i++)\r
- cpustate->irq_state[i] = CLEAR_LINE;\r
- cpustate->nmi_state = CLEAR_LINE;\r
-\r
- cpustate->irq_pending = 0;\r
-}\r
-\r
-static CPU_EXECUTE( h6280 )\r
-{\r
- int in;\r
-\r
- if (ICount == -1) {\r
- cpustate->ICount = 1;\r
- } else {\r
- cpustate->ICount += ICount;\r
- }\r
- int Base_ICount = cpustate->ICount;\r
-\r
- if ( cpustate->irq_pending == 2 ) {\r
- cpustate->irq_pending--;\r
- }\r
-\r
- /* Execute instructions */\r
- do\r
- {\r
- cpustate->ppc = cpustate->pc;\r
-\r
- /* Execute 1 instruction */\r
- in=RDOP();\r
- PCW++;\r
- insnh6280[in](cpustate);\r
-\r
- if ( cpustate->irq_pending ) {\r
- if ( cpustate->irq_pending == 1 ) {\r
- if ( !(P & _fI) ) {\r
- cpustate->irq_pending--;\r
- CHECK_AND_TAKE_IRQ_LINES;\r
- }\r
- } else {\r
- cpustate->irq_pending--;\r
- }\r
- }\r
-\r
- /* Check internal timer */\r
- if(cpustate->timer_status)\r
- {\r
- if(cpustate->timer_value<=0)\r
- {\r
- if ( ! cpustate->irq_pending )\r
- cpustate->irq_pending = 1;\r
- while( cpustate->timer_value <= 0 )\r
- cpustate->timer_value += cpustate->timer_load;\r
- set_irq_line(cpustate, 2,ASSERT_LINE);\r
- }\r
- }\r
- } while (cpustate->ICount > 0);\r
-\r
- return Base_ICount - cpustate->ICount;\r
-}\r
-\r
-/*****************************************************************************/\r
-\r
-static void set_irq_line(h6280_Regs* cpustate, int irqline, int state)\r
-{\r
- if (irqline == INPUT_LINE_NMI)\r
- {\r
- if ( state != ASSERT_LINE ) return;\r
- cpustate->nmi_state = state;\r
- CHECK_IRQ_LINES;\r
- }\r
- else if (irqline < 3)\r
- {\r
- /* If the state has not changed, just return */\r
- if ( cpustate->irq_state[irqline] == state )\r
- return;\r
-\r
- cpustate->irq_state[irqline] = state;\r
-\r
- CHECK_IRQ_LINES;\r
- }\r
-}\r
-\r
-\r
-\r
-/*****************************************************************************/\r
-\r
-READ8_HANDLER( h6280_irq_status_r )\r
-{\r
- int status;\r
-\r
- switch (offset&3)\r
- {\r
- default: return 0;\r
- case 3:\r
- {\r
- status=0;\r
- if(cpustate->irq_state[1]!=CLEAR_LINE) status|=1; /* IRQ 2 */\r
- if(cpustate->irq_state[0]!=CLEAR_LINE) status|=2; /* IRQ 1 */\r
- if(cpustate->irq_state[2]!=CLEAR_LINE) status|=4; /* TIMER */\r
- return status;\r
- }\r
- case 2: return cpustate->irq_mask;\r
- }\r
-}\r
-\r
-WRITE8_HANDLER( h6280_irq_status_w )\r
-{\r
- switch (offset&3)\r
- {\r
- case 2: /* Write irq mask */\r
- cpustate->irq_mask=data&0x7;\r
- CHECK_IRQ_LINES;\r
- break;\r
-\r
- case 3: /* Timer irq ack */\r
- set_irq_line(cpustate, 2, CLEAR_LINE);\r
- break;\r
- }\r
-}\r
-\r
-READ8_HANDLER( h6280_timer_r )\r
-{\r
- /* only returns countdown */\r
- return ((cpustate->timer_value >> 10)&0x7F);\r
-}\r
-\r
-WRITE8_HANDLER( h6280_timer_w )\r
-{\r
- switch (offset & 1) {\r
- case 0: /* Counter preload */\r
- cpustate->timer_load=cpustate->timer_value=((data&127)+1)*1024;\r
- return;\r
-\r
- case 1: /* Counter enable */\r
- if(data&1)\r
- { /* stop -> start causes reload */\r
- if(cpustate->timer_status==0) cpustate->timer_value=cpustate->timer_load;\r
- }\r
- cpustate->timer_status=data&1;\r
- return;\r
- }\r
-}\r
-\r
-/*****************************************************************************/\r
-\r
+/*****************************************************************************
+
+ h6280.c - Portable HuC6280 emulator
+
+ Copyright Bryan McPhail, mish@tendril.co.uk
+
+ This source code is based (with permission!) on the 6502 emulator by
+ Juergen Buchmueller. It is released as part of the Mame emulator project.
+ Let me know if you intend to use this code in any other project.
+
+
+ NOTICE:
+
+ This code is around 99% complete! Several things are unimplemented,
+ some due to lack of time, some due to lack of documentation, mainly
+ due to lack of programs using these features.
+
+ csh, csl opcodes are not supported.
+
+ I am unsure if flag B is set upon execution of rti.
+
+ Cycle counts should be quite accurate.
+
+
+ Changelog, version 1.02:
+ JMP + indirect X (0x7c) opcode fixed.
+ SMB + RMB opcodes fixed in disassembler.
+ change_pc function calls removed.
+ TSB & TRB now set flags properly.
+ BIT opcode altered.
+
+ Changelog, version 1.03:
+ Swapped IRQ mask for IRQ1 & IRQ2 (thanks Yasuhiro)
+
+ Changelog, version 1.04, 28/9/99-22/10/99:
+ Adjusted RTI (thanks Karl)
+ TST opcodes fixed in disassembler (missing break statements in a case!).
+ TST behaviour fixed.
+ SMB/RMB/BBS/BBR fixed in disassembler.
+
+ Changelog, version 1.05, 8/12/99-16/12/99:
+ Added CAB's timer implementation (note: irq ack & timer reload are changed).
+ Fixed STA IDX.
+ Fixed B flag setting on BRK.
+ Assumed CSH & CSL to take 2 cycles each.
+
+ Todo: Performance could be improved by precalculating timer fire position.
+
+ Changelog, version 1.06, 4/5/00 - last opcode bug found?
+ JMP indirect was doing a EAL++; instead of EAD++; - Obviously causing
+ a corrupt read when L = 0xff! This fixes Bloody Wolf and Trio The Punch!
+
+ Changelog, version 1.07, 3/9/00:
+ Changed timer to be single shot - fixes Crude Buster music in level 1.
+
+ Changelog, version 1.08, 8/11/05: (Charles MacDonald)
+
+ Changed timer implementation, no longer single shot and reading the
+ timer registers returns the count only. Fixes the following:
+ - Mesopotamia: Music tempo & in-game timer
+ - Dragon Saber: DDA effects
+ - Magical Chase: Music tempo and speed regulation
+ - Cadash: Allows the first level to start
+ - Turrican: Allows the game to start
+
+ Changed PLX and PLY to set NZ flags. Fixes:
+ - Afterburner: Graphics unpacking
+ - Aoi Blink: Collision detection with background
+
+ Fixed the decimal version of ADC/SBC to *not* update the V flag,
+ only the binary ones do.
+
+ Fixed B flag handling so it is always set outside of an interrupt;
+ even after being set by PLP and RTI.
+
+ Fixed P state after reset to set I and B, leaving T, D cleared and
+ NVZC randomized (cleared in this case).
+
+ Fixed interrupt processing order (Timer has highest priority followed
+ by IRQ1 and finally IRQ2).
+
+ Changelog, version 1.09, 1/07/06: (Rob Bohms)
+
+ Added emulation of the T flag, fixes PCE Ankuku Densetsu title screen
+
+ Changelog, version 1.10, 5/09/07: (Wilbert Pol)
+
+ - Taking of interrupts is delayed to respect a pending instruction already
+ in the instruction pipeline; fixes After Burner.
+ - Added 1 cycle for decimal mode ADC and SBC instructions.
+ - Changed cycle counts for CSH and CSL instructions to 3.
+ - Added T flag support to the SBC instruction.
+ - Fixed ADC T flag to set the Z flag based on the value read.
+ - Added 3 cycle penalty to ADC, AND, EOR, ORA, and SBC instructions
+ when the T flag is set.
+ - Fixed cycle count and support for 65536 byte blocks for the TAI, TDD,
+ TIA, TII, and TIN instructions.
+ - Fixed RDWORD macro in the disassembler.
+ - Fixed setting of N and V flags in the TST instructions.
+ - Removed unneeded debug_mmr code.
+ - Fixed TSB and TRB instructions.
+ - Added 1 delay when accessing the VDC or VCE areas.
+ - Implemented low and high speed cpu modes.
+
+ Changelog, version 1.11, 18/09/07: (Wilbert Pol)
+
+ - Improvements to the handling of taking of delayed interrupts.
+
+******************************************************************************/
+
+//#include "emu.h"
+//#include "debugger.h"
+#include "h6280.h"
+
+static void set_irq_line(h6280_Regs* cpustate, int irqline, int state);
+
+/* include the macros */
+#include "h6280ops.h"
+
+/* include the opcode macros, functions and function pointer tables */
+#include "tblh6280.c"
+
+/*****************************************************************************/
+static CPU_INIT( h6280 )
+{
+ h6280_Regs *cpustate = (h6280_Regs *)calloc(1, sizeof(h6280_Regs));
+
+ return cpustate;
+}
+
+static CPU_RESET( h6280 )
+{
+ int i;
+
+ /* wipe out the h6280 structure */
+ DEVICE *save_program = cpustate->program;
+ DEVICE *save_io = cpustate->io;
+ memset(cpustate, 0, sizeof(h6280_Regs));
+ cpustate->program = save_program;
+ cpustate->io = save_io;
+
+ /* set I and B flags */
+ P = _fI | _fB;
+
+ /* stack starts at 0x01ff */
+ cpustate->sp.d = 0x1ff;
+
+ /* read the reset vector into PC */
+ PCL = RDMEM(cpustate, H6280_RESET_VEC);
+ PCH = RDMEM(cpustate, (H6280_RESET_VEC+1));
+
+ /* CPU starts in low speed mode */
+ cpustate->clocks_per_cycle = 4;
+
+ /* timer off by default */
+ cpustate->timer_status=0;
+ cpustate->timer_load = 128 * 1024;
+
+ /* clear pending interrupts */
+ for (i = 0; i < 3; i++)
+ cpustate->irq_state[i] = CLEAR_LINE;
+ cpustate->nmi_state = CLEAR_LINE;
+
+ cpustate->irq_pending = 0;
+}
+
+static CPU_EXECUTE( h6280 )
+{
+ int in;
+
+ if (ICount == -1) {
+ cpustate->ICount = 1;
+ } else {
+ cpustate->ICount += ICount;
+ }
+ int Base_ICount = cpustate->ICount;
+
+ if ( cpustate->irq_pending == 2 ) {
+ cpustate->irq_pending--;
+ }
+
+ /* Execute instructions */
+ do
+ {
+ cpustate->ppc = cpustate->pc;
+
+ /* Execute 1 instruction */
+ in=RDOP();
+ PCW++;
+ insnh6280[in](cpustate);
+
+ if ( cpustate->irq_pending ) {
+ if ( cpustate->irq_pending == 1 ) {
+ if ( !(P & _fI) ) {
+ cpustate->irq_pending--;
+ CHECK_AND_TAKE_IRQ_LINES;
+ }
+ } else {
+ cpustate->irq_pending--;
+ }
+ }
+
+ /* Check internal timer */
+ if(cpustate->timer_status)
+ {
+ if(cpustate->timer_value<=0)
+ {
+ if ( ! cpustate->irq_pending )
+ cpustate->irq_pending = 1;
+ while( cpustate->timer_value <= 0 )
+ cpustate->timer_value += cpustate->timer_load;
+ set_irq_line(cpustate, 2,ASSERT_LINE);
+ }
+ }
+ } while (cpustate->ICount > 0);
+
+ return Base_ICount - cpustate->ICount;
+}
+
+/*****************************************************************************/
+
+static void set_irq_line(h6280_Regs* cpustate, int irqline, int state)
+{
+ if (irqline == INPUT_LINE_NMI)
+ {
+ if ( state != ASSERT_LINE ) return;
+ cpustate->nmi_state = state;
+ CHECK_IRQ_LINES;
+ }
+ else if (irqline < 3)
+ {
+ /* If the state has not changed, just return */
+ if ( cpustate->irq_state[irqline] == state )
+ return;
+
+ cpustate->irq_state[irqline] = state;
+
+ CHECK_IRQ_LINES;
+ }
+}
+
+
+
+/*****************************************************************************/
+
+READ8_HANDLER( h6280_irq_status_r )
+{
+ int status;
+
+ switch (offset&3)
+ {
+ default: return 0;
+ case 3:
+ {
+ status=0;
+ if(cpustate->irq_state[1]!=CLEAR_LINE) status|=1; /* IRQ 2 */
+ if(cpustate->irq_state[0]!=CLEAR_LINE) status|=2; /* IRQ 1 */
+ if(cpustate->irq_state[2]!=CLEAR_LINE) status|=4; /* TIMER */
+ return status;
+ }
+ case 2: return cpustate->irq_mask;
+ }
+}
+
+WRITE8_HANDLER( h6280_irq_status_w )
+{
+ switch (offset&3)
+ {
+ case 2: /* Write irq mask */
+ cpustate->irq_mask=data&0x7;
+ CHECK_IRQ_LINES;
+ break;
+
+ case 3: /* Timer irq ack */
+ set_irq_line(cpustate, 2, CLEAR_LINE);
+ break;
+ }
+}
+
+READ8_HANDLER( h6280_timer_r )
+{
+ /* only returns countdown */
+ return ((cpustate->timer_value >> 10)&0x7F);
+}
+
+WRITE8_HANDLER( h6280_timer_w )
+{
+ switch (offset & 1) {
+ case 0: /* Counter preload */
+ cpustate->timer_load=cpustate->timer_value=((data&127)+1)*1024;
+ return;
+
+ case 1: /* Counter enable */
+ if(data&1)
+ { /* stop -> start causes reload */
+ if(cpustate->timer_status==0) cpustate->timer_value=cpustate->timer_load;
+ }
+ cpustate->timer_status=data&1;
+ return;
+ }
+}
+
+/*****************************************************************************/
+