From e697566de82eecd9874d3530f0f5427c47939ed1 Mon Sep 17 00:00:00 2001 From: fulivi Date: Fri, 14 Feb 2020 15:33:02 +0100 Subject: [PATCH] hp86b: new working machine --- hash/hp86_rom.xml | 132 ++ src/devices/cpu/capricorn/capricorn.cpp | 46 +- src/devices/cpu/capricorn/capricorn.h | 19 +- src/mame/drivers/hp80.cpp | 1725 ++++++++++++++++------- src/mame/layout/hp86b.lay | 31 + src/mame/mame.lst | 1 + 6 files changed, 1434 insertions(+), 520 deletions(-) create mode 100644 hash/hp86_rom.xml create mode 100644 src/mame/layout/hp86b.lay diff --git a/hash/hp86_rom.xml b/hash/hp86_rom.xml new file mode 100644 index 00000000000..1c2ea6a954e --- /dev/null +++ b/hash/hp86_rom.xml @@ -0,0 +1,132 @@ + + + + + + MIKSAM ROM + 1982 + Hewlett-Packard + + + + + + + + + + + Language ROM + 1983 + Hewlett-Packard + + + + + + + + + + Assembler ROM + 1982 + Hewlett-Packard + + + + + + + + + + Matrix 1 ROM + 1982 + Hewlett-Packard + + + + + + + + + + + Matrix 2 ROM + 1982 + Hewlett-Packard + + + + + + + + + + + I/O ROM + 1982 + Hewlett-Packard + + + + + + + + + + + Extended Mass Storage ROM + 1984 + Structured Software Systems + + + + + + + + + + Advanced programming 1 ROM + 1983 + Hewlett-Packard + + + + + + + + + + + Advanced programming 2 ROM + 1983 + Hewlett-Packard + + + + + + + + + + + Printer/plotter ROM + 1982 + Hewlett-Packard + + + + + + + + + + diff --git a/src/devices/cpu/capricorn/capricorn.cpp b/src/devices/cpu/capricorn/capricorn.cpp index cb2b58c1a37..c47f32dd116 100644 --- a/src/devices/cpu/capricorn/capricorn.cpp +++ b/src/devices/cpu/capricorn/capricorn.cpp @@ -141,7 +141,9 @@ DEFINE_DEVICE_TYPE(HP_CAPRICORN , capricorn_cpu_device , "capricorn" , "HP-Capri capricorn_cpu_device::capricorn_cpu_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : cpu_device(mconfig, HP_CAPRICORN, tag, owner, clock), - m_program_config("program" , ENDIANNESS_LITTLE , 8 , 16) + m_program_config("program" , ENDIANNESS_LITTLE , 8 , 16), + m_opcode_func(*this), + m_lma_out(*this) { } @@ -179,6 +181,9 @@ void capricorn_cpu_device::device_start() save_item(NAME(m_flags)); set_icountptr(m_icount); + + m_opcode_func.resolve_safe(); + m_lma_out.resolve_safe(); } void capricorn_cpu_device::device_reset() @@ -204,6 +209,7 @@ void capricorn_cpu_device::execute_run() debugger_instruction_hook(m_genpc); uint8_t opcode = fetch(); + m_opcode_func(opcode); execute_one(opcode); offset_pc(1); } @@ -243,18 +249,21 @@ std::unique_ptr capricorn_cpu_device::create_disassemble return std::make_unique(); } -void capricorn_cpu_device::start_mem_burst(ea_addr_t addr) +void capricorn_cpu_device::start_mem_burst(ea_addr_t addr , bool lmard) { m_flatten = false; - // Only relevant for memory access (not for internal registers) - m_start_addr = (uint16_t)(addr & ADDR_MASK); + if (!BIT(addr , GP_REG_BIT)) { + // Only relevant for memory access (not for internal registers) + m_start_addr = (uint16_t)(addr & ADDR_MASK); + m_lma_out(lmard); + } } -uint16_t capricorn_cpu_device::read_u16(ea_addr_t addr) +uint16_t capricorn_cpu_device::read_u16(ea_addr_t addr , bool lmard) { PAIR16 tmp; - start_mem_burst(addr); + start_mem_burst(addr , lmard); tmp.b.l = RM(addr); tmp.b.h = RM(addr); return tmp.w; @@ -299,6 +308,7 @@ void capricorn_cpu_device::WM(ea_addr_t& addr , uint8_t v) uint8_t capricorn_cpu_device::fetch() { m_genpc = read_u16(REG_PC | GP_REG_MASK); + start_mem_burst(m_genpc , false); return m_cache->read_byte(m_genpc); } @@ -416,12 +426,22 @@ capricorn_cpu_device::ea_addr_t capricorn_cpu_device::get_ea_idx_dir() // Indexed direct addressing mode m_icount -= 3; offset_pc(1); - uint16_t res = read_u16(m_genpc) + read_u16(m_arp | GP_REG_MASK); + uint16_t res = read_u16(m_genpc , false) + read_u16(m_arp | GP_REG_MASK); offset_pc(1); write_u16(REG_INDEX_SCRATCH | GP_REG_MASK, res); return res; } +capricorn_cpu_device::ea_addr_t capricorn_cpu_device::get_ea_jsbx() +{ + // Indexed direct addressing mode in JSBX instruction + m_icount -= 3; + offset_pc(1); + uint16_t res = read_u16(m_genpc) + read_u16(m_arp | GP_REG_MASK); + offset_pc(1); + return res; +} + capricorn_cpu_device::ea_addr_t capricorn_cpu_device::get_ea_lit_indir() { // Literal indirect addressing mode @@ -437,7 +457,7 @@ capricorn_cpu_device::ea_addr_t capricorn_cpu_device::get_ea_idx_indir() // Indexed indirect addressing mode m_icount -= 5; offset_pc(1); - uint16_t res = read_u16(m_genpc) + read_u16(m_arp | GP_REG_MASK); + uint16_t res = read_u16(m_genpc , false) + read_u16(m_arp | GP_REG_MASK); offset_pc(1); write_u16(REG_INDEX_SCRATCH | GP_REG_MASK, res); return read_u16(res); @@ -975,11 +995,11 @@ void capricorn_cpu_device::do_PAD_op() byte = RM(ea); m_arp = byte & ARP_DRP_MASK; COPY_BIT(BIT(byte , 6), m_flags, FLAGS_CY_BIT); - COPY_BIT(BIT(byte , 7), m_flags, FLAGS_OVF_BIT); byte = RM(ea); m_drp = byte & ARP_DRP_MASK; COPY_BIT(BIT(byte , 6), m_flags, FLAGS_DCM_BIT); + COPY_BIT(BIT(byte , 7), m_flags, FLAGS_OVF_BIT); byte = RM(ea); COPY_BIT(BIT(byte , 0), m_flags, FLAGS_LSB_BIT); @@ -1163,7 +1183,7 @@ void capricorn_cpu_device::execute_one(uint8_t opcode) case 0xc6: // JSB - do_JSB_op(get_ea_idx_dir()); + do_JSB_op(get_ea_jsbx()); break; case 0xc7: @@ -1511,8 +1531,10 @@ void capricorn_cpu_device::execute_one(uint8_t opcode) void capricorn_cpu_device::take_interrupt() { - // According to O. De Smet emulator interrupt handling takes 15 cycles - m_icount -= 15; + // Int. ack sequence takes 9 cycles + // Microcode FSM runs through this state sequence (see patent): + // 31-15-26-13-23-22-30-16-20 + m_icount -= 9; push_pc(); uint8_t vector = (uint8_t)standard_irq_callback(0); vector_to_pc(vector); diff --git a/src/devices/cpu/capricorn/capricorn.h b/src/devices/cpu/capricorn/capricorn.h index a44834c866b..0ebfb18a68d 100644 --- a/src/devices/cpu/capricorn/capricorn.h +++ b/src/devices/cpu/capricorn/capricorn.h @@ -5,6 +5,10 @@ // Emulator for HP Capricorn CPU // ***************************** // +// References: +// HP 00085-90444, Nov 81, HP83/85 Assembler ROM manual +// US Patent 4,424,563 describing the architecture of HP85 +// #ifndef MAME_CPU_CAPRICORN_CAPRICORN_H #define MAME_CPU_CAPRICORN_CAPRICORN_H @@ -17,6 +21,13 @@ public: uint8_t flatten_burst(); + // This CB reports the start of LMA (Load Memory Address) cycles + // Its parameter is true when starting a LMARD cycle + auto lma_cb() { return m_lma_out.bind(); } + + // Tap into fetched opcodes + auto opcode_cb() { return m_opcode_func.bind(); } + protected: // device_t overrides virtual void device_start() override; @@ -61,13 +72,16 @@ private: uint16_t m_start_addr; // Start address of burst uint16_t m_curr_addr; // Current address in burst + devcb_write8 m_opcode_func; + devcb_write_line m_lma_out; + // Effective Addresses // When b17 = 0, b15..b0 hold 16-bit memory address // When b17 = 1, b5..b0 hold register index typedef unsigned ea_addr_t; - void start_mem_burst(ea_addr_t addr); - uint16_t read_u16(ea_addr_t addr); + void start_mem_burst(ea_addr_t addr , bool lmard = false); + uint16_t read_u16(ea_addr_t addr , bool lmard = true); void write_u16(ea_addr_t addr , uint16_t v); uint8_t RM(ea_addr_t& addr); void WM(ea_addr_t& addr , uint8_t v); @@ -86,6 +100,7 @@ private: ea_addr_t get_ea_lit_dir(); ea_addr_t get_ea_reg_indir(); ea_addr_t get_ea_idx_dir(); + ea_addr_t get_ea_jsbx(); ea_addr_t get_ea_lit_indir(); ea_addr_t get_ea_idx_indir(); diff --git a/src/mame/drivers/hp80.cpp b/src/mame/drivers/hp80.cpp index ab2ae8aff56..8b1c9164075 100644 --- a/src/mame/drivers/hp80.cpp +++ b/src/mame/drivers/hp80.cpp @@ -5,7 +5,41 @@ // Driver for HP series 80 systems // ******************************* // -// This is WIP: lot of things still missing +// This driver currently emulates the HP85A & HP86B machines. +// +// What's in HP85A emulation: +// - Capricorn CPU @613 kHz +// - 32K of system ROMs +// - Optional ROMs +// - 16K of RAM +// - Alpha/graphic video +// - Internal timers +// - DC100 tape drive +// - Integrated thermal printer +// - Beeper & 1-bit bitbanged sound +// - I/O slots +// +// What's in HP86B emulation: +// - Capricorn CPU @613 kHz +// - 56K of system ROMs +// - Optional ROMs +// - 128K of RAM (through Extended Memory Controller) +// - Alpha/graphic video (with correct aspect ratio for 82913A 12" monitor) +// - Run light +// - Internal timers +// - Integrated HPIB interface (which is basically a built-in 82937 module) +// - Beeper & 1-bit bitbanged sound +// - I/O slots +// +// Thanks to all the people who made docs available & dumped the various ROMs. +// +// References for these systems: +// https://groups.io/g/hpseries80 - Site with tons of info on HP80 systems +// http://www.kaser.com/hp85.html - A Windows-based emulator of HP80 systems +// https://sites.google.com/site/olivier2smet2/hpseries80 - Another Windows-based emulator +// http://www.series80.org/ - *The* reference site for these machines +// http://www.akso.de/index.php?id=hp_series_80&L=-1%27 - Another interesting site +// http://www.hpmuseum.net/exhibit.php?class=1&cat=9 - Last but not least: HP museum pages for HP80 #include "emu.h" #include "emupal.h" @@ -18,14 +52,23 @@ #include "sound/volt_reg.h" #include "machine/1ma6.h" #include "machine/hp80_optrom.h" +#include "machine/ram.h" #include "softlist.h" #include "machine/bankdev.h" #include "bus/hp80_io/hp80_io.h" +#include "bus/hp80_io/82937.h" #include "imagedev/bitbngr.h" +#include "hp86b.lh" // Debugging -#define VERBOSE 1 #include "logmacro.h" +#define LOG_EMC_MASK (LOG_GENERAL << 1) +#define LOG_EMC(...) LOGMASKED(LOG_EMC_MASK, __VA_ARGS__) +#define LOG_IRQ_MASK (LOG_EMC_MASK << 1) +#define LOG_IRQ(...) LOGMASKED(LOG_IRQ_MASK, __VA_ARGS__) +#undef VERBOSE +//#define VERBOSE (LOG_GENERAL|LOG_EMC_MASK|LOG_IRQ_MASK) +#define VERBOSE LOG_GENERAL // Bit manipulation namespace { @@ -55,21 +98,8 @@ namespace { } // **** Constants **** -static constexpr unsigned MASTER_CLOCK = 9808000; -// Video memory is actually made of 16384 4-bit nibbles -static constexpr unsigned VIDEO_MEM_SIZE= 8192; -static constexpr unsigned ALPHA_MEM_SIZE= 4096; -static constexpr unsigned GRAPH_MEM_SIZE= 16384; -static constexpr unsigned CRT_STS_READY_BIT = 0; -static constexpr unsigned CRT_STS_DISPLAY_BIT = 1; -static constexpr unsigned CRT_STS_BUSY_BIT = 7; -static constexpr unsigned CRT_CTL_RD_RQ_BIT = 0; -static constexpr unsigned CRT_CTL_WIPEOUT_BIT = 1; -static constexpr unsigned CRT_CTL_POWERDN_BIT = 2; -static constexpr unsigned CRT_CTL_GRAPHICS_BIT = 7; -// Time to read/write a byte in video memory (in master clock cycles) -static constexpr unsigned CRT_RW_TIME = 96; -// Time taken by hw timer updating (semi-made up) (in usec) +static constexpr unsigned CPU_CLOCK = 613000; +// Time taken by hw timer updating (semi-made up) (in µsec) static constexpr unsigned TIMER_BUSY_USEC = 128; static constexpr unsigned IRQ_KEYBOARD_BIT = 0; static constexpr unsigned IRQ_TIMER0_BIT = 1; @@ -80,62 +110,23 @@ static constexpr unsigned IOP_COUNT = 4; static constexpr unsigned IRQ_BIT_COUNT = IRQ_IOP0_BIT + IOP_COUNT; static constexpr unsigned NO_IRQ = IRQ_BIT_COUNT; -// Internal printer has a moving printhead with 8 vertically-arranged resistors that print dots -// by heating thermal paper. The horizontal span of the printhead covers 224 columns. -// In alpha mode, each sweep prints up to 32 characters. Each character has a 8x7 cell. -// 8 pixels of cell height are covered by the printhead height, whereas 7 pixels of width -// allow for 32 characters on a row (224 = 32 * 7). -// After an alpha line is printed the paper advances by 10 pixel lines, so that a space of -// 2 lines is left between alpha lines. -// In graphic mode, printing starts at column 16 and covers 192 columns. So on each side of -// the printed area there's a 16-column wide margin (224 = 192 + 2 * 16). -// Once a graphic line is printed, paper advances by 8 pixel lines so that no space is inserted -// between successive sweeps. -// A full image of the graphic screen (256 x 192) is printed rotated 90 degrees clockwise. -// The printer controller chip (1MA9) has an embedded character generator ROM that is used -// when printing alpha lines. This ROM is also read by the CPU when drawing text on the graphic -// screen (BASIC "LABEL" instruction). -constexpr unsigned PRT_BUFFER_SIZE = 192; -constexpr unsigned PRTSTS_PAPER_OK_BIT = 7; -constexpr unsigned PRTSTS_DATARDY_BIT = 6; -constexpr unsigned PRTSTS_PRTRDY_BIT = 0; -constexpr unsigned PRTCTL_GRAPHIC_BIT = 7; -//constexpr unsigned PRTCTL_POWERUP_BIT = 6; -constexpr unsigned PRTCTL_READGEN_BIT = 5; -// Time to print a line (nominal speed is 2 lines/s) -constexpr unsigned PRT_BUSY_MSEC = 500; -// Horizontal start position of graphic print (16 columns from left-hand side) -constexpr unsigned PRT_GRAPH_OFFSET = 16; -// Height of printhead -constexpr unsigned PRT_PH_HEIGHT = 8; -// Height of alpha rows -constexpr unsigned PRT_ALPHA_HEIGHT = 10; -// Width of character cells -constexpr unsigned PRT_CELL_WIDTH = 7; -// Height of graphic rows -//constexpr unsigned PRT_GRAPH_HEIGHT = 8; -// Width of graphic sweeps -constexpr unsigned PRT_GRAPH_WIDTH = 192; -// Width of printhead sweeps -constexpr unsigned PRT_WIDTH = 224; - -// ************ -// hp85_state -// ************ -class hp85_state : public driver_device +// ***************** +// hp80_base_state +// ***************** +class hp80_base_state : public driver_device { public: - hp85_state(const machine_config &mconfig, device_type type, const char *tag); + hp80_base_state(const machine_config &mconfig, device_type type, const char *tag); - void hp85(machine_config &config); +protected: + void hp80_base(machine_config &config); + + virtual void cpu_mem_map(address_map &map); + virtual void rombank_mem_map(address_map &map); + virtual void unmap_optroms(address_space &space); -private: - virtual void machine_start() override; virtual void machine_reset() override; - uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect); - DECLARE_WRITE_LINE_MEMBER(vblank_w); - IRQ_CALLBACK_MEMBER(irq_callback); DECLARE_WRITE8_MEMBER(ginten_w); @@ -144,40 +135,23 @@ private: DECLARE_WRITE8_MEMBER(keysts_w); DECLARE_READ8_MEMBER(keycod_r); DECLARE_WRITE8_MEMBER(keycod_w); - DECLARE_READ8_MEMBER(crtc_r); - DECLARE_WRITE8_MEMBER(crtc_w); DECLARE_READ8_MEMBER(clksts_r); DECLARE_WRITE8_MEMBER(clksts_w); DECLARE_READ8_MEMBER(clkdat_r); DECLARE_WRITE8_MEMBER(clkdat_w); - DECLARE_WRITE8_MEMBER(prtlen_w); - DECLARE_READ8_MEMBER(prchar_r); - DECLARE_WRITE8_MEMBER(prchar_w); - DECLARE_READ8_MEMBER(prtsts_r); - DECLARE_WRITE8_MEMBER(prtctl_w); - DECLARE_WRITE8_MEMBER(prtdat_w); DECLARE_WRITE8_MEMBER(rselec_w); DECLARE_READ8_MEMBER(intrsc_r); DECLARE_WRITE8_MEMBER(intrsc_w); TIMER_DEVICE_CALLBACK_MEMBER(kb_scan); - TIMER_DEVICE_CALLBACK_MEMBER(vm_timer); TIMER_DEVICE_CALLBACK_MEMBER(timer_update); TIMER_DEVICE_CALLBACK_MEMBER(clk_busy_timer); - TIMER_DEVICE_CALLBACK_MEMBER(prt_busy_timer); DECLARE_WRITE8_MEMBER(irl_w); DECLARE_WRITE8_MEMBER(halt_w); - void cpu_mem_map(address_map &map); - void rombank_mem_map(address_map &map); - required_device m_cpu; - required_device m_screen; - required_device m_palette; - required_device m_vm_timer; required_device m_clk_busy_timer; - required_device m_prt_busy_timer; required_device m_beep; required_device m_dac; required_ioport m_io_key0; @@ -187,21 +161,7 @@ private: required_device_array m_rom_drawers; required_device m_rombank; required_device_array m_io_slots; - required_device m_prt_graph_out; - required_device m_prt_alpha_out; - // Character generators - required_region_ptr m_chargen; - required_region_ptr m_prt_chargen; - - bitmap_rgb32 m_bitmap; - std::vector m_video_mem; - uint16_t m_crt_sad; - uint16_t m_crt_bad; - uint8_t m_crt_sts; - uint8_t m_crt_ctl; - uint8_t m_crt_read_byte; - uint8_t m_crt_write_byte; bool m_global_int_en; uint16_t m_int_serv; unsigned m_top_pending; @@ -228,74 +188,94 @@ private: uint8_t m_timer_idx; bool m_clk_busy; - // Printer - uint8_t m_prtlen; - uint8_t m_prt_idx; - uint8_t m_prchar_r; - uint8_t m_prchar_w; - uint8_t m_prtsts; - uint8_t m_prtctl; - uint8_t m_prt_buffer[ PRT_BUFFER_SIZE ]; - - attotime time_to_video_mem_availability() const; - static void get_video_addr(uint16_t addr , uint16_t& byte_addr , bool& lsb_nibble); - uint8_t video_mem_r(uint16_t addr , uint16_t addr_mask) const; - void video_mem_w(uint16_t addr , uint16_t addr_mask , uint8_t data); - void video_mem_read(); - void video_mem_write(); - bool kb_scan_ioport(ioport_value pressed , unsigned idx_base , uint8_t& keycode); void irq_w(unsigned n_irq , bool state); void irq_en_w(unsigned n_irq , bool state); void update_int_bits(); void update_irl(); - - uint8_t get_prt_font(uint8_t ch , unsigned col) const; - void prt_format_alpha(unsigned row , uint8_t *pixel_row) const; - void prt_format_graphic(unsigned row , uint8_t *pixel_row) const; - void prt_output_row(const uint8_t *pixel_row); - void prt_do_printing(); }; -hp85_state::hp85_state(const machine_config &mconfig, device_type type, const char *tag) - : driver_device(mconfig , type , tag), - m_cpu(*this , "cpu"), - m_screen(*this , "screen"), - m_palette(*this , "palette"), - m_vm_timer(*this , "vm_timer"), - m_clk_busy_timer(*this , "clk_busy_timer"), - m_prt_busy_timer(*this , "prt_busy_timer"), - m_beep(*this , "beeper"), - m_dac(*this , "dac"), - m_io_key0(*this , "KEY0"), - m_io_key1(*this , "KEY1"), - m_io_key2(*this , "KEY2"), - m_io_modkeys(*this, "MODKEYS"), - m_rom_drawers(*this , "drawer%u" , 1), - m_rombank(*this , "rombank"), - m_io_slots(*this , "slot%u" , 1), - m_prt_graph_out(*this , "prt_graphic"), - m_prt_alpha_out(*this , "prt_alpha"), - m_chargen(*this , "chargen"), - m_prt_chargen(*this , "prt_chargen") +hp80_base_state::hp80_base_state(const machine_config &mconfig, device_type type, const char *tag) + : driver_device(mconfig , type , tag) + , m_cpu(*this , "cpu") + , m_clk_busy_timer(*this , "clk_busy_timer") + , m_beep(*this , "beeper") + , m_dac(*this , "dac") + , m_io_key0(*this , "KEY0") + , m_io_key1(*this , "KEY1") + , m_io_key2(*this , "KEY2") + , m_io_modkeys(*this, "MODKEYS") + , m_rom_drawers(*this , "drawer%u" , 1) + , m_rombank(*this , "rombank") + , m_io_slots(*this , "slot%u" , 1) { } -void hp85_state::machine_start() +void hp80_base_state::hp80_base(machine_config &config) { - m_screen->register_screen_bitmap(m_bitmap); - m_video_mem.resize(VIDEO_MEM_SIZE); + HP_CAPRICORN(config, m_cpu, CPU_CLOCK); + m_cpu->set_addrmap(AS_PROGRAM, &hp80_base_state::cpu_mem_map); + m_cpu->set_irq_acknowledge_callback(FUNC(hp80_base_state::irq_callback)); + + ADDRESS_MAP_BANK(config, "rombank").set_map(&hp80_base_state::rombank_mem_map).set_options(ENDIANNESS_LITTLE, 8, 21, HP80_OPTROM_SIZE); + + // No idea at all about the actual keyboard scan frequency + TIMER(config, "kb_timer").configure_periodic(FUNC(hp80_base_state::kb_scan), attotime::from_hz(100)); + + // Hw timers are updated at 1 kHz rate + TIMER(config, "hw_timer").configure_periodic(FUNC(hp80_base_state::timer_update), attotime::from_hz(1000)); + TIMER(config, m_clk_busy_timer).configure_generic(FUNC(hp80_base_state::clk_busy_timer)); + + // Beeper + SPEAKER(config, "mono").front_center(); + DAC_1BIT(config, m_dac , 0).add_route(ALL_OUTPUTS, "mono", 0.5, AUTO_ALLOC_INPUT, 0); + voltage_regulator_device &vref(VOLTAGE_REGULATOR(config, "vref")); + vref.add_route(0, "dac", 1.0, DAC_VREF_POS_INPUT); + BEEP(config, m_beep, CPU_CLOCK / 512).add_route(ALL_OUTPUTS, "mono", 0.5, AUTO_ALLOC_INPUT, 0); + + // Optional ROMs + for (auto& finder : m_rom_drawers) { + HP80_OPTROM(config, finder); + } + + // I/O slots + for (unsigned slot = 0; slot < 4; slot++) { + auto& finder = m_io_slots[ slot ]; + HP80_IO_SLOT(config, finder).set_slot_no(slot); + finder->irl_cb().set(FUNC(hp80_base_state::irl_w)); + finder->halt_cb().set(FUNC(hp80_base_state::halt_w)); + } } -void hp85_state::machine_reset() +void hp80_base_state::cpu_mem_map(address_map &map) +{ + map.unmap_value_high(); + map(0x0000, 0x5fff).rom(); + map(0x6000, 0x7fff).m(m_rombank, FUNC(address_map_bank_device::amap8)); + map(0xff00, 0xff00).w(FUNC(hp80_base_state::ginten_w)); + map(0xff01, 0xff01).w(FUNC(hp80_base_state::gintdis_w)); + map(0xff02, 0xff02).rw(FUNC(hp80_base_state::keysts_r), FUNC(hp80_base_state::keysts_w)); + map(0xff03, 0xff03).rw(FUNC(hp80_base_state::keycod_r), FUNC(hp80_base_state::keycod_w)); + map(0xff0a, 0xff0a).rw(FUNC(hp80_base_state::clksts_r), FUNC(hp80_base_state::clksts_w)); + map(0xff0b, 0xff0b).rw(FUNC(hp80_base_state::clkdat_r), FUNC(hp80_base_state::clkdat_w)); + map(0xff18, 0xff18).w(FUNC(hp80_base_state::rselec_w)); + map(0xff40, 0xff40).rw(FUNC(hp80_base_state::intrsc_r), FUNC(hp80_base_state::intrsc_w)); +} + +void hp80_base_state::rombank_mem_map(address_map &map) +{ + map.unmap_value_high(); + // ROM in bank 0 is always present (it's part of system ROMs) + map(0x0000, 0x1fff).rom(); +} + +void hp80_base_state::unmap_optroms(address_space &space) +{ +} + +void hp80_base_state::machine_reset() { - m_crt_sad = 0; - m_crt_bad = 0; - m_crt_sts = 0x7c; - m_crt_ctl = BIT_MASK(CRT_CTL_POWERDN_BIT) | BIT_MASK(CRT_CTL_WIPEOUT_BIT); - m_crt_read_byte = 0; - m_crt_write_byte = 0; m_int_serv = 0; m_top_pending = NO_IRQ; m_int_acked = 0; @@ -322,20 +302,14 @@ void hp85_state::machine_reset() update_irl(); m_halt_lines = 0; m_cpu->set_input_line(INPUT_LINE_HALT , CLEAR_LINE); - m_prtlen = 0; - m_prt_idx = PRT_BUFFER_SIZE; - m_prchar_r = 0; - m_prchar_w = 0; - m_prtsts = BIT_MASK(PRTSTS_PAPER_OK_BIT) | BIT_MASK(PRTSTS_PRTRDY_BIT); - m_prtctl = 0; // Load optional ROMs (if any) - // All entries in rombanks [01..FF] initially not present - m_rombank->space(AS_PROGRAM).unmap_read(HP80_OPTROM_SIZE * 1 , HP80_OPTROM_SIZE * 0x100 - 1); + unmap_optroms(m_rombank->space(AS_PROGRAM)); for (auto& draw : m_rom_drawers) { LOG("Loading opt ROM in drawer %s\n" , draw->tag()); draw->install_read_handler(m_rombank->space(AS_PROGRAM)); } + // Clear RSELEC m_rombank->set_bank(0xff); @@ -346,60 +320,6 @@ void hp85_state::machine_reset() } } -uint32_t hp85_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect) -{ - copybitmap(bitmap, m_bitmap, 0, 0, 0, 0, cliprect); - return 0; -} - -WRITE_LINE_MEMBER(hp85_state::vblank_w) -{ - COPY_BIT(!state , m_crt_sts , CRT_STS_DISPLAY_BIT); - if (state) { - if (BIT(m_crt_ctl , CRT_CTL_WIPEOUT_BIT) || BIT(m_crt_ctl , CRT_CTL_POWERDN_BIT)) { - // Blank video - m_bitmap.fill(rgb_t::black()); - } else if (BIT(m_crt_ctl , CRT_CTL_GRAPHICS_BIT)) { - // Render graphic video - uint16_t video_start = m_crt_sad; - for (unsigned y = 0; y < 192; y++) { - for (unsigned x = 0; x < 256; x += 8) { - uint8_t pixels = video_mem_r(video_start , GRAPH_MEM_SIZE / 2 - 1); - video_start += 2; - for (unsigned sub_x = 0; sub_x < 8; sub_x++) { - m_bitmap.pix32(y , x + sub_x) = m_palette->pen(BIT(pixels , 7)); - pixels <<= 1; - } - } - } - } else { - // Render alpha video - uint16_t video_start = m_crt_sad; - for (unsigned row = 0; row < 192; row += 12) { - for (unsigned col = 0; col < 256; col += 8) { - uint8_t ch = video_mem_r(video_start , ALPHA_MEM_SIZE / 2 - 1); - video_start += 2; - for (unsigned sub_row = 0; sub_row < 12; sub_row++) { - uint8_t pixels; - if (sub_row < 8) { - pixels = m_chargen[ (ch & 0x7f) * 8 + sub_row ]; - } else if (BIT(ch , 7) && (sub_row == 9 || sub_row == 10)) { - // Underline - pixels = 0xfe; - } else { - pixels = 0; - } - for (unsigned sub_x = 0; sub_x < 8; sub_x++) { - m_bitmap.pix32(row + sub_row , col + sub_x) = m_palette->pen(BIT(pixels , 7)); - pixels <<= 1; - } - } - } - } - } - } -} - // Vector table (indexed by bit no. in m_int_serv) static const uint8_t vector_table[] = { 0x04, // Keyboard @@ -414,9 +334,9 @@ static const uint8_t vector_table[] = { 0x00 // No IRQ }; -IRQ_CALLBACK_MEMBER(hp85_state::irq_callback) +IRQ_CALLBACK_MEMBER(hp80_base_state::irq_callback) { - logerror("IRQ ACK %u\n" , m_top_pending); + LOG_IRQ("IRQ ACK %u\n" , m_top_pending); BIT_SET(m_int_acked , m_top_pending); if (m_top_pending > IRQ_IOP0_BIT && m_top_pending < IRQ_BIT_COUNT) { // Interrupts are disabled in all I/O translators of higher priority than @@ -429,19 +349,19 @@ IRQ_CALLBACK_MEMBER(hp85_state::irq_callback) return vector_table[ m_top_pending ]; } -WRITE8_MEMBER(hp85_state::ginten_w) +WRITE8_MEMBER(hp80_base_state::ginten_w) { m_global_int_en = true; update_irl(); } -WRITE8_MEMBER(hp85_state::gintdis_w) +WRITE8_MEMBER(hp80_base_state::gintdis_w) { m_global_int_en = false; update_irl(); } -READ8_MEMBER(hp85_state::keysts_r) +READ8_MEMBER(hp80_base_state::keysts_r) { uint8_t res = 0; if (BIT(m_int_en , IRQ_KEYBOARD_BIT)) { @@ -459,7 +379,7 @@ READ8_MEMBER(hp85_state::keysts_r) return res; } -WRITE8_MEMBER(hp85_state::keysts_w) +WRITE8_MEMBER(hp80_base_state::keysts_w) { if (BIT(data , 0)) { irq_en_w(IRQ_KEYBOARD_BIT , true); @@ -471,96 +391,27 @@ WRITE8_MEMBER(hp85_state::keysts_w) if (BIT(data , 7)) { m_kb_flipped = !m_kb_flipped; } + if (data & 0x1c) { + LOG("Funny write to keysts=%02x\n" , data); + } } -READ8_MEMBER(hp85_state::keycod_r) +READ8_MEMBER(hp80_base_state::keycod_r) { return m_kb_keycode; } -WRITE8_MEMBER(hp85_state::keycod_w) +WRITE8_MEMBER(hp80_base_state::keycod_w) { if (data == 1) { irq_w(IRQ_KEYBOARD_BIT , false); m_kb_enable = true; + } else { + LOG("Funny write to keycod=%02x\n" , data); } } -READ8_MEMBER(hp85_state::crtc_r) -{ - uint8_t res = 0xff; - - // Read from CRT controller (1MA5) - switch (offset) { - case 0: - // CRTSAD: write-only - break; - - case 1: - // CRTBAD: write-only - break; - - case 2: - // CRTSTS - res = m_crt_sts; - break; - - case 3: - // CRTDAT - res = m_crt_read_byte; - break; - } - return res; -} - -WRITE8_MEMBER(hp85_state::crtc_w) -{ - // Write to CRT controller (1MA5) - uint8_t burst_idx = m_cpu->flatten_burst(); - switch (offset) { - case 0: - // CRTSAD - if (burst_idx == 1) { - m_crt_sad = ((uint16_t)data << 8) | (m_crt_sad & 0xff); - } else if (burst_idx == 0) { - m_crt_sad = (m_crt_sad & 0xff00) | data; - } - break; - - case 1: - // CRTBAD - if (burst_idx == 1) { - m_crt_bad = ((uint16_t)data << 8) | (m_crt_bad & 0xff); - } else if (burst_idx == 0) { - m_crt_bad = (m_crt_bad & 0xff00) | data; - } - break; - - case 2: - // CRTCTL - m_crt_ctl = data; - if (BIT(m_crt_ctl , CRT_CTL_RD_RQ_BIT)) { - BIT_CLR(m_crt_sts , CRT_STS_READY_BIT); - BIT_SET(m_crt_sts , CRT_STS_BUSY_BIT); - attotime vm_av = time_to_video_mem_availability(); - m_vm_timer->adjust(vm_av + attotime::from_ticks(CRT_RW_TIME , MASTER_CLOCK)); - } - break; - - case 3: - // CRTDAT - { - m_crt_write_byte = data; - BIT_CLR(m_crt_sts , CRT_STS_READY_BIT); - BIT_SET(m_crt_sts , CRT_STS_BUSY_BIT); - attotime vm_av = time_to_video_mem_availability(); - m_vm_timer->adjust(vm_av + attotime::from_ticks(CRT_RW_TIME , MASTER_CLOCK)); - } - break; - } -} - -READ8_MEMBER(hp85_state::clksts_r) +READ8_MEMBER(hp80_base_state::clksts_r) { uint8_t res = 0; for (unsigned i = 0; i < TIMER_COUNT; i++) { @@ -571,13 +422,11 @@ READ8_MEMBER(hp85_state::clksts_r) if (!m_clk_busy) { BIT_SET(res , 7); } - //logerror("CLKSTS R=%02x\n" , res); return res; } -WRITE8_MEMBER(hp85_state::clksts_w) +WRITE8_MEMBER(hp80_base_state::clksts_w) { - // logerror("CLKSTS W=%02x\n" , data); if (data == 0x0c) { // Set test mode (see timer_update) auto& timer = m_hw_timer[ m_timer_idx ]; @@ -586,7 +435,7 @@ WRITE8_MEMBER(hp85_state::clksts_w) timer.m_timer_cnt[ 1 ] = timer.m_timer_reg[ 1 ]; timer.m_timer_cnt[ 2 ] = timer.m_timer_reg[ 2 ]; timer.m_timer_cnt[ 3 ] = timer.m_timer_reg[ 3 ]; - logerror("Test mode enabled for timer %u\n" , m_timer_idx); + LOG("Test mode enabled for timer %u\n" , m_timer_idx); } else { m_timer_idx = (data >> 6) & 3; auto& timer = m_hw_timer[ m_timer_idx ]; @@ -618,7 +467,7 @@ WRITE8_MEMBER(hp85_state::clksts_w) } } -READ8_MEMBER(hp85_state::clkdat_r) +READ8_MEMBER(hp80_base_state::clkdat_r) { uint8_t res; unsigned burst_idx = m_cpu->flatten_burst(); @@ -626,98 +475,29 @@ READ8_MEMBER(hp85_state::clkdat_r) res = m_hw_timer[ m_timer_idx ].m_timer_cnt[ burst_idx ]; } else { // What happens when loading more than 4 bytes from timers? - logerror("Reading more than 4 bytes from timer %u\n" , m_timer_idx); + LOG("Reading more than 4 bytes from timer %u\n" , m_timer_idx); res = 0; } - //logerror("CLKDAT R %u=%02x\n" , burst_idx , res); return res; } -WRITE8_MEMBER(hp85_state::clkdat_w) +WRITE8_MEMBER(hp80_base_state::clkdat_w) { unsigned burst_idx = m_cpu->flatten_burst(); - //logerror("CLKDAT W %u=%02x\n" , burst_idx , data); if (burst_idx < 4) { m_hw_timer[ m_timer_idx ].m_timer_reg[ burst_idx ] = data; } else { // What happens when storing more than 4 bytes into timers? - logerror("Writing more than 4 bytes into timer %u\n" , m_timer_idx); + LOG("Writing more than 4 bytes into timer %u\n" , m_timer_idx); } } -WRITE8_MEMBER(hp85_state::prtlen_w) -{ - //LOG("PRTLEN=%u\n" , data); - if (data == 0) { - // Advance paper - memset(m_prt_buffer , 0 , sizeof(m_prt_buffer)); - m_prt_idx = 0; - prt_do_printing(); - } else { - m_prtlen = data; - if (!BIT(m_prtctl , PRTCTL_GRAPHIC_BIT)) { - m_prt_idx = 0; - } - } -} - -READ8_MEMBER(hp85_state::prchar_r) -{ - return m_prchar_r; -} - -WRITE8_MEMBER(hp85_state::prchar_w) -{ - m_prchar_w = data; -} - -READ8_MEMBER(hp85_state::prtsts_r) -{ - return m_prtsts; -} - -WRITE8_MEMBER(hp85_state::prtctl_w) -{ - //LOG("PRTCTL=%02x\n" , data); - m_prtctl = data; - BIT_SET(m_prtsts , PRTSTS_PRTRDY_BIT); - if (BIT(m_prtctl , PRTCTL_READGEN_BIT)) { - // Reading printer char. gen. - m_prchar_r = get_prt_font(m_prchar_w , m_prtctl & 7); - BIT_SET(m_prtsts , PRTSTS_DATARDY_BIT); - } else { - BIT_CLR(m_prtsts , PRTSTS_DATARDY_BIT); - } - if (BIT(m_prtctl , PRTCTL_GRAPHIC_BIT)) { - m_prt_idx = 0; - } -} - -WRITE8_MEMBER(hp85_state::prtdat_w) -{ - m_cpu->flatten_burst(); - //LOG("PRTDAT=%02x\n" , data); - if (m_prt_idx < PRT_BUFFER_SIZE) { - m_prt_buffer[ m_prt_idx++ ] = data; - if (m_prt_idx == PRT_BUFFER_SIZE || (!BIT(m_prtctl , PRTCTL_GRAPHIC_BIT) && m_prt_idx >= m_prtlen)) { - //LOG("Print\n"); - prt_do_printing(); - m_prt_idx = PRT_BUFFER_SIZE; - } - } -} - -TIMER_DEVICE_CALLBACK_MEMBER(hp85_state::prt_busy_timer) -{ - BIT_SET(m_prtsts , PRTSTS_PRTRDY_BIT); -} - -WRITE8_MEMBER(hp85_state::rselec_w) +WRITE8_MEMBER(hp80_base_state::rselec_w) { m_rombank->set_bank(data); } -READ8_MEMBER(hp85_state::intrsc_r) +READ8_MEMBER(hp80_base_state::intrsc_r) { if (m_top_pending >= IRQ_IOP0_BIT && m_top_pending < IRQ_BIT_COUNT && BIT(m_int_acked , m_top_pending)) { return (uint8_t)m_io_slots[ m_top_pending - IRQ_IOP0_BIT ]->get_base_addr(); @@ -727,7 +507,7 @@ READ8_MEMBER(hp85_state::intrsc_r) } } -WRITE8_MEMBER(hp85_state::intrsc_w) +WRITE8_MEMBER(hp80_base_state::intrsc_w) { if (m_top_pending >= IRQ_IOP0_BIT && m_top_pending < IRQ_BIT_COUNT && BIT(m_int_acked , m_top_pending)) { // Clear interrupt request in the slot being serviced @@ -744,13 +524,13 @@ WRITE8_MEMBER(hp85_state::intrsc_w) // Outer index: key position [0..79] = r * 8 + c // Inner index: SHIFT state (0 = no SHIFT, 1 = SHIFT) static const uint8_t keyboard_table[ 80 ][ 2 ] = { - // -- SHIFT - { 0xa2 , 0xac }, // 0,0: Down / Auto - { 0xa1 , 0xa5 }, // 0,1: Up / Home - { 0x83 , 0x87 }, // 0,2: k4 / k8 - { 0x82 , 0x86 }, // 0,3: k3 / k7 - { 0x81 , 0x85 }, // 0,4: k2 / k6 - { 0x80 , 0x84 }, // 0,5: k1 / k5 + // -- SHIFT HP85 HP86 + { 0xa2 , 0xac }, // 0,0: Down / Auto k6 / k13 + { 0xa1 , 0xa5 }, // 0,1: Up / Home k5 / k12 + { 0x83 , 0x87 }, // 0,2: k4 / k8 k4 / k11 + { 0x82 , 0x86 }, // 0,3: k3 / k7 k3 / k10 + { 0x81 , 0x85 }, // 0,4: k2 / k6 k2 / k9 + { 0x80 , 0x84 }, // 0,5: k1 / k5 k1 / k8 { 0x96 , 0x60 }, // 0,6: LABEL KEY { 0xff , 0xff }, // 0,7: N/U { 0x38 , 0x2a }, // 1,0: 8 @@ -785,28 +565,28 @@ static const uint8_t keyboard_table[ 80 ][ 2 ] = { { 0x58 , 0x78 }, // 4,5: X { 0x5a , 0x7a }, // 4,6: Z { 0x20 , 0x20 }, // 4,7: Space - { 0x2c , 0x3c }, // 5,0: , - { 0x2e , 0x3e }, // 5,1: . + { 0x2c , 0x3c }, // 5,0: , < + { 0x2e , 0x3e }, // 5,1: . > { 0x2f , 0x3f }, // 5,2: / ? { 0x8e , 0x90 }, // 5,3: PAUSE / STEP { 0x8d , 0x8d }, // 5,4: RUN { 0x2b , 0x7f }, // 5,5: KP + { 0x2d , 0x7d }, // 5,6: KP - - { 0x2a , 0x7e }, // 5,7: KP * + { 0x2a , 0x7e }, // 5,7: KP * N/U { 0x4c , 0x6c }, // 6,0: L - { 0x3b , 0x3a }, // 6,1: ; + { 0x3b , 0x3a }, // 6,1: ; : { 0x27 , 0x22 }, // 6,2: ' " { 0x9a , 0x9a }, // 6,3: END LINE { 0x94 , 0x95 }, // 6,4: LIST / P LST { 0xff , 0xff }, // 6,5: N/U - { 0xff , 0xff }, // 6,6: N/U + { 0x2a , 0x7e }, // 6,6: N/U KP * { 0x2f , 0x7b }, // 6,7: KP / { 0x4f , 0x6f }, // 7,0: O { 0x50 , 0x70 }, // 7,1: P { 0x28 , 0x5b }, // 7,2: ( [ { 0x29 , 0x5d }, // 7,3: ) ] - { 0x8f , 0xad }, // 7,4: CONT / SCRATCH - { 0xa0 , 0x92 }, // 7,5: -LINE / CLEAR + { 0x8f , 0xad }, // 7,4: CONT / SCRATCH CONT / TR/NORM + { 0xa0 , 0x92 }, // 7,5: -LINE / CLEAR E / TEST { 0x29 , 0x8c }, // 7,6: ) INIT { 0xff , 0xff }, // 7,7: N/U { 0x39 , 0x28 }, // 8,0: 9 @@ -817,17 +597,17 @@ static const uint8_t keyboard_table[ 80 ][ 2 ] = { { 0x99 , 0x9b }, // 8,5: BS { 0x28 , 0x8b }, // 8,6: ( RESET { 0x5e , 0xa6 }, // 8,7: ^ / RESLT - { 0x9c , 0x93 }, // 9,0: LEFT / GRAPH - { 0x9d , 0x89 }, // 9,1: RIGHT / COPY - { 0xa3 , 0xa3 }, // 9,2: RPL / INS - { 0xa4 , 0xa8 }, // 9,3: -CHAR / DEL - { 0x9f , 0x9e }, // 9,4: ROLL - { 0xaa , 0x88 }, // 9,5: LOAD / REW - { 0xa9 , 0x91 }, // 9,6: STORE / TEST - { 0x8a , 0x8a } // 9,7: PAPER ADVANCE + { 0x9c , 0x93 }, // 9,0: LEFT / GRAPH k7 / k14 + { 0x9d , 0x89 }, // 9,1: RIGHT / COPY -LINE / CLEAR + { 0xa3 , 0xa3 }, // 9,2: RPL / INS UP / HOME + { 0xa4 , 0xa8 }, // 9,3: -CHAR / DEL DOWN / A/G + { 0x9f , 0x9e }, // 9,4: ROLL LEFT / I/R + { 0xaa , 0x88 }, // 9,5: LOAD / REW RIGHT / -CHAR + { 0xa9 , 0x91 }, // 9,6: STORE / TEST ROLL + { 0x8a , 0x8a } // 9,7: PAPER ADVANCE N/U }; -bool hp85_state::kb_scan_ioport(ioport_value pressed , unsigned idx_base , uint8_t& keycode) +bool hp80_base_state::kb_scan_ioport(ioport_value pressed , unsigned idx_base , uint8_t& keycode) { while (pressed) { unsigned bit_no = 31 - count_leading_zeros(pressed); @@ -854,7 +634,7 @@ bool hp85_state::kb_scan_ioport(ioport_value pressed , unsigned idx_base , uint8 return false; } -TIMER_DEVICE_CALLBACK_MEMBER(hp85_state::kb_scan) +TIMER_DEVICE_CALLBACK_MEMBER(hp80_base_state::kb_scan) { ioport_value input[ 3 ]; input[ 0 ] = m_io_key0->read(); @@ -883,17 +663,7 @@ TIMER_DEVICE_CALLBACK_MEMBER(hp85_state::kb_scan) m_kb_state[ 2 ] = input[ 2 ]; } -TIMER_DEVICE_CALLBACK_MEMBER(hp85_state::vm_timer) -{ - if (BIT(m_crt_ctl , CRT_CTL_RD_RQ_BIT)) { - video_mem_read(); - } else { - video_mem_write(); - } - BIT_CLR(m_crt_sts , CRT_STS_BUSY_BIT); -} - -TIMER_DEVICE_CALLBACK_MEMBER(hp85_state::timer_update) +TIMER_DEVICE_CALLBACK_MEMBER(hp80_base_state::timer_update) { for (unsigned i = 0; i < TIMER_COUNT; i++) { auto& timer = m_hw_timer[ i ]; @@ -987,29 +757,427 @@ TIMER_DEVICE_CALLBACK_MEMBER(hp85_state::timer_update) m_clk_busy_timer->adjust(attotime::from_usec(TIMER_BUSY_USEC)); } -TIMER_DEVICE_CALLBACK_MEMBER(hp85_state::clk_busy_timer) +TIMER_DEVICE_CALLBACK_MEMBER(hp80_base_state::clk_busy_timer) { m_clk_busy = false; } -WRITE8_MEMBER(hp85_state::irl_w) +WRITE8_MEMBER(hp80_base_state::irl_w) { - //LOG("irl_w %u=%u\n" , offset , data); irq_w(offset + IRQ_IOP0_BIT , data != 0); } -WRITE8_MEMBER(hp85_state::halt_w) +WRITE8_MEMBER(hp80_base_state::halt_w) { - //LOG("halt_w %u=%u\n" , offset , data); bool prev_halt = m_halt_lines != 0; COPY_BIT(data != 0 , m_halt_lines , offset); bool new_halt = m_halt_lines != 0; if (prev_halt != new_halt) { - LOG("halt=%d hl=%x\n" , new_halt , m_halt_lines); + LOG_IRQ("halt=%d hl=%x\n" , new_halt , m_halt_lines); m_cpu->set_input_line(INPUT_LINE_HALT , new_halt); } } +void hp80_base_state::irq_w(unsigned n_irq , bool state) +{ + if (state && !BIT(m_int_serv , n_irq)) { + // Set service request + BIT_SET(m_int_serv , n_irq); + BIT_CLR(m_int_acked , n_irq); + } else if (!state && BIT(m_int_serv , n_irq)) { + // Clear service request + BIT_CLR(m_int_serv , n_irq); + BIT_CLR(m_int_acked , n_irq); + } + update_int_bits(); +} + +void hp80_base_state::irq_en_w(unsigned n_irq , bool state) +{ + COPY_BIT(state , m_int_en , n_irq); + update_int_bits(); +} + +void hp80_base_state::update_int_bits() +{ + uint16_t irqs = m_int_en & m_int_serv; + for (m_top_pending = 0; m_top_pending < IRQ_BIT_COUNT && !BIT(irqs , m_top_pending); m_top_pending++) { + } + update_irl(); +} + +void hp80_base_state::update_irl() +{ + m_cpu->set_input_line(0 , m_global_int_en && m_top_pending < IRQ_BIT_COUNT && !BIT(m_int_acked , m_top_pending)); +} + +// ************ +// hp85_state +// ************ +class hp85_state : public hp80_base_state +{ +public: + hp85_state(const machine_config &mconfig, device_type type, const char *tag); + + // **** Constants of HP85 **** + static constexpr unsigned MASTER_CLOCK = 9808000; + // Video memory is actually made of 16384 4-bit nibbles + static constexpr unsigned VIDEO_MEM_SIZE = 8192; + static constexpr unsigned ALPHA_MEM_SIZE = 4096; + static constexpr unsigned GRAPH_MEM_SIZE = 16384; + static constexpr unsigned CRT_STS_READY_BIT = 0; + static constexpr unsigned CRT_STS_DISPLAY_BIT = 1; + static constexpr unsigned CRT_STS_BUSY_BIT = 7; + static constexpr unsigned CRT_CTL_RD_RQ_BIT = 0; + static constexpr unsigned CRT_CTL_WIPEOUT_BIT = 1; + static constexpr unsigned CRT_CTL_POWERDN_BIT = 2; + static constexpr unsigned CRT_CTL_GRAPHICS_BIT = 7; + // Time to read/write a byte in video memory (in master clock cycles) + static constexpr unsigned CRT_RW_TIME = 96; + // Internal printer has a moving printhead with 8 vertically-arranged resistors that print dots + // by heating thermal paper. The horizontal span of the printhead covers 224 columns. + // In alpha mode, each sweep prints up to 32 characters. Each character has a 8x7 cell. + // 8 pixels of cell height are covered by the printhead height, whereas 7 pixels of width + // allow for 32 characters on a row (224 = 32 * 7). + // After an alpha line is printed the paper advances by 10 pixel lines, so that a space of + // 2 lines is left between alpha lines. + // In graphic mode, printing starts at column 16 and covers 192 columns. So on each side of + // the printed area there's a 16-column wide margin (224 = 192 + 2 * 16). + // Once a graphic line is printed, paper advances by 8 pixel lines so that no space is inserted + // between successive sweeps. + // A full image of the graphic screen (256 x 192) is printed rotated 90 degrees clockwise. + // The printer controller chip (1MA9) has an embedded character generator ROM that is used + // when printing alpha lines. This ROM is also read by the CPU when drawing text on the graphic + // screen (BASIC "LABEL" instruction). + static constexpr unsigned PRT_BUFFER_SIZE = 192; + static constexpr unsigned PRTSTS_PAPER_OK_BIT = 7; + static constexpr unsigned PRTSTS_DATARDY_BIT = 6; + static constexpr unsigned PRTSTS_PRTRDY_BIT = 0; + static constexpr unsigned PRTCTL_GRAPHIC_BIT = 7; + //constexpr unsigned PRTCTL_POWERUP_BIT = 6; + static constexpr unsigned PRTCTL_READGEN_BIT = 5; + // Time to print a line (nominal speed is 2 lines/s) + static constexpr unsigned PRT_BUSY_MSEC = 500; + // Horizontal start position of graphic print (16 columns from left-hand side) + static constexpr unsigned PRT_GRAPH_OFFSET = 16; + // Height of printhead + static constexpr unsigned PRT_PH_HEIGHT = 8; + // Height of alpha rows + static constexpr unsigned PRT_ALPHA_HEIGHT = 10; + // Width of character cells + static constexpr unsigned PRT_CELL_WIDTH = 7; + // Height of graphic rows + //constexpr unsigned PRT_GRAPH_HEIGHT = 8; + // Width of graphic sweeps + static constexpr unsigned PRT_GRAPH_WIDTH = 192; + // Width of printhead sweeps + static constexpr unsigned PRT_WIDTH = 224; + + void hp85(machine_config &config); + +private: + virtual void machine_start() override; + virtual void machine_reset() override; + + uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect); + DECLARE_WRITE_LINE_MEMBER(vblank_w); + + DECLARE_READ8_MEMBER(crtc_r); + DECLARE_WRITE8_MEMBER(crtc_w); + DECLARE_WRITE8_MEMBER(prtlen_w); + DECLARE_READ8_MEMBER(prchar_r); + DECLARE_WRITE8_MEMBER(prchar_w); + DECLARE_READ8_MEMBER(prtsts_r); + DECLARE_WRITE8_MEMBER(prtctl_w); + DECLARE_WRITE8_MEMBER(prtdat_w); + + TIMER_DEVICE_CALLBACK_MEMBER(vm_timer); + TIMER_DEVICE_CALLBACK_MEMBER(prt_busy_timer); + + virtual void cpu_mem_map(address_map &map) override; + virtual void unmap_optroms(address_space &space) override; + + required_device m_screen; + required_device m_palette; + required_device m_vm_timer; + required_device m_prt_busy_timer; + required_device m_prt_graph_out; + required_device m_prt_alpha_out; + + // Character generators + required_region_ptr m_chargen; + required_region_ptr m_prt_chargen; + + bitmap_rgb32 m_bitmap; + std::vector m_video_mem; + uint16_t m_crt_sad; + uint16_t m_crt_bad; + uint8_t m_crt_sts; + uint8_t m_crt_ctl; + uint8_t m_crt_read_byte; + uint8_t m_crt_write_byte; + + // Printer + uint8_t m_prtlen; + uint8_t m_prt_idx; + uint8_t m_prchar_r; + uint8_t m_prchar_w; + uint8_t m_prtsts; + uint8_t m_prtctl; + uint8_t m_prt_buffer[ PRT_BUFFER_SIZE ]; + + attotime time_to_video_mem_availability() const; + static void get_video_addr(uint16_t addr , uint16_t& byte_addr , bool& lsb_nibble); + uint8_t video_mem_r(uint16_t addr , uint16_t addr_mask) const; + void video_mem_w(uint16_t addr , uint16_t addr_mask , uint8_t data); + void video_mem_read(); + void video_mem_write(); + + uint8_t get_prt_font(uint8_t ch , unsigned col) const; + void prt_format_alpha(unsigned row , uint8_t *pixel_row) const; + void prt_format_graphic(unsigned row , uint8_t *pixel_row) const; + void prt_output_row(const uint8_t *pixel_row); + void prt_do_printing(); +}; + +hp85_state::hp85_state(const machine_config &mconfig, device_type type, const char *tag) + : hp80_base_state(mconfig , type , tag), + m_screen(*this , "screen"), + m_palette(*this , "palette"), + m_vm_timer(*this , "vm_timer"), + m_prt_busy_timer(*this , "prt_busy_timer"), + m_prt_graph_out(*this , "prt_graphic"), + m_prt_alpha_out(*this , "prt_alpha"), + m_chargen(*this , "chargen"), + m_prt_chargen(*this , "prt_chargen") +{ +} + +void hp85_state::machine_start() +{ + m_screen->register_screen_bitmap(m_bitmap); + m_video_mem.resize(VIDEO_MEM_SIZE); +} + +void hp85_state::machine_reset() +{ + hp80_base_state::machine_reset(); + + m_crt_sad = 0; + m_crt_bad = 0; + m_crt_sts = 0x7c; + m_crt_ctl = BIT_MASK(CRT_CTL_POWERDN_BIT) | BIT_MASK(CRT_CTL_WIPEOUT_BIT); + m_crt_read_byte = 0; + m_crt_write_byte = 0; + m_prtlen = 0; + m_prt_idx = PRT_BUFFER_SIZE; + m_prchar_r = 0; + m_prchar_w = 0; + m_prtsts = BIT_MASK(PRTSTS_PAPER_OK_BIT) | BIT_MASK(PRTSTS_PRTRDY_BIT); + m_prtctl = 0; +} + +uint32_t hp85_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect) +{ + copybitmap(bitmap, m_bitmap, 0, 0, 0, 0, cliprect); + return 0; +} + +WRITE_LINE_MEMBER(hp85_state::vblank_w) +{ + COPY_BIT(!state , m_crt_sts , CRT_STS_DISPLAY_BIT); + if (state) { + if (BIT(m_crt_ctl , CRT_CTL_WIPEOUT_BIT) || BIT(m_crt_ctl , CRT_CTL_POWERDN_BIT)) { + // Blank video + m_bitmap.fill(rgb_t::black()); + } else if (BIT(m_crt_ctl , CRT_CTL_GRAPHICS_BIT)) { + // Render graphic video + uint16_t video_start = m_crt_sad; + for (unsigned y = 0; y < 192; y++) { + for (unsigned x = 0; x < 256; x += 8) { + uint8_t pixels = video_mem_r(video_start , GRAPH_MEM_SIZE / 2 - 1); + video_start += 2; + for (unsigned sub_x = 0; sub_x < 8; sub_x++) { + m_bitmap.pix32(y , x + sub_x) = m_palette->pen(BIT(pixels , 7)); + pixels <<= 1; + } + } + } + } else { + // Render alpha video + uint16_t video_start = m_crt_sad; + for (unsigned row = 0; row < 192; row += 12) { + for (unsigned col = 0; col < 256; col += 8) { + uint8_t ch = video_mem_r(video_start , ALPHA_MEM_SIZE / 2 - 1); + video_start += 2; + for (unsigned sub_row = 0; sub_row < 12; sub_row++) { + uint8_t pixels; + if (sub_row < 8) { + pixels = m_chargen[ (ch & 0x7f) * 8 + sub_row ]; + } else if (BIT(ch , 7) && (sub_row == 9 || sub_row == 10)) { + // Underline + pixels = 0xfe; + } else { + pixels = 0; + } + for (unsigned sub_x = 0; sub_x < 8; sub_x++) { + m_bitmap.pix32(row + sub_row , col + sub_x) = m_palette->pen(BIT(pixels , 7)); + pixels <<= 1; + } + } + } + } + } + } +} + +READ8_MEMBER(hp85_state::crtc_r) +{ + uint8_t res = 0xff; + + // Read from CRT controller (1MA5) + switch (offset) { + case 0: + // CRTSAD: write-only + break; + + case 1: + // CRTBAD: write-only + break; + + case 2: + // CRTSTS + res = m_crt_sts; + break; + + case 3: + // CRTDAT + res = m_crt_read_byte; + break; + } + return res; +} + +WRITE8_MEMBER(hp85_state::crtc_w) +{ + // Write to CRT controller (1MA5) + uint8_t burst_idx = m_cpu->flatten_burst(); + switch (offset) { + case 0: + // CRTSAD + if (burst_idx == 1) { + m_crt_sad = ((uint16_t)data << 8) | (m_crt_sad & 0xff); + } else if (burst_idx == 0) { + m_crt_sad = (m_crt_sad & 0xff00) | data; + } + break; + + case 1: + // CRTBAD + if (burst_idx == 1) { + m_crt_bad = ((uint16_t)data << 8) | (m_crt_bad & 0xff); + } else if (burst_idx == 0) { + m_crt_bad = (m_crt_bad & 0xff00) | data; + } + break; + + case 2: + // CRTCTL + m_crt_ctl = data; + if (BIT(m_crt_ctl , CRT_CTL_RD_RQ_BIT)) { + BIT_CLR(m_crt_sts , CRT_STS_READY_BIT); + BIT_SET(m_crt_sts , CRT_STS_BUSY_BIT); + attotime vm_av = time_to_video_mem_availability(); + m_vm_timer->adjust(vm_av + attotime::from_ticks(CRT_RW_TIME , MASTER_CLOCK)); + } + break; + + case 3: + // CRTDAT + { + m_crt_write_byte = data; + BIT_CLR(m_crt_sts , CRT_STS_READY_BIT); + BIT_SET(m_crt_sts , CRT_STS_BUSY_BIT); + attotime vm_av = time_to_video_mem_availability(); + m_vm_timer->adjust(vm_av + attotime::from_ticks(CRT_RW_TIME , MASTER_CLOCK)); + } + break; + } +} + +WRITE8_MEMBER(hp85_state::prtlen_w) +{ + if (data == 0) { + // Advance paper + memset(m_prt_buffer , 0 , sizeof(m_prt_buffer)); + m_prt_idx = 0; + prt_do_printing(); + } else { + m_prtlen = data; + if (!BIT(m_prtctl , PRTCTL_GRAPHIC_BIT)) { + m_prt_idx = 0; + } + } +} + +READ8_MEMBER(hp85_state::prchar_r) +{ + return m_prchar_r; +} + +WRITE8_MEMBER(hp85_state::prchar_w) +{ + m_prchar_w = data; +} + +READ8_MEMBER(hp85_state::prtsts_r) +{ + return m_prtsts; +} + +WRITE8_MEMBER(hp85_state::prtctl_w) +{ + m_prtctl = data; + BIT_SET(m_prtsts , PRTSTS_PRTRDY_BIT); + if (BIT(m_prtctl , PRTCTL_READGEN_BIT)) { + // Reading printer char. gen. + m_prchar_r = get_prt_font(m_prchar_w , m_prtctl & 7); + BIT_SET(m_prtsts , PRTSTS_DATARDY_BIT); + } else { + BIT_CLR(m_prtsts , PRTSTS_DATARDY_BIT); + } + if (BIT(m_prtctl , PRTCTL_GRAPHIC_BIT)) { + m_prt_idx = 0; + } +} + +WRITE8_MEMBER(hp85_state::prtdat_w) +{ + m_cpu->flatten_burst(); + if (m_prt_idx < PRT_BUFFER_SIZE) { + m_prt_buffer[ m_prt_idx++ ] = data; + if (m_prt_idx == PRT_BUFFER_SIZE || (!BIT(m_prtctl , PRTCTL_GRAPHIC_BIT) && m_prt_idx >= m_prtlen)) { + prt_do_printing(); + m_prt_idx = PRT_BUFFER_SIZE; + } + } +} + +TIMER_DEVICE_CALLBACK_MEMBER(hp85_state::prt_busy_timer) +{ + BIT_SET(m_prtsts , PRTSTS_PRTRDY_BIT); +} + +TIMER_DEVICE_CALLBACK_MEMBER(hp85_state::vm_timer) +{ + if (BIT(m_crt_ctl , CRT_CTL_RD_RQ_BIT)) { + video_mem_read(); + } else { + video_mem_write(); + } + BIT_CLR(m_crt_sts , CRT_STS_BUSY_BIT); +} + attotime hp85_state::time_to_video_mem_availability() const { if (BIT(m_crt_ctl , CRT_CTL_WIPEOUT_BIT) || BIT(m_crt_ctl , CRT_CTL_POWERDN_BIT)) { @@ -1098,41 +1266,6 @@ void hp85_state::video_mem_write() m_crt_bad += 2; } -void hp85_state::irq_w(unsigned n_irq , bool state) -{ - //LOG("irq_w %u=%d GIE=%d SRV=%03x ACK=%03x IE=%03x\n" , n_irq , state , m_global_int_en , m_int_serv , m_int_acked , m_int_en); - if (state && !BIT(m_int_serv , n_irq)) { - // Set service request - BIT_SET(m_int_serv , n_irq); - BIT_CLR(m_int_acked , n_irq); - } else if (!state && BIT(m_int_serv , n_irq)) { - // Clear service request - BIT_CLR(m_int_serv , n_irq); - BIT_CLR(m_int_acked , n_irq); - } - update_int_bits(); -} - -void hp85_state::irq_en_w(unsigned n_irq , bool state) -{ - COPY_BIT(state , m_int_en , n_irq); - update_int_bits(); -} - -void hp85_state::update_int_bits() -{ - uint16_t irqs = m_int_en & m_int_serv; - for (m_top_pending = 0; m_top_pending < IRQ_BIT_COUNT && !BIT(irqs , m_top_pending); m_top_pending++) { - } - update_irl(); -} - -void hp85_state::update_irl() -{ - //LOG("irl GIE=%d top=%u ACK=%03x\n" , m_global_int_en , m_top_pending , m_int_acked); - m_cpu->set_input_line(0 , m_global_int_en && m_top_pending < IRQ_BIT_COUNT && !BIT(m_int_acked , m_top_pending)); -} - uint8_t hp85_state::get_prt_font(uint8_t ch , unsigned col) const { // Bit 7: pixel @ top @@ -1304,40 +1437,27 @@ INPUT_PORTS_END void hp85_state::cpu_mem_map(address_map &map) { - map.unmap_value_high(); - map(0x0000, 0x5fff).rom(); - map(0x6000, 0x7fff).m(m_rombank, FUNC(address_map_bank_device::amap8)); + hp80_base_state::cpu_mem_map(map); map(0x8000, 0xbfff).ram(); - map(0xff00, 0xff00).w(FUNC(hp85_state::ginten_w)); - map(0xff01, 0xff01).w(FUNC(hp85_state::gintdis_w)); - map(0xff02, 0xff02).rw(FUNC(hp85_state::keysts_r), FUNC(hp85_state::keysts_w)); - map(0xff03, 0xff03).rw(FUNC(hp85_state::keycod_r), FUNC(hp85_state::keycod_w)); map(0xff04, 0xff07).rw(FUNC(hp85_state::crtc_r), FUNC(hp85_state::crtc_w)); map(0xff08, 0xff09).rw("tape", FUNC(hp_1ma6_device::reg_r), FUNC(hp_1ma6_device::reg_w)); - map(0xff0a, 0xff0a).rw(FUNC(hp85_state::clksts_r), FUNC(hp85_state::clksts_w)); - map(0xff0b, 0xff0b).rw(FUNC(hp85_state::clkdat_r), FUNC(hp85_state::clkdat_w)); map(0xff0c, 0xff0c).w(FUNC(hp85_state::prtlen_w)); map(0xff0d, 0xff0d).rw(FUNC(hp85_state::prchar_r), FUNC(hp85_state::prchar_w)); map(0xff0e, 0xff0e).rw(FUNC(hp85_state::prtsts_r), FUNC(hp85_state::prtctl_w)); map(0xff0f, 0xff0f).w(FUNC(hp85_state::prtdat_w)); - map(0xff18, 0xff18).w(FUNC(hp85_state::rselec_w)); - map(0xff40, 0xff40).rw(FUNC(hp85_state::intrsc_r), FUNC(hp85_state::intrsc_w)); } -void hp85_state::rombank_mem_map(address_map &map) +void hp85_state::unmap_optroms(address_space &space) { - map.unmap_value_high(); - // ROM in bank 0 is always present (it's part of system ROMs) - map(0x0000, 0x1fff).rom(); + // OptROMs are in rombanks [01..FF] + space.unmap_read(HP80_OPTROM_SIZE * 1 , HP80_OPTROM_SIZE * 0x100 - 1); } void hp85_state::hp85(machine_config &config) { - HP_CAPRICORN(config, m_cpu, MASTER_CLOCK / 16); - m_cpu->set_addrmap(AS_PROGRAM, &hp85_state::cpu_mem_map); - m_cpu->set_irq_acknowledge_callback(FUNC(hp85_state::irq_callback)); + hp80_base(config); - ADDRESS_MAP_BANK(config, "rombank").set_map(&hp85_state::rombank_mem_map).set_options(ENDIANNESS_LITTLE, 8, 21, HP80_OPTROM_SIZE); + m_cpu->set_addrmap(AS_PROGRAM, &hp85_state::cpu_mem_map); SCREEN(config, m_screen, SCREEN_TYPE_RASTER); m_screen->set_raw(MASTER_CLOCK / 2 , 312 , 0 , 256 , 256 , 0 , 192); @@ -1346,46 +1466,11 @@ void hp85_state::hp85(machine_config &config) PALETTE(config, m_palette, palette_device::MONOCHROME); TIMER(config, m_vm_timer).configure_generic(FUNC(hp85_state::vm_timer)); - // No idea at all about the actual keyboard scan frequency - TIMER(config, "kb_timer").configure_periodic(FUNC(hp85_state::kb_scan), attotime::from_hz(100)); - - // Hw timers are updated at 1 kHz rate - TIMER(config, "hw_timer").configure_periodic(FUNC(hp85_state::timer_update), attotime::from_hz(1000)); - TIMER(config, m_clk_busy_timer).configure_generic(FUNC(hp85_state::clk_busy_timer)); TIMER(config, m_prt_busy_timer).configure_generic(FUNC(hp85_state::prt_busy_timer)); - // Beeper - SPEAKER(config, "mono").front_center(); - DAC_1BIT(config, m_dac , 0).add_route(ALL_OUTPUTS, "mono", 0.5, AUTO_ALLOC_INPUT, 0); - voltage_regulator_device &vref(VOLTAGE_REGULATOR(config, "vref")); - vref.add_route(0, "dac", 1.0, DAC_VREF_POS_INPUT); - BEEP(config, m_beep, MASTER_CLOCK / 8192).add_route(ALL_OUTPUTS, "mono", 0.5, AUTO_ALLOC_INPUT, 0); - // Tape drive HP_1MA6(config, "tape", 0); - // Optional ROMs - HP80_OPTROM(config, m_rom_drawers[0]); - HP80_OPTROM(config, m_rom_drawers[1]); - HP80_OPTROM(config, m_rom_drawers[2]); - HP80_OPTROM(config, m_rom_drawers[3]); - HP80_OPTROM(config, m_rom_drawers[4]); - HP80_OPTROM(config, m_rom_drawers[5]); - - // I/O slots - HP80_IO_SLOT(config, m_io_slots[0]).set_slot_no(0); - m_io_slots[0]->irl_cb().set(FUNC(hp85_state::irl_w)); - m_io_slots[0]->halt_cb().set(FUNC(hp85_state::halt_w)); - HP80_IO_SLOT(config, m_io_slots[1]).set_slot_no(1); - m_io_slots[1]->irl_cb().set(FUNC(hp85_state::irl_w)); - m_io_slots[1]->halt_cb().set(FUNC(hp85_state::halt_w)); - HP80_IO_SLOT(config, m_io_slots[2]).set_slot_no(2); - m_io_slots[2]->irl_cb().set(FUNC(hp85_state::irl_w)); - m_io_slots[2]->halt_cb().set(FUNC(hp85_state::halt_w)); - HP80_IO_SLOT(config, m_io_slots[3]).set_slot_no(3); - m_io_slots[3]->irl_cb().set(FUNC(hp85_state::irl_w)); - m_io_slots[3]->halt_cb().set(FUNC(hp85_state::halt_w)); - // Printer output BITBANGER(config, m_prt_graph_out, 0); BITBANGER(config, m_prt_alpha_out, 0); @@ -1409,4 +1494,632 @@ ROM_START(hp85) ROM_LOAD("prt_chrgen.bin" , 0 , 0x400 , CRC(abeaba27) SHA1(fbf6bdd5d96df6aa5963f8cdfdeb180402b1cc85)) ROM_END +// ************ +// hp86_state +// ************ +class hp86_state : public hp80_base_state +{ +public: + hp86_state(const machine_config &mconfig, device_type type, const char *tag); + + // **** Constants of HP86 **** + static constexpr unsigned MASTER_CLOCK = 12260000; + static constexpr unsigned VIDEO_MEM_SIZE = 16384; + static constexpr uint16_t VIDEO_ADDR_MASK = VIDEO_MEM_SIZE - 1; + static constexpr uint16_t VIDEO_ALPHA_N_END = 0x10e0; + static constexpr uint16_t VIDEO_ALPHA_A_END = 0x3fc0; + static constexpr uint16_t VIDEO_GRAPH_START = 0x10e0; + // Time to read/write a byte in video memory (in master clock cycles) TBC + static constexpr unsigned CRT_RW_TIME = 24; + // Duration of on/off states of run light (ms) + static constexpr unsigned RULITE_ON_MS = 373; + static constexpr unsigned RULITE_OFF_MS = 187; + + void hp86(machine_config &config); + +protected: + virtual void machine_start() override; + virtual void machine_reset() override; + + virtual void cpu_mem_map(address_map &map) override; + virtual void rombank_mem_map(address_map &map) override; + virtual void unmap_optroms(address_space &space) override; + +private: + uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect); + DECLARE_WRITE_LINE_MEMBER(vblank_w); + attotime time_to_video_mem_availability() const; + + required_device m_screen; + required_device m_palette; + required_device m_vm_timer; + required_device m_ram; + output_finder<> m_run_light; + required_device m_rulite_timer; + + // Character generator + required_region_ptr m_chargen; + + // Video + bitmap_rgb32 m_bitmap; + std::unique_ptr m_video_mem; + uint16_t m_crt_sad; + uint16_t m_crt_bad; + uint8_t m_crt_sts; + uint8_t m_crt_byte; + bool m_crt_rdrq; + + // Extended RAM access + uint32_t m_emc_ptr1; // PTR1 (24 bits) + uint32_t m_emc_ptr2; // PTR2 (24 bits) + uint8_t m_emc_disp; // Displacement (3 bits) + bool m_emc_mult; // Multibyte access + uint8_t m_emc_mode; // Mode (3 bits) + enum { + EMC_IDLE, + EMC_INDIRECT_1, + EMC_INDIRECT_2 + }; + int m_emc_state; // EMC indirect access state + bool m_lmard; // LMARD cycles in progress + + // Run light + bool m_rulite; + + DECLARE_WRITE8_MEMBER(crtsad_w); + DECLARE_WRITE8_MEMBER(crtbad_w); + DECLARE_READ8_MEMBER(crtsts_r); + DECLARE_WRITE8_MEMBER(crtsts_w); + DECLARE_READ8_MEMBER(crtdat_r); + DECLARE_WRITE8_MEMBER(crtdat_w); + TIMER_DEVICE_CALLBACK_MEMBER(vm_timer); + uint16_t get_video_limit() const; + DECLARE_WRITE8_MEMBER(rulite_w); + TIMER_DEVICE_CALLBACK_MEMBER(rulite_timer); + DECLARE_READ8_MEMBER(direct_ram_r); + DECLARE_WRITE8_MEMBER(direct_ram_w); + DECLARE_READ8_MEMBER(emc_r); + DECLARE_WRITE8_MEMBER(emc_w); + uint32_t& get_ptr(); + void ptr12_decrement(); + DECLARE_WRITE_LINE_MEMBER(lma_cycle); + void opcode_cb(uint8_t opcode); +}; + +hp86_state::hp86_state(const machine_config &mconfig, device_type type, const char *tag) + : hp80_base_state(mconfig , type , tag) + , m_screen(*this , "screen") + , m_palette(*this , "palette") + , m_vm_timer(*this , "vm_timer") + , m_ram(*this , "ram") + , m_run_light(*this , "run_light") + , m_rulite_timer(*this , "rulite_timer") + , m_chargen(*this , "chargen") +{ +} + +void hp86_state::cpu_mem_map(address_map &map) +{ + hp80_base_state::cpu_mem_map(map); + map(0x8000 , 0xfeff).rw(FUNC(hp86_state::direct_ram_r) , FUNC(hp86_state::direct_ram_w)); + map(0xffc0 , 0xffc0).w(FUNC(hp86_state::crtsad_w)); + map(0xffc1 , 0xffc1).w(FUNC(hp86_state::crtbad_w)); + map(0xffc2 , 0xffc2).rw(FUNC(hp86_state::crtsts_r) , FUNC(hp86_state::crtsts_w)); + map(0xffc3 , 0xffc3).rw(FUNC(hp86_state::crtdat_r) , FUNC(hp86_state::crtdat_w)); + map(0xffc4 , 0xffc4).w(FUNC(hp86_state::rulite_w)); + map(0xffc8 , 0xffcf).rw(FUNC(hp86_state::emc_r) , FUNC(hp86_state::emc_w)); +} + +void hp86_state::rombank_mem_map(address_map &map) +{ + hp80_base_state::rombank_mem_map(map); + // rom001 (graphics) + map(0x2000, 0x3fff).rom(); + // rom320 (mass memory) + // rom321 (electronic disk) + map(0x1a0000 , 0x1a3fff).rom(); +} + +void hp86_state::unmap_optroms(address_space &space) +{ + // OptROMs are in rombanks [02..CF] & [D2..FF] + space.unmap_read(HP80_OPTROM_SIZE * 2 , HP80_OPTROM_SIZE * 0xd0 - 1); + space.unmap_read(HP80_OPTROM_SIZE * 0xd2 , HP80_OPTROM_SIZE * 0x100 - 1); +} + +void hp86_state::hp86(machine_config &config) +{ + hp80_base(config); + + m_cpu->opcode_cb().set(FUNC(hp86_state::opcode_cb)); + m_cpu->lma_cb().set(FUNC(hp86_state::lma_cycle)); + + RAM(config , m_ram).set_default_size("128K"); + + SCREEN(config, m_screen, SCREEN_TYPE_RASTER); + m_screen->set_raw(MASTER_CLOCK , 784 , 0 , 640 , 261 , 0 , 240); + m_screen->set_screen_update(FUNC(hp86_state::screen_update)); + m_screen->screen_vblank().set(FUNC(hp86_state::vblank_w)); + PALETTE(config, m_palette, palette_device::MONOCHROME); + + config.set_default_layout(layout_hp86b); + + TIMER(config, m_vm_timer).configure_generic(FUNC(hp86_state::vm_timer)); + TIMER(config, m_rulite_timer).configure_generic(FUNC(hp86_state::rulite_timer)); + + m_io_slots[ 0 ]->option_set("hpib" , HP82937_IO_CARD); + + SOFTWARE_LIST(config, "optrom_list").set_original("hp86_rom"); +} + +void hp86_state::machine_start() +{ + m_run_light.resolve(); + + m_screen->register_screen_bitmap(m_bitmap); + m_video_mem = std::make_unique(VIDEO_MEM_SIZE); + + save_pointer(NAME(m_video_mem) , VIDEO_MEM_SIZE); + save_item(NAME(m_crt_sad)); + save_item(NAME(m_crt_bad)); + save_item(NAME(m_emc_ptr1)); + save_item(NAME(m_emc_ptr2)); + save_item(NAME(m_emc_disp)); + save_item(NAME(m_emc_mult)); + save_item(NAME(m_emc_mode)); + save_item(NAME(m_rulite)); +} + +void hp86_state::machine_reset() +{ + hp80_base_state::machine_reset(); + + m_crt_sad = 0; + m_crt_sts = 0x06; + m_crt_rdrq = false; + m_emc_state = EMC_IDLE; + m_rulite = true; + m_run_light = true; + m_rulite_timer->reset(); +} + +uint32_t hp86_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect) +{ + copybitmap(bitmap, m_bitmap, 0, 0, 0, 0, cliprect); + return 0; +} + +WRITE_LINE_MEMBER(hp86_state::vblank_w) +{ + COPY_BIT(state , m_crt_sts , 4); + if (state) { + if (m_crt_sts & 0x06) { + // Blank display + m_bitmap.fill(rgb_t::black()); + } else { + // Load palette for normal or inverse video + if (BIT(m_crt_sts , 5)) { + m_palette->set_pen_color(0 , rgb_t::green()); + m_palette->set_pen_color(1 , rgb_t::black()); + } else { + m_palette->set_pen_color(1 , rgb_t::green()); + m_palette->set_pen_color(0 , rgb_t::black()); + } + uint16_t limit = get_video_limit(); + if (BIT(m_crt_sts , 7)) { + uint16_t video_ptr = VIDEO_GRAPH_START; + unsigned dots_per_line; + unsigned offset; + if (BIT(m_crt_sts , 6)) { + // GRAPH ALL mode + dots_per_line = 544; + offset = 48; + } else { + // GRAPH NORMAL mode + dots_per_line = 400; + offset = 120; + } + // Fill black bars on either side of the display + m_bitmap.fill(m_palette->pen(0) , rectangle(0 , offset - 1 , 0 , 239)); + m_bitmap.fill(m_palette->pen(0) , rectangle(640 - offset , 639 , 0 , 239)); + + for (unsigned y = 0; y < 240; y++) { + for (unsigned x = offset; x < (dots_per_line + offset); x += 8) { + uint8_t pixels = m_video_mem[ video_ptr ]; + if (++video_ptr >= limit) { + video_ptr = 0; + } + m_bitmap.pix32(y , x) = m_palette->pen(BIT(pixels , 7)); + m_bitmap.pix32(y , x + 1) = m_palette->pen(BIT(pixels , 6)); + m_bitmap.pix32(y , x + 2) = m_palette->pen(BIT(pixels , 5)); + m_bitmap.pix32(y , x + 3) = m_palette->pen(BIT(pixels , 4)); + m_bitmap.pix32(y , x + 4) = m_palette->pen(BIT(pixels , 3)); + m_bitmap.pix32(y , x + 5) = m_palette->pen(BIT(pixels , 2)); + m_bitmap.pix32(y , x + 6) = m_palette->pen(BIT(pixels , 1)); + m_bitmap.pix32(y , x + 7) = m_palette->pen(BIT(pixels , 0)); + } + } + } else { + unsigned rows; + unsigned lines_per_row; + if (BIT(m_crt_sts , 3)) { + // 24 rows + rows = 24; + lines_per_row = 10; + } else { + // 16 rows + rows = 16; + lines_per_row = 15; + } + uint16_t video_ptr = m_crt_sad; + for (unsigned row = 0; row < rows; row++) { + for (unsigned col = 0; col < 640; col += 8) { + uint8_t ch = m_video_mem[ video_ptr ]; + if (++video_ptr >= limit) { + video_ptr = 0; + } + for (unsigned sub_row = 0; sub_row < lines_per_row; sub_row++) { + uint8_t pixels; + if (sub_row < 10) { + pixels = m_chargen[ (ch & 0x7f) * 10 + sub_row ]; + } else { + pixels = 0; + } + if (BIT(ch , 7)) { + pixels = ~pixels; + } + unsigned y = row * lines_per_row + sub_row; + m_bitmap.pix32(y , col) = m_palette->pen(BIT(pixels , 7)); + m_bitmap.pix32(y , col + 1) = m_palette->pen(BIT(pixels , 6)); + m_bitmap.pix32(y , col + 2) = m_palette->pen(BIT(pixels , 5)); + m_bitmap.pix32(y , col + 3) = m_palette->pen(BIT(pixels , 4)); + m_bitmap.pix32(y , col + 4) = m_palette->pen(BIT(pixels , 3)); + m_bitmap.pix32(y , col + 5) = m_palette->pen(BIT(pixels , 2)); + m_bitmap.pix32(y , col + 6) = m_palette->pen(BIT(pixels , 1)); + m_bitmap.pix32(y , col + 7) = m_palette->pen(BIT(pixels , 0)); + } + } + } + } + } + } +} + +attotime hp86_state::time_to_video_mem_availability() const +{ + if ((m_crt_sts & 0x06) != 0 || m_screen->vblank() || m_screen->hblank()) { + // Blank video or vertical/horizontal retrace: immediate access + return attotime::zero; + } else { + // In the active part, wait until next retrace + return m_screen->time_until_pos(m_screen->vpos() , 640); + } +} + +WRITE8_MEMBER(hp86_state::crtsad_w) +{ + auto burst_idx = m_cpu->flatten_burst(); + if (burst_idx == 0) { + m_crt_sad = (m_crt_sad & 0xff00) | data; + } else if (burst_idx == 1) { + m_crt_sad = (uint16_t(data) << 8) | (m_crt_sad & 0xff); + m_crt_sad &= VIDEO_ADDR_MASK; + } +} + +WRITE8_MEMBER(hp86_state::crtbad_w) +{ + auto burst_idx = m_cpu->flatten_burst(); + if (burst_idx == 0) { + m_crt_bad = (m_crt_bad & 0xff00) | data; + } else if (burst_idx == 1) { + m_crt_bad = (uint16_t(data) << 8) | (m_crt_bad & 0xff); + m_crt_bad &= VIDEO_ADDR_MASK; + } +} + +READ8_MEMBER(hp86_state::crtsts_r) +{ + return m_crt_sts; +} + +WRITE8_MEMBER(hp86_state::crtsts_w) +{ + m_crt_sts = (m_crt_sts & 0x11) | (data & ~0x11); + if (BIT(data , 0)) { + // Read request + BIT_SET(m_crt_sts , 0); + m_crt_rdrq = true; + attotime vm_av = time_to_video_mem_availability(); + m_vm_timer->adjust(vm_av + attotime::from_ticks(CRT_RW_TIME , MASTER_CLOCK)); + } +} + +READ8_MEMBER(hp86_state::crtdat_r) +{ + return m_crt_byte; +} + +WRITE8_MEMBER(hp86_state::crtdat_w) +{ + m_crt_byte = data; + BIT_SET(m_crt_sts , 0); + m_crt_rdrq = false; + attotime vm_av = time_to_video_mem_availability(); + m_vm_timer->adjust(vm_av + attotime::from_ticks(CRT_RW_TIME , MASTER_CLOCK)); +} + +TIMER_DEVICE_CALLBACK_MEMBER(hp86_state::vm_timer) +{ + if (m_crt_rdrq) { + m_crt_rdrq = false; + m_crt_byte = m_video_mem[ m_crt_bad ]; + } else { + m_video_mem[ m_crt_bad ] = m_crt_byte; + } + BIT_CLR(m_crt_sts , 0); + if (++m_crt_bad >= get_video_limit()) { + m_crt_bad = 0; + } +} + +uint16_t hp86_state::get_video_limit() const +{ + if (BIT(m_crt_sts , 7)) { + // Graphic mode + return VIDEO_MEM_SIZE; + } else if (BIT(m_crt_sts , 6)) { + // ALPHA ALL mode + return VIDEO_ALPHA_A_END; + } else { + // ALPHA NORMAL mode + return VIDEO_ALPHA_N_END; + } +} + +WRITE8_MEMBER(hp86_state::rulite_w) +{ + bool new_rulite = !BIT(data , 0); + + if (m_rulite && !new_rulite) { + m_run_light = false; + m_rulite_timer->adjust(attotime::from_msec(RULITE_OFF_MS)); + } else if (!m_rulite && new_rulite) { + m_run_light = true; + m_rulite_timer->reset(); + } + m_rulite = new_rulite; +} + +TIMER_DEVICE_CALLBACK_MEMBER(hp86_state::rulite_timer) +{ + m_run_light = !m_run_light; + m_rulite_timer->adjust(attotime::from_msec(m_run_light ? RULITE_ON_MS : RULITE_OFF_MS)); +} + +READ8_MEMBER(hp86_state::direct_ram_r) +{ + return m_ram->read(offset); +} + +WRITE8_MEMBER(hp86_state::direct_ram_w) +{ + m_ram->write(offset , data); +} + +READ8_MEMBER(hp86_state::emc_r) +{ + auto idx = m_cpu->flatten_burst(); + uint8_t res = 0xff; + + if (m_emc_state == EMC_INDIRECT_2) { + uint32_t& ptr = get_ptr(); + if (ptr >= 0x8000 && (ptr - 0x8000) < m_ram->size()) { + res = m_ram->read(ptr - 0x8000); + } + LOG_EMC("EMC r @%06x=%02x\n" , ptr , res); + ptr++; + } else if (m_lmard) { + m_emc_mode = uint8_t(offset); + // During a LMARD pair, address 0xffc8 is returned to CPU and indirect mode is activated + if (idx == 0) { + res = 0xc8; + } else if (idx == 1) { + LOG_EMC("EMC access %u %06x\n" , m_emc_mode & 7 , get_ptr()); + m_emc_state = EMC_INDIRECT_1; + if (BIT(m_emc_mode , 0)) { + // Pre-decrement + ptr12_decrement(); + } + } + } else { + m_emc_mode = uint8_t(offset); + // Read PTRx + if (idx < 3) { + res = uint8_t(get_ptr() >> (8 * idx)); + } + } + return res; +} + +WRITE8_MEMBER(hp86_state::emc_w) +{ + auto idx = m_cpu->flatten_burst(); + + if (m_emc_state == EMC_INDIRECT_2) { + uint32_t& ptr = get_ptr(); + LOG_EMC("EMC w @%06x=%02x\n" , ptr , data); + if (ptr >= 0x8000 && (ptr - 0x8000) < m_ram->size()) { + m_ram->write(ptr - 0x8000 , data); + } + ptr++; + } else { + m_emc_mode = uint8_t(offset); + // Write PTRx + if (idx < 3) { + uint32_t& ptr = get_ptr(); + uint32_t mask = 0xffU << (8 * idx); + ptr = (ptr & ~mask) | (uint32_t(data) << (8 * idx)); + } + } +} + +uint32_t& hp86_state::get_ptr() +{ + return BIT(m_emc_mode , 2) ? m_emc_ptr2 : m_emc_ptr1; +} + +void hp86_state::ptr12_decrement() +{ + if (m_emc_mult) { + get_ptr() -= m_emc_disp; + } else { + get_ptr()--; + } +} + +WRITE_LINE_MEMBER(hp86_state::lma_cycle) +{ + m_lmard = state; + if (m_emc_state == EMC_INDIRECT_1) { + m_emc_state = EMC_INDIRECT_2; + } else if (m_emc_state == EMC_INDIRECT_2) { + LOG_EMC("EMC close %u %06x\n" , m_emc_mode & 7 , get_ptr()); + if (!BIT(m_emc_mode , 1)) { + // In PTRx & PTRx- cases, bring the PTR back to start + ptr12_decrement(); + } + m_emc_state = EMC_IDLE; + } +} + +void hp86_state::opcode_cb(uint8_t opcode) +{ + // Intercept DRP instructions & load displacement + if ((opcode & 0xc0) == 0x40) { + if (BIT(opcode , 5)) { + m_emc_disp = 8 - (opcode & 7); + } else { + m_emc_disp = 2 - (opcode & 1); + } + } + + m_emc_mult = BIT(opcode , 0); +} + +static INPUT_PORTS_START(hp86) + // Keyboard is arranged in a matrix of 10 rows and 8 columns. In addition there are 3 keys with + // dedicated input lines: SHIFT, SHIFT LOCK & CONTROL. + // A key on row "r"=[0..9] and column "c"=[0..7] is mapped to bit "b" of KEY"n" input, where + // n = r / 4 + // b = (r % 4) * 8 + c + PORT_START("KEY0") + PORT_BIT(IOP_MASK(0) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F6) PORT_CHAR(UCHAR_MAMEKEY(F6)) PORT_NAME("k6 k13") // 0,0: k6 / k13 + PORT_BIT(IOP_MASK(1) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5)) PORT_NAME("k5 k12") // 0,1: k5 / k12 + PORT_BIT(IOP_MASK(2) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4)) PORT_NAME("k4 k11") // 0,2: k4 / k11 + PORT_BIT(IOP_MASK(3) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3)) PORT_NAME("k3 k10") // 0,3: k3 / k10 + PORT_BIT(IOP_MASK(4) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2)) PORT_NAME("k2 k9") // 0,4: k2 / k9 + PORT_BIT(IOP_MASK(5) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1)) PORT_NAME("k1 k8") // 0,5: k1 / k8 + PORT_BIT(IOP_MASK(6) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("LABEL KEY") // 0,6: LABEL KEY + PORT_BIT(IOP_MASK(7) , IP_ACTIVE_HIGH , IPT_UNUSED) // 0,7: N/U + PORT_BIT(IOP_MASK(8) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('*') // 1,0: 8 + PORT_BIT(IOP_MASK(9) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('&') // 1,1: 7 + PORT_BIT(IOP_MASK(10) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('^') // 1,2: 6 + PORT_BIT(IOP_MASK(11) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%') // 1,3: 5 + PORT_BIT(IOP_MASK(12) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$') // 1,4: 4 + PORT_BIT(IOP_MASK(13) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#') // 1,5: 3 + PORT_BIT(IOP_MASK(14) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('@') // 1,6: 2 + PORT_BIT(IOP_MASK(15) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!') // 1,7: 1 + PORT_BIT(IOP_MASK(16) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I') // 2,0: I + PORT_BIT(IOP_MASK(17) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U') // 2,1: U + PORT_BIT(IOP_MASK(18) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y') // 2,2: Y + PORT_BIT(IOP_MASK(19) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T') // 2,3: T + PORT_BIT(IOP_MASK(20) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R') // 2,4: R + PORT_BIT(IOP_MASK(21) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E') // 2,5: E + PORT_BIT(IOP_MASK(22) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W') // 2,6: W + PORT_BIT(IOP_MASK(23) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q') // 2,7: Q + PORT_BIT(IOP_MASK(24) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K') // 3,0: K + PORT_BIT(IOP_MASK(25) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J') // 3,1: J + PORT_BIT(IOP_MASK(26) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H') // 3,2: H + PORT_BIT(IOP_MASK(27) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G') // 3,3: G + PORT_BIT(IOP_MASK(28) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F') // 3,4: F + PORT_BIT(IOP_MASK(29) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D') // 3,5: D + PORT_BIT(IOP_MASK(30) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S') // 3,6: S + PORT_BIT(IOP_MASK(31) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A') // 3,7: A + + PORT_START("KEY1") + PORT_BIT(IOP_MASK(0) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M') // 4,0: M + PORT_BIT(IOP_MASK(1) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N') // 4,1: N + PORT_BIT(IOP_MASK(2) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B') // 4,2: B + PORT_BIT(IOP_MASK(3) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V') // 4,3: V + PORT_BIT(IOP_MASK(4) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C') // 4,4: C + PORT_BIT(IOP_MASK(5) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X') // 4,5: X + PORT_BIT(IOP_MASK(6) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z') // 4,6: Z + PORT_BIT(IOP_MASK(7) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ') // 4,7: Space + PORT_BIT(IOP_MASK(8) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<') // 5,0: , + PORT_BIT(IOP_MASK(9) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>') // 5,1: . + PORT_BIT(IOP_MASK(10) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?') // 5,2: / ? + PORT_BIT(IOP_MASK(11) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("PAUSE STEP") // 5,3: PAUSE / STEP + PORT_BIT(IOP_MASK(12) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("RUN") // 5,4: RUN + PORT_BIT(IOP_MASK(13) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_PLUS_PAD) PORT_CHAR(UCHAR_MAMEKEY(PLUS_PAD)) PORT_NAME("KP +") // 5,5: KP + + PORT_BIT(IOP_MASK(14) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS_PAD) PORT_CHAR(UCHAR_MAMEKEY(MINUS_PAD)) PORT_NAME("KP -") // 5,6: KP - + PORT_BIT(IOP_MASK(15) , IP_ACTIVE_HIGH , IPT_UNUSED) // 5,7: N/U + PORT_BIT(IOP_MASK(16) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L') // 6,0: L + PORT_BIT(IOP_MASK(17) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR(':') // 6,1: ; + PORT_BIT(IOP_MASK(18) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('\'') PORT_CHAR('"') // 6,2: ' " + PORT_BIT(IOP_MASK(19) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13) PORT_NAME("END LINE") // 6,3: END LINE + PORT_BIT(IOP_MASK(20) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("LIST P LST") // 6,4: LIST / P LST + PORT_BIT(IOP_MASK(21) , IP_ACTIVE_HIGH , IPT_UNUSED) // 6,5: N/U + PORT_BIT(IOP_MASK(22) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_ASTERISK) PORT_CHAR(UCHAR_MAMEKEY(ASTERISK)) PORT_NAME("KP *") // 6,6: KP * + PORT_BIT(IOP_MASK(23) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH_PAD) PORT_CHAR(UCHAR_MAMEKEY(SLASH_PAD)) PORT_NAME("KP /") // 6,7: KP / + PORT_BIT(IOP_MASK(24) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O') // 7,0: O + PORT_BIT(IOP_MASK(25) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P') // 7,1: P + PORT_BIT(IOP_MASK(26) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('(') PORT_CHAR('[') // 7,2: ( [ + PORT_BIT(IOP_MASK(27) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(')') PORT_CHAR(']') // 7,3: ) ] + PORT_BIT(IOP_MASK(28) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("CONT TR/NORM") // 7,4: CONT / TR/NORM + PORT_BIT(IOP_MASK(29) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("E TEST") // 7,5: KP E / TEST + PORT_BIT(IOP_MASK(30) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME(") INIT") // 7,6: KP ) / INIT + PORT_BIT(IOP_MASK(31) , IP_ACTIVE_HIGH , IPT_UNUSED) // 7,7: N/U + + PORT_START("KEY2") + PORT_BIT(IOP_MASK(0) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR('(') // 8,0: 9 + PORT_BIT(IOP_MASK(1) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR(')') // 8,1: 0 + PORT_BIT(IOP_MASK(2) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('_') // 8,2: - _ + PORT_BIT(IOP_MASK(3) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=') PORT_CHAR('+') // 8,3: = + + PORT_BIT(IOP_MASK(4) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE) PORT_CHAR('\\') PORT_CHAR('|') // 8,4: \ | + PORT_BIT(IOP_MASK(5) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8) // 8,5: BS + PORT_BIT(IOP_MASK(6) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("( RESET") // 8,6: KP ( / RESET + PORT_BIT(IOP_MASK(7) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("^ RESLT") // 8,7: KP ^ / RESLT + PORT_BIT(IOP_MASK(8) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F7) PORT_CHAR(UCHAR_MAMEKEY(F7)) PORT_NAME("k7 k14") // 9,0: k7 / k14 + PORT_BIT(IOP_MASK(9) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("-LINE CLEAR") // 9,1: -LINE / CLEAR + PORT_BIT(IOP_MASK(10) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP)) PORT_NAME("Up Home") // 9,2: Up / Home + PORT_BIT(IOP_MASK(11) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN)) PORT_NAME("Down A/G") // 9,3: Down / A/G + PORT_BIT(IOP_MASK(12) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT)) PORT_NAME("Left I/R") // 9,4: LEFT / I/R + PORT_BIT(IOP_MASK(13) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) PORT_NAME("Right -CHAR") // 9,5: RIGHT / -CHAR + PORT_BIT(IOP_MASK(14) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_PGDN) PORT_NAME("ROLL") // 9,6: ROLL + PORT_BIT(IOP_MASK(15) , IP_ACTIVE_HIGH , IPT_UNUSED) // 9,7: n/u + + PORT_START("MODKEYS") + PORT_BIT(IOP_MASK(0) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1) // Shift + PORT_BIT(IOP_MASK(1) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE PORT_NAME("Shift lock") // Shift lock + PORT_BIT(IOP_MASK(2) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_SHIFT_2) // Control +INPUT_PORTS_END + +ROM_START(hp86b) + ROM_REGION(0x6000 , "cpu" , 0) + ROM_LOAD("romsys1.bin" , 0x0000 , 0x2000 , CRC(bfa473b8) SHA1(cc420742a5f03c466484a5063e0abcbc084bf298)) + ROM_LOAD("romsys2.bin" , 0x2000 , 0x2000 , CRC(2bc3ba4b) SHA1(760bef9c482f562677f80b18d6163a19ee7aea1c)) + ROM_LOAD("romsys3.bin" , 0x4000 , 0x2000 , CRC(86bf3b8b) SHA1(209c91b9b972ab514c600752e2e4af68f984612e)) + + ROM_REGION(0x1a4000 , "rombank" , 0) + ROM_LOAD("rom000.bin" , 0x0000 , 0x2000 , CRC(c3ca5c54) SHA1(2b291607de101c7206bfae9520a18f1009929e9b)) + ROM_LOAD("rom001.bin" , 0x2000 , 0x2000 , CRC(59a1616c) SHA1(e0fe840f9740bdb455fe1872869671f8712b7cff)) + ROM_LOAD("rom320.bin" , 0x1a0000 , 0x2000 , CRC(c921e2e4) SHA1(e37ac61364830cfa214e6d1b9942cc1cde6ad01f)) + ROM_LOAD("rom321.bin" , 0x1a2000 , 0x2000 , CRC(e6e5cc91) SHA1(67711de228cc48a78d04b13f0a1c91dc26f7e87c)) + + ROM_REGION(0x500 , "chargen" , 0) + ROM_LOAD("chrgen.bin" , 0 , 0x500 , CRC(e90fad22) SHA1(6b2ecef96906ead99cd688e54c507611747c8687)) +ROM_END + COMP( 1980, hp85, 0, 0, hp85, hp85, hp85_state, empty_init, "HP", "HP 85", 0) +COMP( 1983, hp86b,0, 0, hp86, hp86, hp86_state, empty_init, "HP", "HP 86B",0) diff --git a/src/mame/layout/hp86b.lay b/src/mame/layout/hp86b.lay new file mode 100644 index 00000000000..c79f54047e6 --- /dev/null +++ b/src/mame/layout/hp86b.lay @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/mame/mame.lst b/src/mame/mame.lst index e9e228ff56d..4f8bad5677f 100644 --- a/src/mame/mame.lst +++ b/src/mame/mame.lst @@ -16161,6 +16161,7 @@ hpz80unk // @source:hp80.cpp hp85 // +hp86b // @source:hshavoc.cpp hshavoc // (c) 1993 Data East