// license:BSD-3-Clause // copyright-holders:Bryan McPhail /***************************************************************************** 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 "h6280.h" #include "6280dasm.h" /* 6280 flags */ enum { _fC = 0x01, _fZ = 0x02, _fI = 0x04, _fD = 0x08, _fB = 0x10, _fT = 0x20, _fV = 0x40, _fN = 0x80 }; /* some shortcuts for improved readability */ #define A m_a #define X m_x #define Y m_y #define P m_p #define S m_sp.b.l #define EAL m_ea.b.l #define EAH m_ea.b.h #define EAW m_ea.w.l #define EAD m_ea.d #define ZPL m_zp.b.l #define ZPH m_zp.b.h #define ZPW m_zp.w.l #define ZPD m_zp.d #define PCL m_pc.b.l #define PCH m_pc.b.h #define PCW m_pc.w.l #define PCD m_pc.d void h6280_device::internal_map(address_map &map) { map(0x1fe800, 0x1fe80f).mirror(0x3f0).rw(FUNC(h6280_device::io_buffer_r), FUNC(h6280_device::psg_w)); map(0x1fec00, 0x1fec01).mirror(0x3fe).rw(FUNC(h6280_device::timer_r), FUNC(h6280_device::timer_w)); map(0x1ff000, 0x1ff000).mirror(0x3ff).rw(FUNC(h6280_device::port_r), FUNC(h6280_device::port_w)); map(0x1ff400, 0x1ff403).mirror(0x3fc).rw(FUNC(h6280_device::irq_status_r), FUNC(h6280_device::irq_status_w)); } //************************************************************************** // DEVICE INTERFACE //************************************************************************** DEFINE_DEVICE_TYPE(H6280, h6280_device, "h6280", "Hudson Soft HuC6280") //------------------------------------------------- // h6280_device - constructor //------------------------------------------------- h6280_device::h6280_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : cpu_device(mconfig, H6280, tag, owner, clock) , device_mixer_interface(mconfig, *this, 2) , m_program_config("program", ENDIANNESS_LITTLE, 8, 21, 0, 16, 0, address_map_constructor(FUNC(h6280_device::internal_map), this)) , m_io_config("io", ENDIANNESS_LITTLE, 8, 2) , m_port_in_cb(*this, 0) , m_port_out_cb(*this) , m_psg(*this, "psg") , m_timer_scale(1) { // build the opcode table for (int op = 0; op < 256; op++) m_opcode[op] = s_opcodetable[op]; } device_memory_interface::space_config_vector h6280_device::memory_space_config() const { return space_config_vector { std::make_pair(AS_PROGRAM, &m_program_config), std::make_pair(AS_IO, &m_io_config) }; } const h6280_device::ophandler h6280_device::s_opcodetable[256] = { &h6280_device::op_00, &h6280_device::op_01, &h6280_device::op_02, &h6280_device::op_03, &h6280_device::op_04, &h6280_device::op_05, &h6280_device::op_06, &h6280_device::op_07, &h6280_device::op_08, &h6280_device::op_09, &h6280_device::op_0a, &h6280_device::op_0b, &h6280_device::op_0c, &h6280_device::op_0d, &h6280_device::op_0e, &h6280_device::op_0f, &h6280_device::op_10, &h6280_device::op_11, &h6280_device::op_12, &h6280_device::op_13, &h6280_device::op_14, &h6280_device::op_15, &h6280_device::op_16, &h6280_device::op_17, &h6280_device::op_18, &h6280_device::op_19, &h6280_device::op_1a, &h6280_device::op_1b, &h6280_device::op_1c, &h6280_device::op_1d, &h6280_device::op_1e, &h6280_device::op_1f, &h6280_device::op_20, &h6280_device::op_21, &h6280_device::op_22, &h6280_device::op_23, &h6280_device::op_24, &h6280_device::op_25, &h6280_device::op_26, &h6280_device::op_27, &h6280_device::op_28, &h6280_device::op_29, &h6280_device::op_2a, &h6280_device::op_2b, &h6280_device::op_2c, &h6280_device::op_2d, &h6280_device::op_2e, &h6280_device::op_2f, &h6280_device::op_30, &h6280_device::op_31, &h6280_device::op_32, &h6280_device::op_33, &h6280_device::op_34, &h6280_device::op_35, &h6280_device::op_36, &h6280_device::op_37, &h6280_device::op_38, &h6280_device::op_39, &h6280_device::op_3a, &h6280_device::op_3b, &h6280_device::op_3c, &h6280_device::op_3d, &h6280_device::op_3e, &h6280_device::op_3f, &h6280_device::op_40, &h6280_device::op_41, &h6280_device::op_42, &h6280_device::op_43, &h6280_device::op_44, &h6280_device::op_45, &h6280_device::op_46, &h6280_device::op_47, &h6280_device::op_48, &h6280_device::op_49, &h6280_device::op_4a, &h6280_device::op_4b, &h6280_device::op_4c, &h6280_device::op_4d, &h6280_device::op_4e, &h6280_device::op_4f, &h6280_device::op_50, &h6280_device::op_51, &h6280_device::op_52, &h6280_device::op_53, &h6280_device::op_54, &h6280_device::op_55, &h6280_device::op_56, &h6280_device::op_57, &h6280_device::op_58, &h6280_device::op_59, &h6280_device::op_5a, &h6280_device::op_5b, &h6280_device::op_5c, &h6280_device::op_5d, &h6280_device::op_5e, &h6280_device::op_5f, &h6280_device::op_60, &h6280_device::op_61, &h6280_device::op_62, &h6280_device::op_63, &h6280_device::op_64, &h6280_device::op_65, &h6280_device::op_66, &h6280_device::op_67, &h6280_device::op_68, &h6280_device::op_69, &h6280_device::op_6a, &h6280_device::op_6b, &h6280_device::op_6c, &h6280_device::op_6d, &h6280_device::op_6e, &h6280_device::op_6f, &h6280_device::op_70, &h6280_device::op_71, &h6280_device::op_72, &h6280_device::op_73, &h6280_device::op_74, &h6280_device::op_75, &h6280_device::op_76, &h6280_device::op_77, &h6280_device::op_78, &h6280_device::op_79, &h6280_device::op_7a, &h6280_device::op_7b, &h6280_device::op_7c, &h6280_device::op_7d, &h6280_device::op_7e, &h6280_device::op_7f, &h6280_device::op_80, &h6280_device::op_81, &h6280_device::op_82, &h6280_device::op_83, &h6280_device::op_84, &h6280_device::op_85, &h6280_device::op_86, &h6280_device::op_87, &h6280_device::op_88, &h6280_device::op_89, &h6280_device::op_8a, &h6280_device::op_8b, &h6280_device::op_8c, &h6280_device::op_8d, &h6280_device::op_8e, &h6280_device::op_8f, &h6280_device::op_90, &h6280_device::op_91, &h6280_device::op_92, &h6280_device::op_93, &h6280_device::op_94, &h6280_device::op_95, &h6280_device::op_96, &h6280_device::op_97, &h6280_device::op_98, &h6280_device::op_99, &h6280_device::op_9a, &h6280_device::op_9b, &h6280_device::op_9c, &h6280_device::op_9d, &h6280_device::op_9e, &h6280_device::op_9f, &h6280_device::op_a0, &h6280_device::op_a1, &h6280_device::op_a2, &h6280_device::op_a3, &h6280_device::op_a4, &h6280_device::op_a5, &h6280_device::op_a6, &h6280_device::op_a7, &h6280_device::op_a8, &h6280_device::op_a9, &h6280_device::op_aa, &h6280_device::op_ab, &h6280_device::op_ac, &h6280_device::op_ad, &h6280_device::op_ae, &h6280_device::op_af, &h6280_device::op_b0, &h6280_device::op_b1, &h6280_device::op_b2, &h6280_device::op_b3, &h6280_device::op_b4, &h6280_device::op_b5, &h6280_device::op_b6, &h6280_device::op_b7, &h6280_device::op_b8, &h6280_device::op_b9, &h6280_device::op_ba, &h6280_device::op_bb, &h6280_device::op_bc, &h6280_device::op_bd, &h6280_device::op_be, &h6280_device::op_bf, &h6280_device::op_c0, &h6280_device::op_c1, &h6280_device::op_c2, &h6280_device::op_c3, &h6280_device::op_c4, &h6280_device::op_c5, &h6280_device::op_c6, &h6280_device::op_c7, &h6280_device::op_c8, &h6280_device::op_c9, &h6280_device::op_ca, &h6280_device::op_cb, &h6280_device::op_cc, &h6280_device::op_cd, &h6280_device::op_ce, &h6280_device::op_cf, &h6280_device::op_d0, &h6280_device::op_d1, &h6280_device::op_d2, &h6280_device::op_d3, &h6280_device::op_d4, &h6280_device::op_d5, &h6280_device::op_d6, &h6280_device::op_d7, &h6280_device::op_d8, &h6280_device::op_d9, &h6280_device::op_da, &h6280_device::op_db, &h6280_device::op_dc, &h6280_device::op_dd, &h6280_device::op_de, &h6280_device::op_df, &h6280_device::op_e0, &h6280_device::op_e1, &h6280_device::op_e2, &h6280_device::op_e3, &h6280_device::op_e4, &h6280_device::op_e5, &h6280_device::op_e6, &h6280_device::op_e7, &h6280_device::op_e8, &h6280_device::op_e9, &h6280_device::op_ea, &h6280_device::op_eb, &h6280_device::op_ec, &h6280_device::op_ed, &h6280_device::op_ee, &h6280_device::op_ef, &h6280_device::op_f0, &h6280_device::op_f1, &h6280_device::op_f2, &h6280_device::op_f3, &h6280_device::op_f4, &h6280_device::op_f5, &h6280_device::op_f6, &h6280_device::op_f7, &h6280_device::op_f8, &h6280_device::op_f9, &h6280_device::op_fa, &h6280_device::op_fb, &h6280_device::op_fc, &h6280_device::op_fd, &h6280_device::op_fe, &h6280_device::op_ff }; //------------------------------------------------- // device_add_mconfig - add machine configuration //------------------------------------------------- void h6280_device::device_add_mconfig(machine_config &config) { C6280(config, m_psg, DERIVED_CLOCK(1,2)); m_psg->add_route(0, *this, 1.0, AUTO_ALLOC_INPUT, 0); m_psg->add_route(1, *this, 1.0, AUTO_ALLOC_INPUT, 1); } void h6280_device::device_start() { // register our state for the debugger state_add(STATE_GENPC, "GENPC", m_pc.w.l).noshow(); state_add(STATE_GENPCBASE, "CURPC", m_pc.w.l).noshow(); state_add(STATE_GENFLAGS, "GENFLAGS", m_p).callimport().callexport().formatstr("%8s").noshow(); state_add(H6280_PC, "PC", m_pc.d).mask(0xffff); state_add(H6280_S, "S", m_sp.b.l).mask(0xff); state_add(H6280_P, "P", m_p).mask(0xff); state_add(H6280_A, "A", m_a).mask(0xff); state_add(H6280_X, "X", m_x).mask(0xff); state_add(H6280_Y, "Y", m_y).mask(0xff); state_add(H6280_IRQ_MASK, "IM", m_irq_mask).mask(0xff); state_add(H6280_TIMER_STATE,"TMR", m_timer_status).mask(0xff); state_add(H6280_NMI_STATE, "NMI", m_nmi_state).mask(0xf); state_add(H6280_IRQ1_STATE, "IRQ1", m_irq_state[0]).mask(0xf); state_add(H6280_IRQ2_STATE, "IRQ2", m_irq_state[1]).mask(0xf); state_add(H6280_IRQT_STATE, "IRQT", m_irq_state[2]).mask(0xf); for (int i = 0; i < 8; i++) state_add(H6280_MPR0 + i, util::string_format("MPR%d", i).c_str(), m_mmr[i]).mask(0xff); save_item(NAME(m_ppc.w.l)); save_item(NAME(m_pc.w.l)); save_item(NAME(m_sp.w.l)); save_item(NAME(m_zp.w.l)); save_item(NAME(m_ea.w.l)); save_item(NAME(m_a)); save_item(NAME(m_x)); save_item(NAME(m_y)); save_item(NAME(m_p)); save_item(NAME(m_mmr)); save_item(NAME(m_irq_mask)); save_item(NAME(m_timer_status)); save_item(NAME(m_timer_ack)); save_item(NAME(m_clocks_per_cycle)); save_item(NAME(m_timer_value)); save_item(NAME(m_timer_load)); save_item(NAME(m_nmi_state)); save_item(NAME(m_irq_state[0])); save_item(NAME(m_irq_state[1])); save_item(NAME(m_irq_state[2])); save_item(NAME(m_irq_pending)); #if H6280_LAZY_FLAGS save_item(NAME(m_nz)); #endif save_item(NAME(m_io_buffer)); // set our instruction counter set_icountptr(m_icount); m_icount = 0; /* clear pending interrupts */ for (auto & elem : m_irq_state) { elem = CLEAR_LINE; } m_nmi_state = CLEAR_LINE; } void h6280_device::device_reset() { /* wipe out the h6280 structure */ m_ppc.d = 0; m_pc.d = 0; m_zp.d = 0; m_ea.d = 0; m_a = 0; m_x = 0; m_y = 0; m_p = 0; memset(m_mmr, 0, sizeof(uint8_t) * 8); m_irq_mask = 0; m_timer_ack = 0; m_timer_value = 0; #if H6280_LAZY_FLAGS m_nz = 0; #endif m_io_buffer = 0; space(AS_PROGRAM).cache(m_cache); space(AS_PROGRAM).specific(m_program); space(AS_IO).specific(m_io); /* set I and B flags */ P = _fI | _fB; /* stack starts at 0x01ff */ m_sp.d = 0x1ff; /* read the reset vector into PC */ PCL = program_read8(H6280_RESET_VEC); PCH = program_read8(H6280_RESET_VEC + 1); /* CPU starts in low speed mode */ m_clocks_per_cycle = 4; /* timer off by default */ m_timer_status = 0; m_timer_load = 128 * 1024 * m_timer_scale; m_irq_pending = 0; } void h6280_device::device_stop() { /* nothing */ } inline uint32_t h6280_device::translated(uint16_t addr) { return ((m_mmr[((addr) >> 13) & 7] << 13) | ((addr) & 0x1fff)); } inline void h6280_device::h6280_cycles(int cyc) { m_icount -= ((cyc) * m_clocks_per_cycle); m_timer_value -= ((cyc) * m_clocks_per_cycle); } #if H6280_LAZY_FLAGS #define NZ m_NZ inline void h6280_device::set_nz(uint8_t n) { P &= ~_fT; NZ = ((n & _fN) << 8) | n; } #else inline void h6280_device::set_nz(uint8_t n) { P = (P & ~(_fN|_fT|_fZ)) | (n & _fN) | ((n == 0) ? _fZ : 0); } #endif inline void h6280_device::clear_t() { P &= ~_fT; } inline void h6280_device::do_interrupt(uint16_t vector) { h6280_cycles(7); /* 7 cycles for an int */ push(PCH); push(PCL); compose_p(0, _fB); push(P); P = (P & ~_fD) | _fI; /* knock out D and set I flag */ PCL = program_read8(vector); PCH = program_read8(vector + 1); } inline void h6280_device::check_and_take_irq_lines() { if ( m_nmi_state != CLEAR_LINE ) { m_nmi_state = CLEAR_LINE; do_interrupt(H6280_NMI_VEC); } else if( !(P & _fI) ) { if ( m_irq_state[2] != CLEAR_LINE && !(m_irq_mask & 0x4) ) { do_interrupt(H6280_TIMER_VEC); } else if ( m_irq_state[0] != CLEAR_LINE && !(m_irq_mask & 0x2) ) { standard_irq_callback(0, PCW); do_interrupt(H6280_IRQ1_VEC); } else if ( m_irq_state[1] != CLEAR_LINE && !(m_irq_mask & 0x1) ) { standard_irq_callback(1, PCW); do_interrupt(H6280_IRQ2_VEC); } } } inline void h6280_device::check_irq_lines() { if (!m_irq_pending) m_irq_pending = 2; } /*************************************************************** * CHECK_VDC_VCE_PENALTY * The CPU inserts 1 clock delay when accessing the VDC or VCE * area. ***************************************************************/ inline void h6280_device::check_vdc_vce_penalty(uint16_t addr) { if ( ( translated(addr) & 0x1FF800 ) == 0x1FE000 ) { h6280_cycles(1); } } /*************************************************************** * BRA branch relative ***************************************************************/ inline void h6280_device::bra(bool cond) { clear_t(); if (cond) { h6280_cycles(4); uint8_t tmp = read_opcode_arg(); PCW++; EAW = PCW + (signed char)tmp; PCD = EAD; } else { PCW++; h6280_cycles(2); } } /*************************************************************** * * Helper macros to build the effective address * ***************************************************************/ /*************************************************************** * EA = zero page address ***************************************************************/ inline void h6280_device::ea_zpg() { ZPL = read_opcode_arg(); PCW++; EAD = ZPD; } /*************************************************************** * EA = zero page address - T flag ***************************************************************/ inline void h6280_device::ea_tflg() { ZPL = X; EAD = ZPD; } /*************************************************************** * EA = zero page address + X ***************************************************************/ inline void h6280_device::ea_zpx() { ZPL = read_opcode_arg() + X; PCW++; EAD = ZPD; } /*************************************************************** * EA = zero page address + Y ***************************************************************/ inline void h6280_device::ea_zpy() { ZPL = read_opcode_arg() + Y; PCW++; EAD = ZPD; } /*************************************************************** * EA = absolute address ***************************************************************/ inline void h6280_device::ea_abs() { EAL = read_opcode_arg(); PCW++; EAH = read_opcode_arg(); PCW++; } /*************************************************************** * EA = absolute address + X ***************************************************************/ inline void h6280_device::ea_abx() { ea_abs(); EAW += X; } /*************************************************************** * EA = absolute address + Y ***************************************************************/ inline void h6280_device::ea_aby() { ea_abs(); EAW += Y; } /*************************************************************** * EA = zero page indirect (65c02 pre indexed w/o X) ***************************************************************/ inline void h6280_device::ea_zpi() { ZPL = read_opcode_arg(); PCW++; EAD = program_read16z(ZPD); } /*************************************************************** * EA = zero page + X indirect (pre indexed) ***************************************************************/ inline void h6280_device::ea_idx() { ZPL = read_opcode_arg() + X; PCW++; EAD = program_read16z(ZPD); } /*************************************************************** * EA = zero page indirect + Y (post indexed) ***************************************************************/ inline void h6280_device::ea_idy() { ZPL = read_opcode_arg(); PCW++; EAD = program_read16z(ZPD); EAW += Y; } /*************************************************************** * EA = indirect (only used by JMP) ***************************************************************/ inline void h6280_device::ea_ind() { ea_abs(); uint8_t tmp = program_read8(EAD); EAD++; EAH = program_read8(EAD); EAL = tmp; } /*************************************************************** * EA = indirect plus x (only used by JMP) ***************************************************************/ inline void h6280_device::ea_iax() { ea_abs(); EAD+=X; uint8_t tmp = program_read8(EAD); EAD++; EAH = program_read8(EAD); EAL = tmp; } inline uint8_t h6280_device::rd_imm() { uint8_t tmp = read_opcode_arg(); PCW++; return tmp; } inline uint8_t h6280_device::rd_zpg() { ea_zpg(); return program_read8z(EAD); } inline uint8_t h6280_device::rd_zpx() { ea_zpx(); return program_read8z(EAD); } inline uint8_t h6280_device::rd_zpy() { ea_zpy(); return program_read8z(EAD); } inline uint8_t h6280_device::rd_abs() { ea_abs(); return program_read8(EAD); } inline uint8_t h6280_device::rd_abx() { ea_abx(); return program_read8(EAD); } inline uint8_t h6280_device::rd_aby() { ea_aby(); return program_read8(EAD); } inline uint8_t h6280_device::rd_zpi() { ea_zpi(); return program_read8(EAD); } inline uint8_t h6280_device::rd_idx() { ea_idx(); return program_read8(EAD); } inline uint8_t h6280_device::rd_idy() { ea_idy(); return program_read8(EAD); } inline uint8_t h6280_device::rd_tfl() { ea_tflg(); return program_read8z(EAD); } inline void h6280_device::wr_zpg(uint8_t tmp) { ea_zpg(); wb_eaz(tmp); } inline void h6280_device::wr_zpx(uint8_t tmp) { ea_zpx(); wb_eaz(tmp); } inline void h6280_device::wr_zpy(uint8_t tmp) { ea_zpy(); wb_eaz(tmp); } inline void h6280_device::wr_abs(uint8_t tmp) { ea_abs(); wb_ea(tmp); } inline void h6280_device::wr_abx(uint8_t tmp) { ea_abx(); wb_ea(tmp); } inline void h6280_device::wr_aby(uint8_t tmp) { ea_aby(); wb_ea(tmp); } inline void h6280_device::wr_zpi(uint8_t tmp) { ea_zpi(); wb_ea(tmp); } inline void h6280_device::wr_idx(uint8_t tmp) { ea_idx(); wb_ea(tmp); } inline void h6280_device::wr_idy(uint8_t tmp) { ea_idy(); wb_ea(tmp); } inline void h6280_device::wb_ea(uint8_t tmp) { program_write8(EAD, tmp); } inline void h6280_device::wb_eaz(uint8_t tmp) { program_write8z(EAD, tmp); } /*************************************************************** * * Macros to emulate the 6280 opcodes * ***************************************************************/ /*************************************************************** * compose the real flag register by * including N and Z and set any * SET and clear any CLR bits also ***************************************************************/ #if H6280_LAZY_FLAGS inline void h6280_device::compose_p(uint8_t SET, uint8_t CLR) { P = (P & ~(_fN | _fZ | CLR)) | (NZ >> 8) | ((NZ & 0xff) ? 0 : _fZ) | SET; } #else inline void h6280_device::compose_p(uint8_t SET, uint8_t CLR) { P = (P & ~CLR) | SET; } #endif /* 6280 ******************************************************** * ADC Add with carry ***************************************************************/ inline void h6280_device::tadc(uint8_t tmp) { clear_t(); int tflagtemp = rd_tfl(); if (P & _fD) { int c = (P & _fC); int lo = (tflagtemp & 0x0f) + (tmp & 0x0f) + c; int hi = (tflagtemp & 0xf0) + (tmp & 0xf0); P &= ~_fC; if (lo > 0x09) { hi += 0x10; lo += 0x06; } if (hi > 0x90) hi += 0x60; if (hi & 0xff00) P |= _fC; tflagtemp = (lo & 0x0f) + (hi & 0xf0); h6280_cycles(1); } else { int c = (P & _fC); int sum = tflagtemp + tmp + c; P &= ~(_fV | _fC); if (~(tflagtemp^tmp) & (tflagtemp^sum) & _fN) P |= _fV; if (sum & 0xff00) P |= _fC; tflagtemp = (uint8_t) sum; } set_nz(tflagtemp); wb_eaz(tflagtemp); h6280_cycles(3); } inline void h6280_device::adc(uint8_t tmp) { if(P & _fT) tadc(tmp); else { if (P & _fD) { int c = (P & _fC); int lo = (A & 0x0f) + (tmp & 0x0f) + c; int hi = (A & 0xf0) + (tmp & 0xf0); P &= ~_fC; if (lo > 0x09) { hi += 0x10; lo += 0x06; } if (hi > 0x90) hi += 0x60; if (hi & 0xff00) P |= _fC; A = (lo & 0x0f) + (hi & 0xf0); h6280_cycles(1); } else { int c = (P & _fC); int sum = A + tmp + c; P &= ~(_fV | _fC); if (~(A^tmp) & (A^sum) & _fN) P |= _fV; if (sum & 0xff00) P |= _fC; A = (uint8_t) sum; } set_nz(A); } } /* 6280 ******************************************************** * AND Logical and ***************************************************************/ inline void h6280_device::tand(uint8_t tmp) { clear_t(); int tflagtemp = rd_tfl(); tflagtemp = (uint8_t)(tflagtemp & tmp); wb_eaz(tflagtemp); set_nz(tflagtemp); h6280_cycles(3); } inline void h6280_device::and_a(uint8_t tmp) { if(P & _fT) tand(tmp); else { A = (uint8_t)(A & tmp); set_nz(A); } } /* 6280 ******************************************************** * ASL Arithmetic shift left ***************************************************************/ inline uint8_t h6280_device::asl(uint8_t tmp) { clear_t(); P = (P & ~_fC) | ((tmp >> 7) & _fC); tmp = (uint8_t)(tmp << 1); set_nz(tmp); return tmp; } /* 6280 ******************************************************** * BBR Branch if bit is reset ***************************************************************/ inline void h6280_device::bbr(int bit, uint8_t tmp) { bra(!(tmp & (1<= tmp) P |= _fC; set_nz((uint8_t)(A - tmp)); } /* 6280 ******************************************************** * CPX Compare index X ***************************************************************/ inline void h6280_device::cpx(uint8_t tmp) { clear_t(); P &= ~_fC; if (X >= tmp) P |= _fC; set_nz((uint8_t)(X - tmp)); } /* 6280 ******************************************************** * CPY Compare index Y ***************************************************************/ inline void h6280_device::cpy(uint8_t tmp) { clear_t(); P &= ~_fC; if (Y >= tmp) P |= _fC; set_nz((uint8_t)(Y - tmp)); } /* 6280 ******************************************************** * DEC Decrement memory ***************************************************************/ inline uint8_t h6280_device::dec(uint8_t tmp) { clear_t(); tmp = (uint8_t)(tmp-1); set_nz(tmp); return tmp; } /* 6280 ******************************************************** * DEX Decrement index X ***************************************************************/ inline void h6280_device::dex() { clear_t(); X = (uint8_t)(X - 1); set_nz(X); } /* 6280 ******************************************************** * DEY Decrement index Y ***************************************************************/ inline void h6280_device::dey() { clear_t(); Y = (uint8_t)(Y - 1); set_nz(Y); } /* 6280 ******************************************************** * EOR Logical exclusive or ***************************************************************/ inline void h6280_device::teor(uint8_t tmp) { clear_t(); int tflagtemp = rd_tfl(); tflagtemp = (uint8_t)(tflagtemp ^ tmp); wb_eaz(tflagtemp); set_nz(tflagtemp); h6280_cycles(3); } inline void h6280_device::eor(uint8_t tmp) { if(P & _fT) teor(tmp); else { A = (uint8_t)(A ^ tmp); set_nz(A); } } /* 6280 ******************************************************** * INC Increment memory ***************************************************************/ inline uint8_t h6280_device::inc(uint8_t tmp) { clear_t(); tmp = (uint8_t)(tmp+1); set_nz(tmp); return tmp; } /* 6280 ******************************************************** * INX Increment index X ***************************************************************/ inline void h6280_device::inx() { clear_t(); X = (uint8_t)(X + 1); set_nz(X); } /* 6280 ******************************************************** * INY Increment index Y ***************************************************************/ inline void h6280_device::iny() { clear_t(); Y = (uint8_t)(Y + 1); set_nz(Y); } /* 6280 ******************************************************** * JMP Jump to address * set PC to the effective address ***************************************************************/ inline void h6280_device::jmp() { clear_t(); PCD = EAD; } /* 6280 ******************************************************** * JSR Jump to subroutine * decrement PC (sic!) push PC hi, push PC lo and set * PC to the effective address ***************************************************************/ inline void h6280_device::jsr() { clear_t(); PCW--; push(PCH); push(PCL); PCD = EAD; } /* 6280 ******************************************************** * LDA Load accumulator ***************************************************************/ inline void h6280_device::lda(uint8_t tmp) { clear_t(); A = (uint8_t)tmp; set_nz(A); } /* 6280 ******************************************************** * LDX Load index X ***************************************************************/ inline void h6280_device::ldx(uint8_t tmp) { clear_t(); X = (uint8_t)tmp; set_nz(X); } /* 6280 ******************************************************** * LDY Load index Y ***************************************************************/ inline void h6280_device::ldy(uint8_t tmp) { clear_t(); Y = (uint8_t)tmp; set_nz(Y); } /* 6280 ******************************************************** * LSR Logic shift right * 0 -> [7][6][5][4][3][2][1][0] -> C ***************************************************************/ inline uint8_t h6280_device::lsr(uint8_t tmp) { clear_t(); P = (P & ~_fC) | (tmp & _fC); tmp = (uint8_t)tmp >> 1; set_nz(tmp); return tmp; } /* 6280 ******************************************************** * NOP No operation ***************************************************************/ inline void h6280_device::nop() { clear_t(); } /* 6280 ******************************************************** * ORA Logical inclusive or ***************************************************************/ inline void h6280_device::tora(uint8_t tmp) { clear_t(); int tflagtemp = rd_tfl(); tflagtemp = (uint8_t)(tflagtemp | tmp); wb_eaz(tflagtemp); set_nz(tflagtemp); h6280_cycles(3); } inline void h6280_device::ora(uint8_t tmp) { if(P & _fT) tora(tmp); else { A = (uint8_t)(A | tmp); set_nz(A); } } /* 6280 ******************************************************** * PHA Push accumulator ***************************************************************/ inline void h6280_device::pha() { clear_t(); push(A); } /* 6280 ******************************************************** * PHP Push processor status (flags) ***************************************************************/ inline void h6280_device::php() { clear_t(); compose_p(0,0); push(P); } /* 6280 ******************************************************** * PHX Push index X ***************************************************************/ inline void h6280_device::phx() { clear_t(); push(X); } /* 6280 ******************************************************** * PHY Push index Y ***************************************************************/ inline void h6280_device::phy() { clear_t(); push(Y); } /* 6280 ******************************************************** * PLA Pull accumulator ***************************************************************/ inline void h6280_device::pla() { clear_t(); pull(A); set_nz(A); } /* 6280 ******************************************************** * PLP Pull processor status (flags) ***************************************************************/ inline void h6280_device::plp() { #if H6280_LAZY_FLAGS pull(P); P |= _fB; NZ = ((P & _fN) << 8) | ((P & _fZ) ^ _fZ); check_irq_lines(); #else pull(P); P |= _fB; check_irq_lines(); #endif } /* 6280 ******************************************************** * PLX Pull index X ***************************************************************/ inline void h6280_device::plx() { clear_t(); pull(X); set_nz(X); } /* 6280 ******************************************************** * PLY Pull index Y ***************************************************************/ inline void h6280_device::ply() { clear_t(); pull(Y); set_nz(Y); } /* 6280 ******************************************************** * RMB Reset memory bit ***************************************************************/ inline uint8_t h6280_device::rmb(int bit, uint8_t tmp) { clear_t(); tmp &= ~(1<> 8) & _fC); tmp = (uint8_t)tmp9; set_nz(tmp); return tmp; } /* 6280 ******************************************************** * ROR Rotate right * C -> [7][6][5][4][3][2][1][0] -> new C ***************************************************************/ inline uint8_t h6280_device::ror(uint8_t tmp) { clear_t(); int tmp9 = tmp | (P & _fC) << 8; P = (P & ~_fC) | (tmp & _fC); tmp = (uint8_t)(tmp9 >> 1); set_nz(tmp); return tmp; } /* 6280 ******************************************************** * RTI Return from interrupt * pull flags, pull PC lo, pull PC hi and increment PC ***************************************************************/ inline void h6280_device::rti() { #if H6280_LAZY_FLAGS pull(P); P |= _fB; NZ = ((P & _fN) << 8) | ((P & _fZ) ^ _fZ); pull(PCL); pull(PCH); check_irq_lines(); #else pull(P); P |= _fB; pull(PCL); pull(PCH); check_irq_lines(); #endif } /* 6280 ******************************************************** * RTS Return from subroutine * pull PC lo, PC hi and increment PC ***************************************************************/ inline void h6280_device::rts() { clear_t(); pull(PCL); pull(PCH); PCW++; } /* 6280 ******************************************************** * SAX Swap accumulator and index X ***************************************************************/ inline void h6280_device::sax() { clear_t(); uint8_t tmp = X; X = A; A = tmp; } /* 6280 ******************************************************** * SAY Swap accumulator and index Y ***************************************************************/ inline void h6280_device::say() { clear_t(); uint8_t tmp = Y; Y = A; A = tmp; } /* 6280 ******************************************************** * SBC Subtract with carry ***************************************************************/ inline void h6280_device::tsbc(uint8_t tmp) { clear_t(); int tflagtemp = rd_tfl(); if (P & _fD) { int c = (P & _fC) ^ _fC; int sum = tflagtemp - tmp -c; int lo = (tflagtemp & 0x0f) - (tmp & 0x0f) - c; int hi = (tflagtemp & 0xf0) - (tmp & 0xf0); P &= ~_fC; if (lo & 0xf0) lo -= 6; if (lo & 0x80) hi -= 0x10; if (hi & 0x0f00) hi -= 0x60; if ((sum & 0xff00) == 0) P |= _fC; tflagtemp = (lo & 0x0f) + (hi & 0xf0); h6280_cycles(1); } else { int c = (P & _fC) ^ _fC; int sum = tflagtemp - tmp - c; P &= ~(_fV | _fC); if ((tflagtemp^tmp) & (tflagtemp^sum) & _fN) P |= _fV; if ((sum & 0xff00) == 0) P |= _fC; tflagtemp = (uint8_t) sum; } set_nz(tflagtemp); wb_eaz(tflagtemp); h6280_cycles(3); } inline void h6280_device::sbc(uint8_t tmp) { if(P & _fT) tsbc(tmp); else { if (P & _fD) { int c = (P & _fC) ^ _fC; int sum = A - tmp - c; int lo = (A & 0x0f) - (tmp & 0x0f) - c; int hi = (A & 0xf0) - (tmp & 0xf0); P &= ~_fC; if (lo & 0xf0) lo -= 6; if (lo & 0x80) hi -= 0x10; if (hi & 0x0f00) hi -= 0x60; if ((sum & 0xff00) == 0) P |= _fC; A = (lo & 0x0f) + (hi & 0xf0); h6280_cycles(1); } else { int c = (P & _fC) ^ _fC; int sum = A - tmp - c; P &= ~(_fV | _fC); if ((A^tmp) & (A^sum) & _fN) P |= _fV; if ((sum & 0xff00) == 0) P |= _fC; A = (uint8_t) sum; } set_nz(A); } } /* 6280 ******************************************************** * SEC Set carry flag ***************************************************************/ inline void h6280_device::sec() { clear_t(); P |= _fC; } /* 6280 ******************************************************** * SED Set decimal flag ***************************************************************/ inline void h6280_device::sed() { clear_t(); P |= _fD; } /* 6280 ******************************************************** * SEI Set interrupt flag ***************************************************************/ inline void h6280_device::sei() { clear_t(); P |= _fI; } /* 6280 ******************************************************** * SET Set t flag ***************************************************************/ inline void h6280_device::set() { P |= _fT; } /* 6280 ******************************************************** * SMB Set memory bit ***************************************************************/ inline uint8_t h6280_device::smb(int bit, uint8_t tmp) { clear_t(); tmp |= (1< h6280_device::create_disassembler() { return std::make_unique(); } //------------------------------------------------- // execute_min_cycles - return minimum number of // cycles it takes for one instruction to execute //------------------------------------------------- uint32_t h6280_device::execute_min_cycles() const noexcept { return 2; } //------------------------------------------------- // execute_max_cycles - return maximum number of // cycles it takes for one instruction to execute //------------------------------------------------- uint32_t h6280_device::execute_max_cycles() const noexcept { return 17 + 6*65536; } //------------------------------------------------- // execute_input_edge_triggered - return true if // the input line has an asynchronous edge trigger //------------------------------------------------- bool h6280_device::execute_input_edge_triggered(int inputnum) const noexcept { return inputnum == H6280_NMI_STATE; } //------------------------------------------------- // execute_set_input - act on a changed input/ // interrupt line //------------------------------------------------- void h6280_device::execute_set_input(int inputnum, int state) { switch(inputnum) { case H6280_IRQ1_STATE: case 0: set_irq_line(0, state); break; case H6280_IRQ2_STATE: case 1: set_irq_line(1, state); break; case H6280_IRQT_STATE: case 2: set_irq_line(2, state); break; case H6280_NMI_STATE: case INPUT_LINE_NMI: set_irq_line(INPUT_LINE_NMI, state); break; } } /*************************************************************** * program_read8 read memory ***************************************************************/ uint8_t h6280_device::program_read8(offs_t addr) { check_vdc_vce_penalty(addr); return m_program.read_byte(translated(addr)); } /*************************************************************** * program_write8 write memory ***************************************************************/ void h6280_device::program_write8(offs_t addr, uint8_t data) { check_vdc_vce_penalty(addr); m_program.write_byte(translated(addr), data); } /*************************************************************** * program_read8z read memory - zero page ***************************************************************/ uint8_t h6280_device::program_read8z(offs_t addr) { return m_program.read_byte((m_mmr[1] << 13) | (addr & 0x1fff)); } /*************************************************************** * program_write8z write memory - zero page ***************************************************************/ void h6280_device::program_write8z(offs_t addr, uint8_t data) { m_program.write_byte((m_mmr[1] << 13) | (addr & 0x1fff), data); } /*************************************************************** * program_read16 read word from memory ***************************************************************/ uint16_t h6280_device::program_read16(offs_t addr) { return m_program.read_byte(translated(addr)) | (m_program.read_byte(translated(addr + 1)) << 8); } /*************************************************************** * program_read16z read a word from a zero page address ***************************************************************/ uint16_t h6280_device::program_read16z(offs_t addr) { if ((addr & 0xff) == 0xff) { return m_program.read_byte((m_mmr[1] << 13) | (addr & 0x1fff)) | (m_program.read_byte((m_mmr[1] << 13) | ((addr - 0xff) & 0x1fff)) << 8); } else { return m_program.read_byte((m_mmr[1] << 13) | (addr & 0x1fff)) | (m_program.read_byte((m_mmr[1] << 13) | ((addr + 1) & 0x1fff)) << 8); } } /*************************************************************** * push a register onto the stack ***************************************************************/ void h6280_device::push(uint8_t value) { m_program.write_byte((m_mmr[1] << 13) | m_sp.d, value); S--; } /*************************************************************** * pull a register from the stack ***************************************************************/ void h6280_device::pull(uint8_t &value) { S++; value = m_program.read_byte((m_mmr[1] << 13) | m_sp.d); } /*************************************************************** * read_opcode read an opcode ***************************************************************/ uint8_t h6280_device::read_opcode() { return m_cache.read_byte(translated(PCW)); } /*************************************************************** * read_opcode_arg read an opcode argument ***************************************************************/ uint8_t h6280_device::read_opcode_arg() { return m_cache.read_byte(translated(PCW)); } //------------------------------------------------- // execute_run - execute a timeslice's worth of // opcodes //------------------------------------------------- void h6280_device::execute_run() { int in; if (m_irq_pending == 2) { m_irq_pending--; } /* Execute instructions */ do { m_ppc = m_pc; debugger_instruction_hook(PCW); /* Execute 1 instruction */ in = read_opcode(); PCW++; (this->*m_opcode[in])(); if (m_irq_pending) { if (m_irq_pending == 1) { if (!(P & _fI)) { m_irq_pending--; check_and_take_irq_lines(); } } else { m_irq_pending--; } } /* Check internal timer */ if (m_timer_status) { if (m_timer_value<=0) { if (!m_irq_pending) { m_irq_pending = 1; } while (m_timer_value <= 0) { m_timer_value += m_timer_load; } set_irq_line(2, ASSERT_LINE); } } } while (m_icount > 0); } //************************************************************************** // IRQ HANDLING //************************************************************************** void h6280_device::set_irq_line(int irqline, int state) { if (irqline == INPUT_LINE_NMI) { if (state != ASSERT_LINE) return; m_nmi_state = state; check_irq_lines(); } else if (irqline < 3) { /* If the state has not changed, just return */ if (m_irq_state[irqline] == state) return; m_irq_state[irqline] = state; check_irq_lines(); } } //************************************************************************** // REGISTER HANDLING //************************************************************************** uint8_t h6280_device::irq_status_r(offs_t offset) { int status; switch (offset & 3) { default: return m_io_buffer; case 3: { status = 0; if (m_irq_state[1] != CLEAR_LINE) status |= 1; /* IRQ 2 */ if (m_irq_state[0] != CLEAR_LINE) status |= 2; /* IRQ 1 */ if (m_irq_state[2] != CLEAR_LINE) status |= 4; /* TIMER */ return status | (m_io_buffer & (~H6280_IRQ_MASK)); } case 2: return m_irq_mask | (m_io_buffer & (~H6280_IRQ_MASK)); } } void h6280_device::irq_status_w(offs_t offset, uint8_t data) { m_io_buffer = data; switch (offset & 3) { default: m_io_buffer = data; break; case 2: /* Write irq mask */ m_irq_mask = data & 0x7; check_irq_lines(); break; case 3: /* Timer irq ack */ set_irq_line(2, CLEAR_LINE); break; } } uint8_t h6280_device::timer_r() { /* only returns countdown */ return ((m_timer_value >> 10) & 0x7F) | (m_io_buffer & 0x80); } void h6280_device::timer_w(offs_t offset, uint8_t data) { m_io_buffer = data; switch (offset & 1) { case 0: /* Counter preload */ // matches HW behaviour, value is latched only with 0->1 counter enable transition m_timer_load = ((data & 127) + 1) * 1024 * m_timer_scale; return; case 1: /* Counter enable */ if (data & 1) {/* stop -> start causes reload */ if(m_timer_status == 0) m_timer_value = m_timer_load; } m_timer_status = data & 1; return; } } uint8_t h6280_device::port_r() { if (!m_port_in_cb.isunset()) return m_port_in_cb(); else return m_io_buffer; } void h6280_device::port_w(uint8_t data) { m_io_buffer = data; m_port_out_cb(data); } uint8_t h6280_device::io_buffer_r() { return m_io_buffer; } void h6280_device::psg_w(offs_t offset, uint8_t data) { m_io_buffer = data; m_psg->c6280_w(offset, data); } bool h6280_device::memory_translate(int spacenum, int intention, offs_t &address, address_space *&target_space) { target_space = &space(spacenum); if (spacenum == AS_PROGRAM) address = translated(address); return true; } uint8_t h6280_device::io_get_buffer() { return m_io_buffer; } void h6280_device::io_set_buffer(uint8_t data) { m_io_buffer = data; }