summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/drivers/hp9825.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mame/drivers/hp9825.cpp')
-rw-r--r--src/mame/drivers/hp9825.cpp317
1 files changed, 296 insertions, 21 deletions
diff --git a/src/mame/drivers/hp9825.cpp b/src/mame/drivers/hp9825.cpp
index 063387c8426..39c92bbe7cc 100644
--- a/src/mame/drivers/hp9825.cpp
+++ b/src/mame/drivers/hp9825.cpp
@@ -8,8 +8,8 @@
// **** Temporary header, will hopefully evolve into proper doc ****
//
// What's in:
-// - Emulation of 9825B system
-// - 12 kw of RAMs
+// - Emulation of 9825B and 9825T systems
+// - 12 kw (9825B) or 31kw (9825T) of RAM
// - 12 kw of system ROM
// - Keyboard (SHIFT LOCK & RESET not implemented)
// - Display & run light
@@ -17,12 +17,23 @@
// - Printer
// - Beeper
// - Internal expansion ROMs
+// - I/O expansion slots: 98032, 98034 & 98035 modules can be connected
+// - For 9825T: the so-called SKOAL mechanism that transparently overlays RAM & ROM
+// in the same address space
// What's not yet in:
// - External expansion ROMs
// - Configurable RAM size
-// - I/O expansion slots: 98034 & 98035 modules from hp9845 emulation can be used here, too
//
-// 9825A & 9825T can also be emulated. At the moment I haven't all the necessary
+// Thanks to Dyke Shaffer for publishing (on https://groups.io/g/VintHPcom)
+// the source code of 98217 mass memory ROM. The 98217.bin image was reconstructed
+// by re-assembling the source code (this is the reason why it's marked as
+// a BAD_DUMP). And thanks to Ansgar Kueckes for adapting his assembler to
+// handle HP9825 source files.
+// For what regards the 9825T, I'd like to thank again Dyke Shaffer for
+// publishing a lot of internal HP docs about the SKOAL card. I recovered the
+// content of SKOAL ROM from its printed & scanned dump.
+//
+// 9825A can also be emulated. At the moment I haven't all the necessary
// ROM dumps, though.
#include "emu.h"
@@ -36,6 +47,10 @@
#include "sound/beep.h"
#include "hp9825.lh"
+// Debugging
+#define VERBOSE 0
+#include "logmacro.h"
+
// CPU clock (generated by a trimmered RC oscillator)
constexpr unsigned MAIN_CLOCK = 6000000;
@@ -75,6 +90,9 @@ namespace {
}
}
+// +--------------+
+// | hp9825_state |
+// +--------------+
class hp9825_state : public driver_device
{
public:
@@ -97,15 +115,16 @@ public:
{
}
- void hp9825b(machine_config &config);
+ void hp9825_base(machine_config &config);
protected:
virtual void machine_start() override;
virtual void device_reset() override;
virtual void machine_reset() override;
-private:
required_device<hp_09825_67907_cpu_device> m_cpu;
+
+private:
required_device<hp98x5_io_sys_device> m_io_sys;
required_device<timer_device> m_cursor_timer;
required_device<hp9825_tape_device> m_tape;
@@ -138,7 +157,6 @@ private:
int m_slot_sc[ 3 ];
void cpu_io_map(address_map &map);
- void cpu_mem_map(address_map &map);
DECLARE_READ16_MEMBER(kb_scancode_r);
DECLARE_WRITE16_MEMBER(disp_w);
@@ -222,15 +240,6 @@ void hp9825_state::cpu_io_map(address_map &map)
// TODO:
}
-void hp9825_state::cpu_mem_map(address_map &map)
-{
- map.unmap_value_low();
- map(0x0000 , 0x2fff).rom();
- map(0x3400 , 0x3bff).rom();
- map(0x4000 , 0x4fff).rom();
- map(0x5000 , 0x7fff).ram();
-}
-
READ16_MEMBER(hp9825_state::kb_scancode_r)
{
uint8_t res = m_scancode;
@@ -613,18 +622,18 @@ void hp9825_state::set_dmar_slot(unsigned slot , int state)
m_io_sys->set_dmar(uint8_t(sc) , state);
}
-MACHINE_CONFIG_START(hp9825_state::hp9825b)
+void hp9825_state::hp9825_base(machine_config &config)
+{
HP_09825_67907(config , m_cpu , MAIN_CLOCK);
// Just guessing... settings borrowed from hp9845
m_cpu->set_rw_cycles(6 , 6);
m_cpu->set_relative_mode(false);
- m_cpu->set_addrmap(AS_PROGRAM , &hp9825_state::cpu_mem_map);
m_cpu->set_addrmap(AS_IO , &hp9825_state::cpu_io_map);
m_cpu->set_irq_acknowledge_callback("io_sys" , FUNC(hp98x5_io_sys_device::irq_callback));
m_cpu->pa_changed_cb().set(m_io_sys , FUNC(hp98x5_io_sys_device::pa_w));
// Needed when 98035 RTC module is connected or time advances at about 1/4 the correct speed (NP misses a lot of 1kHz interrupts)
- MCFG_QUANTUM_TIME(attotime::from_hz(5000));
+ config.m_minimum_quantum = attotime::from_hz(5000);
HP98X5_IO_SYS(config , m_io_sys , 0);
m_io_sys->irl().set_inputline(m_cpu, HPHYBRID_IRL);
@@ -665,7 +674,7 @@ MACHINE_CONFIG_START(hp9825_state::hp9825b)
}
config.set_default_layout(layout_hp9825);
-MACHINE_CONFIG_END
+}
#define IOP_MASK(x) BIT_MASK<ioport_value>((x))
@@ -819,17 +828,283 @@ static INPUT_PORTS_START(hp9825)
PORT_BIT(IOP_MASK(0) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1) // Shift
INPUT_PORTS_END
+// +---------------+
+// | hp9825b_state |
+// +---------------+
+class hp9825b_state : public hp9825_state
+{
+public:
+ hp9825b_state(const machine_config &mconfig, device_type type, const char *tag)
+ : hp9825_state(mconfig , type , tag)
+ {
+ }
+
+ void hp9825b(machine_config &config);
+
+private:
+ void cpu_mem_map(address_map &map);
+};
+
+void hp9825b_state::hp9825b(machine_config &config)
+{
+ hp9825_base(config);
+ m_cpu->set_addrmap(AS_PROGRAM , &hp9825b_state::cpu_mem_map);
+}
+
+void hp9825b_state::cpu_mem_map(address_map &map)
+{
+ map.unmap_value_low();
+ map(0x0000 , 0x3bff).rom();
+ map(0x4000 , 0x4fff).rom();
+ map(0x5000 , 0x7fff).ram();
+}
+
+// +---------------+
+// | hp9825t_state |
+// +---------------+
+class hp9825t_state : public hp9825_state
+{
+public:
+ hp9825t_state(const machine_config &mconfig, device_type type, const char *tag)
+ : hp9825_state(mconfig , type , tag)
+ , m_rom_region(*this , "rom")
+ , m_skoalrom(*this , "skoalrom")
+ {
+ }
+
+ void hp9825t(machine_config &config);
+
+protected:
+ virtual void machine_start() override;
+ virtual void device_reset() override;
+
+private:
+ required_memory_region m_rom_region;
+ required_memory_region m_skoalrom;
+ std::unique_ptr<uint16_t[]> m_ram;
+
+ uint8_t m_cycle_type;
+
+ // SKOAL state
+ bool m_skoalbit; // U53
+ bool m_second_access; // U57-3
+ bool m_mref; // U57-4
+ bool m_ifetch_4400; // U57-5
+ uint8_t m_special_opt; // U42
+ uint16_t m_fetch_addr;
+
+ void cpu_mem_map(address_map &map);
+ DECLARE_READ16_MEMBER(cpu_mem_r);
+ DECLARE_WRITE16_MEMBER(cpu_mem_w);
+ void stm(uint8_t cycle_type);
+ void on_cycle_end();
+ void opcode_fetch(uint16_t opcode);
+ uint8_t get_skoalrom(uint16_t addr);
+ bool is_rom(uint16_t addr , uint8_t cycle_type) const;
+};
+
+void hp9825t_state::hp9825t(machine_config &config)
+{
+ hp9825_base(config);
+ m_cpu->set_addrmap(AS_PROGRAM , &hp9825t_state::cpu_mem_map);
+ m_cpu->stm_cb().set(FUNC(hp9825t_state::stm));
+ m_cpu->opcode_cb().set(FUNC(hp9825t_state::opcode_fetch));
+}
+
+void hp9825t_state::machine_start()
+{
+ hp9825_state::machine_start();
+
+ // 32kw of RAM (the 1st kw is not accessible in normal operation)
+ m_ram = std::make_unique<uint16_t[]>(32768);
+ save_pointer(NAME(m_ram) , 32768);
+}
+
+void hp9825t_state::device_reset()
+{
+ hp9825_state::device_reset();
+
+ // This has to be done before CPU reset or first instruction won't be fetched correctly
+ m_cycle_type = 0;
+ m_special_opt = 0xf;
+}
+
+void hp9825t_state::cpu_mem_map(address_map &map)
+{
+ map.unmap_value_low();
+ map(0x0000 , 0x7fff).rw(FUNC(hp9825t_state::cpu_mem_r) , FUNC(hp9825t_state::cpu_mem_w));
+}
+
+READ16_MEMBER(hp9825t_state::cpu_mem_r)
+{
+ bool from_rom;
+
+ if (m_cycle_type & hp_hybrid_cpu_device::CYCLE_RD_MASK) {
+ if (m_cycle_type & hp_hybrid_cpu_device::CYCLE_IFETCH_MASK) {
+ m_fetch_addr = offset;
+ }
+ from_rom = is_rom(offset , m_cycle_type);
+ LOG("rd @%04x CYC=%x %d%d%d%d ROM=%d\n" , offset , m_cycle_type , m_skoalbit , m_second_access , m_mref , m_ifetch_4400 , from_rom);
+ if (!(m_cycle_type & (hp_hybrid_cpu_device::CYCLE_IFETCH_MASK | hp_hybrid_cpu_device::CYCLE_DMA_MASK))) {
+ on_cycle_end();
+ }
+ m_cycle_type = 0;
+ // TODO: diagnostic read
+ } else {
+ // Read coming from debugger and not from CPU: fake an ifetch
+ from_rom = is_rom(offset , hp_hybrid_cpu_device::CYCLE_IFETCH_MASK);
+ }
+
+ return from_rom ? m_rom_region->as_u16(offset) : m_ram[ offset ];
+}
+
+WRITE16_MEMBER(hp9825t_state::cpu_mem_w)
+{
+ if (m_cycle_type & hp_hybrid_cpu_device::CYCLE_WR_MASK) {
+ if (!(m_cycle_type & hp_hybrid_cpu_device::CYCLE_DMA_MASK)) {
+ on_cycle_end();
+ }
+ m_cycle_type = 0;
+ }
+ // All write cycles go to RAM
+ m_ram[ offset ] = (m_ram[ offset ] & ~mem_mask) | (data & mem_mask);
+}
+
+void hp9825t_state::stm(uint8_t cycle_type)
+{
+ LOG("stm %x\n" , cycle_type);
+ m_cycle_type = cycle_type;
+ if (m_cycle_type & hp_hybrid_cpu_device::CYCLE_IFETCH_MASK) {
+ m_second_access = false;
+ m_mref = false;
+ m_ifetch_4400 = false;
+ // In case of ifetch from register area this is kept at 0 (because cpu_mem_r is not called)
+ // In case of ifetch from RAM/ROM this is set by cpu_mem_r to the fetch address
+ m_fetch_addr = 0;
+ } else if (m_cycle_type & hp_hybrid_cpu_device::CYCLE_RAL_MASK) {
+ if (!(m_cycle_type & hp_hybrid_cpu_device::CYCLE_DMA_MASK)) {
+ on_cycle_end();
+ }
+ m_cycle_type = 0;
+ }
+}
+
+void hp9825t_state::on_cycle_end()
+{
+ m_second_access = false;
+}
+
+void hp9825t_state::opcode_fetch(uint16_t opcode)
+{
+ LOG("oc %04x\n" , opcode);
+ m_cycle_type = 0;
+ // memory referencing instructions
+ m_mref = (opcode & 0x7000) != 0x7000;
+ m_second_access = true;
+ m_ifetch_4400 = (m_fetch_addr & 0x7f00) == 0x0900;
+ if (BIT(m_special_opt , 3) && BIT(m_special_opt , 2)) {
+ // Set SKOAL bit
+ if (m_fetch_addr < 0x20) {
+ // Fetch from registers -> SKOAL bit = 0
+ m_skoalbit = false;
+ } else if ((m_fetch_addr & 0x6000) == 0x6000) {
+ // Fetch in [6000..7fff] range -> SKOAL bit = 0
+ m_skoalbit = false;
+ } else {
+ uint8_t tmp = get_skoalrom(m_fetch_addr);
+ m_skoalbit = (tmp >> ((~m_fetch_addr >> 12) & 7)) & 1;
+ }
+ }
+ // Decode SKOAL instructions. They are ignored by the hybrid processor
+ // as they are not recognized.
+ if ((opcode & 0xffc0) == 0x7040) {
+ m_special_opt = opcode & 0xf;
+ if (!BIT(m_special_opt , 3)) {
+ // RAM/ == 0
+ m_skoalbit = false;
+ } else if (!BIT(m_special_opt , 2)) {
+ // ROM/ == 0
+ m_skoalbit = true;
+ }
+ }
+}
+
+uint8_t hp9825t_state::get_skoalrom(uint16_t addr)
+{
+ return m_skoalrom->as_u8(~addr & 0x0fff);
+}
+
+bool hp9825t_state::is_rom(uint16_t addr , uint8_t cycle_type) const
+{
+ if ((addr & 0x6000) == 0x6000) {
+ // [6000..7fff] -> always RAM
+ return false;
+ } else if ((cycle_type & hp_hybrid_cpu_device::CYCLE_DMA_MASK) != 0 ||
+ !BIT(m_special_opt , 1)) {
+ // DMA cycle or BIT/ == 0 -> RAM
+ return false;
+ } else if (addr >= 0x400 && !BIT(m_special_opt , 0)) {
+ // [0400..5fff] and BIN/ == 0 -> RAM
+ return false;
+ } else {
+ bool addr_0800_7fff = (addr & 0x7800) != 0;
+ bool addr_0400_07ff = !addr_0800_7fff && BIT(addr , 10);
+ bool addr_0000_03ff = !addr_0800_7fff && !BIT(addr , 10);
+
+ // U58-6
+ bool force_rom;
+
+ // ROM when one or more of these is true:
+ // - addr in [0000..03ff]
+ // - Ifetch cycle and addr in [0800..5fff]
+ // - 2nd access of a memory-referencing instruction not in [0400..07ff] range
+ // - skoalbit = 1 and instruction fetched in [0900..09ff] range
+ force_rom =
+ addr_0000_03ff ||
+ ((cycle_type & hp_hybrid_cpu_device::CYCLE_IFETCH_MASK) != 0 && addr_0800_7fff) ||
+ (m_second_access && m_mref && (!BIT(m_special_opt , 2) || !addr_0400_07ff)) ||
+ (m_skoalbit && m_ifetch_4400);
+
+ if (force_rom) {
+ return true;
+ } else if (addr_0400_07ff && BIT(m_special_opt , 2)) {
+ return false;
+ } else {
+ return m_skoalbit;
+ }
+ }
+}
+
ROM_START(hp9825b)
ROM_REGION(0xa000 , "cpu" , ROMREGION_16BIT | ROMREGION_BE)
ROM_LOAD("sysrom1.bin" , 0x0000 , 0x2000 , CRC(fe429268) SHA1(f2fe7c5abca92bd13f81b4385fc4fce0cafb0da0))
ROM_LOAD("sysrom2.bin" , 0x2000 , 0x2000 , CRC(96093b5d) SHA1(c6ec4cafd019887df0fa849b3c7070bb74faee54))
ROM_LOAD("sysrom3.bin" , 0x4000 , 0x2000 , CRC(f9470f67) SHA1(b80cb4a366d93bd7acc3508ce987bb11c5986b2a))
+ ROM_LOAD("98217.bin" , 0x6000 , 0x0800 , BAD_DUMP CRC(ea1fcf63) SHA1(e535c82897210a1c67c1ca16f44f936d4c470463))
+ ROM_LOAD("genio_t.bin" , 0x6800 , 0x0800 , CRC(ade1d1ed) SHA1(9af74a65b29ef1885f74164238ecf8d16ac995d6))
+ ROM_LOAD("plot72.bin" , 0x7000 , 0x0800 , CRC(0a9cb8db) SHA1(d0d126fca108f2715e1e408cb31b09ba69385ac4))
+ ROM_LOAD("advpgm_t.bin", 0x8000 , 0x0800 , CRC(965b5e5a) SHA1(ff44dd15f8fa4ca03dfd970ed8b200e8a071ec13))
+ ROM_LOAD("extio_t.bin" , 0x8800 , 0x1000 , CRC(a708b978) SHA1(baf53c8a2b24d059f95252baf1452188eaf6e4be))
+ ROM_LOAD("strings_t.bin",0x9800 , 0x0800 , CRC(b5ca5da5) SHA1(af13abb3c15836c566863c656e1659f7e6f96d04))
+ROM_END
+
+ROM_START(hp9825t)
+ ROM_REGION(0xc000 , "rom" , ROMREGION_16BIT | ROMREGION_BE | ROMREGION_ERASE | ROMREGION_ERASE00)
+ ROM_LOAD("sysrom1.bin" , 0x0000 , 0x2000 , CRC(fe429268) SHA1(f2fe7c5abca92bd13f81b4385fc4fce0cafb0da0))
+ ROM_LOAD("sysrom2.bin" , 0x2000 , 0x2000 , CRC(96093b5d) SHA1(c6ec4cafd019887df0fa849b3c7070bb74faee54))
+ ROM_LOAD("sysrom3.bin" , 0x4000 , 0x2000 , CRC(f9470f67) SHA1(b80cb4a366d93bd7acc3508ce987bb11c5986b2a))
+ ROM_LOAD("98217.bin" , 0x6000 , 0x0800 , BAD_DUMP CRC(ea1fcf63) SHA1(e535c82897210a1c67c1ca16f44f936d4c470463))
ROM_LOAD("genio_t.bin" , 0x6800 , 0x0800 , CRC(ade1d1ed) SHA1(9af74a65b29ef1885f74164238ecf8d16ac995d6))
ROM_LOAD("plot72.bin" , 0x7000 , 0x0800 , CRC(0a9cb8db) SHA1(d0d126fca108f2715e1e408cb31b09ba69385ac4))
ROM_LOAD("advpgm_t.bin", 0x8000 , 0x0800 , CRC(965b5e5a) SHA1(ff44dd15f8fa4ca03dfd970ed8b200e8a071ec13))
ROM_LOAD("extio_t.bin" , 0x8800 , 0x1000 , CRC(a708b978) SHA1(baf53c8a2b24d059f95252baf1452188eaf6e4be))
ROM_LOAD("strings_t.bin",0x9800 , 0x0800 , CRC(b5ca5da5) SHA1(af13abb3c15836c566863c656e1659f7e6f96d04))
+ ROM_LOAD("syspgm.bin" , 0xa000 , 0x0800 , CRC(8915588f) SHA1(037f497b5ecc3216fb6b8356767cc361fb0b2945))
+
+ ROM_REGION(0x1000 , "skoalrom" , 0)
+ ROM_LOAD("skoalrom.bin" , 0 , 0x1000 , CRC(5e8124d5) SHA1(dedf7f8a10c62b444f04213956083089e97bf219))
ROM_END
// YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY FULLNAME FLAGS
-COMP(1980, hp9825b, 0, 0, hp9825b, hp9825, hp9825_state, empty_init, "Hewlett-Packard", "HP 9825B", 0)
+COMP(1980, hp9825b, 0, 0, hp9825b, hp9825, hp9825b_state,empty_init, "Hewlett-Packard", "HP 9825B", 0)
+COMP(1980, hp9825t, 0, 0, hp9825t, hp9825, hp9825t_state,empty_init, "Hewlett-Packard", "HP 9825T", 0)