summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/drivers/hp9845.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mame/drivers/hp9845.cpp')
-rw-r--r--src/mame/drivers/hp9845.cpp1148
1 files changed, 1028 insertions, 120 deletions
diff --git a/src/mame/drivers/hp9845.cpp b/src/mame/drivers/hp9845.cpp
index 41bf605fd3a..114a2b7f345 100644
--- a/src/mame/drivers/hp9845.cpp
+++ b/src/mame/drivers/hp9845.cpp
@@ -12,27 +12,27 @@
// *******************************
//
// What's in:
-// - Emulation of 45B and 45C systems
+// - Emulation of 45B, 45C and 45T systems
// - Emulation of both 5061-3001 CPUs
// - LPU & PPU ROMs
// - LPU & PPU RAMs
// - Text mode screen
// - Graphic screen
// - Keyboard
-// - T15 tape drive
+// - T14 and T15 tape drive
// - Software list to load optional ROMs
// - Beeper
// - Correct character generator ROMs (a huge "thank you" to Ansgar Kueckes for the dumps!)
+// - 98775 light pen controller
// What's not yet in:
-// - Better naming of tape drive image (it's now "magt", should be "t15")
+// - Better naming of tape drive image (it's now "magt1" and "magt2", should be "t15" and "t14")
// - Better documentation of this file
-// - Emulation of 45T system
// - Display softkeys on 45C & 45T
// - Better keyboard mapping
// - German keyboard
// What's wrong:
// - Speed, as usual
-// - Light pen sometimes behaves erratically in 45C
+// - Light pen tracing sometimes behaves erratically in 45C and 45T
// What will probably never be in:
// - Integral printer (firmware and character generator ROMs are very difficult to dump)
// - Fast LPU processor (dump of microcode PROMs is not available)
@@ -147,24 +147,34 @@
#define GVIDEO_VBSTART (GVIDEO_VBEND + GVIDEO_VPIXELS)
#define GVIDEO_MEM_SIZE 16384
#define GVIDEO_ADDR_MASK (GVIDEO_MEM_SIZE - 1)
-#define GVIDEO_PA 13
// Constants of 98770A video
// HBEND & VBEND probably are not really 0
-#define VIDEO_770_PIXEL_CLOCK 29798400
-#define VIDEO_770_HTOTAL 1024
-#define VIDEO_770_HBEND 0
-#define VIDEO_770_HBSTART (VIDEO_CHAR_COLUMNS * VIDEO_CHAR_WIDTH)
-#define VIDEO_770_VTOTAL 485
-#define VIDEO_770_VBEND 0
-#define VIDEO_770_VBSTART (VIDEO_770_VBEND + GVIDEO_VPIXELS)
-#define VIDEO_770_ALPHA_L_LIM 80 // Left-side limit of alpha-only horizontal part
-#define VIDEO_770_ALPHA_R_LIM 640 // Right-side limit of alpha-only horizontal part
-
-#define I_GR 0xb0 // graphics intensity
-#define I_AL 0xd0 // alpha intensity
-#define I_CU 0xf0 // graphics cursor intensity
-#define I_LP 0xff // light pen cursor intensity
+#define VIDEO_770_PIXEL_CLOCK 29798400
+#define VIDEO_770_HTOTAL 1024
+#define VIDEO_770_HBEND 0
+#define VIDEO_770_HBSTART (VIDEO_CHAR_COLUMNS * VIDEO_CHAR_WIDTH)
+#define VIDEO_770_VTOTAL 485
+#define VIDEO_770_VBEND 0
+#define VIDEO_770_VBSTART (VIDEO_770_VBEND + GVIDEO_VPIXELS)
+#define VIDEO_770_ALPHA_L_LIM 80 // Left-side limit of alpha-only horizontal part
+#define VIDEO_770_ALPHA_R_LIM 640 // Right-side limit of alpha-only horizontal part
+
+// Constants of 98780A video
+#define VIDEO_780_PIXEL_CLOCK 28224000
+#define VIDEO_780_HTOTAL 896
+#define VIDEO_780_VTOTAL 525
+#define VIDEO_780_HBEND 0
+#define VIDEO_780_HBSTART (VIDEO_CHAR_COLUMNS * VIDEO_CHAR_WIDTH)
+#define VIDEO_780_VBEND 0
+#define VIDEO_780_VBSTART (VIDEO_780_VBEND + GVIDEO_VPIXELS)
+#define VIDEO_780_ALPHA_L_LIM 80 // Left-side limit of alpha-only horizontal part
+#define VIDEO_780_ALPHA_R_LIM 640 // Right-side limit of alpha-only horizontal part
+
+#define I_GR 0xb0 // graphics intensity
+#define I_AL 0xd0 // alpha intensity
+#define I_CU 0xf0 // graphics cursor intensity
+#define I_LP 0xff // light pen cursor intensity
// Palette indexes (for monochromatic screens)
#define PEN_BLACK 0 // Black
@@ -178,9 +188,11 @@ constexpr unsigned LP_FOV = 9; // Field of view
constexpr unsigned LP_XOFFSET = 5; // x-offset of LP (due to delay in hit recognition)
// Peripheral Addresses (PA)
-#define IO_SLOT_FIRST_PA 1
-#define IO_SLOT_LAST_PA 12
-#define T15_PA 15
+#define IO_SLOT_FIRST_PA 1
+#define IO_SLOT_LAST_PA 12
+#define GVIDEO_PA 13
+#define T14_PA 14
+#define T15_PA 15
#define KEY_SCAN_OSCILLATOR 327680
@@ -360,6 +372,7 @@ hp9845_base_state::hp9845_base_state(const machine_config &mconfig, device_type
m_io_key1(*this , "KEY1"),
m_io_key2(*this , "KEY2"),
m_io_key3(*this , "KEY3"),
+ m_t14(*this , "t14"),
m_t15(*this , "t15"),
m_beeper(*this , "beeper"),
m_beep_timer(*this , "beep_timer"),
@@ -368,8 +381,7 @@ hp9845_base_state::hp9845_base_state(const machine_config &mconfig, device_type
m_io_slot2(*this , "slot2"),
m_io_slot3(*this , "slot3"),
m_ram(*this , RAM_TAG),
- m_chargen(*this , "chargen"),
- m_optional_chargen(*this , "optional_chargen")
+ m_chargen(*this , "chargen")
{
}
@@ -680,6 +692,21 @@ WRITE8_MEMBER(hp9845_base_state::pa_w)
}
}
+WRITE_LINE_MEMBER(hp9845_base_state::t14_irq_w)
+{
+ irq_w(T14_PA , state);
+}
+
+WRITE_LINE_MEMBER(hp9845_base_state::t14_flg_w)
+{
+ flg_w(T14_PA , state);
+}
+
+WRITE_LINE_MEMBER(hp9845_base_state::t14_sts_w)
+{
+ sts_w(T14_PA , state);
+}
+
WRITE_LINE_MEMBER(hp9845_base_state::t15_irq_w)
{
irq_w(T15_PA , state);
@@ -725,13 +752,17 @@ protected:
virtual void advance_gv_fsm(bool ds , bool trigger) override;
void update_graphic_bits(void);
+ // Optional character generator
+ required_region_ptr<uint8_t> m_optional_chargen;
+
uint8_t m_video_attr;
uint16_t m_gv_cursor_w; // U38 & U39 (GS)
std::vector<uint16_t> m_graphic_mem;
};
hp9845b_state::hp9845b_state(const machine_config &mconfig, device_type type, const char *tag)
- : hp9845_base_state(mconfig , type , tag)
+ : hp9845_base_state(mconfig , type , tag),
+ m_optional_chargen(*this , "optional_chargen")
{
}
@@ -1245,7 +1276,7 @@ INPUT_PORTS_END
class hp9845ct_state : public hp9845_base_state
{
public:
- hp9845ct_state(const machine_config &mconfig, device_type type, const char *tag, unsigned v_total);
+ hp9845ct_state(const machine_config &mconfig, device_type type, const char *tag);
virtual void machine_start() override;
virtual void machine_reset() override;
@@ -1265,8 +1296,9 @@ protected:
virtual void plot(uint16_t x, uint16_t y, bool draw_erase) = 0;
void draw_line(unsigned x0 , unsigned y0 , unsigned x1 , unsigned y1);
void update_line_pattern(void);
+ void pattern_fill(uint16_t x0 , uint16_t y0 , uint16_t x1 , uint16_t y1 , unsigned fill_idx);
static uint16_t get_gv_mem_addr(unsigned x , unsigned y);
- void update_graphic_bits(void);
+ virtual void update_graphic_bits(void) = 0;
int get_lp_cursor_y_top(void) const;
void render_lp_cursor(unsigned video_scanline , unsigned pen_idx);
@@ -1316,18 +1348,16 @@ protected:
bool m_gv_lp_int_en;
bool m_gv_lp_hit_lt192;
bool m_gv_lp_int_256;
- const unsigned m_gv_v_total;
static const uint16_t m_line_type[];
static const uint16_t m_area_fill[];
};
-hp9845ct_state::hp9845ct_state(const machine_config &mconfig, device_type type, const char *tag, unsigned v_total)
+hp9845ct_state::hp9845ct_state(const machine_config &mconfig, device_type type, const char *tag)
: hp9845_base_state(mconfig , type , tag),
m_lightpen_x(*this, "LIGHTPENX"),
m_lightpen_y(*this, "LIGHTPENY"),
- m_lightpen_sw(*this, "GKEY"),
- m_gv_v_total(v_total)
+ m_lightpen_sw(*this, "GKEY")
{
}
@@ -1522,48 +1552,40 @@ void hp9845ct_state::update_line_pattern(void)
}
}
-uint16_t hp9845ct_state::get_gv_mem_addr(unsigned x , unsigned y)
+void hp9845ct_state::pattern_fill(uint16_t x0 , uint16_t y0 , uint16_t x1 , uint16_t y1 , unsigned fill_idx)
{
- return (uint16_t)((x + y * 35) & GVIDEO_ADDR_MASK);
-}
-
-void hp9845ct_state::update_graphic_bits(void)
-{
- bool gv_ready = m_gv_lp_int_en && m_gv_lp_status;
-
- if (m_gv_gr_en && !gv_ready) {
- gv_ready = m_gv_fsm_state == GV_STAT_WAIT_DS_0 ||
- m_gv_fsm_state == GV_STAT_WAIT_TRIG_0 ||
- m_gv_fsm_state == GV_STAT_WAIT_DS_1 ||
- m_gv_fsm_state == GV_STAT_WAIT_DS_2 ||
- m_gv_fsm_state == GV_STAT_WAIT_TRIG_1;
- }
-
- flg_w(GVIDEO_PA , gv_ready);
+ uint16_t x,y,xmax,ymax;
+ uint16_t pixel_mask, fill_mask;
- bool irq = m_gv_int_en && !m_gv_dma_en && gv_ready;
+ x = std::min(x0 , x1);
+ xmax = std::max(x0 , x1);
+ y = std::min(y0 , y1);
+ ymax = std::max(y0 , y1);
-#if 0
- // DEBUG DEBUG DEBUG
- static bool last_irq = false;
- if (!last_irq && irq) {
- logerror("GV IRQ %d %d\n" , m_gv_lp_int_en , m_gv_lp_status);
+ for (;y <= ymax; y++) {
+ fill_mask = (m_area_fill[ fill_idx ] << (y % 4) * 4) & 0xf000;
+ fill_mask |= (fill_mask >> 4) | (fill_mask >> 8) | (fill_mask >> 12);
+ for (;x <= xmax; x++) {
+ pixel_mask = (0x8000 >> (x % 16));
+ plot(x , y , (pixel_mask & fill_mask) != 0);
+ }
}
- last_irq = irq;
-#endif
-
- irq_w(GVIDEO_PA , irq);
-
- bool dmar = gv_ready && m_gv_dma_en;
+}
- m_ppu->dmar_w(dmar);
+uint16_t hp9845ct_state::get_gv_mem_addr(unsigned x , unsigned y)
+{
+ return (uint16_t)((x + y * 35) & GVIDEO_ADDR_MASK);
}
int hp9845ct_state::get_lp_cursor_y_top(void) const
{
+ // The 770's VTOTAL applies to 780, too.
+ // The 780 hw generates a line clock (GVclk in Duell's schematics) that's suppressed
+ // for lines in [485..524] range so that the total number of lines per frame counted
+ // by this clock matches the total count of lines in 770 (i.e. 485).
int wrapped_cursor_y = m_gv_lp_cursor_y;
- if (wrapped_cursor_y >= GVIDEO_VPIXELS && wrapped_cursor_y < m_gv_v_total) {
- wrapped_cursor_y -= m_gv_v_total;
+ if (wrapped_cursor_y >= GVIDEO_VPIXELS && wrapped_cursor_y < VIDEO_770_VTOTAL) {
+ wrapped_cursor_y -= VIDEO_770_VTOTAL;
}
return wrapped_cursor_y;
@@ -1890,17 +1912,20 @@ protected:
void video_render_buff(unsigned video_scanline , unsigned line_in_row, bool buff_idx);
void graphic_video_render(unsigned video_scanline);
virtual void plot(uint16_t x, uint16_t y, bool draw_erase) override;
- void pattern_fill(uint16_t x0 , uint16_t y0 , uint16_t x1 , uint16_t y1);
void check_io_counter_restore(void);
void advance_io_counter(void);
virtual void advance_gv_fsm(bool ds , bool trigger) override;
+ virtual void update_graphic_bits(void) override;
// Palette indexes
static constexpr unsigned pen_graphic(unsigned rgb) { return rgb; }
static constexpr unsigned pen_alpha(unsigned rgb) { return 8 + rgb; }
static constexpr unsigned pen_cursor(unsigned rgb) { return 16 + rgb; }
+ // Optional character generator
+ required_region_ptr<uint8_t> m_optional_chargen;
+
std::vector<uint16_t> m_graphic_mem[ 3 ];
uint16_t m_gv_music_memory;
uint8_t m_gv_cursor_color;
@@ -1909,7 +1934,8 @@ protected:
};
hp9845c_state::hp9845c_state(const machine_config &mconfig, device_type type, const char *tag)
- : hp9845ct_state(mconfig , type , tag, VIDEO_770_VTOTAL)
+ : hp9845ct_state(mconfig , type , tag),
+ m_optional_chargen(*this , "optional_chargen")
{
}
@@ -2274,26 +2300,6 @@ void hp9845c_state::plot(uint16_t x, uint16_t y, bool draw_erase)
}
}
-void hp9845c_state::pattern_fill(uint16_t x0 , uint16_t y0 , uint16_t x1 , uint16_t y1)
-{
- uint16_t x,y,xmax,ymax;
- uint16_t pixel_mask, fill_mask;
-
- x = std::min(x0 , x1);
- xmax = std::max(x0 , x1);
- y = std::min(y0 , y1);
- ymax = std::max(y0 , y1);
-
- for (;y <= ymax; y++) {
- fill_mask = (m_area_fill[ m_gv_line_type_area_fill & 0xf ] << (y % 4) * 4) & 0xf000;
- fill_mask |= (fill_mask >> 4) | (fill_mask >> 8) | (fill_mask >> 12);
- for (;x <= xmax; x++) {
- pixel_mask = (0x8000 >> (x % 16));
- plot(x , y , (pixel_mask & fill_mask) != 0);
- }
- }
-}
-
void hp9845c_state::check_io_counter_restore(void)
{
if (m_gv_last_cmd != m_gv_cmd) {
@@ -2540,7 +2546,7 @@ void hp9845c_state::advance_gv_fsm(bool ds , bool trigger)
// fill area with pattern
LOG(("area fill (%d,%d) -> (%d,%d) pattern=%04x\n", m_gv_last_xpt, m_gv_last_ypt, m_gv_xpt, m_gv_ypt, m_gv_line_type_area_fill));
- pattern_fill(m_gv_xpt , m_gv_ypt , m_gv_last_xpt , m_gv_last_ypt);
+ pattern_fill(m_gv_xpt , m_gv_ypt , m_gv_last_xpt , m_gv_last_ypt , m_gv_line_type_area_fill & 0xf);
}
m_gv_last_xpt = m_gv_xpt;
m_gv_last_ypt = m_gv_ypt;
@@ -2563,6 +2569,876 @@ void hp9845c_state::advance_gv_fsm(bool ds , bool trigger)
update_graphic_bits();
}
+void hp9845c_state::update_graphic_bits(void)
+{
+ bool gv_ready = m_gv_lp_int_en && m_gv_lp_status;
+
+ if (m_gv_gr_en && !gv_ready) {
+ gv_ready = m_gv_fsm_state == GV_STAT_WAIT_DS_0 ||
+ m_gv_fsm_state == GV_STAT_WAIT_TRIG_0 ||
+ m_gv_fsm_state == GV_STAT_WAIT_DS_1 ||
+ m_gv_fsm_state == GV_STAT_WAIT_DS_2 ||
+ m_gv_fsm_state == GV_STAT_WAIT_TRIG_1;
+ }
+
+ flg_w(GVIDEO_PA , gv_ready);
+
+ bool irq = m_gv_int_en && !m_gv_dma_en && gv_ready;
+
+ irq_w(GVIDEO_PA , irq);
+
+ bool dmar = gv_ready && m_gv_dma_en;
+
+ m_ppu->dmar_w(dmar);
+}
+
+// ***************
+// hp9845t_state
+// ***************
+class hp9845t_state : public hp9845ct_state
+{
+public:
+ hp9845t_state(const machine_config &mconfig, device_type type, const char *tag);
+
+ virtual void machine_start() override;
+ virtual void machine_reset() override;
+
+ virtual DECLARE_READ16_MEMBER(graphic_r) override;
+ virtual DECLARE_WRITE16_MEMBER(graphic_w) override;
+
+ TIMER_DEVICE_CALLBACK_MEMBER(scanline_timer);
+
+protected:
+ virtual void set_graphic_mode(bool graphic , bool alpha) override;
+ void video_render_buff(unsigned video_scanline , unsigned line_in_row, bool buff_idx);
+ void graphic_video_render(unsigned video_scanline);
+ virtual void plot(uint16_t x, uint16_t y, bool draw_erase) override;
+ void draw_arc(uint16_t x0, uint16_t y0, int xstart, int ystart, uint16_t radius, int quadrant, int& draw_counter);
+ void draw_full_arc(int x0 , int y0 , int dx , int dy , int draw_counter);
+
+ virtual void advance_gv_fsm(bool ds , bool trigger) override;
+ virtual void update_graphic_bits(void) override;
+
+ std::vector<uint16_t> m_graphic_mem;
+
+ bool m_gv_stat;
+ bool m_gv_increment_to_next_row;
+
+ uint16_t m_gv_scan_start_x;
+ uint16_t m_gv_scan_start_y;
+ uint8_t m_gv_rb_control;
+ uint16_t m_gv_rb_counter;
+ uint16_t m_gv_rb_memory[ 256 ];
+ uint16_t m_gv_arc[ 4 ];
+ uint8_t m_gv_arc_parm;
+ bool m_back_arrow_cursor;
+
+ static const uint8_t m_back_arrow_shape[];
+};
+
+hp9845t_state::hp9845t_state(const machine_config &mconfig, device_type type, const char *tag)
+ : hp9845ct_state(mconfig , type , tag)
+{
+}
+
+void hp9845t_state::machine_start()
+{
+ // Common part first
+ hp9845ct_state::machine_start();
+
+ m_graphic_mem.resize(GVIDEO_MEM_SIZE);
+
+ // initialize palette
+ m_palette->set_pen_color(PEN_BLACK , 0x00, 0x00, 0x00); // black
+ m_palette->set_pen_color(PEN_GRAPHIC, 0x00, I_GR, 0x00); // graphics
+ m_palette->set_pen_color(PEN_ALPHA , 0x00, I_AL, 0x00); // alpha
+ m_palette->set_pen_color(PEN_CURSOR , 0x00, I_CU, 0x00); // graphics cursor
+ m_palette->set_pen_color(PEN_LP , 0x00, I_LP, 0x00); // lightpen cursor
+}
+
+void hp9845t_state::machine_reset()
+{
+ // Common part first
+ hp9845ct_state::machine_reset();
+
+ m_gv_stat = false;
+ m_gv_increment_to_next_row = false;
+ m_gv_scan_start_x = 0;
+ m_gv_scan_start_y = 0;
+ m_gv_rb_control = 0;
+ m_gv_rb_counter = 0;
+ memset(m_gv_rb_memory, 0, sizeof(m_gv_rb_memory));
+ m_back_arrow_cursor = false;
+
+ set_video_mar(0);
+}
+
+READ16_MEMBER(hp9845t_state::graphic_r)
+{
+ uint16_t res = 0;
+
+ switch (offset) {
+ case 0:
+ // R4: data register
+ if (m_gv_lp_en) {
+ res = lp_r4_r();
+ } else {
+ res = m_gv_data_r;
+ }
+ advance_gv_fsm(true , false);
+ break;
+
+ case 1:
+ // R5: status register
+ if (m_gv_int_en) {
+ BIT_SET(res, 7);
+ }
+ if (m_gv_dma_en) {
+ BIT_SET(res, 6);
+ }
+ if (m_gv_lp_status && m_gv_lp_int_en) {
+ BIT_SET(res, 0); // Lightpen service request
+ }
+ // TODO: gsr/
+ // TODO: fix sk status
+ if (m_gv_sk_status) {
+ BIT_SET(res, 1); // Softkey service request
+ m_gv_sk_status = false;
+ }
+ BIT_SET(res, 9); // ID
+ BIT_SET(res, 11); // ID
+ if (m_gv_stat) {
+ BIT_SET(res, 13); // error indication
+ }
+
+ update_graphic_bits();
+ break;
+
+ case 2:
+ // R6: data register with DMA TC
+ m_gv_dma_en = false;
+ if (m_gv_lp_en) {
+ res = lp_r4_r();
+ } else {
+ res = m_gv_data_r;
+ }
+ advance_gv_fsm(true , false);
+ break;
+
+ case 3:
+ // R7: not mapped
+ break;
+ }
+
+ LOG(("rd gv R%u = %04x\n", 4 + offset , res));
+
+ return res;
+}
+
+WRITE16_MEMBER(hp9845t_state::graphic_w)
+{
+ LOG(("wr gv R%u = %04x\n", 4 + offset , data));
+
+ switch (offset) {
+ case 0:
+ // R4: data register
+ m_gv_data_w = data;
+ advance_gv_fsm(true , false);
+ lp_r4_w(data);
+ if (m_gv_lp_int_en) {
+ m_gv_lp_fullbright = BIT(data , 1);
+ }
+ break;
+
+ case 1:
+ // R5: command register
+ m_gv_cmd = (uint8_t)(data & 0xf);
+ m_gv_dma_en = BIT(data , 6) != 0;
+ m_gv_int_en = BIT(data , 7) != 0;
+ m_gv_gr_en = BIT(data , 8); // enables graphics controller & vector generator command processing and IRQs
+ m_gv_sk_en = BIT(data , 9); // enables reads on R4 to return SK keycode, also enables SK IRQs
+ m_gv_opt_en = BIT(data , 11); // not really used
+ m_gv_dsa_en = BIT(data , 12); // for factory use only (function unknown)
+ m_gv_fsm_state = GV_STAT_RESET; // command/reset state machine
+ lp_r5_w(data);
+ advance_gv_fsm(false , false);
+ break;
+
+ case 2:
+ // R6: data register with DMA TC
+ m_gv_dma_en = false;
+ m_gv_data_w = data;
+ lp_r4_w(data);
+ if (m_gv_lp_int_en) {
+ m_gv_lp_fullbright = BIT(data , 1);
+ }
+ advance_gv_fsm(true , false);
+ break;
+
+ case 3:
+ // R7: not mapped
+ break;
+ }
+}
+
+TIMER_DEVICE_CALLBACK_MEMBER(hp9845t_state::scanline_timer)
+{
+ unsigned video_scanline = param;
+
+ if (video_scanline >= VIDEO_780_VBEND && video_scanline < VIDEO_780_VBSTART) {
+ if (m_graphic_sel) {
+ graphic_video_render(video_scanline - VIDEO_780_VBEND);
+ }
+ unsigned row = (video_scanline - VIDEO_780_VBEND) / VIDEO_CHAR_HEIGHT;
+ unsigned line_in_row = (video_scanline - VIDEO_780_VBEND) - row * VIDEO_CHAR_HEIGHT;
+
+ if (line_in_row == 0) {
+ // Start of new row, swap buffers
+ m_video_buff_idx = !m_video_buff_idx;
+ video_fill_buff(!m_video_buff_idx);
+ }
+ video_render_buff(video_scanline , line_in_row , m_video_buff_idx);
+ // Lightpen cursor
+ if (m_graphic_sel) {
+ render_lp_cursor(video_scanline - VIDEO_780_VBEND , PEN_LP);
+ }
+ }
+ lp_scanline_update(video_scanline - VIDEO_780_VBEND);
+}
+
+void hp9845t_state::set_graphic_mode(bool graphic , bool alpha)
+{
+ m_back_arrow_cursor = graphic; // triggers back arrow cursor, 98780A uses video on/off command for enabling/disabling graphics
+ m_alpha_sel = alpha;
+}
+
+void hp9845t_state::video_render_buff(unsigned video_scanline , unsigned line_in_row, bool buff_idx)
+{
+ if (!m_video_buff[ buff_idx ].full) {
+ m_video_blanked = true;
+ }
+
+ const pen_t *pen = m_palette->pens();
+
+ if (m_video_blanked || !m_alpha_sel) {
+ // Blank scanline
+ for (unsigned i = 0; i < VIDEO_780_ALPHA_L_LIM; i++) {
+ m_bitmap.pix32(video_scanline , i) = pen[ PEN_BLACK ];
+ }
+ if (!m_graphic_sel) {
+ for (unsigned i = VIDEO_780_ALPHA_L_LIM; i < VIDEO_780_ALPHA_R_LIM; i++) {
+ m_bitmap.pix32(video_scanline , i) = pen[ PEN_BLACK ];
+ }
+ }
+ for (unsigned i = VIDEO_780_ALPHA_R_LIM; i < VIDEO_TOT_HPIXELS; i++) {
+ m_bitmap.pix32(video_scanline , i) = pen[ PEN_BLACK ];
+ }
+ } else {
+ bool cursor_line = line_in_row == 12;
+ bool ul_line = line_in_row == 14;
+ unsigned video_frame = (unsigned)m_screen->frame_number();
+ bool cursor_blink = BIT(video_frame , 3);
+ bool char_blink = BIT(video_frame , 4);
+
+ for (unsigned i = 0; i < 80; i++) {
+ uint8_t attrs = m_video_buff[ buff_idx ].attrs[ i ];
+ uint16_t pixels;
+
+ if (!BIT(attrs , 2) || char_blink) {
+ if (ul_line && BIT(attrs , 3)) {
+ pixels = ~0;
+ } else {
+ // The 98780A uses two identical 4KB ROMs interlaced to keep up with the speed of
+ // the video circuit. Each of the 4K ROMs contains the full character set.
+ // The 98780A fills row 0 (space between characters) controlled by row 1 and 2 from
+ // the character ROM, thereby providing line drawing characters for continuous lines
+ uint8_t charcode = m_video_buff[ buff_idx ].chars[ i ];
+ uint16_t chrgen_addr = ((uint16_t)(charcode ^ 0xff) << 4) | (line_in_row + 1);
+ pixels = (uint16_t)m_chargen[ chrgen_addr ] << 1;
+ if ((charcode & 0xe0) == 0xe0 && (pixels & 0x6) == 0x6) {
+ pixels |= 1;
+ }
+ }
+ } else {
+ pixels = 0;
+ }
+
+ if (cursor_blink && BIT(attrs , 0)) {
+ if (m_back_arrow_cursor) {
+ // back arrow cursor (HP's hardware easter egg)
+ pixels |= m_back_arrow_shape[ line_in_row ];
+ } else if (cursor_line) {
+ pixels = ~0;
+ }
+ }
+
+ if (BIT(attrs , 1)) {
+ pixels = ~pixels;
+ }
+
+ for (unsigned j = 0; j < 9; j++) {
+ bool pixel = (pixels & (1U << j)) != 0;
+ unsigned x = i * 9 + j;
+
+ if (m_graphic_sel && x >= VIDEO_780_ALPHA_L_LIM && x < VIDEO_780_ALPHA_R_LIM) {
+ // alpha overlays graphics (non-dominating)
+ if (pixel) {
+ m_bitmap.pix32(video_scanline , x) = pen[ PEN_ALPHA ];
+ }
+ } else {
+ // Graphics disabled or alpha-only zone
+ m_bitmap.pix32(video_scanline , x) = pen[ pixel ? PEN_ALPHA : PEN_BLACK ];
+ }
+ }
+ }
+ }
+}
+
+void hp9845t_state::graphic_video_render(unsigned video_scanline)
+{
+ // video_scanline is 0-based, i.e. the topmost visible line of graphic screen is 0
+ const pen_t *pen = m_palette->pens();
+ bool yc, yw, blink;
+ uint16_t word;
+
+ yc = (video_scanline + 44) == m_gv_cursor_y;
+
+ if (m_gv_cursor_fs) {
+ yw = true;
+ // Steady cursor
+ blink = true;
+ } else {
+ // 9 pixel
+ yw = (video_scanline + 44 > m_gv_cursor_y - 5) &&
+ (video_scanline + 44 < m_gv_cursor_y + 5);
+ if (m_gv_cursor_gc) {
+ blink = true;
+ } else {
+ // Blinking cursor (frame freq. / 16)
+ blink = BIT(m_screen->frame_number() , 3) != 0;
+ }
+ }
+
+ unsigned mem_idx = get_gv_mem_addr(m_gv_scan_start_x >> 4, video_scanline + m_gv_scan_start_y);
+ for (unsigned i = 0; i < GVIDEO_HPIXELS; i += 16) {
+ word = m_graphic_mem[ mem_idx ];
+ mem_idx++;
+ // Is wraparound better?
+ if (mem_idx > GVIDEO_ADDR_MASK) return;
+ unsigned x = i;
+ for (uint16_t mask = 0x8000; mask != 0; mask >>= 1) {
+ bool xc = (x + 63) == m_gv_cursor_x;
+ bool xw = m_gv_cursor_fs || ((x + 67 >= m_gv_cursor_x) && (x + 59 <= m_gv_cursor_x));
+ unsigned pixel;
+
+ if (blink && ((xw && yc) || (yw && xc && m_gv_cursor_gc))) {
+ pixel = PEN_CURSOR;
+ } else {
+ // Normal pixel
+ if (m_gv_lp_fullbright)
+ pixel = word & mask ? PEN_LP : PEN_BLACK;
+ else
+ pixel = word & mask ? PEN_GRAPHIC : PEN_BLACK;
+ }
+ m_bitmap.pix32(video_scanline , VIDEO_780_ALPHA_L_LIM + x++) = pen[ pixel ];
+ }
+ }
+}
+
+void hp9845t_state::plot(uint16_t x, uint16_t y, bool draw_erase)
+{
+ uint16_t addr, pixel_mask;
+ bool do_draw;
+
+ pixel_mask = 0x8000 >> (x & 0xf);
+ addr = get_gv_mem_addr(x >> 4 , y);
+ if (BIT(m_gv_rb_control, 1)) {
+ // save graphics memory to rubber band memory
+ if (m_graphic_mem[ addr ] & pixel_mask)
+ m_gv_rb_memory[m_gv_rb_counter/16] |= 0x1 << (m_gv_rb_counter % 16); // set
+ else
+ m_gv_rb_memory[m_gv_rb_counter/16] &= ~(0x1 << (m_gv_rb_counter % 16)); // clear
+ m_gv_rb_counter++;
+ if (m_gv_rb_counter > 4095) {
+ m_gv_stat = true; // we might prevent data corruption here, but the original hardware doesn't
+ m_gv_rb_counter = 0;
+ }
+ } else if (BIT(m_gv_rb_control, 0)) {
+ // restore graphics memory from rubber band memory
+ if (BIT(m_gv_rb_memory[m_gv_rb_counter / 16], m_gv_rb_counter % 16))
+ m_graphic_mem[ addr ] |= pixel_mask; // set
+ else
+ m_graphic_mem[ addr ] &= ~pixel_mask; // clear
+ m_gv_rb_counter++;
+ if (m_gv_rb_counter > 4095) {
+ m_gv_stat = true;
+ m_gv_rb_counter = 0;
+ }
+ } else {
+ // draw/erase pixel
+ do_draw = m_gv_memory_control ? draw_erase : !draw_erase;
+ if (do_draw)
+ m_graphic_mem[ addr ] |= pixel_mask;
+ else
+ m_graphic_mem[ addr ] &= ~pixel_mask;
+ }
+}
+
+void hp9845t_state::draw_arc(uint16_t x0, uint16_t y0, int xstart, int ystart, uint16_t radius, int quadrant, int& draw_counter)
+{
+ int i, x1, y1, d;
+ bool draw_erase;
+ bool do_plot = (xstart < 0) || (ystart < 0);
+ int save_x1[560];
+
+ /* quadrants:
+ *
+ * 1 | 0
+ * ---+---
+ * 2 | 3
+ *
+ * Note: we are using Horn's algorithm here, however it is not 100% pixel
+ * accurate with the original vector generator
+ */
+
+ d = -radius;
+ x1 = radius;
+ y1 = 0;
+ i = 0;
+ while ((x1 > 0) && (draw_counter > 0)) {
+ // check for plot start
+ if (!do_plot) {
+ if ((quadrant % 2) == 0) {
+ if ((x1 <= xstart) && (y1 >= ystart)) do_plot = true;
+ } else {
+ if ((y1 >= xstart) && (x1 <= ystart)) do_plot = true;
+ }
+ }
+
+ // draw pixels in quadrants
+ draw_erase = BIT(m_gv_line_type_mask, 15);
+ if (do_plot) {
+ switch (quadrant) {
+ case 0:
+ plot(x0 + x1, y0 - y1, draw_erase); // quadrant 0
+ break;
+ case 1:
+ plot(x0 - y1, y0 - x1, draw_erase); // quadrant 1
+ break;
+ case 2:
+ plot(x0 - x1, y0 + y1, draw_erase); // quadrant 2
+ break;
+ case 3:
+ plot(x0 + y1, y0 + x1, draw_erase); // quadrant 3
+ break;
+ }
+ }
+
+ // update coordinates
+ if (x1 > y1) {
+ save_x1[i++] = x1;
+ d += 2 * y1 + 1;
+ y1++;
+ if (do_plot) draw_counter--;
+ if (d > 0) {
+ x1--;
+ if (do_plot) draw_counter--;
+ d -= 2 * x1;
+ }
+ if (x1 <= y1) draw_counter++;
+ }
+ else {
+ x1--;
+ y1 = save_x1[--i];
+ if (do_plot) {
+ draw_counter--;
+ if (save_x1[i] != save_x1[i+1])
+ draw_counter--;
+ }
+ }
+ update_line_pattern();
+ }
+}
+
+void hp9845t_state::draw_full_arc(int x0 , int y0 , int dx , int dy , int draw_counter)
+{
+ // radius
+ int radius = sqrt(dx * dx + dy * dy);
+
+ LOG(("midpoint = (%d,%d) radius = %d ctrl = %d count = %d\n", x0, y0, radius, m_gv_memory_control, draw_counter));
+
+ /* quadrants:
+ *
+ * 1 | 0
+ * ---+---
+ * 2 | 3
+ */
+
+ int quadrant = 0;
+
+ // determine the start quadrant
+ if (dx > 0)
+ quadrant = (dy < 0) ? 1 : 2;
+ else
+ quadrant = (dy <= 0) ? 0 : 3;
+
+ draw_arc(x0, y0, abs(dx), abs(dy), radius, quadrant, draw_counter);
+ while (draw_counter > 0) {
+ quadrant++;
+ if (quadrant > 3) {
+ quadrant = 0;
+ }
+ draw_arc(x0, y0, -1, -1, radius, quadrant, draw_counter);
+ }
+}
+
+void hp9845t_state::advance_gv_fsm(bool ds , bool trigger)
+{
+ if (!m_gv_gr_en) {
+ return;
+ }
+
+ bool get_out = false;
+
+ attotime time_mem_av;
+
+ do {
+ switch (m_gv_fsm_state) {
+ case GV_STAT_WAIT_DS_0:
+ // inital state (same as GV_STAT_RESET), command received
+ if (m_gv_cmd == 0x9) {
+ // read words command
+ if (m_gv_last_cmd != m_gv_cmd) {
+ m_gv_io_counter = get_gv_mem_addr(m_gv_word_x_position , m_gv_word_y_position);
+ }
+ LOG(("read words, last = %x\n", m_gv_last_cmd));
+ m_gv_fsm_state = GV_STAT_WAIT_MEM_0; // -> read stream
+ m_gv_last_cmd = m_gv_cmd;
+ } else if (m_gv_cmd == 0xd) {
+ // fast clear/set command
+ m_gv_fsm_state = GV_STAT_WAIT_MEM_2;
+ m_gv_last_cmd = m_gv_cmd;
+ } else if (ds) {
+ if (m_gv_cmd == 0x8) {
+ // write words command
+ if (m_gv_last_cmd != m_gv_cmd) {
+ m_gv_io_counter = get_gv_mem_addr(m_gv_word_x_position , m_gv_word_y_position);
+ }
+ LOG(("write words\n"));
+ m_gv_fsm_state = GV_STAT_WAIT_TRIG_1; // -> write stream
+ } else {
+ // any other command
+ m_gv_fsm_state = GV_STAT_WAIT_TRIG_0; // -> wait for trigger
+ }
+ m_gv_last_cmd = m_gv_cmd;
+ } else {
+ get_out = true;
+ }
+ break;
+
+ case GV_STAT_WAIT_TRIG_0:
+ // process data on R4 or R6
+ switch (m_gv_cmd) {
+ case 0x1: // load end points
+ m_gv_ypt = m_gv_data_w & 0x3ff;
+ LOG(("load end points y = %d\n", m_gv_ypt));
+ break;
+ case 0x3: // load arc
+ m_gv_arc_parm = 0;
+ m_gv_arc[ m_gv_arc_parm ] = m_gv_data_w;
+ LOG(("load arc parm%d = %04x\n", m_gv_arc_parm, m_gv_arc[m_gv_arc_parm]));
+ m_gv_arc_parm++;
+ break;
+ case 0x5: // load scan
+ m_gv_scan_start_x = m_gv_data_w & 0x3ff; // 0..559
+ LOG(("load scan x = %d\n", m_gv_scan_start_x));
+ break;
+ case 0x6: // set line type/area fill
+ m_gv_line_type_area_fill = m_gv_data_w & 0x1ff;
+ if (BIT(m_gv_line_type_area_fill, 4)) {
+ m_gv_line_type_mask = m_line_type[ m_gv_line_type_area_fill & 0x7 ];
+ m_gv_repeat_count = 0;
+ }
+ LOG(("set line type = %04x\n", m_gv_line_type_area_fill));
+ break;
+ case 0x7: // load X/Y I/O address
+ m_gv_word_y_position = m_gv_data_w & 0x1ff; // 0..454
+ LOG(("load X/Y I/O adress y = %04x\n", m_gv_word_y_position));
+ break;
+ case 0xa: // load memory control
+ // A single bit is saved (InvBit)
+ m_gv_memory_control = (m_gv_data_w & 0x9) == 9 || (m_gv_data_w & 0x12) == 0x12 || (m_gv_data_w & 0x24) == 0x24;
+ LOG(("load memory control = %04x\n", m_gv_memory_control));
+ break;
+ case 0xb: // video on/off - enable graphics video output (1=on 2=off)
+ m_graphic_sel = BIT(m_gv_data_w, 0);
+ LOG(("video on/off parm = %d\n", m_gv_data_w & 0x3));
+ break;
+ case 0xc: // load color mask (no effect, just for compatibility with 9845c), takes a single word as parameter
+ break;
+ case 0xe: // Y cursor position
+ m_gv_cursor_fs = (m_gv_data_w & 0x3) == 0;
+ m_gv_cursor_gc = ((m_gv_data_w & 0x3) == 1) || m_gv_cursor_fs;
+ m_gv_cursor_y = 559 - (m_gv_data_w >> 7);
+ if (m_gv_cursor_fs) m_gv_cursor_y -= 4;
+ LOG(("Y cursor position = %d, fs = %d, gc = %d\n", m_gv_cursor_y, m_gv_cursor_fs, m_gv_cursor_gc));
+ break;
+ case 0xf: // X cursor position
+ m_gv_cursor_x = ((m_gv_data_w >> 6) & 0x3ff) - 121;
+ LOG(("X cursor position = %d\n", m_gv_cursor_x));
+ break;
+ default:
+ LOG(("unknown 98780A command = %d, parm = 0x%04x\n", m_gv_cmd, m_gv_data_w));
+ }
+ if ((m_gv_cmd == 0x1) || (m_gv_cmd == 0x3) || (m_gv_cmd == 0x5) || (m_gv_cmd == 0x7)) {
+ m_gv_fsm_state = GV_STAT_WAIT_DS_2; // -> get second data word
+ } else {
+ get_out = true;
+ m_gv_fsm_state = GV_STAT_WAIT_DS_0; // -> done
+ }
+ break;
+
+ case GV_STAT_WAIT_MEM_0:
+ // process data during read transfer
+ time_mem_av = time_to_gv_mem_availability();
+ if (time_mem_av.is_zero()) {
+ // Read a word from graphic memory
+ m_gv_data_r = m_graphic_mem[ m_gv_io_counter ];
+ LOG(("read words @%04x = %04x\n" , m_gv_io_counter , m_gv_data_r));
+ m_gv_io_counter = (m_gv_io_counter + 1) & GVIDEO_ADDR_MASK;
+ m_gv_fsm_state = GV_STAT_WAIT_DS_1; // -> proceed with read stream
+ } else {
+ m_gv_timer->adjust(time_mem_av);
+ get_out = true;
+ }
+ break;
+
+ case GV_STAT_WAIT_DS_1:
+ // wait for data word to be read
+ if (ds) {
+ // -- next word
+ m_gv_fsm_state = GV_STAT_WAIT_MEM_0; // -> process data word
+ } else {
+ // -- done
+ get_out = true;
+ }
+ break;
+
+ case GV_STAT_WAIT_DS_2:
+ // wait for data word to be written
+ if (ds) {
+ // -- next word
+ m_gv_fsm_state = GV_STAT_WAIT_TRIG_1; // -> process data word
+ } else {
+ // done
+ get_out = true;
+ }
+ break;
+
+ case GV_STAT_WAIT_TRIG_1:
+ // process multi-word parameters & data during write transfer
+ switch (m_gv_cmd) {
+ case 0x1:
+ // load endpoints command
+ m_gv_xpt = m_gv_data_w & 0x3ff;
+ // RB control is actually set whenever a data word is written into R4/R6 register, not just here
+ m_gv_rb_control = (m_gv_data_w >> 13) & 0x7;
+ if (BIT(m_gv_rb_control, 2)) {
+ m_gv_rb_counter = 0;
+ m_gv_stat = false;
+ }
+ if (BIT(m_gv_data_w, 11)) {
+ // draw vector
+ LOG(("load end points x = %d, rb = %d (draw)\n", m_gv_xpt, m_gv_rb_control));
+ m_gv_fsm_state = GV_STAT_WAIT_MEM_2; // -> proceed with draw vector
+ } else {
+ LOG(("load end points x = %d, rb = %d (move)\n", m_gv_xpt, m_gv_rb_control));
+ m_gv_last_xpt = m_gv_xpt;
+ m_gv_last_ypt = m_gv_ypt;
+ m_gv_fsm_state = GV_STAT_WAIT_DS_0; // -> proceed with next word pair
+ }
+ break;
+
+ case 0x3:
+ // load arc
+ m_gv_arc[ m_gv_arc_parm ] = m_gv_data_w;
+ LOG(("load arc parm%d = %04x\n", m_gv_arc_parm, m_gv_arc[m_gv_arc_parm]));
+ m_gv_arc_parm++;
+ if (m_gv_arc_parm < 4) {
+ m_gv_fsm_state = GV_STAT_WAIT_DS_2; // -> proceed with next word
+ } else {
+ m_gv_fsm_state = GV_STAT_WAIT_MEM_2; // -> proceed with draw vector
+ }
+ break;
+
+ case 0x5:
+ // load scan
+ m_gv_scan_start_y = m_gv_data_w & 0x3ff; // 0..454
+ LOG(("load scan y = %d\n", m_gv_scan_start_y));
+ m_gv_fsm_state = GV_STAT_WAIT_DS_0;
+ break;
+
+ case 0x7:
+ // load X/Y I/O address
+ m_gv_word_x_position = (m_gv_data_w & 0x3f0) >> 4; // 0..34
+ m_gv_increment_to_next_row = BIT(m_gv_data_w, 11);
+ m_gv_io_counter = get_gv_mem_addr(m_gv_word_x_position , m_gv_word_y_position);
+ LOG(("load X/Y I/O adress x = %04x increment = %d\n", m_gv_word_x_position, m_gv_increment_to_next_row));
+ m_gv_fsm_state = GV_STAT_WAIT_DS_0;
+ break;
+
+ case 0x8:
+ // write words command
+ m_gv_fsm_state = GV_STAT_WAIT_MEM_1; // -> proceed with next word
+ break;
+ }
+ break;
+
+ case GV_STAT_WAIT_MEM_1:
+ // -- transfer from bus to graphics memory to bus within write transfer
+ time_mem_av = time_to_gv_mem_availability();
+ if (time_mem_av.is_zero()) {
+ // Write a full word to graphic memory
+ LOG(("write words @%04x = %04x\n" , m_gv_io_counter , m_gv_data_w));
+ m_graphic_mem[ m_gv_io_counter ] = m_gv_data_w;
+ if (!m_gv_increment_to_next_row || (m_gv_word_x_position < 34)) {
+ m_gv_io_counter = (m_gv_io_counter + 1) & GVIDEO_ADDR_MASK;
+ }
+ m_gv_fsm_state = GV_STAT_WAIT_DS_2; // -> proceed with write stream
+ } else {
+ m_gv_timer->adjust(time_mem_av);
+ get_out = true;
+ }
+ break;
+
+ case GV_STAT_WAIT_MEM_2:
+ // vector generator
+ time_mem_av = time_to_gv_mem_availability();
+ if (time_mem_av.is_zero()) {
+ if (m_gv_cmd == 0xd) {
+ // fast clear/set command
+ if (m_gv_memory_control) {
+ LOG(("fast clear/set (set)\n"));
+ for (auto& el : m_graphic_mem) {
+ el = 0xffff;
+ }
+ } else {
+ LOG(("fast clear/set (clear)\n"));
+ for (auto& el : m_graphic_mem) {
+ el = 0;
+ }
+ }
+ } else {
+ if (m_gv_cmd == 0x3) {
+ // draw arc/circle
+ // drawing is performed counter-clockwise by using Horn's algorithm
+ // m_gv_arc[0] is the delta last load endpoint y coordinate minus the midpoint y coordinate
+ // m_gv_arc[1] is the delta last load endpoint x coordinate minus the midpoint x coordinate
+ // m_gv_arc[2] is the (probably) the start count (?), actually ignored
+ // m_gv_arc[3] is the total horizontal + vertical count for the 4 quadrants counter-clockwise, starting at the last load endpoint (equals 4 times radius for full circles)
+ LOG(("arc draw\n"));
+
+ // midpoint
+ int dx = BIT(m_gv_arc[ 1 ] , 15) ? (int)m_gv_arc[ 1 ] - 65536 : m_gv_arc[ 1 ] & 0x7ff;
+ int dy = BIT(m_gv_arc[ 0 ] , 15) ? (int)m_gv_arc[ 0 ] - 65536 : m_gv_arc[ 0 ];
+ int x0 = m_gv_xpt + dx;
+ int y0 = m_gv_ypt - dy;
+
+ draw_full_arc(x0 , y0 , dx , dy , m_gv_arc[ 3 ]);
+ } else if (BIT (m_gv_line_type_area_fill, 4)) {
+ unsigned x0;
+ unsigned x1;
+ unsigned y0;
+ unsigned y1;
+
+ LOG(("line draw (%d,%d)->(%d,%d)\n", m_gv_last_xpt, m_gv_last_ypt, m_gv_xpt, m_gv_ypt));
+
+ // vector generator uses normalization
+ if (m_gv_xpt > m_gv_last_xpt) {
+ x0 = m_gv_last_xpt;
+ y0 = m_gv_last_ypt;
+ x1 = m_gv_xpt;
+ y1 = m_gv_ypt;
+ } else {
+ x0 = m_gv_xpt;
+ y0 = m_gv_ypt;
+ x1 = m_gv_last_xpt;
+ y1 = m_gv_last_ypt;
+ }
+ draw_line(x0 , y0 , x1 , y1);
+ } else {
+ // fill area with pattern
+ LOG(("area fill (%d,%d) -> (%d,%d) pattern=%04x\n", m_gv_last_xpt, m_gv_last_ypt, m_gv_xpt, m_gv_ypt, m_gv_line_type_area_fill));
+
+ pattern_fill(m_gv_xpt , m_gv_ypt , m_gv_last_xpt , m_gv_last_ypt , 15 - (m_gv_line_type_area_fill & 0xf));
+ }
+ m_gv_last_xpt = m_gv_xpt;
+ m_gv_last_ypt = m_gv_ypt;
+ }
+ m_gv_fsm_state = GV_STAT_WAIT_DS_0;
+ } else {
+ m_gv_timer->adjust(time_mem_av);
+ }
+ get_out = true;
+ break;
+
+ default:
+ logerror("Invalid state reached %d\n" , m_gv_fsm_state);
+ m_gv_fsm_state = GV_STAT_RESET;
+ }
+
+ ds = false;
+ } while (!get_out);
+
+ update_graphic_bits();
+}
+
+void hp9845t_state::update_graphic_bits(void)
+{
+ bool gv_ready = m_gv_lp_int_en && m_gv_lp_status;
+
+ if (m_gv_gr_en && !gv_ready) {
+ gv_ready = m_gv_fsm_state == GV_STAT_WAIT_DS_0 ||
+ m_gv_fsm_state == GV_STAT_WAIT_DS_1 ||
+ m_gv_fsm_state == GV_STAT_WAIT_DS_2;
+ }
+
+ // WARNING! Race conditions here!
+ // FLG and IRQ are raised together. In enhgfxb ROM a SFC instruction
+ // that spins on itself waiting for FLG to be true was getting
+ // stuck because the interrupt always took precedence (and the ISR
+ // cleared the FLG bit).
+ // In real hw there was a non-zero chance that SFC exited the loop before
+ // interrupt was serviced. In case SFC stayed in the loop, it got another
+ // chance at the next interrupt.
+ // Fix for this problem is in commit 27004d00
+ // My apologies to Tony Duell for doubting at one point the correctness
+ // of his 98780A schematics.. :)
+ flg_w(GVIDEO_PA , gv_ready);
+
+ bool irq = m_gv_int_en && !m_gv_dma_en && gv_ready;
+
+#if 0
+ // DEBUG DEBUG DEBUG
+ static bool last_irq = false;
+ if (!last_irq && irq) {
+ logerror("GV IRQ %d %d %d\n" , gv_ready , m_gv_lp_int_en , m_gv_lp_status);
+ }
+ last_irq = irq;
+#endif
+
+ irq_w(GVIDEO_PA , irq);
+
+ bool dmar = gv_ready && m_gv_dma_en;
+
+ m_ppu->dmar_w(dmar);
+}
+
+const uint8_t hp9845t_state::m_back_arrow_shape[] = {
+ 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xfc,
+ 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00, 0x00
+};
+
static MACHINE_CONFIG_START( hp9845a, hp9845_state )
//MCFG_CPU_ADD("lpu", HP_5061_3010, XTAL_11_4MHz)
//MCFG_CPU_ADD("ppu", HP_5061_3011, XTAL_11_4MHz)
@@ -2594,31 +3470,31 @@ static MACHINE_CONFIG_START( hp9835a, hp9845_state )
MACHINE_CONFIG_END
/*
- Global memory map in blocks of 32 kwords / 64 kbytes each:
-
- block 0: 0x000000 - 0x007fff (LPU RAM)
- block 1: 0x008000 - 0x00ffff (PPU RAM, only 0x00c000 - 0x00ffff used)
- block 2: 0x010000 - 0x017fff (unused)
- block 3: 0x018000 - 0x01ffff (LPU system ROM)
- block 4: 0x020000 - 0x027fff (LPU RAM)
- block 5: 0x028000 - 0x02ffff (PPU system ROM)
- block 6: 0x030000 - 0x037fff (LPU RAM)
- block 7: 0x038000 - 0x03ffff (LPU option ROM)
- block 10: 0x040000 - 0x047fff (LPU RAM)
- block 11: 0x048000 - 0x04ffff (PPU option ROM)
- block 12: 0x050000 - 0x057fff (LPU RAM)
- block 13: 0x058000 - 0x05ffff (LPU option ROM)
- block 14: 0x060000 - 0x067fff (LPU RAM)
- block 15: 0x068000 - 0x06ffff (PPU option ROM)
- block 16: 0x070000 - 0x077fff (LPU RAM)
- block 17: 0x078000 - 0x07ffff (unused)
-
- notes:
- - all block numbers are octal
- - blocks 20 to 76 are reserved for 512 kbyte RAM boards (p/n 09845-66590)
- - block 45 is reserved for the Test ROM
- - memory addresses are continuous (for convenience, the mapping below uses block numbers as
- address part above 0xffff, so there are gaps between 0x8000 and 0xffff which are masked out).
+ Global memory map in blocks of 32 kwords / 64 kbytes each:
+
+ block 0: 0x000000 - 0x007fff (LPU RAM)
+ block 1: 0x008000 - 0x00ffff (PPU RAM, only 0x00c000 - 0x00ffff used)
+ block 2: 0x010000 - 0x017fff (unused)
+ block 3: 0x018000 - 0x01ffff (LPU system ROM)
+ block 4: 0x020000 - 0x027fff (LPU RAM)
+ block 5: 0x028000 - 0x02ffff (PPU system ROM)
+ block 6: 0x030000 - 0x037fff (LPU RAM)
+ block 7: 0x038000 - 0x03ffff (LPU option ROM)
+ block 10: 0x040000 - 0x047fff (LPU RAM)
+ block 11: 0x048000 - 0x04ffff (PPU option ROM)
+ block 12: 0x050000 - 0x057fff (LPU RAM)
+ block 13: 0x058000 - 0x05ffff (LPU option ROM)
+ block 14: 0x060000 - 0x067fff (LPU RAM)
+ block 15: 0x068000 - 0x06ffff (PPU option ROM)
+ block 16: 0x070000 - 0x077fff (LPU RAM)
+ block 17: 0x078000 - 0x07ffff (unused)
+
+ notes:
+ - all block numbers are octal
+ - blocks 20 to 76 are reserved for 512 kbyte RAM boards (p/n 09845-66590)
+ - block 45 is reserved for the Test ROM
+ - memory addresses are continuous (for convenience, the mapping below uses block numbers as
+ address part above 0xffff, so there are gaps between 0x8000 and 0xffff which are masked out).
- all LPU RAM is dynamically mapped at machine start according to -ramsize option
*/
@@ -2641,6 +3517,9 @@ static ADDRESS_MAP_START(ppu_io_map , AS_IO , 16 , hp9845_base_state)
// PA = 13, IC = 0..3
// Graphic video
AM_RANGE(HP_MAKE_IOADDR(GVIDEO_PA , 0) , HP_MAKE_IOADDR(GVIDEO_PA , 3)) AM_READWRITE(graphic_r , graphic_w)
+ // PA = 14, IC = 0..3
+ // Left-hand side tape drive (T14)
+ AM_RANGE(HP_MAKE_IOADDR(T14_PA , 0) , HP_MAKE_IOADDR(T14_PA , 3)) AM_DEVREADWRITE("t14" , hp_taco_device , reg_r , reg_w)
// PA = 15, IC = 0..3
// Right-hand side tape drive (T15)
AM_RANGE(HP_MAKE_IOADDR(T15_PA , 0) , HP_MAKE_IOADDR(T15_PA , 3)) AM_DEVREADWRITE("t15" , hp_taco_device , reg_r , reg_w)
@@ -2677,6 +3556,10 @@ static MACHINE_CONFIG_FRAGMENT(hp9845_base)
MCFG_TACO_IRQ_HANDLER(WRITELINE(hp9845_base_state , t15_irq_w))
MCFG_TACO_FLG_HANDLER(WRITELINE(hp9845_base_state , t15_flg_w))
MCFG_TACO_STS_HANDLER(WRITELINE(hp9845_base_state , t15_sts_w))
+ MCFG_DEVICE_ADD("t14" , HP_TACO , 4000000)
+ MCFG_TACO_IRQ_HANDLER(WRITELINE(hp9845_base_state , t14_irq_w))
+ MCFG_TACO_FLG_HANDLER(WRITELINE(hp9845_base_state , t14_flg_w))
+ MCFG_TACO_STS_HANDLER(WRITELINE(hp9845_base_state , t14_sts_w))
// In real machine there were 8 slots for LPU ROMs and 8 slots for PPU ROMs in
// right-hand side and left-hand side drawers, respectively.
@@ -2755,7 +3638,22 @@ static MACHINE_CONFIG_START(hp9845c, hp9845c_state)
MACHINE_CONFIG_END
-ROM_START( hp9845a )
+static MACHINE_CONFIG_START(hp9845t, hp9845t_state)
+ MCFG_FRAGMENT_ADD(hp9845_base)
+ // video hardware
+ MCFG_SCREEN_MODIFY("screen")
+ MCFG_SCREEN_UPDATE_DRIVER(hp9845t_state, screen_update)
+ MCFG_SCREEN_VBLANK_CALLBACK(WRITELINE(hp9845t_state, vblank_w))
+ MCFG_SCREEN_COLOR(rgb_t::green())
+ MCFG_SCREEN_RAW_PARAMS(VIDEO_780_PIXEL_CLOCK , VIDEO_780_HTOTAL , VIDEO_780_HBEND , VIDEO_780_HBSTART , VIDEO_780_VTOTAL , VIDEO_780_VBEND , VIDEO_780_VBSTART)
+ MCFG_PALETTE_ADD("palette", 5)
+ MCFG_TIMER_DRIVER_ADD_SCANLINE("scantimer", hp9845t_state, scanline_timer, "screen", 0, 1)
+
+ MCFG_SOFTWARE_LIST_ADD("optrom_list", "hp9845b_rom")
+
+MACHINE_CONFIG_END
+
+ ROM_START( hp9845a )
ROM_REGION( 0200000, "lpu", ROMREGION_16BIT | ROMREGION_BE )
ROM_LOAD( "09845-65544-65547-03-system_lpu.bin", 0000000, 0200000, CRC(47beb87f) SHA1(456caefacafcf19435e1e7e68b1c1e4010841664) )
@@ -2855,8 +3753,6 @@ ROM_START( hp9845b )
#endif
ROM_END
-#define rom_hp9845t rom_hp9845b
-
ROM_START( hp9845c )
ROM_REGION(0x800 , "chargen" , 0)
ROM_LOAD("chrgen.bin" , 0 , 0x800 , CRC(fe9e844f) SHA1(0c45ae00766ceba94a19bd5e154bd6d23e208cca))
@@ -2871,10 +3767,22 @@ ROM_START( hp9845c )
ROM_LOAD("9845-PPU-Color-Enhanced-Graphics.bin", 0, 0x10000, CRC(96e11edc) SHA1(3f1da50edb35dfc57ec2ecfd816a8c8230e110bd))
ROM_END
-COMP( 1978, hp9845a, 0, 0, hp9845a, hp9845, driver_device, 0, "Hewlett-Packard", "9845A", MACHINE_IS_SKELETON | MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
-COMP( 1978, hp9845s, hp9845a, 0, hp9845a, hp9845, driver_device, 0, "Hewlett-Packard", "9845S", MACHINE_IS_SKELETON | MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
+ROM_START( hp9845t )
+ ROM_REGION(0x1000 , "chargen" , 0)
+ ROM_LOAD("1818-1395.bin" , 0 , 0x1000 , CRC(7b555edf) SHA1(3b08e094635ef02aef9a2e37b049c61bcf1ec037))
+
+ ROM_REGION(0x10000, "lpu", ROMREGION_16BIT | ROMREGION_BE)
+ ROM_LOAD("9845-LPU-Standard-Processor.bin", 0, 0x10000, CRC(dc266c1b) SHA1(1cf3267f13872fbbfc035b70f8b4ec6b5923f182))
+
+ ROM_REGION(0x10000, "ppu", ROMREGION_16BIT | ROMREGION_BE)
+ ROM_LOAD("9845-PPU-Color-Enhanced-Graphics.bin", 0, 0x10000, CRC(96e11edc) SHA1(3f1da50edb35dfc57ec2ecfd816a8c8230e110bd))
+ROM_END
+
+// YEAR NAME PARENT COMPAT MACHINE INPUT INIT COMPANY FULLNAME FLAGS
+COMP( 1977, hp9845a, 0, 0, hp9845a, hp9845, driver_device, 0, "Hewlett-Packard", "9845A", MACHINE_IS_SKELETON | MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
+COMP( 1977, hp9845s, hp9845a, 0, hp9845a, hp9845, driver_device, 0, "Hewlett-Packard", "9845S", MACHINE_IS_SKELETON | MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP( 1979, hp9835a, 0, 0, hp9835a, hp9845, driver_device, 0, "Hewlett-Packard", "9835A", MACHINE_IS_SKELETON | MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
COMP( 1979, hp9835b, hp9835a, 0, hp9835a, hp9845, driver_device, 0, "Hewlett-Packard", "9835B", MACHINE_IS_SKELETON | MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
-COMP( 1980, hp9845b, 0, 0, hp9845b, hp9845_base,driver_device, 0, "Hewlett-Packard", "9845B", 0 )
-COMP( 1980, hp9845t, hp9845b, 0, hp9845b, hp9845_base,driver_device, 0, "Hewlett-Packard", "9845T", MACHINE_IS_SKELETON | MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
-COMP( 1981, hp9845c, 0, 0, hp9845c, hp9845ct,driver_device, 0, "Hewlett-Packard", "9845C", 0 )
+COMP( 1979, hp9845b, 0, 0, hp9845b, hp9845_base,driver_device, 0, "Hewlett-Packard", "9845B", 0 )
+COMP( 1982, hp9845t, 0, 0, hp9845t, hp9845ct,driver_device, 0, "Hewlett-Packard", "9845T", 0 )
+COMP( 1980, hp9845c, 0, 0, hp9845c, hp9845ct,driver_device, 0, "Hewlett-Packard", "9845C", 0 )