summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--src/mame/hp/hp98x6.cpp1594
-rw-r--r--src/mame/hp/hp98x6_upi.cpp48
-rw-r--r--src/mame/hp/hp98x6_upi.h3
-rw-r--r--src/mame/mame.lst3
4 files changed, 1492 insertions, 156 deletions
diff --git a/src/mame/hp/hp98x6.cpp b/src/mame/hp/hp98x6.cpp
index 2ff1cac4c96..a066eacd3e1 100644
--- a/src/mame/hp/hp98x6.cpp
+++ b/src/mame/hp/hp98x6.cpp
@@ -5,24 +5,45 @@
// Driver for HP 98x6 systems
// **************************
//
-// What's in for 9816:
-// - 8MHz M68000 CPU
-// - Boot ROM
-// - Configurable amount of RAM
-// - Text video with attributes
-// - Graphic video
-// - HPIB interface
-// - RS232 interface
-// - HLE of 8041 UPI
-// - Large keyboard (98203B)
-// - Knob
-// - Beeper
-// - ID PROM
+// What's in the emulated systems:
+//
+// | Model | 9 | 9 | 9 | 9 |
+// | | 8 | 8 | 8 | 8 |
+// | | 1 | 2 | 3 | 3 |
+// | | 6 | 6 | 6 | 6 |
+// | Feature | A | A | A | C |
+// |--------------------------------------+---+---+---+---|
+// | 8MHz M68000 CPU | * | * | * | * |
+// | Boot ROM | * | * | * | * |
+// | Variable size RAM | * | * | * | * |
+// | HPIB interface | * | * | * | * |
+// | HLE of 8041 UPI | * | * | * | * |
+// | Large keyboard (98203B) | * | * | * | * |
+// | Knob | * | * | * | * |
+// | Beeper | * | * | * | * |
+// | ID PROM | * | * | * | * |
+// | B/W 80x25 text video w/ attributes | * | | * | |
+// | B/W 50x25 text video w/ attributes | | * | | |
+// | B/W 400x300 graphic video | * | * | | |
+// | B/W 512x390 graphic video | | | * | |
+// | Color 80x25 text video w/ attributes | | | | * |
+// | Color 512x390 graphic video | | | | * |
+// | Correct char. generator | * | * | N | N |
+// | 5.25 floppy drives | | 1 | 2 | 2 |
+// | RS232 interface | * | | | |
+//
+// What's not in for 9836A/C models:
+// - Correct character generator
+//
+// What's not in for all the models:
+// - Expansion cards
+// - Option ROMs
//
// Main references:
// - Olivier De Smet's standalone emulator:
// https://sites.google.com/site/olivier2smet2/hp_projects/hp98x6
// - Tony Duell's reverse engineered schematics (available @ hpmuseum.net)
+// - HP 98615-90074, Feb 85, Pascal 3.0 System's Designer Guide (available @ hpmuseum.net)
#include "emu.h"
@@ -31,15 +52,24 @@
#include "bus/ieee488/ieee488.h"
#include "bus/rs232/rs232.h"
#include "cpu/m68000/m68000.h"
+#include "imagedev/floppy.h"
+#include "machine/74123.h"
#include "machine/ins8250.h"
#include "machine/ram.h"
+#include "machine/rescap.h"
#include "machine/tms9914.h"
+#include "machine/wd_fdc.h"
#include "video/mc6845.h"
#include "emupal.h"
#include "screen.h"
// Debugging
+#define LOG_FDC_MASK (LOG_GENERAL << 1)
+#define LOG_FDC(...) LOGMASKED(LOG_FDC_MASK, __VA_ARGS__)
+#define LOG_FDC_DATA_MASK (LOG_FDC_MASK << 1)
+#define LOG_FDC_DATA(...) LOGMASKED(LOG_FDC_DATA_MASK, __VA_ARGS__)
+#undef VERBOSE
#define VERBOSE 0
#include "logmacro.h"
@@ -48,8 +78,14 @@ namespace {
// CPU clock
constexpr auto CPU_CLOCK = 16_MHz_XTAL / 2;
-// Video dot clock
-constexpr unsigned DOT_CLOCK = 19'988'000;
+// 9816: video dot clock
+constexpr unsigned DOT_CLOCK_9816 = 19'988'000;
+
+// 9826: video dot clock
+constexpr auto DOT_CLOCK_9826 = 10_MHz_XTAL;
+
+// 9836(c): video dot clock
+constexpr unsigned DOT_CLOCK_9836 = 25'771'500;
// HPIB and UPI clock
constexpr auto HPIB_CLOCK = 10_MHz_XTAL / 2;
@@ -73,13 +109,25 @@ template<typename T> void BIT_SET(T& w , unsigned n)
w |= BIT_MASK<T>(n);
}
-// +--------------+
-// | hp9816_state |
-// +--------------+
-class hp9816_state : public driver_device
+// +-------------------+
+// | hp98x6_base_state |
+// +-------------------+
+// ↑
+// +-------------------+
+// ↑ ↑
+// +-----------------+ +--------------+
+// | hp9826_36_state | | hp9816_state |
+// +-----------------+ +--------------+
+// ↑
+// +----------------+------------------+
+// ↑ ↑ ↑
+// +--------------+ +--------------+ +---------------+
+// | hp9826_state | | hp9836_state | | hp9836c_state |
+// +--------------+ +--------------+ +---------------+
+class hp98x6_base_state : public driver_device
{
-public:
- hp9816_state(const machine_config &mconfig, device_type type, const char *tag)
+protected:
+ hp98x6_base_state(const machine_config &mconfig, device_type type, const char *tag)
: driver_device(mconfig, type, tag)
, m_cpu(*this, "cpu")
, m_ram(*this, RAM_TAG)
@@ -88,17 +136,10 @@ public:
, m_screen(*this, "screen")
, m_upi(*this, "upi")
, m_hpib(*this, "hpib")
- , m_uart(*this, "uart")
- , m_rs232(*this, "rs232")
- , m_sw2_dipswitches(*this, "sw2")
- , m_sw3_dipswitches(*this, "sw3")
, m_chargen(*this, "chargen")
{
}
- void hp9816(machine_config &config);
-
-protected:
virtual void machine_start() override;
virtual void machine_reset() override;
@@ -109,78 +150,62 @@ protected:
required_device<screen_device> m_screen;
required_device<hp98x6_upi_device> m_upi;
required_device<tms9914_device> m_hpib;
- required_device<ins8250_device> m_uart;
- required_device<rs232_port_device> m_rs232;
- required_ioport m_sw2_dipswitches;
- required_ioport m_sw3_dipswitches;
// Character generator
required_region_ptr<uint8_t> m_chargen;
-private:
- static inline constexpr unsigned TEXT_VRAM_SIZE = 2048;
- static inline constexpr unsigned GRAPHIC_VRAM_SIZE = 16384;
-
- void cpu_mem_map(address_map &map);
+ void hp98x6_base(machine_config &mconfig, unsigned dot_clock, int char_width);
+ virtual void cpu_mem_map(address_map &map);
void diag_led_w(uint8_t data);
- uint16_t text_r(offs_t offset);
- void text_w(offs_t offset, uint16_t data, uint16_t mem_mask);
- uint16_t graphic_r(offs_t offset, uint16_t mem_mask);
- void graphic_w(offs_t offset, uint16_t data, uint16_t mem_mask);
- uint8_t hpib_r(offs_t offset);
- void hpib_w(offs_t offset, uint8_t data);
- void uart_reset();
- void uart_update_irq();
- uint8_t uart_r(offs_t offset);
- void uart_w(offs_t offset, uint8_t data);
-
- MC6845_UPDATE_ROW(crtc_update_row);
-
- void cpu_reset_w(int state);
+ virtual void cpu_reset_w(int state);
void hpib_irq_w(int state);
void upi_irq7_w(int state);
- void uart_irq_w(int state);
- uint16_t m_text_vram[TEXT_VRAM_SIZE];
- uint8_t m_graphic_vram[GRAPHIC_VRAM_SIZE];
bool m_hsync_en;
bool m_graphic_en;
bool m_hpib_irq;
bool m_hpib_dma_en;
bool m_upi_irq7;
- bool m_uart_int_en;
- bool m_uart_irq;
- bool m_uart_ocd3;
- bool m_uart_ocd4;
};
-void hp9816_state::hp9816(machine_config &config)
+void hp98x6_base_state::machine_start()
{
- M68000(config, m_cpu, CPU_CLOCK);
- m_cpu->set_addrmap(AS_PROGRAM, &hp9816_state::cpu_mem_map);
- m_cpu->reset_cb().set(FUNC(hp9816_state::cpu_reset_w));
+ save_item(NAME(m_hsync_en));
+ save_item(NAME(m_graphic_en));
+ save_item(NAME(m_hpib_irq));
+ save_item(NAME(m_hpib_dma_en));
+ save_item(NAME(m_upi_irq7));
- RAM(config, m_ram).set_default_size("256K").set_extra_options("512K,1M");
+ m_cpu->space(AS_PROGRAM).install_ram(0x1000000 - m_ram->size(), 0xffffff, m_ram->pointer());
+}
- SCREEN(config, m_screen, SCREEN_TYPE_RASTER, rgb_t::white());
- // Parameters for 50 Hz frame rate
- m_screen->set_raw(DOT_CLOCK, 1040, 0, 800, 384, 0, 300);
- m_screen->set_screen_update(m_crtc, FUNC(mc6845_device::screen_update));
+void hp98x6_base_state::machine_reset()
+{
+ m_hsync_en = false;
+ m_graphic_en = false;
+ m_hpib_dma_en = false;
+ diag_led_w(0);
+}
- PALETTE(config, m_palette, palette_device::MONOCHROME_HIGHLIGHT);
+void hp98x6_base_state::hp98x6_base(machine_config &config, unsigned dot_clock, int char_width)
+{
+ M68000(config, m_cpu, CPU_CLOCK);
+ m_cpu->set_addrmap(AS_PROGRAM, &hp98x6_base_state::cpu_mem_map);
+ m_cpu->reset_cb().set(FUNC(hp98x6_base_state::cpu_reset_w));
+
+ RAM(config, m_ram).set_default_size("256K").set_extra_options("512K,1M");
- MC6845(config, m_crtc, DOT_CLOCK / 10);
- m_crtc->set_char_width(10);
+ MC6845(config, m_crtc, dot_clock / char_width);
+ m_crtc->set_char_width(char_width);
m_crtc->set_screen(m_screen);
- m_crtc->set_update_row_callback(FUNC(hp9816_state::crtc_update_row));
m_crtc->set_show_border_area(false);
HP98X6_UPI(config, m_upi, HPIB_CLOCK);
m_upi->irq1_write_cb().set_inputline(m_cpu, M68K_IRQ_1);
- m_upi->irq7_write_cb().set(FUNC(hp9816_state::upi_irq7_w));
+ m_upi->irq7_write_cb().set(FUNC(hp98x6_base_state::upi_irq7_w));
TMS9914(config, m_hpib, HPIB_CLOCK);
- m_hpib->int_write_cb().set(FUNC(hp9816_state::hpib_irq_w));
+ m_hpib->int_write_cb().set(FUNC(hp98x6_base_state::hpib_irq_w));
m_hpib->dio_read_cb().set(IEEE488_TAG, FUNC(ieee488_device::dio_r));
m_hpib->dio_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_dio_w));
m_hpib->eoi_write_cb().set(IEEE488_TAG, FUNC(ieee488_device::host_eoi_w));
@@ -201,8 +226,130 @@ void hp9816_state::hp9816(machine_config &config)
ieee.srq_callback().set(m_hpib , FUNC(tms9914_device::srq_w));
ieee.atn_callback().set(m_hpib , FUNC(tms9914_device::atn_w));
ieee.ren_callback().set(m_hpib , FUNC(tms9914_device::ren_w));
+ IEEE488_SLOT(config, "ieee_dev", 0, hp_ieee488_devices, nullptr);
IEEE488_SLOT(config, "ieee_rem", 0, remote488_devices, nullptr);
- IEEE488_SLOT(config, "ieee0", 0, hp_ieee488_devices, nullptr);
+}
+
+void hp98x6_base_state::cpu_mem_map(address_map &map)
+{
+ map.unmap_value_high();
+ // Range DTACK Device
+ // ==============================
+ // 00'0000-00'ffff * Boot ROM
+ // 01'0000-01'ffff * Diagnostic LEDs (write only)
+ // 02'0000-3f'ffff * None
+ // fx'0000-ff'ffff * RAM
+ map(0x000000, 0x00ffff).rom().region("cpu", 0);
+ map(0x010000, 0x01ffff).w(FUNC(hp98x6_base_state::diag_led_w)).umask16(0x00ff);
+ map(0x020000, 0x3fffff).noprw();
+}
+
+void hp98x6_base_state::diag_led_w(uint8_t data)
+{
+ LOG("LEDS=%02x %c%c%c%c %c%c%c%c\n" ,
+ data,
+ BIT(data , 7) ? '.' : '*',
+ BIT(data , 6) ? '.' : '*',
+ BIT(data , 5) ? '.' : '*',
+ BIT(data , 4) ? '.' : '*',
+ BIT(data , 3) ? '.' : '*',
+ BIT(data , 2) ? '.' : '*',
+ BIT(data , 1) ? '.' : '*',
+ BIT(data , 0) ? '.' : '*');
+}
+
+void hp98x6_base_state::cpu_reset_w(int state)
+{
+ if (state) {
+ LOG("RESET from CPU\n");
+ m_upi->reset();
+ m_hpib->reset();
+ m_hpib_dma_en = false;
+ //m_crtc->reset();
+ m_hsync_en = false;
+ m_graphic_en = false;
+ }
+}
+
+void hp98x6_base_state::hpib_irq_w(int state)
+{
+ m_hpib_irq = bool(state);
+ m_cpu->set_input_line(M68K_IRQ_3, state);
+}
+
+void hp98x6_base_state::upi_irq7_w(int state)
+{
+ m_upi_irq7 = bool(state);
+ m_cpu->set_input_line(M68K_IRQ_7, state);
+}
+
+// +--------------+
+// | hp9816_state |
+// +--------------+
+class hp9816_state : public hp98x6_base_state
+{
+public:
+ hp9816_state(const machine_config &mconfig, device_type type, const char *tag)
+ : hp98x6_base_state(mconfig, type, tag)
+ , m_uart(*this, "uart")
+ , m_rs232(*this, "rs232")
+ , m_sw2_dipswitches(*this, "sw2")
+ , m_sw3_dipswitches(*this, "sw3")
+ {
+ }
+
+ void hp9816(machine_config &config);
+
+protected:
+ virtual void machine_start() override;
+ virtual void machine_reset() override;
+
+ required_device<ins8250_device> m_uart;
+ required_device<rs232_port_device> m_rs232;
+ required_ioport m_sw2_dipswitches;
+ required_ioport m_sw3_dipswitches;
+
+private:
+ static inline constexpr unsigned TEXT_VRAM_SIZE = 2048;
+ static inline constexpr unsigned GRAPHIC_VRAM_SIZE = 16384;
+
+ virtual void cpu_mem_map(address_map &map) override;
+ uint16_t text_r(offs_t offset);
+ void text_w(offs_t offset, uint16_t data, uint16_t mem_mask);
+ uint16_t graphic_r(offs_t offset, uint16_t mem_mask);
+ void graphic_w(offs_t offset, uint16_t data, uint16_t mem_mask);
+ uint8_t hpib_r(offs_t offset);
+ void hpib_w(offs_t offset, uint8_t data);
+ void uart_reset();
+ void uart_update_irq();
+ uint8_t uart_r(offs_t offset);
+ void uart_w(offs_t offset, uint8_t data);
+
+ MC6845_UPDATE_ROW(crtc_update_row);
+
+ virtual void cpu_reset_w(int state) override;
+ void uart_irq_w(int state);
+
+ uint16_t m_text_vram[TEXT_VRAM_SIZE];
+ uint8_t m_graphic_vram[GRAPHIC_VRAM_SIZE];
+ bool m_uart_int_en;
+ bool m_uart_irq;
+ bool m_uart_ocd3;
+ bool m_uart_ocd4;
+};
+
+void hp9816_state::hp9816(machine_config &config)
+{
+ hp98x6_base_state::hp98x6_base(config, DOT_CLOCK_9816, 10);
+
+ SCREEN(config, m_screen, SCREEN_TYPE_RASTER, rgb_t::white());
+ // Parameters for 50 Hz frame rate
+ m_screen->set_raw(DOT_CLOCK_9816, 1040, 0, 800, 384, 0, 300);
+ m_screen->set_screen_update(m_crtc, FUNC(mc6845_device::screen_update));
+
+ PALETTE(config, m_palette, palette_device::MONOCHROME_HIGHLIGHT);
+
+ m_crtc->set_update_row_callback(FUNC(hp9816_state::crtc_update_row));
INS8250(config, m_uart, UART_CLOCK);
m_uart->out_int_callback().set(FUNC(hp9816_state::uart_irq_w));
@@ -222,35 +369,27 @@ void hp9816_state::hp9816(machine_config &config)
void hp9816_state::machine_start()
{
+ hp98x6_base_state::machine_start();
+
save_item(NAME(m_text_vram));
save_item(NAME(m_graphic_vram));
- save_item(NAME(m_hsync_en));
- save_item(NAME(m_graphic_en));
- save_item(NAME(m_hpib_dma_en));
save_item(NAME(m_uart_int_en));
save_item(NAME(m_uart_ocd3));
save_item(NAME(m_uart_ocd4));
-
- m_cpu->space(AS_PROGRAM).install_ram(0x1000000 - m_ram->size(), 0xffffff, m_ram->pointer());
}
void hp9816_state::machine_reset()
{
- m_hsync_en = false;
- m_graphic_en = false;
- m_hpib_dma_en = false;
+ hp98x6_base_state::machine_reset();
uart_reset();
- diag_led_w(0);
}
void hp9816_state::cpu_mem_map(address_map &map)
{
- map.unmap_value_high();
+ hp98x6_base_state::cpu_mem_map(map);
+
// Range DTACK Device
// ==============================
- // 00'0000-00'ffff * Boot ROM
- // 01'0000-01'ffff * Diagnostic LEDs (write only)
- // 02'0000-3f'ffff * None
// 40'0000-40'ffff None
// 41'0000-41'ffff * Text video memory & 6845 (mirror of 51'0000)
// 42'0000-42'ffff * Keyboard UPI
@@ -266,10 +405,6 @@ void hp9816_state::cpu_mem_map(address_map &map)
// 58'0000-68'ffff None
// 69'0000-69'ffff * UART
// 6a'0000-fb'ffff None
- // fx'0000-ff'ffff * RAM
- map(0x000000, 0x00ffff).rom().region("cpu", 0);
- map(0x010000, 0x01ffff).w(FUNC(hp9816_state::diag_led_w)).umask16(0x00ff);
- map(0x020000, 0x3fffff).noprw();
map(0x400000, 0x40ffff).rw(m_cpu, FUNC(m68000_device::berr_r), FUNC(m68000_device::berr_w));
map(0x410000, 0x41ffff).mirror(0x100000).rw(FUNC(hp9816_state::text_r), FUNC(hp9816_state::text_w));
map(0x420000, 0x42ffff).mirror(0x100000).mask(3).rw(m_upi, FUNC(hp98x6_upi_device::read), FUNC(hp98x6_upi_device::write)).umask16(0x00ff);
@@ -283,20 +418,6 @@ void hp9816_state::cpu_mem_map(address_map &map)
map(0x6a0000, 0xfbffff).rw(m_cpu, FUNC(m68000_device::berr_r), FUNC(m68000_device::berr_w));
}
-void hp9816_state::diag_led_w(uint8_t data)
-{
- LOG("LEDS=%02x %c%c%c%c %c%c%c%c\n" ,
- data,
- BIT(data , 7) ? '.' : '*',
- BIT(data , 6) ? '.' : '*',
- BIT(data , 5) ? '.' : '*',
- BIT(data , 4) ? '.' : '*',
- BIT(data , 3) ? '.' : '*',
- BIT(data , 2) ? '.' : '*',
- BIT(data , 1) ? '.' : '*',
- BIT(data , 0) ? '.' : '*');
-}
-
uint16_t hp9816_state::text_r(offs_t offset)
{
if (BIT(offset, 12)) {
@@ -601,30 +722,13 @@ MC6845_UPDATE_ROW(hp9816_state::crtc_update_row)
void hp9816_state::cpu_reset_w(int state)
{
+ hp98x6_base_state::cpu_reset_w(state);
+
if (state) {
- LOG("RESET from CPU\n");
- m_upi->reset();
- m_hpib->reset();
- m_hpib_dma_en = false;
uart_reset();
- //m_crtc->reset();
- m_hsync_en = false;
- m_graphic_en = false;
}
}
-void hp9816_state::hpib_irq_w(int state)
-{
- m_hpib_irq = bool(state);
- m_cpu->set_input_line(M68K_IRQ_3, state);
-}
-
-void hp9816_state::upi_irq7_w(int state)
-{
- m_upi_irq7 = bool(state);
- m_cpu->set_input_line(M68K_IRQ_7, state);
-}
-
void hp9816_state::uart_irq_w(int state)
{
m_uart_irq = bool(state);
@@ -696,6 +800,1245 @@ ROM_START(hp9816a)
ROM_REGION(0x2000, "chargen", 0)
ROM_LOAD("1818-3171.bin", 0, 0x2000, CRC(9cf4d4e1) SHA1(228484fd7ba7a4b59a051af083858383afe30179))
+ ROM_REGION(0x100, "upi:idprom", 0)
+ ROM_LOAD("prom9816.bin", 0, 0x100, CRC(f2e57a04) SHA1(7436c1334f30e6de3cf1a7c30f714c9f203cb4d3))
+
+ ROM_REGION(0x10000, "cpu", 0)
+ ROM_DEFAULT_BIOS("bios40")
+ ROM_SYSTEM_BIOS(0, "bios40", "BIOS v4.0")
+ ROMX_LOAD("rom40.bin", 0x0000, 0x10000, CRC(36005480) SHA1(645a077ffd95e4c31f05cd8bbd6e4554b12813f1), ROM_BIOS(0))
+ ROM_SYSTEM_BIOS(1, "bios30", "BIOS v3.0")
+ ROMX_LOAD("rom30.bin", 0x0000, 0x10000, CRC(05c07e75) SHA1(3066a65e6137482041f9a77d09ee2289fe0974aa), ROM_BIOS(1))
+ROM_END
+
+// +-----------------+
+// | hp9826_36_state |
+// +-----------------+
+class hp9826_36_state : public hp98x6_base_state
+{
+public:
+ hp9826_36_state(const machine_config &mconfig, device_type type, const char *tag)
+ : hp98x6_base_state(mconfig, type, tag)
+ , m_fdc(*this, "fdc")
+ , m_drive0(*this, "drive0")
+ , m_drive1(*this, "drive1")
+ , m_ss(*this, "ss%u", 0)
+ , m_fdc_timer(*this, "fdc_tmr")
+ , m_sw1(*this, "sw1")
+ , m_sys_ctrl_sw(*this, "sys_ctrl_sw")
+ , m_idprom(*this, "idprom")
+ {
+ }
+
+protected:
+ // Bits in m_floppy_ctrl
+ static inline constexpr unsigned FC_DRIVE_SEL_BIT = 6;
+ static inline constexpr unsigned FC_SEC_BUFF_EN_BIT = 5;
+ static inline constexpr unsigned FC_SEC_BUFF_DIR_BIT = 4;
+ static inline constexpr unsigned FC_FDC_RST_BIT = 3;
+ static inline constexpr unsigned FC_SIDE_SEL_BIT = 2;
+ static inline constexpr unsigned FC_DRIVE_EN_BIT = 0;
+
+ virtual void machine_start() override;
+ virtual void machine_reset() override;
+ virtual void device_post_load() override;
+
+ required_device<fd1793_device> m_fdc;
+ required_device<floppy_connector> m_drive0;
+ optional_device<floppy_connector> m_drive1;
+ required_device_array<ttl74123_device, 2> m_ss;
+ required_device<timer_device> m_fdc_timer;
+ required_ioport m_sw1;
+ required_ioport m_sys_ctrl_sw;
+
+ // ID PROM
+ required_region_ptr<uint8_t> m_idprom;
+
+ TIMER_DEVICE_CALLBACK_MEMBER(fdc_ram_io);
+
+ void hp9826_36(machine_config &mconfig, unsigned dot_clock, int char_width);
+ virtual void cpu_mem_map(address_map &map) override;
+ virtual void cpu_reset_w(int state) override;
+ unsigned get_sel_floppy() const;
+ floppy_image_device *get_drive(unsigned idx) const;
+ unsigned get_floppy_idx(floppy_image_device *floppy) const;
+ void floppy_reset();
+ void floppy_update_sel();
+ void floppy_update_mon_hlt();
+ void floppy_wp_cb(floppy_image_device *floppy, int state);
+ void floppy_index_cb(floppy_image_device *floppy, int state);
+ void fdc_hld_w(int state);
+ uint8_t floppy_r(offs_t offset);
+ void floppy_w(offs_t offset, uint8_t data);
+ void fdc_irq_w(int state);
+ void fdc_drq_w(int state);
+ uint8_t hpib_r(offs_t offset);
+ void hpib_w(offs_t offset, uint8_t data);
+ uint16_t id_prom_r(offs_t offset);
+
+ uint8_t m_floppy_ctrl;
+ uint8_t m_floppy_buffer[256];
+ uint8_t m_buffer_addr;
+ bool m_fdc_hld;
+ bool m_fdc_irq;
+ bool m_fdc_drq;
+ bool m_drive_changed[2];
+ floppy_image_device *m_curr_floppy;
+};
+
+void hp9826_36_state::machine_start()
+{
+ hp98x6_base_state::machine_start();
+
+ save_item(NAME(m_floppy_ctrl));
+ save_item(NAME(m_floppy_buffer));
+ save_item(NAME(m_buffer_addr));
+ save_item(NAME(m_fdc_hld));
+ save_item(NAME(m_fdc_irq));
+ save_item(NAME(m_fdc_drq));
+ save_item(NAME(m_drive_changed));
+
+ auto drive0 = get_drive(0);
+ LOG_FDC("drive0 = %p\n", drive0);
+ drive0->setup_wpt_cb(floppy_image_device::wpt_cb(&hp9826_36_state::floppy_wp_cb, this));
+ drive0->setup_index_pulse_cb(floppy_image_device::index_pulse_cb(&hp9826_36_state::floppy_index_cb, this));
+ auto drive1 = get_drive(1);
+ LOG_FDC("drive1 = %p\n", drive1);
+ if (drive1) {
+ drive1->setup_wpt_cb(floppy_image_device::wpt_cb(&hp9826_36_state::floppy_wp_cb, this));
+ drive1->setup_index_pulse_cb(floppy_image_device::index_pulse_cb(&hp9826_36_state::floppy_index_cb, this));
+ }
+}
+
+void hp9826_36_state::machine_reset()
+{
+ hp98x6_base_state::machine_reset();
+ m_curr_floppy = nullptr;
+ floppy_reset();
+}
+
+void hp9826_36_state::device_post_load()
+{
+ m_curr_floppy = nullptr;
+ // Bring m_curr_floppy in synch
+ floppy_update_sel();
+}
+
+static void floppies(device_slot_interface &device)
+{
+ device.option_add("525dd", FLOPPY_525_DD);
+}
+
+void hp9826_36_state::hp9826_36(machine_config &mconfig, unsigned dot_clock, int char_width)
+{
+ hp98x6_base(mconfig, dot_clock, char_width);
+
+ FD1793(mconfig, m_fdc, HPIB_CLOCK / 5);
+ m_fdc->set_force_ready(true);
+ m_fdc->dden_w(0);
+ m_fdc->intrq_wr_callback().set(FUNC(hp9826_36_state::fdc_irq_w));
+ m_fdc->drq_wr_callback().set(FUNC(hp9826_36_state::fdc_drq_w));
+ m_fdc->hld_wr_callback().set(FUNC(hp9826_36_state::fdc_hld_w));
+
+ FLOPPY_CONNECTOR(mconfig, m_drive0, floppies, "525dd", floppy_image_device::default_mfm_floppy_formats, true);
+
+ // ~225 ms duration
+ TTL74123(mconfig, m_ss[0], RES_K(23.7), CAP_U(33));
+ m_ss[0]->set_connection_type(TTL74123_NOT_GROUNDED_NO_DIODE);
+ m_ss[0]->set_a_pin_value(0);
+ m_ss[0]->set_clear_pin_value(1);
+ TTL74123(mconfig, m_ss[1], RES_K(23.7), CAP_U(33));
+ m_ss[1]->set_connection_type(TTL74123_NOT_GROUNDED_NO_DIODE);
+ m_ss[1]->set_a_pin_value(0);
+ m_ss[1]->set_clear_pin_value(1);
+
+ TIMER(mconfig, m_fdc_timer).configure_generic(FUNC(hp9826_36_state::fdc_ram_io));
+}
+
+void hp9826_36_state::cpu_mem_map(address_map &map)
+{
+ hp98x6_base_state::cpu_mem_map(map);
+
+ // Range DTACK Device
+ // ==============================
+ // 40'0000-41'ffff * None
+ // 42'0000-42'ffff * Keyboard UPI
+ // 43'0000-43'ffff * None
+ // 44'0000-44'ffff * Floppy
+ // 45'0000-46'ffff * None
+ // 47'0000-47'ffff * HPIB
+ // 48'0000-4f'ffff * None
+ // 50'0000-5e'ffff None
+ // 5f'0000-5f'3fff * ID PROM & DIP switches
+ // 5f'4000-fb'ffff None
+ map(0x400000, 0x41ffff).noprw();
+ map(0x420000, 0x42ffff).mask(3).rw(m_upi, FUNC(hp98x6_upi_device::read), FUNC(hp98x6_upi_device::write)).umask16(0x00ff);
+ map(0x430000, 0x43ffff).noprw();
+ map(0x440000, 0x44ffff).rw(FUNC(hp9826_36_state::floppy_r), FUNC(hp9826_36_state::floppy_w)).umask16(0xff00);
+ map(0x450000, 0x46ffff).noprw();
+ map(0x470000, 0x47ffff).rw(FUNC(hp9826_36_state::hpib_r), FUNC(hp9826_36_state::hpib_w)).umask16(0x00ff);
+ map(0x480000, 0x4fffff).noprw();
+ map(0x500000, 0x5effff).rw(m_cpu, FUNC(m68000_device::berr_r), FUNC(m68000_device::berr_w));
+ map(0x5f0000, 0x5f3fff).nopw().r(FUNC(hp9826_36_state::id_prom_r));
+ map(0x5f4000, 0xfbffff).rw(m_cpu, FUNC(m68000_device::berr_r), FUNC(m68000_device::berr_w));
+}
+
+void hp9826_36_state::cpu_reset_w(int state)
+{
+ hp98x6_base_state::cpu_reset_w(state);
+
+ if (state) {
+ floppy_reset();
+ }
+}
+
+unsigned hp9826_36_state::get_sel_floppy() const
+{
+ return BIT(m_floppy_ctrl, FC_DRIVE_SEL_BIT) ? 1 : 0;
+}
+
+floppy_image_device *hp9826_36_state::get_drive(unsigned idx) const
+{
+ if (idx) {
+ return bool(m_drive1) ? m_drive1->get_device() : nullptr;
+ } else {
+ return m_drive0->get_device();
+ }
+}
+
+unsigned hp9826_36_state::get_floppy_idx(floppy_image_device *floppy) const
+{
+ return get_drive(0) == floppy ? 0 : 1;
+}
+
+void hp9826_36_state::floppy_reset()
+{
+ m_floppy_ctrl = 0;
+ m_fdc->mr_w(0);
+ floppy_update_sel();
+}
+
+void hp9826_36_state::floppy_update_sel()
+{
+ floppy_image_device *new_floppy = get_drive(get_sel_floppy());
+
+ if (new_floppy != m_curr_floppy) {
+ m_curr_floppy = new_floppy;
+ m_fdc->set_floppy(m_curr_floppy);
+ }
+ if (m_curr_floppy) {
+ m_curr_floppy->setup_index_pulse_cb(floppy_image_device::index_pulse_cb(&hp9826_36_state::floppy_index_cb, this));
+ m_curr_floppy->ss_w(BIT(m_floppy_ctrl, FC_SIDE_SEL_BIT));
+ }
+ floppy_update_mon_hlt();
+}
+
+void hp9826_36_state::floppy_update_mon_hlt()
+{
+ bool ss_state = bool(m_ss[get_sel_floppy()]->q_r());
+ bool mon = BIT(m_floppy_ctrl, FC_DRIVE_EN_BIT) || (m_fdc_hld && ss_state);
+ LOG_FDC("mon %d: hld %d ss %d\n", mon, m_fdc_hld, ss_state);
+ get_drive(0)->mon_w(!mon);
+ auto drive1 = get_drive(1);
+ if (drive1) {
+ drive1->mon_w(!mon);
+ }
+}
+
+void hp9826_36_state::floppy_wp_cb(floppy_image_device *floppy, int state)
+{
+ LOG_FDC("floppy wp %p %d\n", floppy, state);
+ if (state) {
+ m_drive_changed[get_floppy_idx(floppy)] = true;
+ }
+}
+
+void hp9826_36_state::floppy_index_cb(floppy_image_device *floppy, int state)
+{
+ LOG_FDC("floppy id %s %p %d\n", machine().time().as_string(), floppy, state);
+ m_ss[get_floppy_idx(floppy)]->b_w(state);
+ floppy_update_mon_hlt();
+ if (floppy == m_curr_floppy) {
+ m_fdc->index_callback(floppy, state);
+ }
+}
+
+void hp9826_36_state::fdc_hld_w(int state)
+{
+ m_fdc_hld = bool(state);
+ floppy_update_mon_hlt();
+}
+
+uint8_t hp9826_36_state::floppy_r(offs_t offset)
+{
+ uint8_t res = 0;
+ if ((offset & 0x1e00) == 0x0800) {
+ // Status register
+ if (m_fdc_irq) {
+ BIT_SET(res, 3);
+ }
+ // Margin error = 0 (bit 2)
+ if (m_drive_changed[get_sel_floppy()]) {
+ BIT_SET(res, 1);
+ }
+ if (m_fdc_drq) {
+ BIT_SET(res, 0);
+ }
+ } else if (!BIT(m_floppy_ctrl, FC_SEC_BUFF_EN_BIT)) {
+ if (BIT(offset, 12)) {
+ // Buffer RAM
+ m_buffer_addr = offset & 0xff;
+ res = m_floppy_buffer[m_buffer_addr];
+ } else if ((offset & 0x1800) == 0) {
+ // FDC
+ res = m_fdc->read(offset & 3);
+ }
+ }
+ LOG_FDC_DATA("fdc R %04x=%02x\n", offset * 2, res);
+ return res;
+}
+
+void hp9826_36_state::floppy_w(offs_t offset, uint8_t data)
+{
+ LOG_FDC_DATA("fdc W %s %04x=%02x\n", machine().time().as_string(), offset * 2, data);
+ offs_t masked_off = offset & 0x1e00;
+ if (masked_off == 0x800) {
+ // Floppy control register
+ m_floppy_ctrl = data;
+ m_fdc->mr_w(BIT(m_floppy_ctrl, FC_FDC_RST_BIT));
+ floppy_update_sel();
+ } else if (masked_off == 0xa00) {
+ // Disk changed reset command
+ m_drive_changed[0] = false;
+ m_drive_changed[1] = false;
+ } else if (!BIT(m_floppy_ctrl, FC_SEC_BUFF_EN_BIT)) {
+ if (BIT(offset, 12)) {
+ // Buffer RAM
+ m_buffer_addr = offset & 0xff;
+ m_floppy_buffer[m_buffer_addr] = data;
+ } else if ((offset & 0x1800) == 0) {
+ // FDC
+ m_fdc->write(offset & 3, data);
+ }
+ }
+}
+
+void hp9826_36_state::fdc_irq_w(int state)
+{
+ LOG_FDC("fdc IRQ %d\n", state);
+ m_fdc_irq = bool(state);
+ m_cpu->set_input_line(M68K_IRQ_2, state);
+}
+
+void hp9826_36_state::fdc_drq_w(int state)
+{
+ LOG_FDC("fdc drq %d\n", state);
+ if (!m_fdc_drq && state && BIT(m_floppy_ctrl, FC_SEC_BUFF_EN_BIT)) {
+ m_fdc_timer->adjust(attotime::zero);
+ }
+ m_fdc_drq = bool(state);
+}
+
+TIMER_DEVICE_CALLBACK_MEMBER(hp9826_36_state::fdc_ram_io)
+{
+ if (BIT(m_floppy_ctrl, FC_SEC_BUFF_EN_BIT)) {
+ if (BIT(m_floppy_ctrl, FC_SEC_BUFF_DIR_BIT)) {
+ // FDC -> RAM
+ uint8_t data = m_fdc->data_r();
+ m_floppy_buffer[m_buffer_addr] = data;
+ LOG_FDC_DATA("fdc RAM W @%02x=%02x\n", m_buffer_addr, data);
+ } else {
+ // RAM -> FDC
+ m_fdc->data_w(m_floppy_buffer[m_buffer_addr]);
+ LOG_FDC_DATA("fdc RAM R @%02x=%02x\n", m_buffer_addr, m_floppy_buffer[m_buffer_addr]);
+ }
+ m_buffer_addr++;
+ }
+}
+
+uint8_t hp9826_36_state::hpib_r(offs_t offset)
+{
+ uint8_t res = 0;
+
+ if (BIT(offset, 3)) {
+ res = m_hpib->read(offset & 7);
+ } else if (BIT(offset, 1)) {
+ if (m_sys_ctrl_sw->read()) {
+ BIT_SET(res, 7);
+ }
+ if (!m_hpib->cont_r()) {
+ BIT_SET(res, 6);
+ }
+ if (m_upi_irq7) {
+ BIT_SET(res, 2);
+ }
+ BIT_SET(res, 0);
+ } else {
+ BIT_SET(res, 7);
+ if (m_hpib_irq) {
+ BIT_SET(res, 6);
+ }
+ if (m_hpib_dma_en) {
+ BIT_SET(res, 0);
+ }
+ }
+
+ return res;
+}
+
+void hp9826_36_state::hpib_w(offs_t offset, uint8_t data)
+{
+ if (BIT(offset, 3)) {
+ m_hpib->write(offset & 7, data);
+ } else {
+ m_hpib_dma_en = BIT(data, 0);
+ }
+}
+
+uint16_t hp9826_36_state::id_prom_r(offs_t offset)
+{
+ uint16_t res;
+
+ res = m_idprom[offset & 0xff];
+ res |= (m_sw1->read() << 8);
+
+ return res;
+}
+
+static INPUT_PORTS_START(hp9826_36)
+ PORT_START("sw1")
+ PORT_CONFNAME(0x80, 0x80, "SW1-8")
+ PORT_CONFSETTING(0x00, DEF_STR(On))
+ PORT_CONFSETTING(0x80, DEF_STR(Off))
+ PORT_CONFNAME(0x40, 0x40, "SW1-7")
+ PORT_CONFSETTING(0x00, DEF_STR(On))
+ PORT_CONFSETTING(0x40, DEF_STR(Off))
+ PORT_CONFNAME(0x20, 0x20, "SW1-6")
+ PORT_CONFSETTING(0x00, DEF_STR(On))
+ PORT_CONFSETTING(0x20, DEF_STR(Off))
+ PORT_CONFNAME(0x10, 0x00, "SW1-5")
+ PORT_CONFSETTING(0x00, DEF_STR(On))
+ PORT_CONFSETTING(0x10, DEF_STR(Off))
+ PORT_CONFNAME(0x08, 0x00, "SW1-4")
+ PORT_CONFSETTING(0x00, DEF_STR(On))
+ PORT_CONFSETTING(0x08, DEF_STR(Off))
+ PORT_CONFNAME(0x04, 0x00, "SW1-3")
+ PORT_CONFSETTING(0x00, DEF_STR(On))
+ PORT_CONFSETTING(0x04, DEF_STR(Off))
+ PORT_CONFNAME(0x02, 0x00, "SW1-2")
+ PORT_CONFSETTING(0x00, DEF_STR(On))
+ PORT_CONFSETTING(0x02, DEF_STR(Off))
+ PORT_CONFNAME(0x01, 0x00, "SW1-1")
+ PORT_CONFSETTING(0x00, DEF_STR(On))
+ PORT_CONFSETTING(0x01, DEF_STR(Off))
+
+ PORT_START("sys_ctrl_sw")
+ PORT_CONFNAME(1, 1, "HPIB Sys controller")
+ PORT_CONFSETTING(1, DEF_STR(On))
+ PORT_CONFSETTING(0, DEF_STR(Off))
+INPUT_PORTS_END
+
+// +--------------+
+// | hp9826_state |
+// +--------------+
+class hp9826_state : public hp9826_36_state
+{
+public:
+ hp9826_state(const machine_config &mconfig, device_type type, const char *tag)
+ : hp9826_36_state(mconfig, type, tag)
+ {
+ }
+
+ void hp9826(machine_config &mconfig);
+
+protected:
+ virtual void machine_start() override;
+
+private:
+ static inline constexpr unsigned TEXT_VRAM_SIZE = 2048;
+ static inline constexpr unsigned GRAPHIC_VRAM_SIZE = 16384;
+
+ virtual void cpu_mem_map(address_map &map) override;
+ uint16_t text_r(offs_t offset, uint16_t mem_mask);
+ void text_w(offs_t offset, uint16_t data, uint16_t mem_mask);
+ uint16_t graphic_r(offs_t offset, uint16_t mem_mask);
+ void graphic_w(offs_t offset, uint16_t data, uint16_t mem_mask);
+
+ MC6845_UPDATE_ROW(crtc_update_row);
+
+ uint8_t m_text_vram[TEXT_VRAM_SIZE];
+ uint8_t m_graphic_vram[GRAPHIC_VRAM_SIZE];
+};
+
+void hp9826_state::hp9826(machine_config &mconfig)
+{
+ hp9826_36(mconfig, DOT_CLOCK_9826.value(), 8);
+
+ SCREEN(mconfig, m_screen, SCREEN_TYPE_RASTER, rgb_t::white());
+ m_screen->set_raw(DOT_CLOCK_9826, 520, 0, 400, 321, 0, 300);
+ m_screen->set_screen_update(m_crtc, FUNC(mc6845_device::screen_update));
+
+ PALETTE(mconfig, m_palette, palette_device::MONOCHROME_HIGHLIGHT);
+
+ m_crtc->set_update_row_callback(FUNC(hp9826_state::crtc_update_row));
+}
+
+void hp9826_state::machine_start()
+{
+ hp9826_36_state::machine_start();
+ save_item(NAME(m_text_vram));
+ save_item(NAME(m_graphic_vram));
+}
+
+void hp9826_state::cpu_mem_map(address_map &map)
+{
+ hp9826_36_state::cpu_mem_map(map);
+
+ // Range DTACK Device
+ // ==============================
+ // 41'0000-41'ffff * Text video memory & 6845 (mirror of 51'0000)
+ // 43'0000-43'ffff * Graphic video memory (Mirror of 53'0000)
+ map(0x410000, 0x41ffff).mirror(0x100000).rw(FUNC(hp9826_state::text_r), FUNC(hp9826_state::text_w));
+ map(0x430000, 0x43ffff).mirror(0x100000).rw(FUNC(hp9826_state::graphic_r), FUNC(hp9826_state::graphic_w));
+}
+
+uint16_t hp9826_state::text_r(offs_t offset, uint16_t mem_mask)
+{
+ if (BIT(offset, 12)) {
+ if (ACCESSING_BITS_0_7) {
+ return m_text_vram[offset & (TEXT_VRAM_SIZE - 1)];
+ } else {
+ return m_cpu->berr_r();
+ }
+ } else if (BIT(offset, 0)) {
+ return m_crtc->register_r();
+ } else {
+ LOG("Reading from non-existing register of 6845!\n");
+ return 0;
+ }
+}
+
+void hp9826_state::text_w(offs_t offset, uint16_t data, uint16_t mem_mask)
+{
+ if (BIT(offset, 12)) {
+ m_hsync_en = !BIT(offset, 14);
+ if (ACCESSING_BITS_0_7) {
+ m_text_vram[offset & (TEXT_VRAM_SIZE - 1)] = uint8_t(data);
+ } else {
+ m_cpu->berr_w(data);
+ }
+ } else if (ACCESSING_BITS_0_7) {
+ if (BIT(offset, 0)) {
+ m_crtc->register_w(uint8_t(data));
+ } else {
+ m_crtc->address_w(uint8_t(data));
+ }
+ } else {
+ LOG("Access to 6845 on top byte!\n");
+ }
+}
+
+uint16_t hp9826_state::graphic_r(offs_t offset, uint16_t mem_mask)
+{
+ m_graphic_en = !BIT(offset, 14);
+
+ if (ACCESSING_BITS_0_7) {
+ return m_graphic_vram[offset & (GRAPHIC_VRAM_SIZE - 1)];
+ } else {
+ return m_cpu->berr_r();
+ }
+}
+
+void hp9826_state::graphic_w(offs_t offset, uint16_t data, uint16_t mem_mask)
+{
+ m_graphic_en = !BIT(offset, 14);
+
+ if (ACCESSING_BITS_0_7) {
+ m_graphic_vram[offset & (GRAPHIC_VRAM_SIZE - 1)] = uint8_t(data);
+ } else {
+ return m_cpu->berr_w(0);
+ }
+}
+
+MC6845_UPDATE_ROW(hp9826_state::crtc_update_row)
+{
+ const pen_t *pen = m_palette->pens();
+ // Text enable multiplexer
+ //
+ // | MA13 | VSEL | RA=11 | text_en |
+ // |------+------+-------+------------|
+ // | 0 | 0 | 0 | 0 |
+ // | 0 | 0 | 1 | 0 |
+ // | 0 | 1 | 0 | m_hsync_en |
+ // | 0 | 1 | 1 | 0 |
+ // | 1 | 0 | 0 | m_hsync_en |
+ // | 1 | 0 | 1 | m_hsync_en |
+ // | 1 | 1 | 0 | m_hsync_en |
+ // | 1 | 1 | 1 | 0 |
+ bool vmux[] = { false, false, m_hsync_en, false, m_hsync_en, m_hsync_en, m_hsync_en, false };
+ unsigned vmux_idx = (ra & 0xb) == 0xb ? 1 : 0;
+ uint16_t graph_addr = y * x_count;
+ for (int i = 0; i < x_count; i++) {
+ uint16_t text_addr = ma + i;
+ bool vsel = (text_addr & 0x1800) == 0x1800;
+ bool text_en = vmux[vmux_idx + (vsel ? 2 : 0) + (BIT(text_addr, 13) ? 4 : 0)];
+ uint8_t text_pixels = text_en ? m_chargen[unsigned(m_text_vram[text_addr & (TEXT_VRAM_SIZE - 1)]) << 4 | ra] : 0;
+ bool inv = text_en ? (vsel ^ (cursor_x == i)) : false;
+ if (inv) {
+ text_pixels = ~text_pixels;
+ }
+ uint8_t graph_pixels = m_graphic_en ? m_graphic_vram[(graph_addr + i) & (GRAPHIC_VRAM_SIZE - 1)] : 0;
+ for (unsigned col = 0; col < 8; col++) {
+ if (vsel ? BIT(graph_pixels, 7 - col) : BIT(graph_pixels, 7 - col) ^ BIT(text_pixels, 7 - col)) {
+ bitmap.pix(y, i * 8 + col) = pen[2];
+ } else if (vsel && BIT(text_pixels, 7 - col)) {
+ bitmap.pix(y, i * 8 + col) = pen[1];
+ } else {
+ bitmap.pix(y, i * 8 + col) = pen[0];
+ }
+ }
+ }
+}
+
+ROM_START(hp9826a)
+ ROM_REGION(0x1000, "chargen", 0)
+ ROM_LOAD("9826-chargen.bin", 0, 0x1000, CRC(dc070d09) SHA1(f11d45fb11b95cb02429ef54dd8a802b77436296))
+
+ ROM_REGION(0x100, "idprom", 0)
+ ROM_LOAD("prom9826.bin", 0, 0x100, CRC(358a1a8b) SHA1(2d87a445efdcd45a8606838c6d66a10cc68af85a))
+
+ ROM_REGION(0x10000, "cpu", 0)
+ ROM_DEFAULT_BIOS("bios40")
+ ROM_SYSTEM_BIOS(0, "bios40", "BIOS v4.0")
+ ROMX_LOAD("rom40.bin", 0x0000, 0x10000, CRC(36005480) SHA1(645a077ffd95e4c31f05cd8bbd6e4554b12813f1), ROM_BIOS(0))
+ ROM_SYSTEM_BIOS(1, "bios30", "BIOS v3.0")
+ ROMX_LOAD("rom30.bin", 0x0000, 0x10000, CRC(05c07e75) SHA1(3066a65e6137482041f9a77d09ee2289fe0974aa), ROM_BIOS(1))
+ROM_END
+
+// +--------------+
+// | hp9836_state |
+// +--------------+
+class hp9836_state : public hp9826_36_state
+{
+public:
+ hp9836_state(const machine_config &mconfig, device_type type, const char *tag)
+ : hp9826_36_state(mconfig, type, tag)
+ , m_frame_rate_sw(*this, "frame_rate_sw")
+ , m_60hz(false)
+ {
+ }
+
+ void hp9836(machine_config &mconfig);
+
+protected:
+ virtual void machine_start() override;
+
+private:
+ static inline constexpr unsigned TEXT_VRAM_SIZE = 2048;
+ static inline constexpr unsigned GRAPHIC_VRAM_SIZE = 16384;
+
+ required_ioport m_frame_rate_sw;
+
+ virtual void cpu_mem_map(address_map &map) override;
+ uint16_t text_r(offs_t offset, uint16_t mem_mask);
+ void text_w(offs_t offset, uint16_t data, uint16_t mem_mask);
+ uint16_t graphic_r(offs_t offset, uint16_t mem_mask);
+ void graphic_w(offs_t offset, uint16_t data, uint16_t mem_mask);
+
+ MC6845_UPDATE_ROW(crtc_update_row);
+
+ uint16_t m_text_vram[TEXT_VRAM_SIZE];
+ uint16_t m_graphic_vram[GRAPHIC_VRAM_SIZE];
+ bool m_60hz;
+};
+
+void hp9836_state::hp9836(machine_config &mconfig)
+{
+ // Screen is arranged in this way.
+ //
+ // ↑ +----------------------------------------+ ↑
+ // 15 | Graphic only |
+ // ↓ +----------------------------------------+ 390
+ // ↑ | Graphic & text | lines
+ // 375| Text: 25 rows, 15 scan lines each |
+ // ↓ +----------------------------------------+ ↓
+ // ← 512 graphic pixels →
+ // ← 16G →← 720 text pixels (80 x 9) →← 16G →
+ // ← 48H →← 1440 half pixels →← 48H →
+ // ← 1536 half pixels →
+ //
+ // 1 graphic pixel = 1.5 text pixels
+ // 2 half pixels = 1 text pixel
+ // 3 half pixels = 1 graphic pixel
+ //
+ // Character cell: 15 lines x 9 columns (18 half pixels)
+ //
+ // MAME only knows about half pixels in the horizontal direction
+
+ // In the horizontal direction everything is doubled to account for half pixels
+ hp9826_36(mconfig, DOT_CLOCK_9836 * 2, 18);
+
+ SCREEN(mconfig, m_screen, SCREEN_TYPE_RASTER, rgb_t::white());
+ // Parameters for 50 Hz frame rate
+ m_screen->set_raw(DOT_CLOCK_9836 * 2, 2070, 0, 1536, 498, 0, 390);
+ m_screen->set_screen_update(m_crtc, FUNC(mc6845_device::screen_update));
+
+ PALETTE(mconfig, m_palette, palette_device::MONOCHROME_HIGHLIGHT);
+
+ m_crtc->set_update_row_callback(FUNC(hp9836_state::crtc_update_row));
+ // Expand visible area to include 16 graphic pixels (48 text half-pixels) on either side and
+ // 15 graphic lines at the top
+ m_crtc->set_visarea_adjust(0, 96, 0, 15);
+
+ FLOPPY_CONNECTOR(mconfig, m_drive1, floppies, "525dd", floppy_image_device::default_mfm_floppy_formats, true);
+}
+
+void hp9836_state::machine_start()
+{
+ hp9826_36_state::machine_start();
+ save_item(NAME(m_text_vram));
+ save_item(NAME(m_graphic_vram));
+ save_item(NAME(m_60hz));
+}
+
+void hp9836_state::cpu_mem_map(address_map &map)
+{
+ hp9826_36_state::cpu_mem_map(map);
+
+ // Range DTACK Device
+ // ==============================
+ // 51'0000-51'ffff * Text video memory & 6845
+ // 53'0000-53'ffff * Graphic video memory
+ map(0x510000, 0x51ffff).rw(FUNC(hp9836_state::text_r), FUNC(hp9836_state::text_w));
+ map(0x530000, 0x53ffff).rw(FUNC(hp9836_state::graphic_r), FUNC(hp9836_state::graphic_w));
+}
+
+uint16_t hp9836_state::text_r(offs_t offset, uint16_t mem_mask)
+{
+ if (BIT(offset, 12)) {
+ if (BIT(offset, 13)) {
+ bool new_60hz = BIT(offset, 1);
+ if (new_60hz != m_60hz) {
+ m_60hz = new_60hz;
+ LOG("60/50 Hz FF set to %d\n", m_60hz);
+ }
+ }
+ uint16_t res = m_text_vram[offset & (TEXT_VRAM_SIZE - 1)];
+ BIT_CLR(res, 15);
+ if (m_frame_rate_sw->read()) {
+ BIT_SET(res, 15);
+ }
+ return res;
+ } else if (BIT(offset, 0)) {
+ return m_crtc->register_r();
+ } else {
+ LOG("Reading from non-existing register of 6845!\n");
+ return 0;
+ }
+}
+
+void hp9836_state::text_w(offs_t offset, uint16_t data, uint16_t mem_mask)
+{
+ if (BIT(offset, 12)) {
+ m_hsync_en = !BIT(offset, 14);
+ if (BIT(offset, 13)) {
+ bool new_60hz = BIT(offset, 1);
+ if (new_60hz != m_60hz) {
+ m_60hz = new_60hz;
+ LOG("60/50 Hz FF set to %d\n", m_60hz);
+ }
+ }
+ COMBINE_DATA(&m_text_vram[offset & (TEXT_VRAM_SIZE - 1)]);
+ } else if (ACCESSING_BITS_0_7) {
+ if (BIT(offset, 0)) {
+ m_crtc->register_w(uint8_t(data));
+ } else {
+ m_crtc->address_w(uint8_t(data));
+ }
+ } else {
+ LOG("Access to 6845 on top byte!\n");
+ }
+}
+
+uint16_t hp9836_state::graphic_r(offs_t offset, uint16_t mem_mask)
+{
+ m_graphic_en = !BIT(offset, 14);
+
+ return m_graphic_vram[offset & (GRAPHIC_VRAM_SIZE - 1)];
+}
+
+void hp9836_state::graphic_w(offs_t offset, uint16_t data, uint16_t mem_mask)
+{
+ m_graphic_en = !BIT(offset, 14);
+
+ COMBINE_DATA(&m_graphic_vram[offset & (GRAPHIC_VRAM_SIZE - 1)]);
+}
+
+MC6845_UPDATE_ROW(hp9836_state::crtc_update_row)
+{
+ // 1536 (64 * 24) half pixels
+ uint64_t gdots[24] = { 0 };
+ uint64_t tdots[24] = { 0 };
+ uint64_t invert[24] = { 0 };
+ uint64_t half_b[24] = { 0 };
+
+ if (y < 15 * 25) {
+ // Text
+ // 48 is the leftmost half-pixel of text part
+ uint64_t tdots_mask = 1ULL << (63 - 48);
+ unsigned tdots_pos = 0;
+ // Text enable multiplexer
+ //
+ // | MA13 | MA12 | MA11 | text_en |
+ // |------+------+------+------------|
+ // | 0 | 0 | 0 | 0 |
+ // | 0 | 0 | 1 | 0 |
+ // | 0 | 1 | 0 | 0 |
+ // | 0 | 1 | 1 | m_hsync_en |
+ // | 1 | 0 | 0 | m_hsync_en |
+ // | 1 | 0 | 1 | 0 |
+ // | 1 | 1 | 0 | m_hsync_en |
+ // | 1 | 1 | 1 | m_hsync_en |
+ bool ten[] = { false, false, false, m_hsync_en, m_hsync_en, false, m_hsync_en, m_hsync_en };
+
+ for (int i = 0; i < x_count; i++) {
+ uint16_t t_addr = ma + i;
+ bool text_en = ten[(t_addr >> 11) & 7];
+ uint16_t char_attr = m_text_vram[t_addr & (TEXT_VRAM_SIZE - 1)];
+ // | Bit(s) | Meaning |
+ // |--------+----------------------------|
+ // | 15..12 | N/U |
+ // | 11 | Half brightness |
+ // | 10 | Underline |
+ // | 9 | Blink |
+ // | 8 | Inverted |
+ // | 7..0 | Character code |
+ bool hb = BIT(char_attr, 11);
+ bool inv = false;
+ uint16_t text_pixels = 0;
+ bool half_shift = false;
+ if (text_en) {
+ inv = BIT(char_attr, 8);
+ bool blink = BIT(char_attr, 9) && BIT(m_screen->frame_number(), 5);
+ if (!blink) {
+ uint8_t cg = m_chargen[ra | ((char_attr & 0xff) << 4)];
+ text_pixels = (cg & 0x7f) << 1;
+ if (cursor_x == i || (BIT(char_attr, 10) && ra == 14)) {
+ text_pixels = ~text_pixels;
+ }
+ half_shift = BIT(cg, 7);
+ }
+ }
+ for (unsigned j = 0; j < 18; j++) {
+ unsigned bit_pos = half_shift ? (9 - (j + 1) / 2) : (8 - j / 2);
+ if (text_pixels & (1 << bit_pos)) {
+ tdots[tdots_pos] |= tdots_mask;
+ }
+ if (hb) {
+ half_b[tdots_pos] |= tdots_mask;
+ }
+ if (inv) {
+ invert[tdots_pos] |= tdots_mask;
+ }
+ tdots_mask >>= 1;
+ if (!tdots_mask) {
+ tdots_pos++;
+ tdots_mask = 1ULL << 63;
+ }
+ }
+ }
+ }
+
+ int real_y = (y + 15) % 390;
+ if (m_graphic_en) {
+ uint64_t gdots_mask = 1ULL << 63;
+ unsigned gdots_pos = 0;
+ // 32 16-bit words -> 512 pixels
+ for (unsigned i = 0; i < 32; i++) {
+ uint16_t w = m_graphic_vram[real_y * 32 + i];
+ for (uint16_t mask = 0x8000; mask; mask >>= 1) {
+ bool dot = (w & mask) != 0;
+ // 1 graphic pixel = 3 half pixels
+ if (dot) {
+ gdots[gdots_pos] |= gdots_mask;
+ }
+ gdots_mask >>= 1;
+ if (!gdots_mask) {
+ gdots_pos++;
+ gdots_mask = 1ULL << 63;
+ }
+ if (dot) {
+ gdots[gdots_pos] |= gdots_mask;
+ }
+ gdots_mask >>= 1;
+ if (!gdots_mask) {
+ gdots_pos++;
+ gdots_mask = 1ULL << 63;
+ }
+ if (dot) {
+ gdots[gdots_pos] |= gdots_mask;
+ }
+ gdots_mask >>= 1;
+ if (!gdots_mask) {
+ gdots_pos++;
+ gdots_mask = 1ULL << 63;
+ }
+ }
+ }
+ }
+
+ // Combiner truth table
+ //
+ // | Gdots | Tdots | Inv | h_b | Video | Int | out |
+ // |-------+-------+-----+-----+-------+-----+-----|
+ // | 0 | 0 | 0 | 0 | 1 | 1 | 0 |
+ // | 0 | 0 | 0 | 1 | 1 | 1 | 0 |
+ // | 0 | 0 | 1 | 0 | 0 | 1 | 2 |
+ // | 0 | 0 | 1 | 1 | 1 | 0 | 1 |
+ // | 0 | 1 | 0 | 0 | 0 | 1 | 2 |
+ // | 0 | 1 | 0 | 1 | 1 | 0 | 1 |
+ // | 0 | 1 | 1 | 0 | 1 | 1 | 0 |
+ // | 0 | 1 | 1 | 1 | 1 | 1 | 0 |
+ // | 1 | 0 | 0 | 0 | 0 | 1 | 2 |
+ // | 1 | 0 | 0 | 1 | 0 | 1 | 2 |
+ // | 1 | 0 | 1 | 0 | 0 | 1 | 2 |
+ // | 1 | 0 | 1 | 1 | 0 | 1 | 2 |
+ // | 1 | 1 | 0 | 0 | 1 | 1 | 0 |
+ // | 1 | 1 | 0 | 1 | 1 | 1 | 0 |
+ // | 1 | 1 | 1 | 0 | 0 | 1 | 2 |
+ // | 1 | 1 | 1 | 1 | 0 | 1 | 2 |
+ const pen_t *pen = m_palette->pens();
+ int real_x = 0;
+ for (unsigned i = 0; i < 24; i++) {
+ uint64_t gd = gdots[i];
+ uint64_t not_gd = ~gd;
+ uint64_t td = tdots[i];
+ uint64_t not_td = ~td;
+ uint64_t inv = invert[i];
+ uint64_t not_inv = ~inv;
+ uint64_t hb = half_b[i];
+ uint64_t video = (not_gd & not_td & not_inv) |
+ (gd & td & not_inv) |
+ (not_gd & hb) |
+ (not_gd & td & inv);
+ uint64_t half_bright = ~(not_gd & (td ^ inv) & hb);
+ for (uint64_t mask = 1ULL << 63; mask; mask >>= 1) {
+ if (video & mask) {
+ if (half_bright & mask) {
+ // Video = 1 & half_bright = 1: OFF
+ bitmap.pix(real_y, real_x) = pen[0];
+ } else {
+ // Video = 1 & half_bright = 0: MID
+ bitmap.pix(real_y, real_x) = pen[1];
+ }
+ } else {
+ // Video = 0 & half_bright = x: HI
+ bitmap.pix(real_y, real_x) = pen[2];
+ }
+ real_x++;
+ }
+ }
+}
+
+static INPUT_PORTS_START(hp9836)
+ PORT_INCLUDE(hp9826_36)
+
+ PORT_START("frame_rate_sw")
+ PORT_CONFNAME(1, 1, "CRT refresh rate")
+ PORT_CONFSETTING(1, "50 Hz")
+ PORT_CONFSETTING(0, "60 Hz")
+INPUT_PORTS_END
+
+ROM_START(hp9836a)
+ ROM_REGION(0x1000, "chargen", 0)
+ // Content of this ROM is not original, I reconstructed it by adapting 9816 character generator
+ // AFAIK no dump is available
+ ROM_LOAD("9836-chargen.bin", 0, 0x1000, BAD_DUMP CRC(69754023) SHA1(952dc7fd59e0039fa7c7436fb596ab460241f256))
+
+ ROM_REGION(0x100, "idprom", 0)
+ ROM_LOAD("prom9836.bin", 0, 0x100, CRC(52251c32) SHA1(3437c5a78b4c3323ce37340022a4843fcb9f4327))
+
+ ROM_REGION(0x10000, "cpu", 0)
+ ROM_DEFAULT_BIOS("bios40")
+ ROM_SYSTEM_BIOS(0, "bios40", "BIOS v4.0")
+ ROMX_LOAD("rom40.bin", 0x0000, 0x10000, CRC(36005480) SHA1(645a077ffd95e4c31f05cd8bbd6e4554b12813f1), ROM_BIOS(0))
+ ROM_SYSTEM_BIOS(1, "bios30", "BIOS v3.0")
+ ROMX_LOAD("rom30.bin", 0x0000, 0x10000, CRC(05c07e75) SHA1(3066a65e6137482041f9a77d09ee2289fe0974aa), ROM_BIOS(1))
+ROM_END
+
+// +---------------+
+// | hp9836c_state |
+// +---------------+
+class hp9836c_state : public hp9826_36_state
+{
+public:
+ hp9836c_state(const machine_config &mconfig, device_type type, const char *tag)
+ : hp9826_36_state(mconfig, type, tag)
+ , m_frame_rate_sw(*this, "frame_rate_sw")
+ , m_clut(*this, "palette", 24 * 2, ENDIANNESS_LITTLE)
+ , m_60hz(false)
+ {
+ }
+
+ void hp9836c(machine_config &mconfig);
+
+protected:
+ virtual void machine_start() override;
+
+private:
+ static inline constexpr unsigned TEXT_VRAM_SIZE = 2048;
+ static inline constexpr unsigned GRAPHIC_VRAM_SIZE = 131072;
+
+ required_ioport m_frame_rate_sw;
+ memory_share_creator<uint8_t> m_clut;
+
+ virtual void cpu_mem_map(address_map &map) override;
+ uint16_t text_r(offs_t offset, uint16_t mem_mask);
+ void text_w(offs_t offset, uint16_t data, uint16_t mem_mask);
+ uint16_t graphic_r(offs_t offset, uint16_t mem_mask);
+ void graphic_w(offs_t offset, uint16_t data, uint16_t mem_mask);
+
+ MC6845_UPDATE_ROW(crtc_update_row);
+
+ uint16_t m_text_vram[TEXT_VRAM_SIZE];
+ uint16_t m_graphic_vram[GRAPHIC_VRAM_SIZE];
+ bool m_60hz;
+};
+
+void hp9836c_state::hp9836c(machine_config &mconfig)
+{
+ // Screen layout is the same as that of 9836A (see hp9836_state::hp9836)
+ // In the horizontal direction everything is doubled to account for half pixels
+ hp9826_36(mconfig, DOT_CLOCK_9836 * 2, 18);
+
+ SCREEN(mconfig, m_screen, SCREEN_TYPE_RASTER);
+ // Parameters for 50 Hz frame rate
+ m_screen->set_raw(DOT_CLOCK_9836 * 2, 2070, 0, 1536, 498, 0, 390);
+ m_screen->set_screen_update(m_crtc, FUNC(mc6845_device::screen_update));
+
+ PALETTE(mconfig, m_palette).set_format(palette_device::xRGB_444, 24);
+ // Entries 0..7 are fixed for text colors
+ // Entries 8..23 reflect the state of CLUT memory
+
+ m_crtc->set_update_row_callback(FUNC(hp9836c_state::crtc_update_row));
+ // Expand visible area to include 16 graphic pixels (48 text half-pixels) on either side and
+ // 15 graphic lines at the top
+ m_crtc->set_visarea_adjust(0, 96, 0, 15);
+
+ FLOPPY_CONNECTOR(mconfig, m_drive1, floppies, "525dd", floppy_image_device::default_mfm_floppy_formats, true);
+}
+
+void hp9836c_state::machine_start()
+{
+ hp9826_36_state::machine_start();
+ save_item(NAME(m_text_vram));
+ save_item(NAME(m_graphic_vram));
+ save_item(NAME(m_60hz));
+
+ // Fixed text colors
+ // Index:b2 = blue, b1 = not green, b0 = red
+ m_palette->set_pen_color(0, 0, 255, 0); // 000: Green
+ m_palette->set_pen_color(1, 255, 255, 0); // 001: Yellow
+ m_palette->set_pen_color(2, 0, 0, 0); // 010: Black
+ m_palette->set_pen_color(3, 255, 0, 0); // 011: Red
+ m_palette->set_pen_color(4, 0, 255, 255); // 100: Cyan
+ m_palette->set_pen_color(5, 255, 255, 255); // 101: White
+ m_palette->set_pen_color(6, 0, 0, 255); // 110: Blue
+ m_palette->set_pen_color(7, 255, 0, 255); // 111: Magenta
+}
+
+void hp9836c_state::cpu_mem_map(address_map &map)
+{
+ hp9826_36_state::cpu_mem_map(map);
+
+ // Range DTACK Device
+ // ==============================
+ // 51'0000-51'ffff * Text video memory, CLUT & 6845
+ // 52'0000-55'ffff * Graphic video memory
+ map(0x510000, 0x51ffff).rw(FUNC(hp9836c_state::text_r), FUNC(hp9836c_state::text_w));
+ map(0x520000, 0x55ffff).rw(FUNC(hp9836c_state::graphic_r), FUNC(hp9836c_state::graphic_w));
+}
+
+uint16_t hp9836c_state::text_r(offs_t offset, uint16_t mem_mask)
+{
+ uint16_t res = 0;
+
+ if (BIT(offset, 12)) {
+ bool texttop = (offset & 0x7c00) == 0x7c00;
+ if (texttop) {
+ if (BIT(offset, 9)) {
+ if (BIT(offset, 0)) {
+ // ID register
+ res = 0x0a30;
+ if (m_frame_rate_sw->read()) {
+ BIT_SET(res, 3);
+ }
+ } else {
+ // Vblank
+ if (m_screen->vpos() >= 390) {
+ BIT_SET(res, 0);
+ }
+ }
+ } else {
+ // Read CLUT
+ res = m_palette->read16((offset & 0xf) + 8) | 0xf000;
+ }
+ } else {
+ // Text VRAM
+ res = m_text_vram[offset & (TEXT_VRAM_SIZE - 1)];
+ BIT_CLR(res, 15);
+ }
+ } else if (BIT(offset, 0)) {
+ res = m_crtc->register_r();
+ } else {
+ LOG("Reading from non-existing register of 6845!\n");
+ }
+
+ return res;
+}
+
+void hp9836c_state::text_w(offs_t offset, uint16_t data, uint16_t mem_mask)
+{
+ if (BIT(offset, 12)) {
+ bool texttop = (offset & 0x7c00) == 0x7c00;
+ if (texttop) {
+ if (BIT(offset, 9)) {
+ if (BIT(offset, 0)) {
+ LOG("Writing %04x to 51fffe/f\n", data);
+ } else {
+ // Schematics and Pascal doc disagree over the address of this register
+ // Pascal doc makes more sense though
+ m_graphic_en = BIT(data, 0);
+ }
+ } else {
+ if (mem_mask != 0xffff) {
+ LOG("Writing to CLUT a single byte!\n");
+ }
+ m_palette->write16((offset & 0xf) + 8, ~data & 0xfff);
+ }
+ } else {
+ if (BIT(offset, 13)) {
+ bool new_60hz = BIT(offset, 1);
+ if (new_60hz != m_60hz) {
+ m_60hz = new_60hz;
+ LOG("60/50 Hz FF set to %d\n", m_60hz);
+ }
+ }
+ m_hsync_en = !BIT(offset, 14);
+ COMBINE_DATA(&m_text_vram[offset & (TEXT_VRAM_SIZE - 1)]);
+ }
+ } else if (ACCESSING_BITS_0_7) {
+ if (BIT(offset, 0)) {
+ m_crtc->register_w(uint8_t(data));
+ } else {
+ m_crtc->address_w(uint8_t(data));
+ }
+ } else {
+ LOG("Access to 6845 on top byte!\n");
+ }
+}
+
+uint16_t hp9836c_state::graphic_r(offs_t offset, uint16_t mem_mask)
+{
+ return m_graphic_vram[offset & (GRAPHIC_VRAM_SIZE - 1)];
+}
+
+void hp9836c_state::graphic_w(offs_t offset, uint16_t data, uint16_t mem_mask)
+{
+ offset &= (GRAPHIC_VRAM_SIZE - 1);
+ COMBINE_DATA(&m_graphic_vram[offset]);
+ // Only bits 11..8 and 3..0 are actually present
+ m_graphic_vram[offset] &= 0x0f0f;
+}
+
+// Text enable multiplexer
+//
+// | MA13 | MA12 | MA11 | text_en |
+// |------+------+------+------------|
+// | 0 | 0 | 0 | 0 |
+// | 0 | 0 | 1 | 0 |
+// | 0 | 1 | 0 | 0 |
+// | 0 | 1 | 1 | 1 |
+// | 1 | 0 | 0 | 1 |
+// | 1 | 0 | 1 | 0 |
+// | 1 | 1 | 0 | 1 |
+// | 1 | 1 | 1 | 1 |
+static const bool text_enable[] = { false, false, false, true, true, false, true, true };
+
+MC6845_UPDATE_ROW(hp9836c_state::crtc_update_row)
+{
+ const pen_t *pen = m_palette->pens();
+ int real_y = (y + 15) % 390;
+ if (m_graphic_en) {
+ // Graphic
+ const uint16_t *ptr = &m_graphic_vram[real_y * 256];
+ for (int real_x = 0; real_x < 1536; ptr++) {
+ pen_t pen_pix = pen[8 + ((*ptr >> 8) & 0xf)];
+ bitmap.pix(real_y, real_x++) = pen_pix;
+ bitmap.pix(real_y, real_x++) = pen_pix;
+ bitmap.pix(real_y, real_x++) = pen_pix;
+ pen_pix = pen[8 + (*ptr & 0xf)];
+ bitmap.pix(real_y, real_x++) = pen_pix;
+ bitmap.pix(real_y, real_x++) = pen_pix;
+ bitmap.pix(real_y, real_x++) = pen_pix;
+ }
+ } else {
+ bitmap.fill(rgb_t::black(), rectangle(0, 1535, real_y, real_y));
+ }
+ if (y < 15 * 25) {
+ // Text
+ for (int i = 0; i < x_count; i++) {
+ uint16_t t_addr = ma + i;
+ bool text_en = m_hsync_en ? text_enable[(t_addr >> 11) & 7] : false;
+ if (text_en) {
+ // | Bit(s) | Meaning |
+ // |--------+----------------|
+ // | 15 | N/U |
+ // | 14 | Blue |
+ // | 13 | !Green |
+ // | 12 | Red |
+ // | 11 | N/U |
+ // | 10 | Underline |
+ // | 9 | Blink |
+ // | 8 | Inverted |
+ // | 7..0 | Character code |
+ uint16_t char_attr = m_text_vram[t_addr & (TEXT_VRAM_SIZE - 1)];
+ bool blink = BIT(char_attr, 9) && BIT(m_screen->frame_number(), 5);
+ uint16_t text_pixels;
+ bool half_shift;
+ if (blink) {
+ text_pixels = 0;
+ half_shift = false;
+ } else {
+ uint8_t cg = m_chargen[ra | ((char_attr & 0xff) << 4)];
+ text_pixels = (cg & 0x7f) << 1;
+ half_shift = BIT(cg, 7);
+ }
+ bool inv = BIT(char_attr, 8);
+ if (inv ^ ((cursor_x == i) || (ra == 14 && BIT(char_attr, 10) && !blink))) {
+ text_pixels = ~text_pixels;
+ }
+ if (text_pixels) {
+ pen_t text_pen = pen[(char_attr >> 12) & 7];
+ for (unsigned j = 0; j < 18; j++) {
+ unsigned bit_pos = half_shift ? (9 - (j + 1) / 2) : (8 - j / 2);
+ if (text_pixels & (1 << bit_pos)) {
+ bitmap.pix(real_y, 48 + i * 18 + j) = text_pen;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+ROM_START(hp9836c)
+ ROM_REGION(0x1000, "chargen", 0)
+ ROM_LOAD("9836-chargen.bin", 0, 0x1000, BAD_DUMP CRC(69754023) SHA1(952dc7fd59e0039fa7c7436fb596ab460241f256))
+
+ ROM_REGION(0x100, "idprom", 0)
+ ROM_LOAD("prom9836c.bin", 0, 0x100, CRC(092a1815) SHA1(1c143b0bb6e8715ba6fc7d45070820d7e0dba25e))
+
ROM_REGION(0x10000, "cpu", 0)
ROM_DEFAULT_BIOS("bios40")
ROM_SYSTEM_BIOS(0, "bios40", "BIOS v4.0")
@@ -708,3 +2051,6 @@ ROM_END
// YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY FULLNAME FLAGS
COMP(1982, hp9816a, 0, 0, hp9816, hp9816, hp9816_state,empty_init, "Hewlett-Packard", "HP 9816A", 0)
+COMP(1981, hp9826a, 0, 0, hp9826, hp9826_36,hp9826_state,empty_init,"Hewlett-Packard","HP 9826A", 0)
+COMP(1981, hp9836a, 0, 0, hp9836, hp9836, hp9836_state,empty_init, "Hewlett-Packard", "HP 9836A", 0)
+COMP(1983, hp9836c, 0, 0, hp9836c,hp9836, hp9836c_state,empty_init,"Hewlett-Packard", "HP 9836C", 0)
diff --git a/src/mame/hp/hp98x6_upi.cpp b/src/mame/hp/hp98x6_upi.cpp
index f8a62e71cd5..07ee06394fd 100644
--- a/src/mame/hp/hp98x6_upi.cpp
+++ b/src/mame/hp/hp98x6_upi.cpp
@@ -3,14 +3,14 @@
// **** High level emulation of 8041 UPI in HP98X6 systems ****
//
-// Configuration jumpers I identified in RAM_POS_CFG_JUMPERS register:
+// Configuration jumpers in RAM_POS_CFG_JUMPERS register:
//
// | Bit | Meaning |
// |------+---------------------------------------------------|
// | 7 | ID PROM is installed |
-// | 6 | ? |
-// | 5 | Enter key is named either "ENTER" or "RETURN" (?) |
-// | 4..1 | ? |
+// | 6 | UPI revision |
+// | 5 | 0: HP-HIL keyboards not supported |
+// | 4..1 | - |
// | 0 | Large (0) or small (1) keyboard |
//
// Sequence to read ID PROM:
@@ -27,7 +27,6 @@
//
// TODO:
// - Find if FHS and delay timers are self canceling
-// - Identify more configuration jumpers
#include "emu.h"
#include "hp98x6_upi.h"
@@ -35,7 +34,7 @@
#include "speaker.h"
// Debugging
-#define VERBOSE 1
+#define VERBOSE 0
#include "logmacro.h"
@@ -60,26 +59,6 @@ namespace {
// Device type definition
DEFINE_DEVICE_TYPE(HP98X6_UPI, hp98x6_upi_device, "hp98x6_upi", "UPI of HP98x6 systems")
-// ID PROM for 9816A (it comes straight from O. De Smet's emulator)
-static const uint8_t id_prom[] = {
- 0x3e,0x38,0x00,0x32,0x30,0x31,0x30,0x41,0x30,0x30,0x30,0x30,0x30,0x30,0x39,0x38,
- 0x31,0x36,0x41,0x20,0x20,0xff,0x01,0x02,0x03,0x04,0xff,0xff,0xff,0xff,0xff,0xff,
- 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,
- 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
- 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
- 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,
- 0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
- 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
- 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
- 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
- 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
- 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
- 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
- 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
- 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
- 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff
-};
-
// Positions in internal RAM
enum : uint8_t {
RAM_POS_0_R0,
@@ -228,6 +207,7 @@ hp98x6_upi_device::hp98x6_upi_device(const machine_config &mconfig, const char *
, m_10ms_timer(*this, "timer")
, m_delay_timer(*this, "dly")
, m_input_delay_timer(*this, "inp_dly")
+ , m_idprom(*this, "idprom")
, m_irq1_write_func(*this)
, m_irq7_write_func(*this)
{
@@ -456,8 +436,8 @@ void hp98x6_upi_device::device_reset()
m_ram[RAM_POS_0_R7_KEY_DOWN] = SCANCODE_NONE;
// Assume RESET key is down
m_ram[RAM_POS_RST_DEB_CNT] = R2_FLAGS1_DEB_INIT;
- // PROM is present
- m_ram[RAM_POS_CFG_JUMPERS] = BIT_MASK<uint8_t>(CFG_JUMPERS_PROM_BIT);
+ // PROM is present if m_idprom != nullptr
+ m_ram[RAM_POS_CFG_JUMPERS] = bool(m_idprom) ? BIT_MASK<uint8_t>(CFG_JUMPERS_PROM_BIT) : 0;
// Assume US English
m_ram[RAM_POS_LNG_JUMPERS] = 0;
m_ram[RAM_POS_1_R3_TIMER_STS] = 0;
@@ -623,7 +603,7 @@ void hp98x6_upi_device::decode_cmd(uint8_t cmd)
// Read from ID PROM when enabled
if (m_ram[RAM_POS_READING_PROM] &&
m_ram[RAM_POS_1_R6_R_PTR] == RAM_POS_0_R1) {
- m_ram[RAM_POS_0_R1] = id_prom[m_ram[RAM_POS_PROM_ADDR]];
+ m_ram[RAM_POS_0_R1] = m_idprom[m_ram[RAM_POS_PROM_ADDR]];
LOG("PROM @%02x=%02x\n", m_ram[RAM_POS_PROM_ADDR], m_ram[RAM_POS_0_R1]);
m_ram[RAM_POS_PROM_ADDR]++;
}
@@ -659,9 +639,13 @@ void hp98x6_upi_device::decode_cmd(uint8_t cmd)
case CMD_RD_PROM_START:
// 1100'0001
// Start reading ID PROM
- LOG("Start PROM read\n");
- m_ram[RAM_POS_READING_PROM] = 1;
- m_ram[RAM_POS_PROM_ADDR] = 0;
+ if (bool(m_idprom)) {
+ LOG("Start PROM read\n");
+ m_ram[RAM_POS_READING_PROM] = 1;
+ m_ram[RAM_POS_PROM_ADDR] = 0;
+ } else {
+ LOG("Attempt to read from non-existing PROM\n");
+ }
break;
case CMD_RD_PROM_STOP:
// 1100'0000
diff --git a/src/mame/hp/hp98x6_upi.h b/src/mame/hp/hp98x6_upi.h
index cda96d2e7a9..bd895497c33 100644
--- a/src/mame/hp/hp98x6_upi.h
+++ b/src/mame/hp/hp98x6_upi.h
@@ -55,6 +55,9 @@ private:
required_device<timer_device> m_delay_timer;
required_device<timer_device> m_input_delay_timer;
+ // ID PROM
+ optional_region_ptr<uint8_t> m_idprom;
+
devcb_write_line m_irq1_write_func;
devcb_write_line m_irq7_write_func;
uint8_t m_ram[64];
diff --git a/src/mame/mame.lst b/src/mame/mame.lst
index 392879d0003..240ea210bb7 100644
--- a/src/mame/mame.lst
+++ b/src/mame/mame.lst
@@ -19831,6 +19831,9 @@ hp9845t_de //
@source:hp/hp98x6.cpp
hp9816a //
+hp9826a //
+hp9836a //
+hp9836c //
@source:hp/hp9k.cpp
hp9816 //