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