summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices
diff options
context:
space:
mode:
author etabeta78 <etabeta78@users.noreply.github.com>2016-04-25 14:51:24 +0200
committer etabeta78 <etabeta78@users.noreply.github.com>2016-04-25 14:51:24 +0200
commiteac382d629a694ea4003afa4801634d308ed00cb (patch)
tree70113199ba5ffe00acd046f1d4576b097034e43d /src/devices
parentfe63ac9a20875837229069d83f3304de323029d4 (diff)
parent13c88cdfd9f449c7762a2bc905afe5cbd6c5793e (diff)
Merge pull request #829 from fulivi/hp9845_dev
Hp9845: added capabilty to load option ROMs
Diffstat (limited to 'src/devices')
-rw-r--r--src/devices/bus/hp_optroms/hp_optrom.cpp143
-rw-r--r--src/devices/bus/hp_optroms/hp_optrom.h74
-rw-r--r--src/devices/cpu/hphybrid/hphybrid.cpp87
-rw-r--r--src/devices/cpu/hphybrid/hphybrid.h6
-rw-r--r--src/devices/cpu/hphybrid/hphybrid_dasm.cpp3
5 files changed, 311 insertions, 2 deletions
diff --git a/src/devices/bus/hp_optroms/hp_optrom.cpp b/src/devices/bus/hp_optroms/hp_optrom.cpp
new file mode 100644
index 00000000000..aa3ef766d71
--- /dev/null
+++ b/src/devices/bus/hp_optroms/hp_optrom.cpp
@@ -0,0 +1,143 @@
+// license:BSD-3-Clause
+// copyright-holders: F. Ulivi
+/*********************************************************************
+
+ hp_optrom.cpp
+
+ Optional ROMs for HP9845 systems
+
+*********************************************************************/
+
+#include "hp_optrom.h"
+#include "softlist.h"
+#include "cpu/hphybrid/hphybrid.h"
+
+const device_type HP_OPTROM_CART = &device_creator<hp_optrom_cart_device>;
+const device_type HP_OPTROM_SLOT = &device_creator<hp_optrom_slot_device>;
+
+// +---------------------+
+// |hp_optrom_cart_device|
+// +---------------------+
+hp_optrom_cart_device::hp_optrom_cart_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) :
+ device_t(mconfig, type, name, tag, owner, clock, shortname, source),
+ device_slot_card_interface(mconfig, *this)
+{
+}
+
+hp_optrom_cart_device::hp_optrom_cart_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) :
+ device_t(mconfig, HP_OPTROM_CART, "HP9845 optional ROM cartridge", tag, owner, clock, "hp_optrom_cart", __FILE__),
+ device_slot_card_interface(mconfig, *this)
+{
+}
+
+// +---------------------+
+// |hp_optrom_slot_device|
+// +---------------------+
+hp_optrom_slot_device::hp_optrom_slot_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) :
+ device_t(mconfig, HP_OPTROM_SLOT, "HP9845 optional ROM Slot", tag, owner, clock, "hp_optrom_slot", __FILE__),
+ device_image_interface(mconfig, *this),
+ device_slot_interface(mconfig, *this),
+ m_cart(nullptr),
+ m_base_addr(0),
+ m_end_addr(0)
+{
+}
+
+hp_optrom_slot_device::~hp_optrom_slot_device()
+{
+}
+
+void hp_optrom_slot_device::device_start()
+{
+ m_cart = dynamic_cast<hp_optrom_cart_device *>(get_card_device());
+}
+
+void hp_optrom_slot_device::device_config_complete()
+{
+ update_names(HP_OPTROM_SLOT , "optional_rom" , "optrom");
+}
+
+bool hp_optrom_slot_device::call_load()
+{
+ logerror("hp_optrom: call_load\n");
+ if (m_cart == nullptr || !m_from_swlist) {
+ logerror("hp_optrom: must be loaded from sw list\n");
+ return IMAGE_INIT_FAIL;
+ }
+
+ const char *base_feature = get_feature("base");
+ if (base_feature == nullptr) {
+ logerror("hp_optrom: no 'base' feature\n");
+ return IMAGE_INIT_FAIL;
+ }
+
+ offs_t base_addr;
+ if (base_feature[ 0 ] != '0' || base_feature[ 1 ] != 'x' || sscanf(&base_feature[ 2 ] , "%x" , &base_addr) != 1) {
+ logerror("hp_optrom: can't parse 'base' feature\n");
+ return IMAGE_INIT_FAIL;
+ }
+
+ // Valid BSC values for ROMs on LPU drawer: 0x07 0x0b .... 0x3b
+ // Valid BSC values for ROMs on PPU drawer: 0x09 0x0d .... 0x3d
+ // (BSC is field in bits 16..21 of base address)
+ // Bit 15 of base address must be 0
+ // Base address must be multiple of 0x1000
+ if ((base_addr & ~0x3f7000UL) != 0 || ((base_addr & 0x30000) != 0x10000 && (base_addr & 0x30000) != 0x30000) || base_addr < 0x70000) {
+ logerror("hp_optrom: illegal base address (%x)\n" , base_addr);
+ return IMAGE_INIT_FAIL;
+ }
+
+ auto length = get_software_region_length("rom") / 2;
+
+ if (length < 0x1000 || length > 0x8000 || (length & 0xfff) != 0 || ((base_addr & 0x7000) + length) > 0x8000) {
+ logerror("hp_optrom: illegal region length (%x)\n" , length);
+ return IMAGE_INIT_FAIL;
+ }
+
+ offs_t end_addr = base_addr + length - 1;
+ logerror("hp_optrom: base_addr = %06x end_addr = %06x\n" , base_addr , end_addr);
+
+ m_content.resize(length * 2);
+ UINT8 *buffer = m_content.data();
+ memcpy(buffer , get_software_region("rom") , length * 2);
+
+ // Install ROM in address space of every CPU
+ for (hp_hybrid_cpu_device& cpu : device_interface_iterator<hp_hybrid_cpu_device>(machine().root_device())) {
+ logerror("hp_optrom: install in %s AS\n" , cpu.tag());
+ cpu.space(AS_PROGRAM).install_rom(base_addr , end_addr , buffer);
+ }
+
+ m_base_addr = base_addr;
+ m_end_addr = end_addr;
+
+ return IMAGE_INIT_PASS;
+}
+
+void hp_optrom_slot_device::call_unload()
+{
+ logerror("hp_optrom: call_unload\n");
+ if (m_cart != nullptr && m_base_addr != 0 && m_end_addr != 0) {
+ for (hp_hybrid_cpu_device& cpu : device_interface_iterator<hp_hybrid_cpu_device>(machine().root_device())) {
+ cpu.space(AS_PROGRAM).unmap_read(m_base_addr , m_end_addr);
+ }
+ m_content.resize(0);
+ m_base_addr = 0;
+ m_end_addr = 0;
+ }
+}
+
+bool hp_optrom_slot_device::call_softlist_load(software_list_device &swlist, const char *swname, const rom_entry *start_entry)
+{
+ logerror("hp_optrom: call_softlist_load\n");
+ machine().rom_load().load_software_part_region(*this, swlist, swname, start_entry);
+ return TRUE;
+}
+
+std::string hp_optrom_slot_device::get_default_card_software()
+{
+ return software_get_default_slot("rom");
+}
+
+SLOT_INTERFACE_START(hp_optrom_slot_device)
+ SLOT_INTERFACE_INTERNAL("rom", HP_OPTROM_CART)
+SLOT_INTERFACE_END
diff --git a/src/devices/bus/hp_optroms/hp_optrom.h b/src/devices/bus/hp_optroms/hp_optrom.h
new file mode 100644
index 00000000000..a18fd5e4087
--- /dev/null
+++ b/src/devices/bus/hp_optroms/hp_optrom.h
@@ -0,0 +1,74 @@
+// license:BSD-3-Clause
+// copyright-holders: F. Ulivi
+/*********************************************************************
+
+ hp_optrom.h
+
+ Optional ROMs for HP9845 systems
+
+*********************************************************************/
+
+#pragma once
+
+#ifndef _HP_OPTROM_H_
+#define _HP_OPTROM_H_
+
+#include "emu.h"
+
+class hp_optrom_cart_device : public device_t,
+ public device_slot_card_interface
+{
+public:
+ // construction/destruction
+ hp_optrom_cart_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);
+ hp_optrom_cart_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start() override {}
+};
+
+class hp_optrom_slot_device : public device_t,
+ public device_image_interface,
+ public device_slot_interface
+{
+public:
+ // construction/destruction
+ hp_optrom_slot_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ virtual ~hp_optrom_slot_device();
+
+ // device-level overrides
+ virtual void device_start() override;
+ virtual void device_config_complete() override;
+
+ // image-level overrides
+ virtual bool call_load() override;
+ virtual void call_unload() override;
+ virtual bool call_softlist_load(software_list_device &swlist, const char *swname, const rom_entry *start_entry) override;
+
+ virtual iodevice_t image_type() const override { return IO_CARTSLOT; }
+ virtual bool is_readable() const override { return true; }
+ virtual bool is_writeable() const override { return false; }
+ virtual bool is_creatable() const override { return false; }
+ virtual bool must_be_loaded() const override { return false; }
+ virtual bool is_reset_on_load() const override { return true; }
+ virtual const option_guide *create_option_guide() const override { return nullptr; }
+ virtual const char *image_interface() const override { return "hp9845b_rom"; }
+ virtual const char *file_extensions() const override { return "bin"; }
+
+ // slot interface overrides
+ virtual std::string get_default_card_software() override;
+
+protected:
+ hp_optrom_cart_device *m_cart;
+ dynamic_buffer m_content;
+ offs_t m_base_addr;
+ offs_t m_end_addr;
+};
+
+// device type definition
+extern const device_type HP_OPTROM_SLOT;
+extern const device_type HP_OPTROM_CART;
+
+SLOT_INTERFACE_EXTERN(hp_optrom_slot_device);
+
+#endif /* _HP_OPTROM_H_ */
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 53a2d401efb..d61785cb146 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 }
};