diff options
Diffstat (limited to 'src/devices/cpu/m6502/m6502.c')
-rw-r--r-- | src/devices/cpu/m6502/m6502.c | 724 |
1 files changed, 724 insertions, 0 deletions
diff --git a/src/devices/cpu/m6502/m6502.c b/src/devices/cpu/m6502/m6502.c new file mode 100644 index 00000000000..01b2b8f0f98 --- /dev/null +++ b/src/devices/cpu/m6502/m6502.c @@ -0,0 +1,724 @@ +// license:BSD-3-Clause +// copyright-holders:Olivier Galibert +/*************************************************************************** + + m6502.c + + Mostek 6502, original NMOS variant + +***************************************************************************/ + +#include "emu.h" +#include "debugger.h" +#include "m6502.h" + +const device_type M6502 = &device_creator<m6502_device>; + +m6502_device::m6502_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : + cpu_device(mconfig, M6502, "M6502", tag, owner, clock, "m6502", __FILE__), + sync_w(*this), + program_config("program", ENDIANNESS_LITTLE, 8, 16), + sprogram_config("decrypted_opcodes", ENDIANNESS_LITTLE, 8, 16) +{ + direct_disabled = false; +} + +m6502_device::m6502_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source) : + cpu_device(mconfig, type, name, tag, owner, clock, shortname, source), + sync_w(*this), + program_config("program", ENDIANNESS_LITTLE, 8, 16), + sprogram_config("decrypted_opcodes", ENDIANNESS_LITTLE, 8, 16) +{ + direct_disabled = false; +} + +void m6502_device::device_start() +{ + if(direct_disabled) + mintf = new mi_default_nd; + else + mintf = new mi_default_normal; + + init(); +} + +void m6502_device::init() +{ + mintf->program = &space(AS_PROGRAM); + mintf->sprogram = has_space(AS_DECRYPTED_OPCODES) ? &space(AS_DECRYPTED_OPCODES) : mintf->program; + + mintf->direct = &mintf->program->direct(); + mintf->sdirect = &mintf->sprogram->direct(); + + sync_w.resolve_safe(); + + state_add(STATE_GENPC, "GENPC", NPC).noshow(); + state_add(STATE_GENPCBASE, "GENPCBASE", PPC).noshow(); + state_add(STATE_GENSP, "GENSP", SP).noshow(); + state_add(STATE_GENFLAGS, "GENFLAGS", P).callimport().formatstr("%6s").noshow(); + state_add(M6502_PC, "PC", NPC).callimport(); + state_add(M6502_A, "A", A); + state_add(M6502_X, "X", X); + state_add(M6502_Y, "Y", Y); + state_add(M6502_P, "P", P).callimport(); + state_add(M6502_S, "SP", SP); + state_add(M6502_IR, "IR", IR); + + save_item(NAME(PC)); + save_item(NAME(NPC)); + save_item(NAME(A)); + save_item(NAME(X)); + save_item(NAME(Y)); + save_item(NAME(P)); + save_item(NAME(SP)); + save_item(NAME(TMP)); + save_item(NAME(TMP2)); + save_item(NAME(IR)); + save_item(NAME(nmi_state)); + save_item(NAME(irq_state)); + save_item(NAME(apu_irq_state)); + save_item(NAME(v_state)); + save_item(NAME(inst_state)); + save_item(NAME(inst_substate)); + save_item(NAME(inst_state_base)); + save_item(NAME(irq_taken)); + save_item(NAME(inhibit_interrupts)); + + m_icountptr = &icount; + + PC = 0x0000; + NPC = 0x0000; + A = 0x00; + X = 0x80; + Y = 0x00; + P = 0x36; + SP = 0x01bd; + TMP = 0x0000; + TMP2 = 0x00; + IR = 0x00; + nmi_state = false; + irq_state = false; + apu_irq_state = false; + irq_taken = false; + v_state = false; + inst_state = STATE_RESET; + inst_substate = 0; + inst_state_base = 0; + sync = false; + inhibit_interrupts = false; +} + +void m6502_device::device_reset() +{ + inst_state = STATE_RESET; + inst_substate = 0; + inst_state_base = 0; + nmi_state = false; + irq_state = false; + apu_irq_state = false; + irq_taken = false; + v_state = false; + sync = false; + sync_w(CLEAR_LINE); + inhibit_interrupts = false; +} + + +UINT32 m6502_device::execute_min_cycles() const +{ + return 1; +} + +UINT32 m6502_device::execute_max_cycles() const +{ + return 10; +} + +UINT32 m6502_device::execute_input_lines() const +{ + return NMI_LINE+1; +} + +void m6502_device::do_adc_d(UINT8 val) +{ + UINT8 c = P & F_C ? 1 : 0; + P &= ~(F_N|F_V|F_Z|F_C); + UINT8 al = (A & 15) + (val & 15) + c; + if(al > 9) + al += 6; + UINT8 ah = (A >> 4) + (val >> 4) + (al > 15); + if(!UINT8(A + val + c)) + P |= F_Z; + else if(ah & 8) + P |= F_N; + if(~(A^val) & (A^(ah << 4)) & 0x80) + P |= F_V; + if(ah > 9) + ah += 6; + if(ah > 15) + P |= F_C; + A = (ah << 4) | (al & 15); +} + +void m6502_device::do_adc_nd(UINT8 val) +{ + UINT16 sum; + sum = A + val + (P & F_C ? 1 : 0); + P &= ~(F_N|F_V|F_Z|F_C); + if(!UINT8(sum)) + P |= F_Z; + else if(INT8(sum) < 0) + P |= F_N; + if(~(A^val) & (A^sum) & 0x80) + P |= F_V; + if(sum & 0xff00) + P |= F_C; + A = sum; +} + +void m6502_device::do_adc(UINT8 val) +{ + if(P & F_D) + do_adc_d(val); + else + do_adc_nd(val); +} + +void m6502_device::do_arr_nd() +{ + bool c = P & F_C; + P &= ~(F_N|F_Z|F_C|F_V); + A >>= 1; + if(c) + A |= 0x80; + if(!A) + P |= F_Z; + else if(INT8(A)<0) + P |= F_N; + if(A & 0x40) + P |= F_V|F_C; + if(A & 0x20) + P ^= F_V; +} + +void m6502_device::do_arr_d() +{ + // The adc/ror interaction gives an extremely weird result + bool c = P & F_C; + P &= ~(F_N|F_Z|F_C|F_V); + UINT8 a = A >> 1; + if(c) + a |= 0x80; + if(!a) + P |= F_Z; + else if(INT8(a) < 0) + P |= F_N; + if((a ^ A) & 0x40) + P |= F_V; + + if((A & 0x0f) >= 0x05) + a = ((a + 6) & 0x0f) | (a & 0xf0); + + if((A & 0xf0) >= 0x50) { + a += 0x60; + P |= F_C; + } + A = a; +} + +void m6502_device::do_arr() +{ + if(P & F_D) + do_arr_d(); + else + do_arr_nd(); +} + +void m6502_device::do_cmp(UINT8 val1, UINT8 val2) +{ + P &= ~(F_N|F_Z|F_C); + UINT16 r = val1-val2; + if(!r) + P |= F_Z; + else if(INT8(r) < 0) + P |= F_N; + if(!(r & 0xff00)) + P |= F_C; +} + +void m6502_device::do_sbc_d(UINT8 val) +{ + UINT8 c = P & F_C ? 0 : 1; + P &= ~(F_N|F_V|F_Z|F_C); + UINT16 diff = A - val - c; + UINT8 al = (A & 15) - (val & 15) - c; + if(INT8(al) < 0) + al -= 6; + UINT8 ah = (A >> 4) - (val >> 4) - (INT8(al) < 0); + if(!UINT8(diff)) + P |= F_Z; + else if(diff & 0x80) + P |= F_N; + if((A^val) & (A^diff) & 0x80) + P |= F_V; + if(!(diff & 0xff00)) + P |= F_C; + if(INT8(ah) < 0) + ah -= 6; + A = (ah << 4) | (al & 15); +} + +void m6502_device::do_sbc_nd(UINT8 val) +{ + UINT16 diff = A - val - (P & F_C ? 0 : 1); + P &= ~(F_N|F_V|F_Z|F_C); + if(!UINT8(diff)) + P |= F_Z; + else if(INT8(diff) < 0) + P |= F_N; + if((A^val) & (A^diff) & 0x80) + P |= F_V; + if(!(diff & 0xff00)) + P |= F_C; + A = diff; +} + +void m6502_device::do_sbc(UINT8 val) +{ + if(P & F_D) + do_sbc_d(val); + else + do_sbc_nd(val); +} + +void m6502_device::do_bit(UINT8 val) +{ + P &= ~(F_N|F_Z|F_V); + UINT8 r = A & val; + if(!r) + P |= F_Z; + if(val & 0x80) + P |= F_N; + if(val & 0x40) + P |= F_V; +} + +UINT8 m6502_device::do_asl(UINT8 v) +{ + P &= ~(F_N|F_Z|F_C); + UINT8 r = v<<1; + if(!r) + P |= F_Z; + else if(INT8(r) < 0) + P |= F_N; + if(v & 0x80) + P |= F_C; + return r; +} + +UINT8 m6502_device::do_lsr(UINT8 v) +{ + P &= ~(F_N|F_Z|F_C); + if(v & 1) + P |= F_C; + v >>= 1; + if(!v) + P |= F_Z; + return v; +} + +UINT8 m6502_device::do_ror(UINT8 v) +{ + bool c = P & F_C; + P &= ~(F_N|F_Z|F_C); + if(v & 1) + P |= F_C; + v >>= 1; + if(c) + v |= 0x80; + if(!v) + P |= F_Z; + else if(INT8(v)<0) + P |= F_N; + return v; +} + +UINT8 m6502_device::do_rol(UINT8 v) +{ + bool c = P & F_C; + P &= ~(F_N|F_Z|F_C); + if(v & 0x80) + P |= F_C; + v <<= 1; + if(c) + v |= 0x01; + if(!v) + P |= F_Z; + else if(INT8(v)<0) + P |= F_N; + return v; +} + +UINT8 m6502_device::do_asr(UINT8 v) +{ + P &= ~(F_N|F_Z|F_C); + if(v & 1) + P |= F_C; + v >>= 1; + if(!v) + P |= F_Z; + else if(v & 0x40) { + P |= F_N; + v |= 0x80; + } + return v; +} + +void m6502_device::execute_run() +{ + if(inst_substate) + do_exec_partial(); + + while(icount > 0) { + if(inst_state < 0xff00) { + PPC = NPC; + inst_state = IR | inst_state_base; + if(machine().debug_flags & DEBUG_FLAG_ENABLED) + debugger_instruction_hook(this, NPC); + } + do_exec_full(); + } +} + +void m6502_device::execute_set_input(int inputnum, int state) +{ + switch(inputnum) { + case IRQ_LINE: irq_state = state == ASSERT_LINE; break; + case APU_IRQ_LINE: apu_irq_state = state == ASSERT_LINE; break; + case NMI_LINE: nmi_state = nmi_state || (state == ASSERT_LINE); break; + case V_LINE: + if(!v_state && state == ASSERT_LINE) + P |= F_V; + v_state = state == ASSERT_LINE; + break; + } +} + + +const address_space_config *m6502_device::memory_space_config(address_spacenum spacenum) const +{ + switch(spacenum) + { + case AS_PROGRAM: return &program_config; + case AS_DECRYPTED_OPCODES: return has_configured_map(AS_DECRYPTED_OPCODES) ? &sprogram_config : NULL; + default: return NULL; + } +} + + +void m6502_device::state_import(const device_state_entry &entry) +{ + switch(entry.index()) { + case STATE_GENFLAGS: + case M6502_P: + P = P | (F_B|F_E); + break; + case M6502_PC: + PC = NPC; + irq_taken = false; + prefetch(); + PPC = NPC; + inst_state = IR | inst_state_base; + break; + } +} + +void m6502_device::state_export(const device_state_entry &entry) +{ +} + +void m6502_device::state_string_export(const device_state_entry &entry, std::string &str) +{ + switch(entry.index()) { + case STATE_GENFLAGS: + case M6502_P: + strprintf(str, "%c%c%c%c%c%c", + P & F_N ? 'N' : '.', + P & F_V ? 'V' : '.', + P & F_D ? 'D' : '.', + P & F_I ? 'I' : '.', + P & F_Z ? 'Z' : '.', + P & F_C ? 'C' : '.'); + break; + } +} + + +UINT32 m6502_device::disasm_min_opcode_bytes() const +{ + return 1; +} + +UINT32 m6502_device::disasm_max_opcode_bytes() const +{ + return 4; +} + +offs_t m6502_device::disassemble_generic(char *buffer, offs_t pc, const UINT8 *oprom, const UINT8 *opram, UINT32 options, const disasm_entry *table) +{ + const disasm_entry &e = table[oprom[0] | inst_state_base]; + UINT32 flags = e.flags | DASMFLAG_SUPPORTED; + buffer += sprintf(buffer, "%s", e.opcode); + + switch(e.mode) { + case DASM_non: + flags |= 1; + break; + + case DASM_aba: + sprintf(buffer, " $%02x%02x", opram[2], opram[1]); + flags |= 3; + break; + + case DASM_abx: + sprintf(buffer, " $%02x%02x, x", opram[2], opram[1]); + flags |= 3; + break; + + case DASM_aby: + sprintf(buffer, " $%02x%02x, y", opram[2], opram[1]); + flags |= 3; + break; + + case DASM_acc: + sprintf(buffer, " a"); + flags |= 1; + break; + + case DASM_adr: + sprintf(buffer, " $%02x%02x", opram[2], opram[1]); + flags |= 3; + break; + + case DASM_bzp: + sprintf(buffer, "%d $%02x", (oprom[0] >> 4) & 7, opram[1]); + flags |= 2; + break; + + case DASM_iax: + sprintf(buffer, " ($%02x%02x, x)", opram[2], opram[1]); + flags |= 3; + break; + + case DASM_idx: + sprintf(buffer, " ($%02x, x)", opram[1]); + flags |= 2; + break; + + case DASM_idy: + sprintf(buffer, " ($%02x), y", opram[1]); + flags |= 2; + break; + + case DASM_idz: + sprintf(buffer, " ($%02x), z", opram[1]); + flags |= 2; + break; + + case DASM_imm: + sprintf(buffer, " #$%02x", opram[1]); + flags |= 2; + break; + + case DASM_imp: + flags |= 1; + break; + + case DASM_ind: + sprintf(buffer, " ($%02x%02x)", opram[2], opram[1]); + flags |= 3; + break; + + case DASM_isy: + sprintf(buffer, " ($%02x, s), y", opram[1]); + flags |= 2; + break; + + case DASM_iw2: + sprintf(buffer, " #$%02x%02x", opram[2], opram[1]); + flags |= 3; + break; + + case DASM_iw3: + sprintf(buffer, " #$%02x%02x%02x", opram[3], opram[2], opram[1]); + flags |= 4; + break; + + case DASM_rel: + sprintf(buffer, " $%04x", (pc & 0xf0000) | UINT16(pc + 2 + INT8(opram[1]))); + flags |= 2; + break; + + case DASM_rw2: + sprintf(buffer, " $%04x", (pc & 0xf0000) | UINT16(pc + 2 + INT16((opram[2] << 8) | opram[1]))); + flags |= 3; + break; + + case DASM_zpb: + sprintf(buffer, "%d $%02x, $%04x", (oprom[0] >> 4) & 7, opram[1], (pc & 0xf0000) | UINT16(pc + 3 + INT8(opram[2]))); + flags |= 3; + break; + + case DASM_zpg: + sprintf(buffer, " $%02x", opram[1]); + flags |= 2; + break; + + case DASM_zpi: + sprintf(buffer, " ($%02x)", opram[1]); + flags |= 2; + break; + + case DASM_zpx: + sprintf(buffer, " $%02x, x", opram[1]); + flags |= 2; + break; + + case DASM_zpy: + sprintf(buffer, " $%02x, y", opram[1]); + flags |= 2; + break; + + case DASM_imz: + sprintf(buffer, " #$%02x, $%02x", opram[1], opram[2]); + flags |= 3; + break; + + case DASM_spg: + sprintf(buffer, " \\$%02x", opram[1]); + flags |= 2; + break; + + case DASM_biz: + sprintf(buffer, " %d, $%02x", (opram[0] >> 5) & 7, opram[1]); + flags |= 2; + break; + + case DASM_bzr: + sprintf(buffer, " %d, $%02x, $%04x", (opram[0] >> 5) & 7, opram[1], (pc & 0xf0000) | UINT16(pc + 3 + INT8(opram[2]))); + flags |= 3; + break; + + case DASM_bar: + sprintf(buffer, " %d, a, $%04x", (opram[0] >> 5) & 7, (pc & 0xf0000) | UINT16(pc + 3 + INT8(opram[1]))); + flags |= 2; + break; + + case DASM_bac: + sprintf(buffer, " %d, a", (opram[0] >> 5) & 7); + flags |= 1; + break; + + default: + fprintf(stderr, "Unhandled dasm mode %d\n", e.mode); + abort(); + } + return flags; +} + +void m6502_device::prefetch() +{ + sync = true; + sync_w(ASSERT_LINE); + NPC = PC; + IR = mintf->read_sync(PC); + sync = false; + sync_w(CLEAR_LINE); + + if((nmi_state || ((irq_state || apu_irq_state) && !(P & F_I))) && !inhibit_interrupts) { + irq_taken = true; + IR = 0x00; + } else + PC++; +} + +void m6502_device::prefetch_noirq() +{ + sync = true; + sync_w(ASSERT_LINE); + NPC = PC; + IR = mintf->read_sync(PC); + sync = false; + sync_w(CLEAR_LINE); + PC++; +} + +void m6502_device::set_nz(UINT8 v) +{ + P &= ~(F_Z|F_N); + if(v & 0x80) + P |= F_N; + if(!v) + P |= F_Z; +} + +offs_t m6502_device::disasm_disassemble(char *buffer, offs_t pc, const UINT8 *oprom, const UINT8 *opram, UINT32 options) +{ + return disassemble_generic(buffer, pc, oprom, opram, options, disasm_entries); +} + + +UINT8 m6502_device::memory_interface::read_9(UINT16 adr) +{ + return read(adr); +} + +void m6502_device::memory_interface::write_9(UINT16 adr, UINT8 val) +{ + write(adr, val); +} + + +UINT8 m6502_device::mi_default_normal::read(UINT16 adr) +{ + return program->read_byte(adr); +} + +UINT8 m6502_device::mi_default_normal::read_sync(UINT16 adr) +{ + return sdirect->read_byte(adr); +} + +UINT8 m6502_device::mi_default_normal::read_arg(UINT16 adr) +{ + return direct->read_byte(adr); +} + + +void m6502_device::mi_default_normal::write(UINT16 adr, UINT8 val) +{ + program->write_byte(adr, val); +} + +UINT8 m6502_device::mi_default_nd::read_sync(UINT16 adr) +{ + return sprogram->read_byte(adr); +} + +UINT8 m6502_device::mi_default_nd::read_arg(UINT16 adr) +{ + return program->read_byte(adr); +} + + +WRITE_LINE_MEMBER( m6502_device::irq_line ) +{ + set_input_line( M6502_IRQ_LINE, state ); +} + +WRITE_LINE_MEMBER( m6502_device::nmi_line ) +{ + set_input_line( M6502_NMI_LINE, state ); +} + +#include "cpu/m6502/m6502.inc" |