diff options
author | 2016-04-19 12:26:31 +0200 | |
---|---|---|
committer | 2016-04-23 14:33:32 +0200 | |
commit | 996a4fd9c02fbb45618ca09d5862ffc60741d1a0 (patch) | |
tree | ff7efde5ad1277da758c394ea76cff28cbabed9e /src/devices | |
parent | 01657a63c13c79c5f045ba4ecb4c2fd7f7c5a1f8 (diff) |
hphybrid: found 2 undocumented instructions
Diffstat (limited to 'src/devices')
-rw-r--r-- | src/devices/cpu/hphybrid/hphybrid.cpp | 87 | ||||
-rw-r--r-- | src/devices/cpu/hphybrid/hphybrid.h | 6 | ||||
-rw-r--r-- | src/devices/cpu/hphybrid/hphybrid_dasm.cpp | 3 |
3 files changed, 94 insertions, 2 deletions
diff --git a/src/devices/cpu/hphybrid/hphybrid.cpp b/src/devices/cpu/hphybrid/hphybrid.cpp index fd7870d319f..da9470a91ae 100644 --- a/src/devices/cpu/hphybrid/hphybrid.cpp +++ b/src/devices/cpu/hphybrid/hphybrid.cpp @@ -1,10 +1,31 @@ // license:BSD-3-Clause // copyright-holders:F. Ulivi +// I found 2 undocumented instructions in 5061-3001. First I noticed that PPU processor in +// hp9845b emulator executed 2 unknown instructions at each keyboard interrupt whose opcodes +// were 0x7026 & 0x7027. +// I searched for a while for any kind of documentation about them but found nothing at all. +// Some time later I found the mnemonics in the binary dump of assembly development option ROM: +// CIM & SIM, respectively. From the mnemonic I deduced their function: Clear & Set Interrupt Mask. +// I think they are basically used to temporarily disable/enable interrupt recognition inside +// ISRs. This is consistent with their usage in PPU firmware and test ROM. The official EIR & +// DIR instructions cannot be used while servicing an interrupt because they probably reset +// the "in ISR" condition of the processor. +// Using CIM&SIM only makes sense in low-level ISRs because high-level ones can't be interrupted +// by anyone. +// Now, I still have some doubts about the "polarity" of interrupt mask. Is interrupt +// recognition disabled when the mask is cleared or is it the opposite? +// I'm leaning towards the "no interrupts with mask cleared" interpretation, but I'm not 100% +// convinced. CIM & SIM at the moment are implemented with this interpretation (see also +// NO_ISR_WITH_IM_CLEARED macro below). + #include "emu.h" #include "debugger.h" #include "hphybrid.h" +// Define this to have "IM cleared" == "No interrupt recognition" +#define NO_ISR_WITH_IM_CLEARED + enum { HPHYBRID_A, HPHYBRID_B, @@ -58,6 +79,7 @@ enum { #define HPHYBRID_STS_BIT 13 // Status flag #define HPHYBRID_FLG_BIT 14 // "Flag" flag #define HPHYBRID_DC_BIT 15 // Decimal carry +#define HPHYBRID_IM_BIT 16 // Interrupt mask #define HPHYBRID_IV_MASK 0xfff0 // IV mask @@ -512,6 +534,7 @@ UINT16 hp_hybrid_cpu_device::execute_one_sub(UINT16 opcode) memmove(&m_reg_PA[ 0 ] , &m_reg_PA[ 1 ] , HPHYBRID_INT_LVLS); m_pa_changed_func((UINT8)CURRENT_PA); } + BIT_CLR(m_flags, HPHYBRID_IM_BIT); } tmp = RM(AEC_CASE_C , m_reg_R--) + (opcode & 0x1f); return BIT(opcode , 5) ? tmp - 0x20 : tmp; @@ -878,6 +901,21 @@ UINT16 hp_hybrid_cpu_device::get_skip_addr_sc(UINT16 opcode , UINT16& v , unsign return get_skip_addr(opcode , val); } +UINT16 hp_hybrid_cpu_device::get_skip_addr_sc(UINT16 opcode , UINT32& v , unsigned n) +{ + bool val = BIT(v , n); + + if (BIT(opcode , 7)) { + if (BIT(opcode , 6)) { + BIT_SET(v , n); + } else { + BIT_CLR(v , n); + } + } + + return get_skip_addr(opcode , val); +} + void hp_hybrid_cpu_device::do_pw(UINT16 opcode) { UINT16 tmp; @@ -969,7 +1007,7 @@ void hp_hybrid_cpu_device::do_pw(UINT16 opcode) void hp_hybrid_cpu_device::check_for_interrupts(void) { - if (!BIT(m_flags , HPHYBRID_INTEN_BIT) || BIT(m_flags , HPHYBRID_IRH_SVC_BIT)) { + if (!BIT(m_flags , HPHYBRID_INTEN_BIT) || BIT(m_flags , HPHYBRID_IRH_SVC_BIT) || BIT(m_flags , HPHYBRID_IM_BIT)) { return; } @@ -979,6 +1017,9 @@ void hp_hybrid_cpu_device::check_for_interrupts(void) // Service high-level interrupt BIT_SET(m_flags , HPHYBRID_IRH_SVC_BIT); irqline = HPHYBRID_IRH; + if (BIT(m_flags , HPHYBRID_IRL_SVC_BIT)) { + logerror("H pre-empted L @ %06x\n" , m_genpc); + } } else if (BIT(m_flags , HPHYBRID_IRL_BIT) && !BIT(m_flags , HPHYBRID_IRL_SVC_BIT)) { // Service low-level interrupt BIT_SET(m_flags , HPHYBRID_IRL_SVC_BIT); @@ -1014,12 +1055,20 @@ void hp_hybrid_cpu_device::check_for_interrupts(void) // lasts for 32 cycles m_icount -= 32; + // Allow special processing in 5061-3001 + enter_isr(); + // Do a double-indirect JSM IV,I instruction WM(AEC_CASE_C , ++m_reg_R , m_reg_P); m_reg_P = RM(AEC_CASE_I , m_reg_IV + CURRENT_PA); m_reg_I = fetch(); } +void hp_hybrid_cpu_device::enter_isr(void) +{ + // Do nothing special +} + void hp_hybrid_cpu_device::handle_dma(void) { // Patent hints at the fact that terminal count is detected by bit 15 of dmac being 1 after decrementing @@ -1428,6 +1477,34 @@ UINT16 hp_5061_3001_cpu_device::execute_no_bpc_ioc(UINT16 opcode) do_mpy(); break; + case 0x7026: + // CIM + // Undocumented instruction, see beginning of this file + // Probably "Clear Interrupt Mask" + // No idea at all about exec. time: make it 9 cycles + m_icount -= 9; +#ifndef NO_ISR_WITH_IM_CLEARED + BIT_CLR(m_flags, HPHYBRID_IM_BIT); +#else + BIT_SET(m_flags, HPHYBRID_IM_BIT); +#endif + logerror("hp-5061-3001: CIM, P = %06x flags = %05x\n" , m_genpc , m_flags); + break; + + case 0x7027: + // SIM + // Undocumented instruction, see beginning of this file + // Probably "Set Interrupt Mask" + // No idea at all about exec. time: make it 9 cycles + m_icount -= 9; +#ifndef NO_ISR_WITH_IM_CLEARED + BIT_SET(m_flags, HPHYBRID_IM_BIT); +#else + BIT_CLR(m_flags, HPHYBRID_IM_BIT); +#endif + logerror("hp-5061-3001: SIM, P = %06x flags = %05x\n" , m_genpc , m_flags); + break; + default: if ((opcode & 0xfec0) == 0x74c0) { // SDS @@ -1576,6 +1653,14 @@ void hp_5061_3001_cpu_device::write_non_common_reg(UINT16 addr , UINT16 v) } } +void hp_5061_3001_cpu_device::enter_isr(void) +{ + // Set interrupt mask when entering an ISR +#ifndef NO_ISR_WITH_IM_CLEARED + BIT_SET(m_flags, HPHYBRID_IM_BIT); +#endif +} + hp_5061_3011_cpu_device::hp_5061_3011_cpu_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : hp_hybrid_cpu_device(mconfig, HP_5061_3011, "HP-5061-3011", tag, owner, clock, "5061-3011", 16) { diff --git a/src/devices/cpu/hphybrid/hphybrid.h b/src/devices/cpu/hphybrid/hphybrid.h index 97aea0febce..8969e22ce6e 100644 --- a/src/devices/cpu/hphybrid/hphybrid.h +++ b/src/devices/cpu/hphybrid/hphybrid.h @@ -161,7 +161,7 @@ protected: UINT16 m_reg_IV; // Register IV UINT16 m_reg_W; // Register W UINT8 m_reg_PA[ HPHYBRID_INT_LVLS + 1 ]; // Stack of register PA (4 bit-long) - UINT16 m_flags; // Flags + UINT32 m_flags; // Flags UINT8 m_dmapa; // DMA peripheral address (4 bits) UINT16 m_dmama; // DMA address UINT16 m_dmac; // DMA counter @@ -179,8 +179,10 @@ private: UINT32 get_ea(UINT16 opcode); void do_add(UINT16& addend1 , UINT16 addend2); UINT16 get_skip_addr_sc(UINT16 opcode , UINT16& v , unsigned n); + UINT16 get_skip_addr_sc(UINT16 opcode , UINT32& v , unsigned n); void do_pw(UINT16 opcode); void check_for_interrupts(void); + virtual void enter_isr(void); void handle_dma(void); UINT16 RIO(UINT8 pa , UINT8 ic); @@ -225,6 +227,8 @@ private: UINT16 m_reg_r26; // R26 register UINT16 m_reg_r27; // R27 register UINT16 m_reg_aec[ HP_REG_R37_ADDR - HP_REG_R32_ADDR + 1 ]; // AEC registers R32-R37 + + virtual void enter_isr(void) override; }; class hp_5061_3011_cpu_device : public hp_hybrid_cpu_device diff --git a/src/devices/cpu/hphybrid/hphybrid_dasm.cpp b/src/devices/cpu/hphybrid/hphybrid_dasm.cpp index 48b03b4afb4..78b586f2310 100644 --- a/src/devices/cpu/hphybrid/hphybrid_dasm.cpp +++ b/src/devices/cpu/hphybrid/hphybrid_dasm.cpp @@ -355,6 +355,9 @@ static const dis_entry_t dis_table_emc[] = { {0xffff , 0x7b40 , "MRY" , param_none , 0 }, {0xffff , 0x7b61 , "MLY" , param_none , 0 }, {0xffff , 0x7b8f , "MPY" , param_none , 0 }, + // *** Undocumented instructions of 5061-3001 *** + {0xffff , 0x7026 , "CIM" , param_none , 0 }, + {0xffff , 0x7027 , "SIM" , param_none , 0 }, // *** END *** {0 , 0 , nullptr , nullptr , 0 } }; |