diff options
Diffstat (limited to 'src/mame/drivers/hp9825.cpp')
-rw-r--r-- | src/mame/drivers/hp9825.cpp | 317 |
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) |