diff options
Diffstat (limited to 'src/devices/cpu/sm8500/sm8500.c')
-rw-r--r-- | src/devices/cpu/sm8500/sm8500.c | 428 |
1 files changed, 428 insertions, 0 deletions
diff --git a/src/devices/cpu/sm8500/sm8500.c b/src/devices/cpu/sm8500/sm8500.c new file mode 100644 index 00000000000..6159d8a45fa --- /dev/null +++ b/src/devices/cpu/sm8500/sm8500.c @@ -0,0 +1,428 @@ +// license:BSD-3-Clause +// copyright-holders:Wilbert Pol +/* + Implementation for Sharp sm8500 cpu. There is hardly any information available + on this cpu. Currently we've only found documentation on the microcontroller + parts of the cpu, but nothing on the cpu itself. + + Through looking at binary data we have attempted to figure out the opcodes for + this cpu, and made educated guesses on the number of cycles for each instruction. + + Code by Wilbert Pol + + +There is some internal ram for the main cpu registers. They are offset by an index value. +The address is (PS0 & 0xF8) + register number. It is not known what happens when PS0 >= F8. +The assumption is that F8 to 107 is used, but it might wrap around instead. +The registers also mirror out to main RAM, appearing at 0000 to 000F regardless of where +they are internally. + +*/ + +#include "emu.h" +#include "debugger.h" +#include "sm8500.h" + + +const device_type SM8500 = &device_creator<sm8500_cpu_device>; + + +static const UINT8 sm8500_b2w[8] = { + 0, 8, 2, 10, 4, 12, 6, 14 +}; + + +sm8500_cpu_device::sm8500_cpu_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : cpu_device(mconfig, SM8500, "SM8500", tag, owner, clock, "sm8500", __FILE__) + , m_program_config("program", ENDIANNESS_BIG, 8, 16, 0) + , m_dma_func(*this) + , m_timer_func(*this) + , m_PC(0) + , m_SYS(0) + , m_SP(0) + , m_PS0(0) + , m_PS1(0) +{ +} + + +void sm8500_cpu_device::get_sp() +{ + m_SP = m_program->read_byte(0x1d); + if (m_SYS & 0x40) m_SP |= ( m_program->read_byte(0x1c) << 8 ); +} + + +UINT8 sm8500_cpu_device::mem_readbyte( UINT32 offset ) +{ + offset &= 0xffff; + if ( offset < 0x10) + { + return m_register_ram[offset + (m_PS0 & 0xF8)]; + } + + return m_program->read_byte( offset ); +} + + +void sm8500_cpu_device::mem_writebyte( UINT32 offset, UINT8 data ) +{ + UINT8 i; + offset &= 0xffff; + if (offset < 0x10) + { + m_register_ram[offset + (m_PS0 & 0xF8)] = data; + } + + m_program->write_byte( offset, data ); + + switch (offset) + { + case 0x10: m_IE0 = data; break; + case 0x11: m_IE1 = data; break; + case 0x12: m_IR0 = data; break; + case 0x13: m_IR1 = data; break; + case 0x19: m_SYS = data; break; + case 0x1a: m_CKC = data; break; + case 0x1c: + case 0x1d: get_sp(); break; + case 0x1e: m_PS0 = data; + for (i = 0; i < 16; i++) // refresh register contents in debugger + { + m_program->write_byte(i, mem_readbyte(i)); + } + break; + case 0x1f: m_PS1 = data; break; + } +} + + +void sm8500_cpu_device::device_start() +{ + m_program = &space(AS_PROGRAM); + + m_dma_func.resolve_safe(); + m_timer_func.resolve_safe(); + + save_item(NAME(m_PC)); + save_item(NAME(m_IE0)); + save_item(NAME(m_IE1)); + save_item(NAME(m_IR0)); + save_item(NAME(m_IR1)); + save_item(NAME(m_SYS)); + save_item(NAME(m_CKC)); + save_item(NAME(m_clock_changed)); + save_item(NAME(m_SP)); + save_item(NAME(m_PS0)); + save_item(NAME(m_PS1)); + save_item(NAME(m_IFLAGS)); + save_item(NAME(m_CheckInterrupts)); + save_item(NAME(m_halted)); + save_item(NAME(m_oldpc)); + save_pointer(NAME(m_register_ram),0x108); + + // Register state for debugger + state_add(SM8500_PC, "PC", m_PC ).callimport().callexport().formatstr("%04X"); + state_add(SM8500_SP, "SP", m_SP ).callimport().callexport().formatstr("%04X"); + state_add(SM8500_PS, "PS", m_PS0 ).callimport().callexport().formatstr("%04s"); + state_add(SM8500_SYS, "SYS", m_SYS ).callimport().callexport().formatstr("%04X"); + state_add(SM8500_RR0, "RR0", m_PC ).callimport().callexport().formatstr("%04s"); + state_add(SM8500_RR2, "RR2", m_PC ).callimport().callexport().formatstr("%04s"); + state_add(SM8500_RR4, "RR4", m_PC ).callimport().callexport().formatstr("%04s"); + state_add(SM8500_RR6, "RR6", m_PC ).callimport().callexport().formatstr("%04s"); + state_add(SM8500_RR8, "RR8", m_PC ).callimport().callexport().formatstr("%04s"); + state_add(SM8500_RR10, "RR10", m_PC ).callimport().callexport().formatstr("%04s"); + state_add(SM8500_RR12, "RR12", m_PC ).callimport().callexport().formatstr("%04s"); + state_add(SM8500_RR14, "RR14", m_PC ).callimport().callexport().formatstr("%04s"); + state_add(STATE_GENPC, "curpc", m_PC).callimport().callexport().formatstr("%8s").noshow(); + state_add(STATE_GENFLAGS, "GENFLAGS", m_PS1).formatstr("%8s").noshow(); + + m_icountptr = &m_icount; +} + + +void sm8500_cpu_device::state_string_export(const device_state_entry &entry, std::string &str) +{ + switch (entry.index()) + { + case SM8500_PS: + strprintf(str, "%04X", ( m_PS0 << 8 ) | m_PS1 ); + break; + + case SM8500_RR0: + strprintf(str, "%04X", mem_readword( 0x00 ) ); + break; + + case SM8500_RR2: + strprintf(str, "%04X", mem_readword( 0x02 ) ); + break; + + case SM8500_RR4: + strprintf(str, "%04X", mem_readword( 0x04 ) ); + break; + + case SM8500_RR6: + strprintf(str, "%04X", mem_readword( 0x06 ) ); + break; + + case SM8500_RR8: + strprintf(str, "%04X", mem_readword( 0x08 ) ); + break; + + case SM8500_RR10: + strprintf(str, "%04X", mem_readword( 0x0a ) ); + break; + + case SM8500_RR12: + strprintf(str, "%04X", mem_readword( 0x0c ) ); + break; + + case SM8500_RR14: + strprintf(str, "%04X", mem_readword( 0x0e ) ); + break; + + case STATE_GENFLAGS: + strprintf(str, "%c%c%c%c%c%c%c%c", + m_PS1 & FLAG_C ? 'C' : '.', + m_PS1 & FLAG_Z ? 'Z' : '.', + m_PS1 & FLAG_S ? 'S' : '.', + m_PS1 & FLAG_V ? 'V' : '.', + m_PS1 & FLAG_D ? 'D' : '.', + m_PS1 & FLAG_H ? 'H' : '.', + m_PS1 & FLAG_B ? 'B' : '.', + m_PS1 & FLAG_I ? 'I' : '.' ); + break; + } +} + + +void sm8500_cpu_device::device_reset() +{ + for ( int i = 0; i < 0x108; i++ ) + { + m_register_ram[i] = 0; + } + + m_PC = 0x1020; + m_clock_changed = 0; + m_CheckInterrupts = 0; + m_halted = 0; + m_IFLAGS = 0; + mem_writeword(0x10, 0); // IE0, IE1 + mem_writeword(0x12, 0); // IR0, IR1 + mem_writeword(0x14, 0xffff); // P0, P1 + mem_writeword(0x16, 0xff00); // P2, P3 + mem_writebyte(0x19, 0); // SYS + mem_writebyte(0x1a, 0); // CKC + mem_writebyte(0x1f, 0); // PS1 + mem_writebyte(0x2b, 0xff); // URTT + mem_writebyte(0x2d, 0x42); // URTS + mem_writebyte(0x5f, 0x38); // WDTC +} + + +#define PUSH_BYTE(X) m_SP--; \ + if ( ( m_SYS & 0x40 ) == 0 ) m_SP &= 0xFF; \ + mem_writebyte( m_SP, X ); + + +void sm8500_cpu_device::take_interrupt(UINT16 vector) +{ + /* Get regs from ram */ + get_sp(); + m_SYS = m_program->read_byte(0x19); + m_PS1 = m_program->read_byte(0x1f); + /* Push PC */ + PUSH_BYTE( m_PC & 0xFF ); + PUSH_BYTE( m_PC >> 8 ); + /* Push PS1 */ + PUSH_BYTE( m_PS1 ); + /* Clear I flag */ + m_PS1 &= ~ 0x01; + /* save regs to ram */ + m_program->write_byte(0x1f, m_PS1); + m_program->write_byte(0x1d, m_SP&0xFF); + if (m_SYS&0x40) m_program->write_byte(0x1c, m_SP>>8); + /* Change PC to address stored at "vector" */ + m_PC = mem_readword( vector ); +} + + +void sm8500_cpu_device::process_interrupts() +{ + if ( m_CheckInterrupts ) + { + int irqline = 0; + while( irqline < 11 ) + { + if ( m_IFLAGS & ( 1 << irqline ) ) + { + m_halted = 0; + m_IE0 = m_program->read_byte(0x10); + m_IE1 = m_program->read_byte(0x11); + m_IR0 = m_program->read_byte(0x12); + m_IR1 = m_program->read_byte(0x13); + m_PS0 = m_program->read_byte(0x1e); + m_PS1 = m_program->read_byte(0x1f); + switch( irqline ) + { + case WDT_INT: + take_interrupt( 0x101C ); + break; + case ILL_INT: + case NMI_INT: + take_interrupt( 0x101E ); + break; + case DMA_INT: + m_IR0 |= 0x80; + if ( ( m_IE0 & 0x80 ) && ( ( m_PS0 & 0x07 ) < 8 ) && ( m_PS1 & 0x01 ) ) + { + take_interrupt( 0x1000 ); + } + break; + case TIM0_INT: + m_IR0 |= 0x40; + if ( ( m_IE0 & 0x40 ) && ( ( m_PS0 & 0x07 ) < 8 ) && ( m_PS1 & 0x01 ) ) + { + take_interrupt( 0x1002 ); + } + break; + case EXT_INT: + m_IR0 |= 0x10; + if ( ( m_IE0 & 0x10 ) && ( ( m_PS0 & 0x07 ) < 7 ) && ( m_PS1 & 0x01 ) ) + { + take_interrupt( 0x1006 ); + } + break; + case UART_INT: + m_IR0 |= 0x08; + if ( ( m_IE0 & 0x08 ) && ( ( m_PS0 & 0x07 ) < 6 ) && ( m_PS1 & 0x01 ) ) + { + take_interrupt( 0x1008 ); + } + break; + case LCDC_INT: + m_IR0 |= 0x01; + if ( ( m_IE0 & 0x01 ) && ( ( m_PS0 & 0x07 ) < 5 ) && ( m_PS1 & 0x01 ) ) + { + take_interrupt( 0x100E ); + } + break; + case TIM1_INT: + m_IR1 |= 0x40; + if ( ( m_IE1 & 0x40 ) && ( ( m_PS0 & 0x07 ) < 4 ) && ( m_PS1 & 0x01 ) ) + { + take_interrupt( 0x1012 ); + } + break; + case CK_INT: + m_IR1 |= 0x10; + if ( ( m_IE1 & 0x10 ) && ( ( m_PS0 & 0x07 ) < 3 ) && ( m_PS1 & 0x01 ) ) + { + take_interrupt( 0x1016 ); + } + break; + case PIO_INT: + m_IR1 |= 0x04; + if ( ( m_IE1 & 0x04 ) && ( ( m_PS0 & 0x07 ) < 2 ) && ( m_PS1 & 0x01 ) ) + { + take_interrupt( 0x101A ); + } + break; + } + m_IFLAGS &= ~ ( 1 << irqline ); + m_program->write_byte(0x12, m_IR0); + m_program->write_byte(0x13, m_IR1); + } + irqline++; + } + } +} + + +offs_t sm8500_cpu_device::disasm_disassemble(char *buffer, offs_t pc, const UINT8 *oprom, const UINT8 *opram, UINT32 options) +{ + extern CPU_DISASSEMBLE( sm8500 ); + return CPU_DISASSEMBLE_NAME(sm8500)(this, buffer, pc, oprom, opram, options); +} + + +void sm8500_cpu_device::execute_run() +{ + do + { + int mycycles = 0; + UINT8 r1,r2; + UINT16 s1,s2; + UINT32 d1,d2; + UINT32 res; + + debugger_instruction_hook(this, m_PC); + m_oldpc = m_PC; + process_interrupts(); + if ( !m_halted ) { + UINT8 op = mem_readbyte( m_PC++ ); + m_SYS = m_program->read_byte(0x19); + m_PS0 = m_program->read_byte(0x1e); + m_PS1 = m_program->read_byte(0x1f); + get_sp(); + switch( op ) + { +#include "sm85ops.h" + } + if (m_SYS&0x40) m_program->write_byte(0x1c,m_SP>>8); + m_program->write_byte(0x1d,m_SP&0xFF); + mem_writebyte(0x1e,m_PS0); // need to update debugger + m_program->write_byte(0x1f,m_PS1); + } else { + mycycles = 4; + m_dma_func( mycycles ); + } + m_timer_func( mycycles ); + m_icount -= mycycles; + } while ( m_icount > 0 ); +} + + +void sm8500_cpu_device::execute_set_input( int inptnum, int state ) +{ + m_IR0 = m_program->read_byte(0x12); + m_IR1 = m_program->read_byte(0x13); + if ( state == ASSERT_LINE ) + { + m_IFLAGS |= ( 0x01 << inptnum ); + m_CheckInterrupts = 1; + switch( inptnum ) + { + case DMA_INT: m_IR0 |= 0x80; break; + case TIM0_INT: m_IR0 |= 0x40; break; + case EXT_INT: m_IR0 |= 0x10; break; + case UART_INT: m_IR0 |= 0x08; break; + case LCDC_INT: m_IR0 |= 0x01; break; + case TIM1_INT: m_IR1 |= 0x40; break; + case CK_INT: m_IR1 |= 0x10; break; + case PIO_INT: m_IR1 |= 0x04; break; + } + } + else + { + m_IFLAGS &= ~( 0x01 << inptnum ); + switch( inptnum ) + { + case DMA_INT: m_IR0 &= ~0x80; break; + case TIM0_INT: m_IR0 &= ~0x40; break; + case EXT_INT: m_IR0 &= ~0x10; break; + case UART_INT: m_IR0 &= ~0x08; break; + case LCDC_INT: m_IR0 &= ~0x01; break; + case TIM1_INT: m_IR1 &= ~0x40; break; + case CK_INT: m_IR1 &= ~0x10; break; + case PIO_INT: m_IR1 &= ~0x04; break; + } + if ( 0 == m_IFLAGS ) + { + m_CheckInterrupts = 0; + } + } + m_program->write_byte(0x12, m_IR0); + m_program->write_byte(0x13, m_IR1); +} |