1 // license:BSD-3-Clause
2 // copyright-holders:Curt Coder, F. Ulivi, Ansgar Kueckes
3 /*
4 
5     HP 9845
6 
7     http://www.hp9845.net/
8 
9 */
10 // *******************************
11 // Driver for HP 9845B/C/T systems
12 // *******************************
13 //
14 // What's in:
15 // - Emulation of 45B, 45C and 45T systems
16 // - Emulation of both 5061-3001 CPUs
17 // - LPU & PPU ROMs
18 // - LPU & PPU RAMs
19 // - Text mode screen
20 // - Graphic screen
21 // - Keyboard (US & German layouts)
22 // - T14 and T15 tape drive
23 // - Software list to load optional ROMs
24 // - Beeper
25 // - Correct character generator ROMs (a huge "thank you" to Ansgar Kueckes for the dumps!)
26 // - 98775 light pen controller
27 // - Display softkeys on 45C & 45T
28 // - HLE of integral printer
29 // What's not yet in:
30 // - Better naming of tape drive image (it's now "magt1" and "magt2", should be "t15" and "t14")
31 // - Better documentation of this file
32 // What's wrong:
33 // - Speed, as usual
34 // - Light pen tracing sometimes behaves erratically in 45C and 45T
35 // What will probably never be in:
36 // - Fast LPU processor (dump of microcode PROMs is not available)
37 
38 #include "emu.h"
39 #include "includes/hp9845.h"
40 
41 #include "machine/hp9845_optrom.h"
42 #include "bus/hp9845_io/hp9845_io.h"
43 #include "machine/timer.h"
44 
45 #include "render.h"
46 #include "softlist.h"
47 #include "speaker.h"
48 
49 #include "hp9845b.lh"
50 
51 #include "machine/hp9845_printer.h"
52 
53 // Debugging
54 #define VERBOSE 0
55 #include "logmacro.h"
56 
57 #define BIT_MASK(n) (1U << (n))
58 
59 // Macros to clear/set single bits
60 #define BIT_CLR(w , n)  ((w) &= ~BIT_MASK(n))
61 #define BIT_SET(w , n)  ((w) |= BIT_MASK(n))
62 
63 /*
64 
65  The 9845 has three possible display options:
66 
67  98750A: Standard monochrome (alpha with graphics option)
68  98780A: Enhanced monochrome (alpha with hardware accelerated monochrome graphics)
69  98770A: Color (color alpha with hardware accelerated color graphics with three planes)
70 
71  All displays use a 560x455 graphics raster. Alpha and graphics share the
72  same dots within a 720x455 super matrix. All pixels have a 1:1 ratio (square
73  pixels).
74 
75  The 98750A uses a 25x80 alpha area, either alpha or graphics can be enabled,
76  but not both at the same time. In fact, both modes use different video circuits.
77 
78  Timing and pixel size for real 98750A are slightly different between
79  alpha and graphics raster (dual raster):
80 
81                                   alpha       graphics
82  ------------------------------------------------------
83  Matrix:                          720x375     560x455
84  Clock frequency:                 20.85 MHz   20.85 MHz
85  Horizontal scan frequency:       23.4 kHz    28.7 kHz
86  Horizontal retrace time:         8.2 us      8.0 us
87  Frame frequency:                 60 Hz       60 Hz
88  Vertical retrace time:           641 us      800 us
89  Size on screen:                  9.3"x4.84"  7.9"x6.4"
90 
91 
92  The 98770A and 98780A both use a 720x455 raster, implemented with a single video
93  circuit, which again is shared by the alpha and graphics logic, with alpha
94  dominant over graphics. So, nominally the alpha area for those systems can
95  hold up to 30 rows with full size characters plus some lines for one row with
96  cropped characters:
97 
98                                   98770A       98780A
99  ------------------------------------------------------
100  Matrix:                          720x455      720x455
101  Clock frequency:                 29.7984 MHz  28.224 MHz
102  Horizontal scan frequency:       29.1 kHz     31.5 kHz
103  Horizontal retrace time:         10.02 us     4.145 us
104  Frame frequency:                 60 Hz        60 Hz
105  Vertical retrace time:           1.03 ms      2.22 ms
106  Size on screen:                  247x154 mm   236x149 mm
107  Dot size:                        0.343 mm     0.33 mm
108 
109 */
110 
111 // Base address of video buffer
112 #define VIDEO_BUFFER_BASE_LOW       0x16000         // for 98770A and 98780A
113 #define VIDEO_BUFFER_BASE_HIGH      0x17000         // for 98750A
114 
115 // For test "B" of alpha video to succeed this must be < 234
116 // Basically "B" test is designed to intentionally prevent line buffer to be filled so that display is blanked
117 // from 2nd row on. This in turn prevents "BAD" text to be visible on screen.
118 #define MAX_WORD_PER_ROW        220
119 
120 // Constants of alpha video
121 #define VIDEO_PIXEL_CLOCK       20849400
122 #define VIDEO_CHAR_WIDTH        9
123 #define VIDEO_CHAR_HEIGHT       15
124 #define VIDEO_CHAR_COLUMNS      80
125 #define VIDEO_CHAR_TOTAL        99
126 #define VIDEO_CHAR_ROWS         25
127 #define VIDEO_ROWS_TOTAL        26
128 #define VIDEO_HBSTART           (VIDEO_CHAR_WIDTH * VIDEO_CHAR_COLUMNS)
129 #define VIDEO_HTOTAL            (VIDEO_CHAR_WIDTH * VIDEO_CHAR_TOTAL)
130 #define VIDEO_VTOTAL            (VIDEO_CHAR_HEIGHT * VIDEO_ROWS_TOTAL)
131 #define VIDEO_ACTIVE_SCANLINES  (VIDEO_CHAR_HEIGHT * VIDEO_CHAR_ROWS)
132 #define VIDEO_TOT_HPIXELS       (VIDEO_CHAR_WIDTH * VIDEO_CHAR_COLUMNS)
133 
134 // Constants of graphic video
135 // Pixel clock is 20.8494 MHz (the same as alpha video)
136 // Horizontal counter counts in [1..727] range
137 // Vertical counter counts in [34..511] range
138 #define GVIDEO_HTOTAL           727
139 #define GVIDEO_HCNT_OFF         1       // Actual start value of h counter
140 #define GVIDEO_HBEND            (69 - GVIDEO_HCNT_OFF)
141 #define GVIDEO_HPIXELS          560
142 #define GVIDEO_HBSTART          (GVIDEO_HBEND + GVIDEO_HPIXELS)
143 #define GVIDEO_VTOTAL           478
144 #define GVIDEO_VCNT_OFF         34      // Actual start value of v counter
145 #define GVIDEO_VBEND            (50 - GVIDEO_VCNT_OFF)
146 #define GVIDEO_VPIXELS          455
147 #define GVIDEO_VBSTART          (GVIDEO_VBEND + GVIDEO_VPIXELS)
148 #define GVIDEO_MEM_SIZE         16384
149 #define GVIDEO_ADDR_MASK        (GVIDEO_MEM_SIZE - 1)
150 
151 // Constants of 98770A video
152 // HBEND & VBEND probably are not really 0
153 #define VIDEO_770_PIXEL_CLOCK   29798400
154 #define VIDEO_770_HTOTAL        1024
155 #define VIDEO_770_HBEND         0
156 #define VIDEO_770_HBSTART       (VIDEO_CHAR_COLUMNS * VIDEO_CHAR_WIDTH)
157 #define VIDEO_770_VTOTAL        485
158 #define VIDEO_770_VBEND         0
159 #define VIDEO_770_VBSTART       (VIDEO_770_VBEND + GVIDEO_VPIXELS)
160 #define VIDEO_770_ALPHA_L_LIM   80  // Left-side limit of alpha-only horizontal part
161 #define VIDEO_770_ALPHA_R_LIM   640 // Right-side limit of alpha-only horizontal part
162 
163 // Constants of 98780A video
164 #define VIDEO_780_PIXEL_CLOCK   28224000
165 #define VIDEO_780_HTOTAL        896
166 #define VIDEO_780_VTOTAL        525
167 #define VIDEO_780_HBEND         0
168 #define VIDEO_780_HBSTART       (VIDEO_CHAR_COLUMNS * VIDEO_CHAR_WIDTH)
169 #define VIDEO_780_VBEND         0
170 #define VIDEO_780_VBSTART       (VIDEO_780_VBEND + GVIDEO_VPIXELS)
171 #define VIDEO_780_ALPHA_L_LIM   80  // Left-side limit of alpha-only horizontal part
172 #define VIDEO_780_ALPHA_R_LIM   640 // Right-side limit of alpha-only horizontal part
173 
174 #define I_GR    0xb0    // graphics intensity
175 #define I_AL    0xd0    // alpha intensity
176 #define I_CU    0xf0    // graphics cursor intensity
177 #define I_LP    0xff    // light pen cursor intensity
178 
179 // Palette indexes (for monochromatic screens)
180 #define PEN_BLACK   0   // Black
181 #define PEN_GRAPHIC 1   // Graphics
182 #define PEN_ALPHA   2   // Text
183 #define PEN_CURSOR  3   // Graphic cursor
184 #define PEN_LP      4   // Light pen cursor
185 
186 // Light pen constants
187 constexpr unsigned LP_FOV = 9;  // Field of view
188 constexpr unsigned LP_XOFFSET = 5;  // x-offset of LP (due to delay in hit recognition)
189 
190 // Peripheral Addresses (PA)
191 #define PRINTER_PA          0
192 #define IO_SLOT_FIRST_PA    1
193 #define IO_SLOT_LAST_PA     12
194 #define GVIDEO_PA           13
195 #define T14_PA              14
196 #define T15_PA              15
197 
198 #define KEY_SCAN_OSCILLATOR     327680
199 
200 class hp9845_state : public driver_device
201 {
202 public:
hp9845_state(const machine_config & mconfig,device_type type,const char * tag)203 	hp9845_state(const machine_config &mconfig, device_type type, const char *tag)
204 		: driver_device(mconfig, type, tag)
205 	{ }
206 
207 	void hp9845a(machine_config &config);
208 	void hp9835a(machine_config &config);
209 
210 private:
211 	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
212 };
213 
INPUT_PORTS_START(hp9845)214 static INPUT_PORTS_START( hp9845 )
215 INPUT_PORTS_END
216 
217 uint32_t hp9845_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
218 {
219 	return 0;
220 }
221 
222 static INPUT_PORTS_START(hp9845_base)
223 	// Keyboard is arranged in a 8 x 16 matrix. Of the 128 possible positions, 118 are used.
224 	// Keys are mapped on bit b of KEYn
225 	// where b = (row & 1) << 4 + column, n = row >> 1
226 	// column = [0..15]
227 	// row = [0..7]
228 	PORT_START("KEY0")
229 	PORT_BIT(BIT_MASK(0)  , IP_ACTIVE_HIGH , IPT_UNUSED)    // N/U
230 	PORT_BIT(BIT_MASK(1)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_TOGGLE PORT_NAME("Prt all") PORT_CHANGED_MEMBER(DEVICE_SELF, hp9845_base_state, togglekey_changed, 1) // Print All
231 	PORT_BIT(BIT_MASK(2)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_PLUS_PAD) PORT_CHAR(UCHAR_MAMEKEY(PLUS_PAD))                           // KP +
232 	PORT_BIT(BIT_MASK(3)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_COMMA_PAD) PORT_CHAR(UCHAR_MAMEKEY(COMMA_PAD))                         // KP ,
233 	PORT_BIT(BIT_MASK(4)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_DEL_PAD) PORT_CHAR(UCHAR_MAMEKEY(DEL_PAD))                             // KP .
234 	PORT_BIT(BIT_MASK(5)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_0_PAD) PORT_CHAR(UCHAR_MAMEKEY(0_PAD))                                 // KP 0
235 	PORT_BIT(BIT_MASK(6)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_ENTER_PAD) PORT_CHAR(UCHAR_MAMEKEY(ENTER_PAD)) PORT_NAME("Execute")    // Execute
236 	PORT_BIT(BIT_MASK(7)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_ENTER) PORT_NAME("Cont") PORT_CHAR(13)    // Cont
237 	PORT_BIT(BIT_MASK(8)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))        // Right
238 	PORT_BIT(BIT_MASK(9)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')  // Space
239 	PORT_BIT(BIT_MASK(10)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')  // /
240 	PORT_BIT(BIT_MASK(11)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')  // <
241 	PORT_BIT(BIT_MASK(12)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_N)     PORT_CHAR('n') PORT_CHAR('N')  // N
242 	PORT_BIT(BIT_MASK(13)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_V)     PORT_CHAR('v') PORT_CHAR('V')  // V
243 	PORT_BIT(BIT_MASK(14)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_X)     PORT_CHAR('x') PORT_CHAR('X')  // X
244 	PORT_BIT(BIT_MASK(15)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT)    PORT_CHAR(UCHAR_SHIFT_1)   // Shift
245 	PORT_BIT(BIT_MASK(16)  , IP_ACTIVE_HIGH , IPT_UNUSED)   // N/U
246 	PORT_BIT(BIT_MASK(17)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_TOGGLE PORT_NAME("Auto st") PORT_CHANGED_MEMBER(DEVICE_SELF, hp9845_base_state, togglekey_changed, 2) // Auto Start
247 	PORT_BIT(BIT_MASK(18)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS_PAD) PORT_CHAR(UCHAR_MAMEKEY(MINUS_PAD)) // KP -
248 	PORT_BIT(BIT_MASK(19)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_3_PAD) PORT_CHAR(UCHAR_MAMEKEY(3_PAD))         // KP 3
249 	PORT_BIT(BIT_MASK(20)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_2_PAD) PORT_CHAR(UCHAR_MAMEKEY(2_PAD))         // KP 2
250 	PORT_BIT(BIT_MASK(21)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_1_PAD) PORT_CHAR(UCHAR_MAMEKEY(1_PAD))         // KP 1
251 	PORT_BIT(BIT_MASK(22)  , IP_ACTIVE_HIGH , IPT_UNUSED)   // N/U
252 	PORT_BIT(BIT_MASK(23)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))  // Left
253 	PORT_BIT(BIT_MASK(24)  , IP_ACTIVE_HIGH , IPT_UNUSED)   // Repeat
254 	PORT_BIT(BIT_MASK(25)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))  // Down
255 	PORT_BIT(BIT_MASK(26)  , IP_ACTIVE_HIGH , IPT_UNUSED)   // N/U
256 	PORT_BIT(BIT_MASK(27)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')   // >
257 	PORT_BIT(BIT_MASK(28)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_M)     PORT_CHAR('m') PORT_CHAR('M')  // M
258 	PORT_BIT(BIT_MASK(29)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_B)     PORT_CHAR('b') PORT_CHAR('B')  // B
259 	PORT_BIT(BIT_MASK(30)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_C)     PORT_CHAR('c') PORT_CHAR('C')  // C
260 	PORT_BIT(BIT_MASK(31)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)     PORT_CHAR('z') PORT_CHAR('Z')  // Z
261 
262 	PORT_START("KEY1")
263 	PORT_BIT(BIT_MASK(0)  , IP_ACTIVE_HIGH , IPT_UNUSED)    // N/U
264 	PORT_BIT(BIT_MASK(1)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_INSERT) PORT_NAME("Ins chr")    // Ins Char
265 	PORT_BIT(BIT_MASK(2)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_ASTERISK) PORT_CHAR(UCHAR_MAMEKEY(ASTERISK))     // KP *
266 	PORT_BIT(BIT_MASK(3)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_6_PAD) PORT_CHAR(UCHAR_MAMEKEY(6_PAD))           // KP 6
267 	PORT_BIT(BIT_MASK(4)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_5_PAD) PORT_CHAR(UCHAR_MAMEKEY(5_PAD))           // KP 5
268 	PORT_BIT(BIT_MASK(5)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_4_PAD) PORT_CHAR(UCHAR_MAMEKEY(4_PAD))           // KP 4
269 	PORT_BIT(BIT_MASK(6)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_EQUALS_PAD) PORT_CHAR(UCHAR_MAMEKEY(EQUALS_PAD)) // KP =
270 	PORT_BIT(BIT_MASK(7)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_NAME("Pause")      // Pause
271 	PORT_BIT(BIT_MASK(8)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_UP)   PORT_CHAR(UCHAR_MAMEKEY(UP))    // Up
272 	PORT_BIT(BIT_MASK(9)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_NAME("Store")  // Store
273 	PORT_BIT(BIT_MASK(10)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)     PORT_CHAR(';') PORT_CHAR(':')      // :
274 	PORT_BIT(BIT_MASK(11)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_K)     PORT_CHAR('k') PORT_CHAR('K')  // K
275 	PORT_BIT(BIT_MASK(12)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_H)     PORT_CHAR('h') PORT_CHAR('H')  // H
276 	PORT_BIT(BIT_MASK(13)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F)     PORT_CHAR('f') PORT_CHAR('F')  // F
277 	PORT_BIT(BIT_MASK(14)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_S)     PORT_CHAR('s') PORT_CHAR('S')  // S
278 	PORT_BIT(BIT_MASK(15)  , IP_ACTIVE_HIGH , IPT_UNUSED)   // N/U
279 	PORT_BIT(BIT_MASK(16)  , IP_ACTIVE_HIGH , IPT_UNUSED)   // N/U
280 	PORT_BIT(BIT_MASK(17)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Ins ln")      // Ins Ln
281 	PORT_BIT(BIT_MASK(18)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH_PAD) PORT_CHAR(UCHAR_MAMEKEY(SLASH_PAD)) // KP /
282 	PORT_BIT(BIT_MASK(19)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_9_PAD) PORT_CHAR(UCHAR_MAMEKEY(9_PAD))         // KP 9
283 	PORT_BIT(BIT_MASK(20)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_8_PAD) PORT_CHAR(UCHAR_MAMEKEY(8_PAD))         // KP 8
284 	PORT_BIT(BIT_MASK(21)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_7_PAD) PORT_CHAR(UCHAR_MAMEKEY(7_PAD))         // KP 7
285 	PORT_BIT(BIT_MASK(22)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Result")     // Result
286 	PORT_BIT(BIT_MASK(23)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Run")        // Run
287 	PORT_BIT(BIT_MASK(24)  , IP_ACTIVE_HIGH , IPT_UNUSED)   // N/U
288 	PORT_BIT(BIT_MASK(25)  , IP_ACTIVE_HIGH , IPT_UNUSED)   // N/U
289 	PORT_BIT(BIT_MASK(26)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('\'') PORT_CHAR('"') // "
290 	PORT_BIT(BIT_MASK(27)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_L)     PORT_CHAR('l') PORT_CHAR('L')  // L
291 	PORT_BIT(BIT_MASK(28)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_J)     PORT_CHAR('j') PORT_CHAR('J')  // J
292 	PORT_BIT(BIT_MASK(29)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_G)     PORT_CHAR('g') PORT_CHAR('G')  // G
293 	PORT_BIT(BIT_MASK(30)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_D)     PORT_CHAR('d') PORT_CHAR('D')  // D
294 	PORT_BIT(BIT_MASK(31)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_A)     PORT_CHAR('a') PORT_CHAR('A')  // A
295 
296 	PORT_START("KEY2")
297 	PORT_BIT(BIT_MASK(0)  , IP_ACTIVE_HIGH , IPT_UNUSED)    // N/U
298 	PORT_BIT(BIT_MASK(1)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_NAME("Del ln")          // Del Ln
299 	PORT_BIT(BIT_MASK(2)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_NAME("Keypad ^")        // KP ^
300 	PORT_BIT(BIT_MASK(3)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_NAME("Keypad )")        // KP )
301 	PORT_BIT(BIT_MASK(4)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_NAME("Keypad (")        // KP (
302 	PORT_BIT(BIT_MASK(5)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_NAME("Keypad E")        // KP E
303 	PORT_BIT(BIT_MASK(6)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_NAME("Clear line")      // Clear Line
304 	PORT_BIT(BIT_MASK(7)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(ESC)) PORT_NAME("Stop")       // Stop
305 	PORT_BIT(BIT_MASK(8)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\') PORT_CHAR('|')      // |
306 	PORT_BIT(BIT_MASK(9)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_CLOSEBRACE)   PORT_CHAR(']') PORT_CHAR('}')   // ]
307 	PORT_BIT(BIT_MASK(10)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_P)     PORT_CHAR('p') PORT_CHAR('P')  // P
308 	PORT_BIT(BIT_MASK(11)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_I)     PORT_CHAR('i') PORT_CHAR('I')  // I
309 	PORT_BIT(BIT_MASK(12)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)     PORT_CHAR('y') PORT_CHAR('Y')  // Y
310 	PORT_BIT(BIT_MASK(13)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_R)     PORT_CHAR('r') PORT_CHAR('R')  // R
311 	PORT_BIT(BIT_MASK(14)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_W)     PORT_CHAR('w') PORT_CHAR('W')  // W
312 	PORT_BIT(BIT_MASK(15)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_LCONTROL)  PORT_CHAR(UCHAR_SHIFT_2)   // Control
313 	PORT_BIT(BIT_MASK(16)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Typwtr")     // Typwtr
314 	PORT_BIT(BIT_MASK(17)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL)  PORT_NAME("Del chr")    // Del Char
315 	PORT_BIT(BIT_MASK(18)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_PGDN) PORT_NAME("Roll down")   // Roll down
316 	PORT_BIT(BIT_MASK(19)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_PGUP) PORT_NAME("Roll up")     // Roll up
317 	PORT_BIT(BIT_MASK(20)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_HOME) PORT_NAME("Home")       // Home
318 	PORT_BIT(BIT_MASK(21)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_END) PORT_NAME("Clr to end") // Clr to end
319 	PORT_BIT(BIT_MASK(22)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Clear")      // Clear
320 	PORT_BIT(BIT_MASK(23)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE)     PORT_CHAR('`') PORT_CHAR('~')      // ~
321 	PORT_BIT(BIT_MASK(24)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)       // BS
322 	PORT_BIT(BIT_MASK(25)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)    PORT_CHAR('=') PORT_CHAR('+')      // +
323 	PORT_BIT(BIT_MASK(26)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[') PORT_CHAR('{')      // [
324 	PORT_BIT(BIT_MASK(27)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_O)     PORT_CHAR('o') PORT_CHAR('O')  // O
325 	PORT_BIT(BIT_MASK(28)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_U)     PORT_CHAR('u') PORT_CHAR('U')  // U
326 	PORT_BIT(BIT_MASK(29)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_T)     PORT_CHAR('t') PORT_CHAR('T')  // T
327 	PORT_BIT(BIT_MASK(30)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_E)     PORT_CHAR('e') PORT_CHAR('E')  // E
328 	PORT_BIT(BIT_MASK(31)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)     PORT_CHAR('q') PORT_CHAR('Q')  // Q
329 
330 	PORT_START("KEY3")
331 	PORT_BIT(BIT_MASK(0)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_NAME("Tab set")    // Tab set
332 	PORT_BIT(BIT_MASK(1)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_NAME("Recall")     // Recall
333 	PORT_BIT(BIT_MASK(2)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_NAME("K15")        // K15
334 	PORT_BIT(BIT_MASK(3)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_NAME("K14")        // K14
335 	PORT_BIT(BIT_MASK(4)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_NAME("K13")        // K13
336 	PORT_BIT(BIT_MASK(5)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_F12) PORT_CHAR(UCHAR_MAMEKEY(F12)) PORT_NAME("K12")      // K12
337 	PORT_BIT(BIT_MASK(6)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_F11) PORT_CHAR(UCHAR_MAMEKEY(F11)) PORT_NAME("K11")      // K11
338 	PORT_BIT(BIT_MASK(7)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_F10) PORT_CHAR(UCHAR_MAMEKEY(F10)) PORT_NAME("K10")      // K10
339 	PORT_BIT(BIT_MASK(8)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_F9) PORT_CHAR(UCHAR_MAMEKEY(F9)) PORT_NAME("K9")         // K9
340 	PORT_BIT(BIT_MASK(9)  , IP_ACTIVE_HIGH , IPT_KEYBOARD)  PORT_CODE(KEYCODE_F8) PORT_CHAR(UCHAR_MAMEKEY(F8)) PORT_NAME("K8")         // K8
341 	PORT_BIT(BIT_MASK(10)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_0)     PORT_CHAR('0') PORT_CHAR(')') // 0
342 	PORT_BIT(BIT_MASK(11)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_8)     PORT_CHAR('8') PORT_CHAR('*')  // 8
343 	PORT_BIT(BIT_MASK(12)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_6)     PORT_CHAR('6') PORT_CHAR('^')  // 6
344 	PORT_BIT(BIT_MASK(13)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_4)     PORT_CHAR('4') PORT_CHAR('$')  // 4
345 	PORT_BIT(BIT_MASK(14)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_2)     PORT_CHAR('2') PORT_CHAR('@')  // 2
346 	PORT_BIT(BIT_MASK(15)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB)   PORT_CHAR('\t')        // Tab
347 	PORT_BIT(BIT_MASK(16)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Tab clr")    // Tab clr
348 	PORT_BIT(BIT_MASK(17)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("Step")  // Step
349 	PORT_BIT(BIT_MASK(18)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F7) PORT_CHAR(UCHAR_MAMEKEY(F7)) PORT_NAME("K7") // K7
350 	PORT_BIT(BIT_MASK(19)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F6) PORT_CHAR(UCHAR_MAMEKEY(F6)) PORT_NAME("K6") // K6
351 	PORT_BIT(BIT_MASK(20)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5)) PORT_NAME("K5") // K5
352 	PORT_BIT(BIT_MASK(21)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4)) PORT_NAME("K4") // K4
353 	PORT_BIT(BIT_MASK(22)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3)) PORT_NAME("K3") // K3
354 	PORT_BIT(BIT_MASK(23)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2)) PORT_NAME("K2") // K2
355 	PORT_BIT(BIT_MASK(24)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1)) PORT_NAME("K1") // K1
356 	PORT_BIT(BIT_MASK(25)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_NAME("K0") // K0
357 	PORT_BIT(BIT_MASK(26)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)     PORT_CHAR('-') PORT_CHAR('_')      // _
358 	PORT_BIT(BIT_MASK(27)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_9)     PORT_CHAR('9') PORT_CHAR('(')  // 9
359 	PORT_BIT(BIT_MASK(28)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_7)     PORT_CHAR('7') PORT_CHAR('&') // 7
360 	PORT_BIT(BIT_MASK(29)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_5)     PORT_CHAR('5') PORT_CHAR('%')  // 5
361 	PORT_BIT(BIT_MASK(30)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_3)     PORT_CHAR('3') PORT_CHAR('#')  // 3
362 	PORT_BIT(BIT_MASK(31)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_1)     PORT_CHAR('1') PORT_CHAR('!')  // 1
363 
364 	PORT_START("SHIFTLOCK");
PORT_CODE(KEYCODE_CAPSLOCK)365 	PORT_BIT(BIT_MASK(0)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_CAPSLOCK) PORT_TOGGLE PORT_NAME("Shift lock") PORT_CHANGED_MEMBER(DEVICE_SELF, hp9845_base_state, togglekey_changed, 0) // Shift lock
366 
367 INPUT_PORTS_END
368 
369 /*
370     German keyboard layout
371 
372     Remarks:
373     - Most keys including umlauts map correctly to the German keyboard layout of the 9845 without special configuration,
374       provided that the German keyboard firmware ROM is used on the 9845
375     - '#' maps positionally correct to Shift+3
376     - AltGr modifier on the Germany PC keyboard for 9845 shifted keycodes 23=| and 5=@ need to get assigned dynamically
377     - ~{}\'` are not available on the German 9845 keyboard, ^ is available via keypad only
378 */
379 static INPUT_PORTS_START(hp9845_base_de)
380 	PORT_INCLUDE(hp9845_base)
381 
382 	PORT_MODIFY("KEY0")
383 	PORT_BIT(BIT_MASK(10) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('-') PORT_CHAR('_')           // - _
384 	PORT_BIT(BIT_MASK(11) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR(';')           // , ;
385 	PORT_BIT(BIT_MASK(27) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR(':')            // . :
386 	PORT_BIT(BIT_MASK(31) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('y') PORT_CHAR('Y')               // Y
387 
388 	PORT_MODIFY("KEY1")
389 	PORT_BIT(BIT_MASK(10) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(0x00f6) PORT_CHAR(0x00d6) // Ö
390 	PORT_BIT(BIT_MASK(26) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(0x00e4) PORT_CHAR(0x00c4) // Ä
391 
392 	PORT_MODIFY("KEY2")
393 	PORT_BIT(BIT_MASK(8)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH2) PORT_CHAR('<') PORT_CHAR('>')      // < >
394 	PORT_BIT(BIT_MASK(9)  , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('+') PORT_CHAR('*')      // + *
395 	PORT_BIT(BIT_MASK(12) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('z') PORT_CHAR('Z')               // Z
396 	PORT_BIT(BIT_MASK(23) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)                        // Backspace
397 	PORT_BIT(BIT_MASK(24) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE) PORT_CHAR(']') PORT_CHAR('@')           // ] @
398 	PORT_BIT(BIT_MASK(25) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('[') PORT_CHAR('|')          // [ |
399 	PORT_BIT(BIT_MASK(26) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR(0x00fc) PORT_CHAR(0x00dc) // Ü
400 
401 	PORT_MODIFY("KEY3")
402 	PORT_BIT(BIT_MASK(10) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR('=')               // 0 =
403 	PORT_BIT(BIT_MASK(11) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')               // 8 (
404 	PORT_BIT(BIT_MASK(12) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')               // 6 &
405 	PORT_BIT(BIT_MASK(14) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"')               // 2 "
406 	PORT_BIT(BIT_MASK(26) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR(0x00df) PORT_CHAR('?')    // ß ?
407 	PORT_BIT(BIT_MASK(27) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')               // 9 )
408 	PORT_BIT(BIT_MASK(28) , IP_ACTIVE_HIGH , IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('/')               // 7 /
409 INPUT_PORTS_END
410 
411 // *******************
412 //  hp9845_base_state
413 // *******************
414 hp9845_base_state::hp9845_base_state(const machine_config &mconfig, device_type type, const char *tag) :
415 	driver_device(mconfig, type, tag),
416 	m_lpu(*this, "lpu"),
417 	m_ppu(*this, "ppu"),
418 	m_io_sys(*this , "io_sys"),
419 	m_screen(*this, "screen"),
420 	m_palette(*this, "palette"),
421 	m_gv_timer(*this, "gv_timer"),
422 	m_io_key(*this, "KEY%u", 0U),
423 	m_io_shiftlock(*this, "SHIFTLOCK"),
424 	m_t14(*this, "t14"),
425 	m_t15(*this, "t15"),
426 	m_beeper(*this, "beeper"),
427 	m_beep_timer(*this, "beep_timer"),
428 	m_io_slot(*this, "slot%u", 0U),
429 	m_ram(*this, RAM_TAG),
430 	m_softkeys(*this, "Softkey%u", 0U),
431 	m_shift_lock_led(*this, "shift_lock_led"),
432 	m_prt_all_led(*this, "prt_all_led"),
433 	m_auto_st_led(*this, "auto_st_led"),
434 	m_chargen(*this, "chargen")
435 {
436 }
437 
setup_ram_block(unsigned block,unsigned offset)438 void hp9845_base_state::setup_ram_block(unsigned block , unsigned offset)
439 {
440 	unsigned block_addr = block << 16;
441 	m_lpu->space(AS_PROGRAM).install_ram(block_addr , block_addr + 0x7fff , m_ram->pointer() + offset);
442 	m_ppu->space(AS_PROGRAM).install_ram(block_addr , block_addr + 0x7fff , m_ram->pointer() + offset);
443 }
444 
machine_start()445 void hp9845_base_state::machine_start()
446 {
447 	m_softkeys.resolve();
448 	m_shift_lock_led.resolve();
449 	m_prt_all_led.resolve();
450 	m_auto_st_led.resolve();
451 
452 	m_screen->register_screen_bitmap(m_bitmap);
453 
454 	// setup RAM dynamically for -ramsize
455 	// 0K..64K
456 	setup_ram_block(0 , 0);
457 	if (m_ram->size() >= 192 * 1024) {
458 		// 64K..192K
459 		setup_ram_block(004 , 0x10000);
460 		setup_ram_block(006 , 0x20000);
461 	}
462 	if (m_ram->size() >= 320 * 1024) {
463 		// 192K..320K
464 		setup_ram_block(010 , 0x30000);
465 		setup_ram_block(012 , 0x40000);
466 	}
467 	if (m_ram->size() >= 448 * 1024) {
468 		// 320K..448K
469 		setup_ram_block(014 , 0x50000);
470 		setup_ram_block(016 , 0x60000);
471 	}
472 }
473 
device_reset()474 void hp9845_base_state::device_reset()
475 {
476 	// First, unmap every r/w handler in 1..12 select codes
477 	for (unsigned sc = IO_SLOT_FIRST_PA; sc < (IO_SLOT_LAST_PA + 1); sc++) {
478 		m_ppu->space(AS_IO).unmap_readwrite(sc * 4 , sc * 4 + 3);
479 	}
480 
481 	// Then, set r/w handlers of all installed I/O cards
482 	int sc;
483 	read16m_delegate rhandler(*this);
484 	write16m_delegate whandler(*this);
485 	for (unsigned i = 0; 4 > i; ++i) {
486 		if ((sc = m_io_slot[i]->get_rw_handlers(rhandler , whandler)) >= 0) {
487 			logerror("Install R/W handlers for slot %u @ SC = %d\n", i, sc);
488 			m_ppu->space(AS_IO).install_readwrite_handler(sc * 4 , sc * 4 + 3 , rhandler , whandler);
489 			if (m_io_slot[ i ]->has_dual_sc()) {
490 				logerror("Installing dual SC\n");
491 				m_ppu->space(AS_IO).install_readwrite_handler(sc * 4 + 4 , sc * 4 + 7 , rhandler , whandler);
492 			}
493 		}
494 		m_slot_sc[ i ] = sc;
495 	}
496 }
497 
machine_reset()498 void hp9845_base_state::machine_reset()
499 {
500 	m_lpu->halt_w(1);
501 	m_ppu->halt_w(0);
502 
503 	// Some sensible defaults
504 	m_video_load_mar = false;
505 	m_video_first_mar = false;
506 	m_video_byte_idx = false;
507 	m_video_buff_idx = false;
508 	m_video_blanked = false;
509 	m_graphic_sel = false;
510 	m_gv_fsm_state = GV_STAT_RESET;
511 	m_gv_int_en = false;
512 	m_gv_dma_en = false;
513 
514 	m_io_sys->set_sts(GVIDEO_PA , true);
515 
516 	memset(&m_kb_state[ 0 ] , 0 , sizeof(m_kb_state));
517 	m_kb_scancode = 0x7f;
518 	m_kb_status = 0;
519 
520 	m_beeper->set_state(0);
521 
522 	m_prt_irl = false;
523 }
524 
TIMER_DEVICE_CALLBACK_MEMBER(hp9845_base_state::gv_timer)525 TIMER_DEVICE_CALLBACK_MEMBER(hp9845_base_state::gv_timer)
526 {
527 		advance_gv_fsm(false , false);
528 }
529 
time_to_gv_mem_availability() const530 attotime hp9845_base_state::time_to_gv_mem_availability() const
531 {
532 		if (m_graphic_sel) {
533 				int hpos = m_screen->hpos();
534 				if (hpos < (34 - GVIDEO_HCNT_OFF) || hpos >= (628 - GVIDEO_HCNT_OFF)) {
535 						// Access to graphic memory available now
536 						return attotime::zero;
537 				} else {
538 						// Wait until start of hblank
539 						return m_screen->time_until_pos(m_screen->vpos() , 628);
540 				}
541 		} else {
542 				// TODO:
543 				return attotime::zero;
544 		}
545 }
546 
kb_scan_ioport(ioport_value pressed,ioport_port & port,unsigned idx_base,int & max_seq_len,unsigned & max_seq_idx)547 void hp9845_base_state::kb_scan_ioport(ioport_value pressed , ioport_port &port , unsigned idx_base , int& max_seq_len , unsigned& max_seq_idx)
548 {
549 	while (pressed) {
550 		unsigned bit_no = 31 - count_leading_zeros(pressed);
551 		ioport_value mask = BIT_MASK(bit_no);
552 		int seq_len = port.field(mask)->seq().length();
553 		if (seq_len > max_seq_len) {
554 			max_seq_len = seq_len;
555 			max_seq_idx = bit_no + idx_base;
556 		}
557 		pressed &= ~mask;
558 	}
559 }
560 
TIMER_DEVICE_CALLBACK_MEMBER(hp9845_base_state::kb_scan)561 TIMER_DEVICE_CALLBACK_MEMBER(hp9845_base_state::kb_scan)
562 {
563 		ioport_value input[ 4 ]{
564 				m_io_key[0]->read(),
565 				m_io_key[1]->read(),
566 				m_io_key[2]->read(),
567 				m_io_key[3]->read() };
568 
569 		// Shift lock
570 		ioport_value shiftlock = m_io_shiftlock->read();
571 
572 		// Set status bits for "shift", "control", "auto start" & "print all" keys
573 		// ** Print all **
574 		// (R,C) = (0,1)
575 		// Bit 12 in kb status
576 		if (BIT(input[ 0 ] , 1)) {
577 				BIT_SET(m_kb_status , 12);
578 				BIT_CLR(input[ 0 ] , 1);
579 		} else {
580 				BIT_CLR(m_kb_status, 12);
581 		}
582 		// ** Auto start **
583 		// (R,C) = (1,1)
584 		// Bit 13 in kb status
585 		if (BIT(input[ 0 ] , 17)) {
586 				BIT_SET(m_kb_status , 13);
587 				BIT_CLR(input[ 0 ] , 17);
588 		} else {
589 				BIT_CLR(m_kb_status, 13);
590 		}
591 		// ** Control **
592 		// (R,C) = (4,15)
593 		// Bit 14 in kb status
594 		if (BIT(input[ 2 ] , 15)) {
595 				BIT_SET(m_kb_status , 14);
596 				BIT_CLR(input[ 2 ] , 15);
597 		} else {
598 				BIT_CLR(m_kb_status, 14);
599 		}
600 		// ** Shift **
601 		// (R,C) = (0,15)
602 		// Bit 15 in kb status
603 		if (BIT(input[ 0 ] , 15) || shiftlock) {
604 				BIT_SET(m_kb_status , 15);
605 				BIT_CLR(input[ 0 ] , 15);
606 		} else {
607 				BIT_CLR(m_kb_status, 15);
608 		}
609 
610 		int max_seq_len = 0;
611 		unsigned max_seq_idx = 0;
612 		for (unsigned i = 0; 4 > i; ++i)
613 			kb_scan_ioport(input[i] & ~m_kb_state[i] , *m_io_key[i] , i << 5 , max_seq_len , max_seq_idx);
614 		// TODO: handle repeat key
615 		// TODO: handle ctrl+stop
616 
617 		if (max_seq_len) {
618 			// Key pressed, store scancode & generate IRL
619 			m_kb_scancode = max_seq_idx;
620 			BIT_SET(m_kb_status, 0);
621 			update_kb_prt_irq();
622 
623 			// Special case: pressing stop key sets LPU "status" flag
624 			if (max_seq_idx == 0x47) {
625 				m_lpu->status_w(1);
626 			}
627 		}
628 
629 		memcpy(&m_kb_state[ 0 ] , &input[ 0 ] , sizeof(m_kb_state));
630 }
631 
kb_scancode_r()632 uint16_t hp9845_base_state::kb_scancode_r()
633 {
634 		return ~m_kb_scancode & 0x7f;
635 }
636 
kb_status_r()637 uint16_t hp9845_base_state::kb_status_r()
638 {
639 		return m_kb_status;
640 }
641 
kb_irq_clear_w(uint16_t data)642 void hp9845_base_state::kb_irq_clear_w(uint16_t data)
643 {
644 		BIT_CLR(m_kb_status, 0);
645 		update_kb_prt_irq();
646 		m_lpu->status_w(0);
647 
648 		if (BIT(data , 15)) {
649 			// Start beeper
650 			m_beep_timer->adjust(attotime::from_ticks(64, KEY_SCAN_OSCILLATOR / 512));
651 			m_beeper->set_state(1);
652 		}
653 }
654 
update_kb_prt_irq()655 void hp9845_base_state::update_kb_prt_irq()
656 {
657 	bool state = BIT(m_kb_status , 0) || m_prt_irl;
658 	m_io_sys->set_irq(0 , state);
659 }
660 
set_irq_slot(unsigned slot,int state)661 void hp9845_base_state::set_irq_slot(unsigned slot , int state)
662 {
663 	int sc = m_slot_sc[ slot ];
664 	assert(sc >= 0);
665 	m_io_sys->set_irq(uint8_t(sc) , state);
666 }
667 
set_sts_slot(unsigned slot,int state)668 void hp9845_base_state::set_sts_slot(unsigned slot , int state)
669 {
670 	int sc = m_slot_sc[ slot ];
671 	assert(sc >= 0);
672 	m_io_sys->set_sts(uint8_t(sc) , state);
673 }
674 
set_flg_slot(unsigned slot,int state)675 void hp9845_base_state::set_flg_slot(unsigned slot , int state)
676 {
677 	int sc = m_slot_sc[ slot ];
678 	assert(sc >= 0);
679 	m_io_sys->set_flg(uint8_t(sc) , state);
680 }
681 
set_irq_nextsc_slot(unsigned slot,int state)682 void hp9845_base_state::set_irq_nextsc_slot(unsigned slot , int state)
683 {
684 	int sc = m_slot_sc[ slot ];
685 	assert(sc >= 0);
686 	m_io_sys->set_irq(uint8_t(sc + 1) , state);
687 }
688 
set_sts_nextsc_slot(unsigned slot,int state)689 void hp9845_base_state::set_sts_nextsc_slot(unsigned slot , int state)
690 {
691 	int sc = m_slot_sc[ slot ];
692 	assert(sc >= 0);
693 	m_io_sys->set_sts(uint8_t(sc + 1) , state);
694 }
695 
set_flg_nextsc_slot(unsigned slot,int state)696 void hp9845_base_state::set_flg_nextsc_slot(unsigned slot , int state)
697 {
698 	int sc = m_slot_sc[ slot ];
699 	assert(sc >= 0);
700 	m_io_sys->set_flg(uint8_t(sc + 1) , state);
701 }
702 
set_dmar_slot(unsigned slot,int state)703 void hp9845_base_state::set_dmar_slot(unsigned slot , int state)
704 {
705 	int sc = m_slot_sc[ slot ];
706 	assert(sc >= 0);
707 	m_io_sys->set_dmar(uint8_t(sc) , state);
708 }
709 
TIMER_DEVICE_CALLBACK_MEMBER(hp9845_base_state::beeper_off)710 TIMER_DEVICE_CALLBACK_MEMBER(hp9845_base_state::beeper_off)
711 {
712 	m_beeper->set_state(0);
713 }
714 
WRITE_LINE_MEMBER(hp9845_base_state::prt_irl_w)715 WRITE_LINE_MEMBER(hp9845_base_state::prt_irl_w)
716 {
717 	m_prt_irl = state;
718 	update_kb_prt_irq();
719 }
720 
INPUT_CHANGED_MEMBER(hp9845_base_state::togglekey_changed)721 INPUT_CHANGED_MEMBER(hp9845_base_state::togglekey_changed)
722 {
723 	uint32_t togglekey = param;
724 	switch (togglekey) {
725 	case 0: // Shift lock
726 		{
727 			bool state = m_io_shiftlock->read();
728 			popmessage("SHIFT LOCK %s", state ? "ON" : "OFF");
729 			m_shift_lock_led = state;
730 		}
731 		break;
732 	case 1: // Prt all
733 		{
734 			bool state = BIT(m_io_key[0]->read(), 1);
735 			popmessage("PRT ALL %s", state ? "ON" : "OFF");
736 			m_prt_all_led = state;
737 		}
738 		break;
739 	case 2: // Auto st
740 		{
741 			bool state = BIT(m_io_key[0]->read(), 17);
742 			popmessage("AUTO ST %s", state ? "ON" : "OFF");
743 			m_auto_st_led = state;
744 		}
745 		break;
746 	}
747 }
748 
749 // ***************
750 //  hp9845b_state
751 // ***************
752 class hp9845b_state : public hp9845_base_state
753 {
754 public:
755 	hp9845b_state(const machine_config &mconfig, device_type type, const char *tag);
756 
757 	void hp9845b(machine_config &config);
758 
759 private:
760 	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
761 
762 	virtual void machine_start() override;
763 	virtual void machine_reset() override;
764 
765 	virtual uint16_t graphic_r(offs_t offset) override;
766 	virtual void graphic_w(offs_t offset, uint16_t data) override;
767 
768 	TIMER_DEVICE_CALLBACK_MEMBER(scanline_timer);
769 
770 	DECLARE_WRITE_LINE_MEMBER(vblank_w);
771 
772 	void set_graphic_mode(bool graphic);
773 	void set_video_mar(uint16_t mar);
774 	void video_fill_buff(bool buff_idx);
775 	void video_render_buff(unsigned video_scanline , unsigned line_in_row, bool buff_idx);
776 	void graphic_video_render(unsigned video_scanline);
777 
778 	virtual void advance_gv_fsm(bool ds , bool trigger) override;
779 	void update_graphic_bits();
780 
781 	// Optional character generator
782 	required_region_ptr<uint8_t> m_optional_chargen;
783 
784 	uint8_t m_video_attr;
785 	uint16_t m_gv_cursor_w;   // U38 & U39 (GS)
786 	std::vector<uint16_t> m_graphic_mem;
787 };
788 
hp9845b_state(const machine_config & mconfig,device_type type,const char * tag)789 hp9845b_state::hp9845b_state(const machine_config &mconfig, device_type type, const char *tag)
790 	: hp9845_base_state(mconfig , type , tag),
791 	  m_optional_chargen(*this , "optional_chargen")
792 {
793 }
794 
screen_update(screen_device & screen,bitmap_rgb32 & bitmap,const rectangle & cliprect)795 uint32_t hp9845b_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
796 {
797 	if (m_graphic_sel) {
798 		copybitmap(bitmap, m_bitmap, 0, 0, GVIDEO_HBEND, GVIDEO_VBEND, cliprect);
799 	} else {
800 		copybitmap(bitmap, m_bitmap, 0, 0, 0, 0, cliprect);
801 	}
802 
803 	return 0;
804 }
805 
machine_start()806 void hp9845b_state::machine_start()
807 {
808 	// Common part first
809 	hp9845_base_state::machine_start();
810 
811 	m_graphic_mem.resize(GVIDEO_MEM_SIZE);
812 
813 	// initialize palette
814 	m_palette->set_pen_color(PEN_BLACK  , 0x00, 0x00, 0x00);    // black
815 	m_palette->set_pen_color(PEN_GRAPHIC, 0x00, I_GR, 0x00);    // graphics
816 	m_palette->set_pen_color(PEN_ALPHA  , 0x00, I_AL, 0x00);    // alpha
817 	m_palette->set_pen_color(PEN_CURSOR , 0x00, I_CU, 0x00);    // graphics cursor
818 }
819 
machine_reset()820 void hp9845b_state::machine_reset()
821 {
822 	// Common part first
823 	hp9845_base_state::machine_reset();
824 
825 	set_video_mar(0);
826 	m_video_attr = 0;
827 	update_graphic_bits();
828 }
829 
graphic_r(offs_t offset)830 uint16_t hp9845b_state::graphic_r(offs_t offset)
831 {
832 	uint16_t res = 0;
833 
834 	switch (offset) {
835 	case 0:
836 		// R4: data register
837 		res = m_gv_data_r;
838 		advance_gv_fsm(true , false);
839 		break;
840 
841 	case 1:
842 		// R5: status register
843 		if (m_gv_int_en) {
844 			BIT_SET(res, 7);
845 		}
846 		if (m_gv_dma_en) {
847 			BIT_SET(res, 6);
848 		}
849 		BIT_SET(res, 5);    // ID
850 		break;
851 
852 	case 2:
853 		// R6: data register with DMA TC
854 		m_gv_dma_en = false;
855 		res = m_gv_data_r;
856 		advance_gv_fsm(true , false);
857 		break;
858 
859 	case 3:
860 		// R7: not mapped
861 		break;
862 	}
863 
864 	//logerror("rd gv R%u = %04x\n", 4 + offset , res);
865 
866 	return res;
867 }
868 
graphic_w(offs_t offset,uint16_t data)869 void hp9845b_state::graphic_w(offs_t offset, uint16_t data)
870 {
871 		//logerror("wr gv R%u = %04x\n", 4 + offset , data);
872 
873 		switch (offset) {
874 		case 0:
875 				// R4: data register
876 				m_gv_data_w = data;
877 				m_gv_cursor_w = data;
878 				advance_gv_fsm(true , false);
879 				break;
880 
881 		case 1:
882 				// R5: command register
883 				m_gv_cmd = (uint8_t)(data & 0xf);
884 				m_gv_dma_en = BIT(data , 6) != 0;
885 				m_gv_int_en = BIT(data , 7) != 0;
886 				if (BIT(data , 5)) {
887 					m_gv_fsm_state = GV_STAT_RESET;
888 				}
889 				advance_gv_fsm(false , false);
890 				break;
891 
892 		case 2:
893 				// R6: data register with DMA TC
894 				m_gv_dma_en = false;
895 				m_gv_data_w = data;
896 				m_gv_cursor_w = data;
897 				advance_gv_fsm(true , false);
898 				break;
899 
900 		case 3:
901 				// R7: trigger
902 				advance_gv_fsm(false , true);
903 				break;
904 		}
905 }
906 
TIMER_DEVICE_CALLBACK_MEMBER(hp9845b_state::scanline_timer)907 TIMER_DEVICE_CALLBACK_MEMBER(hp9845b_state::scanline_timer)
908 {
909 	unsigned video_scanline = param;
910 
911 	if (m_graphic_sel) {
912 		if (video_scanline >= GVIDEO_VBEND && video_scanline < GVIDEO_VBSTART) {
913 			graphic_video_render(video_scanline);
914 		}
915 	} else if (video_scanline < VIDEO_ACTIVE_SCANLINES) {
916 		unsigned row = video_scanline / VIDEO_CHAR_HEIGHT;
917 		unsigned line_in_row = video_scanline - row * VIDEO_CHAR_HEIGHT;
918 
919 		if (line_in_row == 0) {
920 			// Start of new row, swap buffers
921 			m_video_buff_idx = !m_video_buff_idx;
922 			video_fill_buff(!m_video_buff_idx);
923 		}
924 
925 		video_render_buff(video_scanline , line_in_row , m_video_buff_idx);
926 	}
927 }
928 
WRITE_LINE_MEMBER(hp9845b_state::vblank_w)929 WRITE_LINE_MEMBER(hp9845b_state::vblank_w)
930 {
931 	// VBlank signal is fed into HALT flag of PPU
932 	m_ppu->halt_w(state);
933 
934 	if (state) {
935 		// Start of V blank
936 		set_video_mar(0);
937 		m_video_load_mar = true;
938 		m_video_first_mar = true;
939 		m_video_byte_idx = false;
940 		m_video_blanked = false;
941 		m_video_buff_idx = !m_video_buff_idx;
942 		video_fill_buff(!m_video_buff_idx);
943 	}
944 }
945 
set_graphic_mode(bool graphic)946 void hp9845b_state::set_graphic_mode(bool graphic)
947 {
948 	if (graphic != m_graphic_sel) {
949 		m_graphic_sel = graphic;
950 		logerror("GS=%d\n" , graphic);
951 		if (m_graphic_sel) {
952 			m_screen->configure(GVIDEO_HTOTAL , GVIDEO_VTOTAL , rectangle(GVIDEO_HBEND , GVIDEO_HBSTART - 1 , GVIDEO_VBEND , GVIDEO_VBSTART - 1) , HZ_TO_ATTOSECONDS(VIDEO_PIXEL_CLOCK) * GVIDEO_HTOTAL * GVIDEO_VTOTAL);
953 			// Set graphic mode view (1.23:1 aspect ratio)
954 			machine().render().first_target()->set_view(1);
955 		} else {
956 			m_screen->configure(VIDEO_HTOTAL , VIDEO_VTOTAL , rectangle(0 , VIDEO_HBSTART - 1 , 0 , VIDEO_ACTIVE_SCANLINES - 1) , HZ_TO_ATTOSECONDS(VIDEO_PIXEL_CLOCK) * VIDEO_HTOTAL * VIDEO_VTOTAL);
957 			// Set alpha mode view (1.92:1 aspect ratio)
958 			machine().render().first_target()->set_view(0);
959 		}
960 	}
961 }
962 
set_video_mar(uint16_t mar)963 void hp9845b_state::set_video_mar(uint16_t mar)
964 {
965 	m_video_mar = (mar & 0xfff) | VIDEO_BUFFER_BASE_HIGH;
966 }
967 
video_fill_buff(bool buff_idx)968 void hp9845b_state::video_fill_buff(bool buff_idx)
969 {
970 	unsigned char_idx = 0;
971 	unsigned iters = 0;
972 	uint8_t byte;
973 	address_space& prog_space = m_ppu->space(AS_PROGRAM);
974 
975 	m_video_buff[ buff_idx ].full = false;
976 
977 	while (1) {
978 		if (!m_video_byte_idx) {
979 			if (iters++ >= MAX_WORD_PER_ROW) {
980 				// Limit on accesses per row reached
981 				break;
982 			}
983 			m_video_word = prog_space.read_word(m_video_mar);
984 			if (m_video_load_mar) {
985 				// Load new address into MAR after start of a new frame or NWA instruction
986 				if (m_video_first_mar) {
987 					set_graphic_mode(!BIT(m_video_word , 15));
988 					m_video_first_mar = false;
989 				}
990 				set_video_mar(~m_video_word);
991 				m_video_load_mar = false;
992 				continue;
993 			} else {
994 				// Read normal word from frame buffer, start parsing at MSB
995 				set_video_mar(m_video_mar + 1);
996 				byte = (uint8_t)(m_video_word >> 8);
997 				m_video_byte_idx = true;
998 			}
999 		} else {
1000 			// Parse LSB
1001 			byte = (uint8_t)(m_video_word & 0xff);
1002 			m_video_byte_idx = false;
1003 		}
1004 		if ((byte & 0xc0) == 0x80) {
1005 			// Attribute command
1006 			m_video_attr = byte & 0x1f;
1007 		} else if ((byte & 0xc1) == 0xc0) {
1008 			// New Word Address (NWA)
1009 			m_video_load_mar = true;
1010 			m_video_byte_idx = false;
1011 		} else if ((byte & 0xc1) == 0xc1) {
1012 			// End of line (EOL)
1013 			// Fill rest of buffer with spaces
1014 			memset(&m_video_buff[ buff_idx ].chars[ char_idx ] , 0x20 , 80 - char_idx);
1015 			memset(&m_video_buff[ buff_idx ].attrs[ char_idx ] , m_video_attr , 80 - char_idx);
1016 			m_video_buff[ buff_idx ].full = true;
1017 			break;
1018 		} else {
1019 			// Normal character
1020 			m_video_buff[ buff_idx ].chars[ char_idx ] = byte;
1021 			m_video_buff[ buff_idx ].attrs[ char_idx ] = m_video_attr;
1022 			char_idx++;
1023 			if (char_idx == 80) {
1024 				m_video_buff[ buff_idx ].full = true;
1025 				break;
1026 			}
1027 		}
1028 	}
1029 }
1030 
video_render_buff(unsigned video_scanline,unsigned line_in_row,bool buff_idx)1031 void hp9845b_state::video_render_buff(unsigned video_scanline , unsigned line_in_row, bool buff_idx)
1032 {
1033 	if (!m_video_buff[ buff_idx ].full) {
1034 		m_video_blanked = true;
1035 	}
1036 
1037 	const pen_t *pen = m_palette->pens();
1038 
1039 	if (m_video_blanked) {
1040 		// Blank scanline
1041 		for (unsigned i = 0; i < VIDEO_HBSTART; i++) {
1042 			m_bitmap.pix(video_scanline , i) = pen[ PEN_BLACK ];
1043 		}
1044 	} else {
1045 		bool cursor_line = line_in_row == 12;
1046 		bool ul_line = line_in_row == 14;
1047 		unsigned video_frame = (unsigned)m_screen->frame_number();
1048 		bool cursor_blink = BIT(video_frame , 3);
1049 		bool char_blink = BIT(video_frame , 4);
1050 
1051 		for (unsigned i = 0; i < 80; i++) {
1052 			uint8_t charcode = m_video_buff[ buff_idx ].chars[ i ];
1053 			uint8_t attrs = m_video_buff[ buff_idx ].attrs[ i ];
1054 			uint16_t chrgen_addr = ((uint16_t)(charcode ^ 0x7f) << 4) | line_in_row;
1055 			uint16_t pixels;
1056 
1057 			if ((ul_line && BIT(attrs , 3)) ||
1058 				(cursor_line && cursor_blink && BIT(attrs , 0))) {
1059 				pixels = ~0;
1060 			} else if (char_blink && BIT(attrs , 2)) {
1061 				pixels = 0;
1062 			} else if (BIT(attrs , 4)) {
1063 				pixels = (uint16_t)(m_optional_chargen[ chrgen_addr ] & 0x7f) << 1;
1064 			} else {
1065 				pixels = (uint16_t)(m_chargen[ chrgen_addr ] & 0x7f) << 1;
1066 			}
1067 
1068 			if (BIT(attrs , 1)) {
1069 				pixels = ~pixels;
1070 			}
1071 
1072 			for (unsigned j = 0; j < 9; j++) {
1073 				bool pixel = (pixels & (1U << j)) != 0;
1074 
1075 				m_bitmap.pix(video_scanline , i * 9 + j) = pen[ pixel ? PEN_ALPHA : PEN_BLACK ];
1076 			}
1077 		}
1078 	}
1079 }
1080 
graphic_video_render(unsigned video_scanline)1081 void hp9845b_state::graphic_video_render(unsigned video_scanline)
1082 {
1083 	const pen_t *pen = m_palette->pens();
1084 	bool yc = (video_scanline + GVIDEO_VCNT_OFF) == (m_gv_cursor_y + 6);
1085 	bool yw;
1086 	bool blink;
1087 
1088 	if (m_gv_cursor_fs) {
1089 		yw = true;
1090 		// Steady cursor
1091 		blink = true;
1092 	} else {
1093 		yw = (video_scanline + GVIDEO_VCNT_OFF) >= (m_gv_cursor_y + 2) &&
1094 			(video_scanline + GVIDEO_VCNT_OFF) <= (m_gv_cursor_y + 10);
1095 		// Blinking cursor (frame freq. / 16)
1096 		blink = BIT(m_screen->frame_number() , 3) != 0;
1097 	}
1098 
1099 	unsigned mem_idx = 36 * (video_scanline - GVIDEO_VBEND);
1100 	for (unsigned i = 0; i < GVIDEO_HPIXELS; i += 16) {
1101 		uint16_t word = m_graphic_mem[ mem_idx++ ];
1102 		unsigned x = i;
1103 		for (uint16_t mask = 0x8000; mask != 0; mask >>= 1) {
1104 			unsigned cnt_h = x + GVIDEO_HBEND + GVIDEO_HCNT_OFF;
1105 			bool xc = cnt_h == (m_gv_cursor_x + 6);
1106 			bool xw = m_gv_cursor_fs || (cnt_h >= (m_gv_cursor_x + 2) && cnt_h <= (m_gv_cursor_x + 10));
1107 			unsigned pixel;
1108 			if (blink && ((xw && yc) || (yw && xc && m_gv_cursor_gc))) {
1109 				// Cursor
1110 				pixel = PEN_CURSOR;
1111 			} else {
1112 				// Normal pixel
1113 				pixel = (word & mask) != 0 ? PEN_GRAPHIC : PEN_BLACK;
1114 			}
1115 			m_bitmap.pix(video_scanline - GVIDEO_VBEND , x++) = pen[ pixel ];
1116 		}
1117 	}
1118 }
1119 
advance_gv_fsm(bool ds,bool trigger)1120 void hp9845b_state::advance_gv_fsm(bool ds , bool trigger)
1121 {
1122 	bool get_out = false;
1123 
1124 	attotime time_mem_av;
1125 
1126 	do {
1127 		bool act_trig = trigger || m_gv_dma_en || !BIT(m_gv_cmd , 2);
1128 
1129 		switch (m_gv_fsm_state) {
1130 		case GV_STAT_WAIT_DS_0:
1131 			if ((m_gv_cmd & 0xc) == 0xc) {
1132 				// Read command (11xx)
1133 				m_gv_fsm_state = GV_STAT_WAIT_MEM_0;
1134 			} else if (ds) {
1135 				// Wait for data strobe (r/w on r4 or r6)
1136 				m_gv_fsm_state = GV_STAT_WAIT_TRIG_0;
1137 			} else {
1138 				get_out = true;
1139 			}
1140 			break;
1141 
1142 		case GV_STAT_WAIT_TRIG_0:
1143 			// Wait for trigger
1144 			if (act_trig) {
1145 				if (BIT(m_gv_cmd , 3)) {
1146 					// Not a cursor command
1147 					// Load memory address
1148 					m_gv_io_counter = ~m_gv_data_w & GVIDEO_ADDR_MASK;
1149 					// Write commands (10xx)
1150 					m_gv_fsm_state = GV_STAT_WAIT_DS_2;
1151 				} else {
1152 					// Cursor command (0xxx)
1153 					if (BIT(m_gv_cmd , 2)) {
1154 						// Write X cursor position (01xx)
1155 						m_gv_cursor_x = (~m_gv_cursor_w >> 6) & 0x3ff;
1156 					} else {
1157 						// Write Y cursor position and type (00xx)
1158 						m_gv_cursor_y = (~m_gv_cursor_w >> 6) & 0x1ff;
1159 						m_gv_cursor_gc = BIT(m_gv_cmd , 1) == 0;
1160 						m_gv_cursor_fs = BIT(m_gv_cmd , 0) != 0;
1161 					}
1162 					m_gv_fsm_state = GV_STAT_WAIT_DS_0;
1163 				}
1164 			} else {
1165 				get_out = true;
1166 			}
1167 			break;
1168 
1169 		case GV_STAT_WAIT_MEM_0:
1170 			time_mem_av = time_to_gv_mem_availability();
1171 			if (time_mem_av.is_zero()) {
1172 				// Read a word from graphic memory
1173 				m_gv_data_r = m_graphic_mem[ m_gv_io_counter ];
1174 				m_gv_io_counter = (m_gv_io_counter + 1) & GVIDEO_ADDR_MASK;
1175 				m_gv_fsm_state = GV_STAT_WAIT_DS_1;
1176 			} else {
1177 				m_gv_timer->adjust(time_mem_av);
1178 				get_out = true;
1179 			}
1180 			break;
1181 
1182 		case GV_STAT_WAIT_DS_1:
1183 			if (ds) {
1184 				m_gv_fsm_state = GV_STAT_WAIT_MEM_0;
1185 			} else {
1186 				get_out = true;
1187 			}
1188 			break;
1189 
1190 		case GV_STAT_WAIT_DS_2:
1191 			// Wait for data word to be written
1192 			if (ds) {
1193 				m_gv_fsm_state = GV_STAT_WAIT_TRIG_1;
1194 			} else {
1195 				get_out = true;
1196 			}
1197 			break;
1198 
1199 		case GV_STAT_WAIT_TRIG_1:
1200 			// Wait for trigger
1201 			if (act_trig) {
1202 				if (BIT(m_gv_cmd , 1)) {
1203 					// Clear words (101x)
1204 					m_gv_data_w = 0;
1205 					m_gv_fsm_state = GV_STAT_WAIT_MEM_1;
1206 				} else if (BIT(m_gv_cmd , 0)) {
1207 					// Write a single pixel (1001)
1208 					m_gv_fsm_state = GV_STAT_WAIT_MEM_2;
1209 				} else {
1210 					// Write words (1000)
1211 					m_gv_fsm_state = GV_STAT_WAIT_MEM_1;
1212 				}
1213 			} else {
1214 				get_out = true;
1215 			}
1216 			break;
1217 
1218 		case GV_STAT_WAIT_MEM_1:
1219 			time_mem_av = time_to_gv_mem_availability();
1220 			if (time_mem_av.is_zero()) {
1221 				// Write a full word to graphic memory
1222 				m_graphic_mem[ m_gv_io_counter ] = m_gv_data_w;
1223 				m_gv_io_counter = (m_gv_io_counter + 1) & GVIDEO_ADDR_MASK;
1224 				m_gv_fsm_state = GV_STAT_WAIT_DS_2;
1225 			} else {
1226 				m_gv_timer->adjust(time_mem_av);
1227 				get_out = true;
1228 			}
1229 			break;
1230 
1231 		case GV_STAT_WAIT_MEM_2:
1232 			time_mem_av = time_to_gv_mem_availability();
1233 			if (time_mem_av.is_zero()) {
1234 				// Write a single pixel to graphic memory
1235 				uint16_t mask = 0x8000 >> (m_gv_data_w & 0xf);
1236 				if (BIT(m_gv_data_w , 15)) {
1237 					// Set pixel
1238 					m_graphic_mem[ m_gv_io_counter ] |= mask;
1239 				} else {
1240 					// Clear pixel
1241 					m_graphic_mem[ m_gv_io_counter ] &= ~mask;
1242 				}
1243 				// Not really needed
1244 				m_gv_io_counter = (m_gv_io_counter + 1) & GVIDEO_ADDR_MASK;
1245 				m_gv_fsm_state = GV_STAT_WAIT_DS_0;
1246 			} else {
1247 				m_gv_timer->adjust(time_mem_av);
1248 				get_out = true;
1249 			}
1250 			break;
1251 
1252 		default:
1253 			logerror("Invalid state reached %d\n" , m_gv_fsm_state);
1254 			m_gv_fsm_state = GV_STAT_RESET;
1255 		}
1256 
1257 		ds = false;
1258 		trigger = false;
1259 	} while (!get_out);
1260 
1261 	update_graphic_bits();
1262 }
1263 
update_graphic_bits()1264 void hp9845b_state::update_graphic_bits()
1265 {
1266 		bool gv_ready = m_gv_fsm_state == GV_STAT_WAIT_DS_0 ||
1267 			m_gv_fsm_state == GV_STAT_WAIT_DS_1 ||
1268 			m_gv_fsm_state == GV_STAT_WAIT_DS_2;
1269 
1270 		m_io_sys->set_flg(GVIDEO_PA , gv_ready);
1271 
1272 		bool irq = m_gv_int_en && !m_gv_dma_en && gv_ready;
1273 
1274 		m_io_sys->set_irq(GVIDEO_PA , irq);
1275 
1276 		bool dmar = gv_ready && m_gv_dma_en;
1277 
1278 		m_io_sys->set_dmar(GVIDEO_PA , dmar);
1279 }
1280 
1281 // ***************
1282 //  hp9845ct_base_state
1283 // ***************
1284 
1285 class hp9845ct_base_state : public hp9845_base_state
1286 {
1287 public:
1288 	hp9845ct_base_state(const machine_config &mconfig, device_type type, const char *tag);
1289 
1290 	virtual void machine_start() override;
1291 	virtual void machine_reset() override;
1292 
1293 	uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
1294 
1295 	DECLARE_WRITE_LINE_MEMBER(vblank_w);
1296 	DECLARE_INPUT_CHANGED_MEMBER(softkey_changed);
1297 
1298 protected:
1299 	required_ioport m_io_softkeys;
1300 	required_ioport m_lightpen_x;
1301 	required_ioport m_lightpen_y;
1302 	required_ioport m_lightpen_sw;
1303 
1304 	virtual void set_graphic_mode(bool graphic , bool alpha) = 0;
1305 	void set_video_mar(uint16_t mar);
1306 	void video_fill_buff(bool buff_idx);
1307 	virtual void plot(uint16_t x, uint16_t y, bool draw_erase) = 0;
1308 	void draw_line(unsigned x0 , unsigned y0 , unsigned x1 , unsigned y1);
1309 	void update_line_pattern();
1310 	void pattern_fill(uint16_t x0 , uint16_t y0 , uint16_t x1 , uint16_t y1 , unsigned fill_idx);
1311 	static uint16_t get_gv_mem_addr(unsigned x , unsigned y);
1312 	virtual void update_graphic_bits() = 0;
1313 	static int get_wrapped_scanline(unsigned scanline);
1314 	void render_lp_cursor(unsigned video_scanline , unsigned pen_idx);
1315 
1316 	void lp_r4_w(uint16_t data);
1317 	uint16_t lp_r4_r();
1318 	void lp_r5_w(uint16_t data);
1319 	bool lp_segment_intersect(unsigned yline) const;
1320 	void compute_lp_data();
1321 	void lp_scanline_update(unsigned video_scanline);
1322 
1323 	virtual void update_gcursor() = 0;
1324 
1325 	bool m_alpha_sel;
1326 	bool m_gv_sk_en;
1327 	bool m_gv_gr_en;
1328 	bool m_gv_opt_en;
1329 	bool m_gv_dsa_en;
1330 	bool m_gv_lp_status;
1331 	bool m_gv_sk_status;
1332 	uint16_t m_gv_lp_cursor_x;
1333 	uint16_t m_gv_lp_cursor_y;
1334 	bool m_gv_lp_cursor_fs;
1335 	bool m_gv_lp_en;
1336 	uint8_t m_gv_last_cmd;
1337 	uint16_t m_gv_word_x_position;
1338 	uint16_t m_gv_word_y_position;
1339 	uint16_t m_gv_memory_control;
1340 	uint16_t m_gv_line_type_area_fill;
1341 	uint16_t m_gv_line_type_mask;
1342 	uint8_t m_gv_repeat_count;
1343 	uint16_t m_gv_xpt;
1344 	uint16_t m_gv_ypt;
1345 	uint16_t m_gv_last_xpt;
1346 	uint16_t m_gv_last_ypt;
1347 	uint16_t m_gv_lp_data[ 3 ];
1348 	uint16_t m_gv_next_lp_data[ 3 ];
1349 	unsigned m_gv_next_lp_scanline[ 3 ];
1350 	bool m_gv_lp_selftest;
1351 	bool m_gv_lp_interlace;
1352 	bool m_gv_lp_vblank;
1353 	bool m_gv_lp_1sthit;
1354 	bool m_gv_lp_vbint;
1355 	bool m_gv_lp_fullbright;
1356 	bool m_gv_lp_threshold;
1357 	uint16_t m_gv_lp_x;
1358 	uint16_t m_gv_lp_y;
1359 	bool m_gv_lp_sw;
1360 	uint8_t m_gv_lp_reg_cnt;
1361 	bool m_gv_lp_int_en;
1362 	bool m_gv_lp_hit_lt192;
1363 	bool m_gv_lp_int_256;
1364 	uint16_t m_gv_lxc;
1365 	uint16_t m_gv_lyc;
1366 	uint8_t m_gv_softkey;
1367 
1368 	static const uint16_t m_line_type[];
1369 	static const uint16_t m_area_fill[];
1370 };
1371 
1372 /*
1373    For 9845C and 9845T we just add the light pen support via MAME's lightgun device.
1374 
1375    Note that the LIGHTGUN device needs '-lightgun' and '-lightgun_device mouse' for light gun emulation if no real light gun device is installed.
1376  */
1377 static INPUT_PORTS_START(hp9845ct)
PORT_INCLUDE(hp9845_base)1378 	PORT_INCLUDE(hp9845_base)
1379 	PORT_START("SOFTKEYS")
1380 	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Softkey0") PORT_CHANGED_MEMBER(DEVICE_SELF, hp9845ct_base_state, softkey_changed, 0)
1381 	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Softkey1") PORT_CHANGED_MEMBER(DEVICE_SELF, hp9845ct_base_state, softkey_changed, 0)
1382 	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Softkey2") PORT_CHANGED_MEMBER(DEVICE_SELF, hp9845ct_base_state, softkey_changed, 0)
1383 	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Softkey3") PORT_CHANGED_MEMBER(DEVICE_SELF, hp9845ct_base_state, softkey_changed, 0)
1384 	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Softkey4") PORT_CHANGED_MEMBER(DEVICE_SELF, hp9845ct_base_state, softkey_changed, 0)
1385 	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Softkey5") PORT_CHANGED_MEMBER(DEVICE_SELF, hp9845ct_base_state, softkey_changed, 0)
1386 	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Softkey6") PORT_CHANGED_MEMBER(DEVICE_SELF, hp9845ct_base_state, softkey_changed, 0)
1387 	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Softkey7") PORT_CHANGED_MEMBER(DEVICE_SELF, hp9845ct_base_state, softkey_changed, 0)
1388 
1389 	PORT_START("LIGHTPENX")
1390 	PORT_BIT( 0x3ff, 0x000, IPT_LIGHTGUN_X ) PORT_SENSITIVITY(20) PORT_MINMAX(0, VIDEO_TOT_HPIXELS - 1) PORT_CROSSHAIR(X, 1.0, 0.0, 0)
1391 
1392 	PORT_START("LIGHTPENY")
1393 	PORT_BIT( 0x3ff, 0x000, IPT_LIGHTGUN_Y ) PORT_SENSITIVITY(20) PORT_MINMAX(0, GVIDEO_VPIXELS - 1) PORT_CROSSHAIR(Y, 1.0, 0.0, 0)
1394 
1395 	PORT_START("GKEY")
1396 	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_CODE(MOUSECODE_BUTTON1) PORT_NAME("Gkey")
1397 INPUT_PORTS_END
1398 
1399 static INPUT_PORTS_START(hp9845ct_de)
1400 	PORT_INCLUDE(hp9845_base_de)
1401 	PORT_START("SOFTKEYS")
1402 	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Softkey0") PORT_CHANGED_MEMBER(DEVICE_SELF, hp9845ct_base_state, softkey_changed, 0)
1403 	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Softkey1") PORT_CHANGED_MEMBER(DEVICE_SELF, hp9845ct_base_state, softkey_changed, 0)
1404 	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Softkey2") PORT_CHANGED_MEMBER(DEVICE_SELF, hp9845ct_base_state, softkey_changed, 0)
1405 	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Softkey3") PORT_CHANGED_MEMBER(DEVICE_SELF, hp9845ct_base_state, softkey_changed, 0)
1406 	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Softkey4") PORT_CHANGED_MEMBER(DEVICE_SELF, hp9845ct_base_state, softkey_changed, 0)
1407 	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Softkey5") PORT_CHANGED_MEMBER(DEVICE_SELF, hp9845ct_base_state, softkey_changed, 0)
1408 	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Softkey6") PORT_CHANGED_MEMBER(DEVICE_SELF, hp9845ct_base_state, softkey_changed, 0)
1409 	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Softkey7") PORT_CHANGED_MEMBER(DEVICE_SELF, hp9845ct_base_state, softkey_changed, 0)
1410 
1411 	PORT_START("LIGHTPENX")
1412 	PORT_BIT( 0x3ff, 0x000, IPT_LIGHTGUN_X ) PORT_SENSITIVITY(20) PORT_MINMAX(0, VIDEO_TOT_HPIXELS - 1) PORT_CROSSHAIR(X, 1.0, 0.0, 0)
1413 
1414 	PORT_START("LIGHTPENY")
1415 	PORT_BIT( 0x3ff, 0x000, IPT_LIGHTGUN_Y ) PORT_SENSITIVITY(20) PORT_MINMAX(0, GVIDEO_VPIXELS - 1) PORT_CROSSHAIR(Y, 1.0, 0.0, 0)
1416 
1417 	PORT_START("GKEY")
1418 	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_CODE(MOUSECODE_BUTTON1) PORT_NAME("Gkey")
1419 INPUT_PORTS_END
1420 
1421 hp9845ct_base_state::hp9845ct_base_state(const machine_config &mconfig, device_type type, const char *tag)
1422 	: hp9845_base_state(mconfig , type , tag),
1423 	  m_io_softkeys(*this, "SOFTKEYS"),
1424 	  m_lightpen_x(*this, "LIGHTPENX"),
1425 	  m_lightpen_y(*this, "LIGHTPENY"),
1426 	  m_lightpen_sw(*this, "GKEY")
1427 {
1428 }
1429 
machine_start()1430 void hp9845ct_base_state::machine_start()
1431 {
1432 	// Common part first
1433 	hp9845_base_state::machine_start();
1434 }
1435 
machine_reset()1436 void hp9845ct_base_state::machine_reset()
1437 {
1438 	// Common part first
1439 	hp9845_base_state::machine_reset();
1440 
1441 	m_alpha_sel = true;
1442 	m_gv_sk_en = false;
1443 	m_gv_gr_en = false;
1444 	m_gv_opt_en = false;
1445 	m_gv_dsa_en = false;
1446 	m_gv_lp_status = false;
1447 	m_gv_sk_status = false;
1448 	m_gv_lp_cursor_x = 944;
1449 	m_gv_lp_cursor_y = 50;
1450 	m_gv_lp_cursor_fs = false;
1451 	m_gv_lp_en = false;
1452 	m_gv_last_cmd = 0;
1453 	m_gv_word_x_position = 0;
1454 	m_gv_word_y_position = 0;
1455 	m_gv_memory_control = 0;
1456 	m_gv_line_type_area_fill = 0;
1457 	m_gv_line_type_mask = 0xffff;
1458 	m_gv_repeat_count = 0;
1459 	m_gv_xpt = 0;
1460 	m_gv_ypt = 0;
1461 	m_gv_last_xpt = 0;
1462 	m_gv_last_ypt = 0;
1463 	m_gv_lp_selftest = false;
1464 	m_gv_lp_interlace = false;
1465 	m_gv_lp_vblank = false;
1466 	m_gv_lp_1sthit = false;
1467 	m_gv_lp_vbint = false;
1468 	m_gv_lp_fullbright = false;
1469 	m_gv_lp_threshold = false;
1470 	m_gv_lp_x = 0;
1471 	m_gv_lp_y = 0;
1472 	m_gv_lp_sw = false;
1473 	m_gv_lp_int_en = false;
1474 
1475 	update_graphic_bits();
1476 }
1477 
screen_update(screen_device & screen,bitmap_rgb32 & bitmap,const rectangle & cliprect)1478 uint32_t hp9845ct_base_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
1479 {
1480 	copybitmap(bitmap, m_bitmap, 0, 0, 0, 0, cliprect);
1481 
1482 	return 0;
1483 }
1484 
WRITE_LINE_MEMBER(hp9845ct_base_state::vblank_w)1485 WRITE_LINE_MEMBER(hp9845ct_base_state::vblank_w)
1486 {
1487 	// VBlank signal is fed into HALT flag of PPU
1488 	m_ppu->halt_w(state);
1489 
1490 	if (state) {
1491 		// Start of V blank
1492 		set_video_mar(0);
1493 		m_video_load_mar = true;
1494 		m_video_first_mar = true;
1495 		m_video_blanked = false;
1496 		m_video_buff_idx = !m_video_buff_idx;
1497 		video_fill_buff(!m_video_buff_idx);
1498 
1499 		// lightpen
1500 		m_gv_lp_vblank = true;
1501 		m_gv_lp_sw = m_lightpen_sw->read();
1502 		m_gv_lp_x = m_lightpen_x->read();
1503 		if (m_gv_lp_x > (VIDEO_TOT_HPIXELS - 1)) {
1504 			m_gv_lp_x = VIDEO_TOT_HPIXELS - 1;
1505 		}
1506 		m_gv_lp_y = m_lightpen_y->read();
1507 		if (m_gv_lp_y > (GVIDEO_VPIXELS - 1)) {
1508 			m_gv_lp_y = GVIDEO_VPIXELS - 1;
1509 		}
1510 	} else {
1511 		m_gv_lp_vblank = false;
1512 		update_gcursor();
1513 	}
1514 }
1515 
INPUT_CHANGED_MEMBER(hp9845ct_base_state::softkey_changed)1516 INPUT_CHANGED_MEMBER(hp9845ct_base_state::softkey_changed)
1517 {
1518 	uint8_t softkey_data = m_io_softkeys->read();
1519 	unsigned softkey;
1520 	for (softkey = 0; softkey < 8; softkey++) {
1521 		m_softkeys[softkey] = !BIT(softkey_data , 7 - softkey);
1522 	}
1523 	for (softkey = 0; softkey < 8 && BIT(softkey_data , 7 - softkey); softkey++) {
1524 	}
1525 	LOG("SK %02x => %u\n" , softkey_data , softkey);
1526 	if (softkey < 8 && !m_gv_sk_status) {
1527 		// softkey pressed
1528 		m_gv_softkey = softkey;
1529 		m_gv_sk_status = true;
1530 		update_graphic_bits();
1531 	}
1532 }
1533 
set_video_mar(uint16_t mar)1534 void hp9845ct_base_state::set_video_mar(uint16_t mar)
1535 {
1536 	m_video_mar = (mar & 0x1fff) | VIDEO_BUFFER_BASE_LOW;
1537 }
1538 
video_fill_buff(bool buff_idx)1539 void hp9845ct_base_state::video_fill_buff(bool buff_idx)
1540 {
1541 	unsigned char_idx = 0;
1542 	unsigned iters = 0;
1543 	address_space& prog_space = m_ppu->space(AS_PROGRAM);
1544 
1545 	m_video_buff[ buff_idx ].full = false;
1546 
1547 	while (1) {
1548 		if ((m_video_mar & 0x1fff) > 0x1dff) {
1549 			// CRT buffer ends at 0x7dff
1550 			break;
1551 		}
1552 		// Get video word
1553 		if (iters++ >= MAX_WORD_PER_ROW) {
1554 			// Limit on accesses per row reached
1555 			break;
1556 		}
1557 		m_video_word = prog_space.read_word(m_video_mar);
1558 		if (m_video_load_mar) {
1559 			// Load new address into MAR after start of a new frame or NWA instruction
1560 			if (m_video_first_mar) {
1561 				set_graphic_mode(BIT(m_video_word , 15), BIT(m_video_word , 14));
1562 				m_video_first_mar = false;
1563 			}
1564 			set_video_mar(~m_video_word);
1565 			m_video_load_mar = false;
1566 			continue;
1567 		} else {
1568 			// Update counter for next word fetch
1569 			set_video_mar(m_video_mar + 1);
1570 		}
1571 		// Parse video word
1572 		if (m_video_word == 0x8020) {
1573 			// End-of-line (EOL)
1574 			// Fill rest of buffer with spaces
1575 			memset(&m_video_buff[ buff_idx ].chars[ char_idx ] , 0x20 , 80 - char_idx);
1576 			memset(&m_video_buff[ buff_idx ].attrs[ char_idx ] , 0 , 80 - char_idx);
1577 			m_video_buff[ buff_idx ].full = true;
1578 			break;
1579 		} else if ((m_video_word & 0xc020) == 0x8000) {
1580 			// New word address (NWA)
1581 			m_video_load_mar = true;
1582 		} else if ((m_video_word & 0xc000) == 0xc000) {
1583 			// NOP
1584 		} else {
1585 			// fill line buffer
1586 			m_video_buff[ buff_idx ].chars[ char_idx ] = (uint8_t)(m_video_word & 0xff);
1587 			m_video_buff[ buff_idx ].attrs[ char_idx ] = (uint8_t)(m_video_word >> 8);
1588 			char_idx++;
1589 			if (char_idx == 80) {
1590 				m_video_buff[ buff_idx ].full = true;
1591 				break;
1592 			}
1593 		}
1594 	}
1595 }
1596 
draw_line(unsigned x0,unsigned y0,unsigned x1,unsigned y1)1597 void hp9845ct_base_state::draw_line(unsigned x0 , unsigned y0 , unsigned x1 , unsigned y1)
1598 {
1599 	int dx, dy, sx, sy, x, y, err, e2;
1600 
1601 	// draw line, vector generator uses Bresenham's algorithm
1602 	x = x0;
1603 	y = y0;
1604 	dx = abs((int) (x1 - x));
1605 	sx = x < x1 ? 1 : -1;   // actually always 1 because of normalization
1606 	dy = abs((int) (y1 - y));
1607 	sy = y < y1 ? 1 : -1;
1608 	err = (dx > dy ? dx : -dy) / 2;
1609 
1610 	for(;;){
1611 		plot(x, y, BIT(m_gv_line_type_mask, 15));
1612 		update_line_pattern();
1613 
1614 		if (x == x1 && y == y1) break;
1615 
1616 		e2 = err;
1617 		if (e2 > -dx) {
1618 			err -= dy;
1619 			x += sx;
1620 		}
1621 		if (e2 < dy) {
1622 			err += dx;
1623 			y += sy;
1624 		}
1625 	}
1626 }
1627 
update_line_pattern()1628 void hp9845ct_base_state::update_line_pattern()
1629 {
1630 	// update line pattern
1631 	m_gv_repeat_count++;
1632 	if (m_gv_repeat_count > ((m_gv_line_type_area_fill >> 5) & 0xf)) {
1633 		// Rotate m_gv_line_type_mask 1 bit to the left
1634 		bool save_bit = BIT(m_gv_line_type_mask , 15);
1635 		m_gv_line_type_mask = save_bit | (m_gv_line_type_mask << 1);
1636 		m_gv_repeat_count = 0;
1637 	}
1638 }
1639 
pattern_fill(uint16_t x0,uint16_t y0,uint16_t x1,uint16_t y1,unsigned fill_idx)1640 void hp9845ct_base_state::pattern_fill(uint16_t x0 , uint16_t y0 , uint16_t x1 , uint16_t y1 , unsigned fill_idx)
1641 {
1642 	uint16_t x,y,xmax,ymax;
1643 	uint16_t pixel_mask, fill_mask;
1644 
1645 	x = std::min(x0 , x1);
1646 	xmax = std::max(x0 , x1);
1647 	y = std::min(y0 , y1);
1648 	ymax = std::max(y0 , y1);
1649 
1650 	for (;y <= ymax; y++) {
1651 		fill_mask = (m_area_fill[ fill_idx ] << (y % 4) * 4) & 0xf000;
1652 		fill_mask |= (fill_mask >> 4) | (fill_mask >> 8) | (fill_mask >> 12);
1653 		for (;x <= xmax; x++) {
1654 			pixel_mask = (0x8000 >> (x % 16));
1655 			plot(x , y , (pixel_mask & fill_mask) != 0);
1656 		}
1657 	}
1658 }
1659 
get_gv_mem_addr(unsigned x,unsigned y)1660 uint16_t hp9845ct_base_state::get_gv_mem_addr(unsigned x , unsigned y)
1661 {
1662 	return (uint16_t)((x + y * 35) & GVIDEO_ADDR_MASK);
1663 }
1664 
get_wrapped_scanline(unsigned scanline)1665 int hp9845ct_base_state::get_wrapped_scanline(unsigned scanline)
1666 {
1667 	// The 770's VTOTAL applies to 780, too.
1668 	// The 780 hw generates a line clock (GVclk in Duell's schematics) that's suppressed
1669 	// for lines in [485..524] range so that the total number of lines per frame counted
1670 	// by this clock matches the total count of lines in 770 (i.e. 485).
1671 	int wrapped_cursor_y = (int)scanline;
1672 	if (wrapped_cursor_y >= GVIDEO_VPIXELS && wrapped_cursor_y < VIDEO_770_VTOTAL) {
1673 		wrapped_cursor_y -= VIDEO_770_VTOTAL;
1674 	}
1675 
1676 	return wrapped_cursor_y;
1677 }
1678 
render_lp_cursor(unsigned video_scanline,unsigned pen_idx)1679 void hp9845ct_base_state::render_lp_cursor(unsigned video_scanline , unsigned pen_idx)
1680 {
1681 	int cursor_y_top = get_wrapped_scanline(m_gv_lp_cursor_y);
1682 
1683 	bool yw;
1684 	if (m_gv_lp_cursor_fs) {
1685 		yw = true;
1686 	} else {
1687 		yw = (int)video_scanline >= cursor_y_top &&
1688 			(int)video_scanline <= (cursor_y_top + 48);
1689 	}
1690 	if (!yw) {
1691 		return;
1692 	}
1693 
1694 	if (!m_gv_lp_cursor_fs && m_gv_lp_cursor_x >= (VIDEO_TOT_HPIXELS + 24)) {
1695 		return;
1696 	}
1697 
1698 	bool yc = video_scanline == (cursor_y_top + 24);
1699 
1700 	const pen_t &pen = m_palette->pen(pen_idx);
1701 	if (!yc) {
1702 		if (m_gv_lp_cursor_x < VIDEO_TOT_HPIXELS) {
1703 			m_bitmap.pix(video_scanline , m_gv_lp_cursor_x) = pen;
1704 		}
1705 	} else if (m_gv_lp_cursor_fs) {
1706 		for (unsigned x = 0; x < VIDEO_TOT_HPIXELS; x++) {
1707 			m_bitmap.pix(video_scanline , x) = pen;
1708 		}
1709 	} else {
1710 		for (unsigned x = std::max(0 , (int)m_gv_lp_cursor_x - 24); x <= (m_gv_lp_cursor_x + 25) && x < VIDEO_TOT_HPIXELS; x++) {
1711 			m_bitmap.pix(video_scanline , x) = pen;
1712 		}
1713 	}
1714 }
1715 
lp_r4_w(uint16_t data)1716 void hp9845ct_base_state::lp_r4_w(uint16_t data)
1717 {
1718 	if (m_gv_lp_en) {
1719 		switch (m_gv_lp_reg_cnt) {
1720 		case 2:
1721 			// LP Y cursor + threshold + interlace + vertical blank interrupt
1722 			m_gv_lp_cursor_y = ((~data >> 6) & 0x1ff);
1723 			m_gv_lp_fullbright = BIT(data, 1);
1724 			m_gv_lp_threshold = BIT(data, 3);
1725 			m_gv_lp_interlace = !BIT(data, 4);
1726 			m_gv_lp_vbint = BIT(data, 5);
1727 			LOG("LP Y cursor y = %d, threshold = %d, interlace = %d, vbint = %d\n",
1728 				 m_gv_lp_cursor_y, m_gv_lp_threshold, m_gv_lp_interlace, m_gv_lp_vbint);
1729 			m_gv_lp_reg_cnt--;
1730 			break;
1731 
1732 		case 3:
1733 			// LP X cursor + cursor type
1734 			m_gv_lp_cursor_x = ((data >> 6) & 0x3ff) + 1;
1735 			m_gv_lp_cursor_fs = !BIT(data, 0);
1736 			LOG("LP X cursor x = %d, fs = %d\n", m_gv_lp_cursor_x, m_gv_lp_cursor_fs);
1737 			m_gv_lp_reg_cnt--;
1738 			break;
1739 
1740 		default:
1741 			logerror("Writing to unmapped LP register %u\n" , m_gv_lp_reg_cnt);
1742 		}
1743 	}
1744 }
1745 
lp_r4_r()1746 uint16_t hp9845ct_base_state::lp_r4_r()
1747 {
1748 	uint16_t res = 0;
1749 
1750 	if (m_gv_lp_en) {
1751 		switch (m_gv_lp_reg_cnt) {
1752 		case 4:
1753 			// YLO
1754 			res = m_gv_lp_data[ 2 ];
1755 			m_gv_lp_reg_cnt--;
1756 			m_gv_lp_status = false;
1757 			m_gv_lp_1sthit = false;
1758 			update_graphic_bits();
1759 			break;
1760 
1761 		case 5:
1762 			// XLEFT
1763 			res = m_gv_lp_data[ 1 ];
1764 			m_gv_lp_reg_cnt--;
1765 			break;
1766 
1767 		case 6:
1768 			// YHI
1769 			res = m_gv_lp_data[ 0 ];
1770 			if (!m_gv_lp_vblank) {
1771 				BIT_SET(res, 12);
1772 			}
1773 			if (m_gv_lp_sw) {
1774 				BIT_SET(res, 14);
1775 			}
1776 			if (m_gv_lp_1sthit) {
1777 				BIT_SET(res, 15);
1778 			}
1779 			m_gv_lp_reg_cnt--;
1780 			break;
1781 
1782 		default:
1783 			logerror("Reading from unmapped LP register %u\n" , m_gv_lp_reg_cnt);
1784 		}
1785 	}
1786 	return res;
1787 }
1788 
lp_r5_w(uint16_t data)1789 void hp9845ct_base_state::lp_r5_w(uint16_t data)
1790 {
1791 	m_gv_lp_reg_cnt = data & 7;
1792 	m_gv_lp_en = (data & 0x700) == 0x400;   // enables writes on R4 to set LP data (actually FB bit), also enables LP command processing and LP IRQs
1793 	m_gv_lp_int_en = (data & 0x500) == 0x400;
1794 	m_gv_lp_selftest = m_gv_lp_en && m_gv_lp_reg_cnt == 7;
1795 	update_graphic_bits();
1796 }
1797 
lp_segment_intersect(unsigned yline) const1798 bool hp9845ct_base_state::lp_segment_intersect(unsigned yline) const
1799 {
1800 	int xp = m_gv_lp_x;
1801 	int yp = m_gv_lp_y;
1802 	int xc = (int)m_gv_lp_cursor_x + 1 - LP_XOFFSET;
1803 
1804 	unsigned h = (unsigned)abs((int)yline - yp);
1805 
1806 	if (h > LP_FOV) {
1807 		return false;
1808 	}
1809 
1810 	int dt = (int)sqrt(LP_FOV * LP_FOV - h * h);
1811 	int ex = xp - dt;
1812 	int fx = xp + dt;
1813 
1814 	// Consider [xc..xc+24] segment (i.e. the right-hand side of cursor interlace window)
1815 	return fx >= xc && ex <= (xc + 24);
1816 }
1817 
compute_lp_data()1818 void hp9845ct_base_state::compute_lp_data()
1819 {
1820 	// get LP hit data, returns three words for cmd=6 and one word for cmd=4
1821 	// actually simulating the 9845 lightpen is a bit more complex, since YHI, XLEFT and YLO
1822 	// depend on an circular field of view, moving on the screen
1823 	// bit 0..10 x bzw y
1824 	// bit 11 = IRQ (YHI + XLEFT + YLO)
1825 	// bit 12 = vblank (YHI)
1826 	// bit 13 = xwindow (YHI + XLEFT + YLO) = X is in [xcursor-24, xcursor+24] and Y in [ycursor-8,ycursor+8]
1827 	// bit 14 = sw (YHI) bzw. ywindow (XLEFT + YLO)
1828 	// bit 15 = 1st hit (YHI) = valid hit
1829 
1830 	bool xwindow[ 3 ] = { false , false , false };
1831 	bool ywindow[ 3 ] = { false , false , false };
1832 	// hit coordinates
1833 	uint16_t yhi = 0;
1834 	uint16_t xleft = 0;
1835 	uint16_t yleft = 0;
1836 	uint16_t ylo = 0;
1837 	uint16_t xp = m_gv_lp_x;                    // light gun pointer
1838 	uint16_t yp = m_gv_lp_y;
1839 	int yc = get_wrapped_scanline(m_gv_lp_cursor_y) + 24;
1840 
1841 	if (m_gv_lp_selftest) {
1842 		constexpr int offset = 57 - VIDEO_770_ALPHA_L_LIM;
1843 		xwindow[ 0 ] = xwindow[ 1 ] = xwindow[ 2 ] = true;
1844 		ywindow[ 0 ] = ywindow[ 1 ] = ywindow[ 2 ] = true;
1845 		yhi = m_gv_lp_cursor_y + 16;    // YHI
1846 		xleft = m_gv_lp_cursor_x + offset;  // XLEFT
1847 		yleft = yhi;
1848 		ylo = m_gv_lp_cursor_y + 32;    // YLO
1849 	} else {
1850 		// Hit in a cursor-only part.
1851 		bool yhi_hit = false;
1852 		uint16_t y_top = std::max(0 , (int)yp - (int)LP_FOV);
1853 
1854 		// XLEFT: x coordinate of 1st hit in the frame or hit on cursor line
1855 		unsigned dy = abs((int)yp - yc);
1856 		if (dy <= LP_FOV) {
1857 			// Hit on horizontal cursor line
1858 			xleft = (uint16_t)std::max(0 , (int)xp - (int)sqrt(LP_FOV * LP_FOV - dy * dy));
1859 			yleft = yc;
1860 		} else {
1861 			// 1st hit in the frame
1862 			dy = abs((int)yp - (int)y_top);
1863 			xleft = (uint16_t)std::max(0 , (int)xp - (int)sqrt(LP_FOV * LP_FOV - dy * dy));
1864 			yleft = y_top;
1865 		}
1866 		xleft += LP_XOFFSET;
1867 
1868 		if (m_gv_lp_interlace) {
1869 			// **** Interlaced mode ****
1870 			unsigned ywd_top = (unsigned)std::max(yc - 24 , 0);
1871 			unsigned ywd_bot = std::min((unsigned)(GVIDEO_VPIXELS - 1) , (unsigned)(yc + 24));
1872 			unsigned even_odd = (unsigned)m_screen->frame_number() & 1;
1873 
1874 			// Scan the cursor window [yc-24..yc+24]
1875 			// Only consider each other line. LSB of frame number selects either even-numbered
1876 			// (0) or odd-numbered lines (1).
1877 			for (unsigned line = ywd_top; line <= ywd_bot; line++) {
1878 				if ((line & 1) == even_odd) {
1879 					// YHI: y coordinate of 1st hit in the frame or 1st hit in cursor-only part
1880 					bool curs_hit = lp_segment_intersect(line);
1881 					if (!yhi_hit && curs_hit) {
1882 						yhi = line;
1883 						yhi_hit = true;
1884 					}
1885 					// YLO: y coordinate of last hit in cursor-only part
1886 					if (curs_hit) {
1887 						ylo = line;
1888 					}
1889 				}
1890 			}
1891 		}
1892 
1893 		if (!m_gv_lp_interlace || !yhi_hit) {
1894 			// **** Non-interlaced mode ****
1895 			// YHI: y coordinate of 1st hit in the frame
1896 			yhi = y_top;
1897 
1898 			// YLO: y coordinate of last hit in the frame
1899 			ylo = std::min((unsigned)(GVIDEO_VPIXELS - 1) , yp + LP_FOV);
1900 		}
1901 
1902 		xwindow[ 0 ] = yhi_hit;
1903 		xwindow[ 1 ] = yhi_hit && yleft >= yhi;
1904 		xwindow[ 2 ] = yhi_hit;
1905 
1906 		ywindow[ 1 ] = yleft == yc;
1907 		ywindow[ 2 ] = yleft == yc && ylo >= yleft;
1908 	}
1909 	m_gv_next_lp_data[ 0 ] = ~yhi & 0x1ff;  // YHI
1910 	m_gv_next_lp_data[ 1 ] = ~xleft & 0x3ff;    // XLEFT
1911 	m_gv_next_lp_data[ 2 ] = ~ylo & 0x1ff;  // YLO
1912 
1913 	if (!xwindow[ 0 ]) {
1914 		BIT_SET(m_gv_next_lp_data[ 0 ], 13);
1915 	}
1916 	if (!xwindow[ 1 ]) {
1917 		BIT_SET(m_gv_next_lp_data[ 1 ], 13);
1918 	}
1919 	if (!xwindow[ 2 ]) {
1920 		BIT_SET(m_gv_next_lp_data[ 2 ], 13);
1921 	}
1922 	if (!ywindow[ 1 ]) {
1923 		BIT_SET(m_gv_next_lp_data[ 1 ], 14);
1924 	}
1925 	if (!ywindow[ 2 ]) {
1926 		BIT_SET(m_gv_next_lp_data[ 2 ], 14);
1927 	}
1928 
1929 	m_gv_next_lp_scanline[ 0 ] = yhi;
1930 	m_gv_next_lp_scanline[ 1 ] = yleft;
1931 	m_gv_next_lp_scanline[ 2 ] = ylo;
1932 
1933 	m_gv_lp_hit_lt192 = yhi < 192;
1934 	LOG("LP data %d %d %d %d (%u;%d) (%u;%u) %u %u %u %04x %04x %04x\n" , m_gv_lp_selftest , m_gv_lp_interlace , m_gv_lp_sw , m_gv_lp_hit_lt192 , m_gv_lp_cursor_x , yc , m_gv_lp_x , m_gv_lp_y , yhi , xleft , ylo , m_gv_next_lp_data[ 0 ] , m_gv_next_lp_data[ 1 ] , m_gv_next_lp_data[ 2 ]);
1935 }
1936 
lp_scanline_update(unsigned video_scanline)1937 void hp9845ct_base_state::lp_scanline_update(unsigned video_scanline)
1938 {
1939 	if (video_scanline == 0) {
1940 		compute_lp_data();
1941 	}
1942 
1943 	if (video_scanline == 256 && !m_gv_lp_status && m_gv_lp_hit_lt192) {
1944 		LOG("Hit < 192 @%d\n" , m_screen->vpos());
1945 		m_gv_lp_status = true;
1946 		m_gv_lp_int_256 = true;
1947 		update_graphic_bits();
1948 	}
1949 
1950 	if (video_scanline == 456) {
1951 		// VB interrupt
1952 		if (m_gv_lp_vbint || !m_gv_lp_int_256) {
1953 			m_gv_lp_status = true;
1954 		}
1955 		m_gv_lp_int_256 = false;
1956 		update_graphic_bits();
1957 	}
1958 
1959 	for (unsigned i = 0; i < 3; i++) {
1960 		if (m_gv_next_lp_scanline[ i ] == video_scanline) {
1961 			m_gv_lp_data[ i ] = m_gv_next_lp_data[ i ];
1962 			if (!m_gv_lp_status) {
1963 				BIT_SET(m_gv_lp_data[ i ], 11);
1964 			}
1965 			m_gv_lp_1sthit = true;
1966 		}
1967 	}
1968 }
1969 
1970 const uint16_t hp9845ct_base_state::m_line_type[] = {
1971 	0xffff, 0xaaaa, 0xff00, 0xfff0, 0xfffa, 0xfff6, 0xffb6, 0x0000
1972 };
1973 
1974 const uint16_t hp9845ct_base_state::m_area_fill[] = {
1975 	0xffff, 0xefff, 0xefbf, 0xefaf, 0xafaf, 0xadaf, 0xada7, 0xada5,
1976 	0xa5a5, 0xa4a5, 0xa4a1, 0xa4a0, 0xa0a0, 0x80a0, 0x8020, 0x8000
1977 };
1978 
1979 // ***************
1980 //  hp9845c_state
1981 // ***************
1982 class hp9845c_state : public hp9845ct_base_state
1983 {
1984 public:
1985 	hp9845c_state(const machine_config &mconfig, device_type type, const char *tag);
1986 
1987 	void hp9845c(machine_config &config);
1988 
1989 private:
1990 	virtual void machine_start() override;
1991 	virtual void machine_reset() override;
1992 
1993 	virtual uint16_t graphic_r(offs_t offset) override;
1994 	virtual void graphic_w(offs_t offset, uint16_t data) override;
1995 
1996 	TIMER_DEVICE_CALLBACK_MEMBER(scanline_timer);
1997 
1998 	virtual void set_graphic_mode(bool graphic , bool alpha) override;
1999 	void video_render_buff(unsigned video_scanline , unsigned line_in_row, bool buff_idx);
2000 	void graphic_video_render(unsigned video_scanline);
2001 	virtual void plot(uint16_t x, uint16_t y, bool draw_erase) override;
2002 
2003 	void advance_io_counter();
2004 	virtual void advance_gv_fsm(bool ds , bool trigger) override;
2005 	virtual void update_graphic_bits() override;
2006 
2007 	virtual void update_gcursor() override;
2008 
2009 	// Palette indexes
pen_graphic(unsigned rgb)2010 	static constexpr unsigned pen_graphic(unsigned rgb) { return rgb; }
pen_alpha(unsigned rgb)2011 	static constexpr unsigned pen_alpha(unsigned rgb) { return 8 + rgb; }
pen_cursor(unsigned rgb)2012 	static constexpr unsigned pen_cursor(unsigned rgb) { return 16 + rgb; }
2013 
2014 	// Optional character generator
2015 	required_region_ptr<uint8_t> m_optional_chargen;
2016 
2017 	std::vector<uint16_t> m_graphic_mem[ 3 ];
2018 	uint16_t m_gv_music_memory;
2019 	uint8_t m_gv_cursor_color;
2020 	uint8_t m_gv_plane;
2021 	bool m_gv_lp_int_latched;
2022 	bool m_gv_sk_int_latched;
2023 };
2024 
hp9845c_state(const machine_config & mconfig,device_type type,const char * tag)2025 hp9845c_state::hp9845c_state(const machine_config &mconfig, device_type type, const char *tag)
2026 	: hp9845ct_base_state(mconfig , type , tag),
2027 	  m_optional_chargen(*this , "optional_chargen")
2028 {
2029 }
2030 
machine_start()2031 void hp9845c_state::machine_start()
2032 {
2033 	// Common part first
2034 	hp9845ct_base_state::machine_start();
2035 
2036 	m_graphic_mem[ 0 ].resize(GVIDEO_MEM_SIZE);
2037 	m_graphic_mem[ 1 ].resize(GVIDEO_MEM_SIZE);
2038 	m_graphic_mem[ 2 ].resize(GVIDEO_MEM_SIZE);
2039 
2040 	// initialize palette
2041 	// graphics colors
2042 	m_palette->set_pen_color(0,  0x00, 0x00, 0x00); // black
2043 	m_palette->set_pen_color(1,  I_GR, 0x00, 0x00); // red
2044 	m_palette->set_pen_color(2,  0x00, I_GR, 0x00); // green
2045 	m_palette->set_pen_color(3,  I_GR, I_GR, 0x00); // yellow
2046 	m_palette->set_pen_color(4,  0x00, 0x00, I_GR); // blue
2047 	m_palette->set_pen_color(5,  I_GR, 0x00, I_GR); // magenta
2048 	m_palette->set_pen_color(6,  0x00, I_GR, I_GR); // cyan
2049 	m_palette->set_pen_color(7,  I_GR, I_GR, I_GR); // white
2050 
2051 	// alpha colors
2052 	m_palette->set_pen_color(8,  0x00, 0x00, 0x00); // black
2053 	m_palette->set_pen_color(9,  I_AL, 0x00, 0x00); // red
2054 	m_palette->set_pen_color(10, 0x00, I_AL, 0x00); // green
2055 	m_palette->set_pen_color(11, I_AL, I_AL, 0x00); // yellow
2056 	m_palette->set_pen_color(12, 0x00, 0x00, I_AL); // blue
2057 	m_palette->set_pen_color(13, I_AL, 0x00, I_AL); // magenta
2058 	m_palette->set_pen_color(14, 0x00, I_AL, I_AL); // cyan
2059 	m_palette->set_pen_color(15, I_AL, I_AL, I_AL); // white
2060 
2061 	// cursor colors
2062 	m_palette->set_pen_color(16, 0x80, 0x80, 0x80); // grey
2063 	m_palette->set_pen_color(17, I_CU, 0x00, 0x00); // red
2064 	m_palette->set_pen_color(18, 0x00, I_CU, 0x00); // green
2065 	m_palette->set_pen_color(19, I_CU, I_CU, 0x00); // yellow
2066 	m_palette->set_pen_color(20, 0x00, 0x00, I_CU); // blue
2067 	m_palette->set_pen_color(21, I_CU, 0x00, I_CU); // magenta
2068 	m_palette->set_pen_color(22, 0x00, I_CU, I_CU); // cyan
2069 	m_palette->set_pen_color(23, I_CU, I_CU, I_CU); // white
2070 }
2071 
machine_reset()2072 void hp9845c_state::machine_reset()
2073 {
2074 	// Common part first
2075 	hp9845ct_base_state::machine_reset();
2076 
2077 	set_video_mar(0);
2078 
2079 	// red -> plane #1, green -> plane #2, blue -> plane #3
2080 	m_gv_music_memory = 0x1 | (0x2 << 3) | (0x4 << 6);
2081 	// TODO: correct?
2082 	m_gv_cursor_color = 7;
2083 	m_gv_plane = 0;
2084 	m_gv_lp_int_latched = false;
2085 	m_gv_sk_int_latched = false;
2086 }
2087 
graphic_r(offs_t offset)2088 uint16_t hp9845c_state::graphic_r(offs_t offset)
2089 {
2090 	uint16_t res = 0;
2091 
2092 	switch (offset) {
2093 	case 2:
2094 		// R6: data register with DMA TC
2095 		m_gv_dma_en = false;
2096 		// Intentional fall-through
2097 
2098 	case 0:
2099 		// R4: data register
2100 		if (m_gv_lp_en) {
2101 			res = lp_r4_r();
2102 		} else if (m_gv_sk_int_latched) {
2103 			res = m_gv_softkey;
2104 			m_gv_sk_status = false;
2105 		} else {
2106 			res = m_gv_data_r;
2107 		}
2108 		advance_gv_fsm(true , false);
2109 		update_graphic_bits();
2110 		break;
2111 
2112 	case 1:
2113 		// R5: status register
2114 		if (m_gv_int_en) {
2115 			BIT_SET(res, 7);
2116 		}
2117 		if (m_gv_dma_en) {
2118 			BIT_SET(res, 6);
2119 		}
2120 		if (m_gv_lp_int_latched) {
2121 			BIT_SET(res, 0);    // Lightpen service request
2122 		}
2123 		if (m_gv_sk_int_latched) {
2124 			BIT_SET(res, 1);    // Softkey service request
2125 		}
2126 		// TODO: check! Should it be 10 instead?
2127 		BIT_SET(res, 11);   // ID
2128 		break;
2129 
2130 	case 3:
2131 		// R7: not mapped
2132 		break;
2133 	}
2134 
2135 	LOG("rd gv R%u = %04x\n", 4 + offset , res);
2136 
2137 	return res;
2138 }
2139 
graphic_w(offs_t offset,uint16_t data)2140 void hp9845c_state::graphic_w(offs_t offset, uint16_t data)
2141 {
2142 	LOG("wr gv R%u = %04x\n", 4 + offset , data);
2143 
2144 	switch (offset) {
2145 	case 0:
2146 		// R4: data register
2147 		m_gv_data_w = data;
2148 		advance_gv_fsm(true , false);
2149 		lp_r4_w(data);
2150 		break;
2151 
2152 	case 1:
2153 		// R5: command register
2154 		m_gv_cmd = (uint8_t)(data & 0xf);
2155 		m_gv_dma_en = BIT(data , 6) != 0;
2156 		m_gv_int_en = BIT(data , 7) != 0;
2157 		m_gv_gr_en = BIT(data , 8); // enables graphics controller & vector generator command processing and IRQs
2158 		m_gv_sk_en = BIT(data , 9); // enables reads on R4 to return SK keycode, also enables SK IRQs
2159 		m_gv_opt_en = BIT(data , 11);   // not really used
2160 		m_gv_dsa_en = BIT(data , 12);   // for factory use only (unknown)
2161 		if (BIT(data, 5)) {
2162 			m_gv_fsm_state = GV_STAT_RESET;     // command/reset state machine
2163 		}
2164 		advance_gv_fsm(false , false);
2165 		lp_r5_w(data);
2166 		update_graphic_bits();
2167 		break;
2168 
2169 	case 2:
2170 		// R6: data register with DMA TC
2171 		m_gv_dma_en = false;
2172 		m_gv_data_w = data;
2173 		advance_gv_fsm(true , false);
2174 		lp_r4_w(data);
2175 		break;
2176 
2177 	case 3:
2178 		// R7: trigger
2179 		advance_gv_fsm(false , true);
2180 		break;
2181 	}
2182 }
2183 
TIMER_DEVICE_CALLBACK_MEMBER(hp9845c_state::scanline_timer)2184 TIMER_DEVICE_CALLBACK_MEMBER(hp9845c_state::scanline_timer)
2185 {
2186 	unsigned video_scanline = param;
2187 
2188 	if (video_scanline >= VIDEO_770_VBEND && video_scanline < VIDEO_770_VBSTART) {
2189 		if (m_graphic_sel) {
2190 			graphic_video_render(video_scanline - VIDEO_770_VBEND);
2191 		}
2192 		unsigned row = (video_scanline - VIDEO_770_VBEND) / VIDEO_CHAR_HEIGHT;
2193 		unsigned line_in_row = (video_scanline - VIDEO_770_VBEND) - row * VIDEO_CHAR_HEIGHT;
2194 
2195 		if (line_in_row == 0) {
2196 			// Start of new row, swap buffers
2197 			m_video_buff_idx = !m_video_buff_idx;
2198 			video_fill_buff(!m_video_buff_idx);
2199 		}
2200 		video_render_buff(video_scanline , line_in_row , m_video_buff_idx);
2201 		// Lightpen cursor
2202 		if (m_graphic_sel) {
2203 			render_lp_cursor(video_scanline - VIDEO_770_VBEND , pen_cursor(7));
2204 		}
2205 	}
2206 	lp_scanline_update(video_scanline - VIDEO_770_VBEND);
2207 }
2208 
set_graphic_mode(bool graphic,bool alpha)2209 void hp9845c_state::set_graphic_mode(bool graphic , bool alpha)
2210 {
2211 	m_graphic_sel = graphic;
2212 	m_alpha_sel = alpha;
2213 }
2214 
video_render_buff(unsigned video_scanline,unsigned line_in_row,bool buff_idx)2215 void hp9845c_state::video_render_buff(unsigned video_scanline , unsigned line_in_row, bool buff_idx)
2216 {
2217 	if (!m_video_buff[ buff_idx ].full) {
2218 		m_video_blanked = true;
2219 	}
2220 
2221 	const pen_t *pen = m_palette->pens();
2222 
2223 	if (m_video_blanked || !m_alpha_sel) {
2224 		// Blank scanline
2225 		for (unsigned i = 0; i < VIDEO_770_ALPHA_L_LIM; i++) {
2226 			m_bitmap.pix(video_scanline , i) = pen[ pen_alpha(0) ];
2227 		}
2228 		if (!m_graphic_sel) {
2229 			for (unsigned i = VIDEO_770_ALPHA_L_LIM; i < VIDEO_770_ALPHA_R_LIM; i++) {
2230 				m_bitmap.pix(video_scanline , i) = pen[ pen_alpha(0) ];
2231 			}
2232 		}
2233 		for (unsigned i = VIDEO_770_ALPHA_R_LIM; i < VIDEO_TOT_HPIXELS; i++) {
2234 			m_bitmap.pix(video_scanline , i) = pen[ pen_alpha(0) ];
2235 		}
2236 	} else {
2237 		bool cursor_line = line_in_row == 12;
2238 		bool ul_line = line_in_row == 14;
2239 		unsigned video_frame = (unsigned)m_screen->frame_number();
2240 		bool cursor_blink = BIT(video_frame , 4);
2241 		bool char_blink = !BIT(video_frame , 4);
2242 
2243 		for (unsigned i = 0; i < 80; i++) {
2244 			uint8_t charcode = m_video_buff[ buff_idx ].chars[ i ] & 0x7f;
2245 			uint8_t attrs = m_video_buff[ buff_idx ].attrs[ i ];
2246 			uint16_t chrgen_addr = ((uint16_t)(charcode ^ 0x7f) << 4) | line_in_row;
2247 			uint16_t pixels;
2248 			uint8_t color = (attrs >> 4) & 7;
2249 
2250 			if (ul_line && BIT(attrs , 3)) {
2251 				// Color of underline: same as character
2252 				pixels = ~0;
2253 			} else if (cursor_line && cursor_blink && BIT(attrs , 0)) {
2254 				// Color of cursor: white
2255 				color = 7;
2256 				pixels = ~0;
2257 			} else if (char_blink && BIT(attrs , 2)) {
2258 				pixels = 0;
2259 			} else if (BIT(m_video_buff[ buff_idx ].chars[ i ] , 7)) {
2260 				// 98770A has hw support to fill the 1st and the 9th column of character matrix
2261 				// with pixels in 2nd and 8th columns, respectively. This feature is used in
2262 				// 98780A to make horizontal lines of line-drawing characters appear continuous
2263 				// (see hp9845t_state::video_render_buff).
2264 				// Apparently, though, HP did not use this feature at all in real
2265 				// machines (i.e. horizontal lines are broken by gaps)
2266 				pixels = (uint16_t)(m_optional_chargen[ chrgen_addr ] & 0x7f) << 1;
2267 			} else {
2268 				pixels = (uint16_t)(m_chargen[ chrgen_addr ] & 0x7f) << 1;
2269 			}
2270 
2271 			if (BIT(attrs , 1)) {
2272 				pixels = ~pixels;
2273 			}
2274 
2275 			for (unsigned j = 0; j < 9; j++) {
2276 				bool pixel = (pixels & (1U << j)) != 0;
2277 				unsigned x = i * 9 + j;
2278 
2279 				if (m_graphic_sel && x >= VIDEO_770_ALPHA_L_LIM && x < VIDEO_770_ALPHA_R_LIM) {
2280 					// alpha overlays graphics (non-dominating)
2281 					if (pixel) {
2282 						m_bitmap.pix(video_scanline , x) = pen[ pen_alpha(color) ];
2283 					}
2284 				} else {
2285 					// Graphics disabled or alpha-only zone
2286 					m_bitmap.pix(video_scanline , x) = pen[ pixel ? pen_alpha(color) : pen_alpha(0) ];
2287 				}
2288 			}
2289 		}
2290 	}
2291 }
2292 
graphic_video_render(unsigned video_scanline)2293 void hp9845c_state::graphic_video_render(unsigned video_scanline)
2294 {
2295 	// video_scanline is 0-based, i.e. the topmost visible line of graphic screen is 0
2296 	const pen_t *pen = m_palette->pens();
2297 	bool yc, yw;
2298 	uint16_t word0, word1, word2;
2299 	uint8_t pen0, pen1, pen2;
2300 
2301 	// apply music memory
2302 	pen0 = (m_gv_music_memory & 0x001) | ((m_gv_music_memory & 0x008) >> 2) | ((m_gv_music_memory & 0x040) >> 4);
2303 	pen1 = ((m_gv_music_memory & 0x002) >> 1) | ((m_gv_music_memory & 0x010) >> 3) | ((m_gv_music_memory & 0x080) >> 5);
2304 	pen2 = ((m_gv_music_memory & 0x004) >> 2) | ((m_gv_music_memory & 0x020) >> 4) | ((m_gv_music_memory & 0x100) >> 6);
2305 
2306 	if (m_gv_cursor_fs) {
2307 		// Full-screen cursor
2308 		yw = false;
2309 		yc = video_scanline == m_gv_cursor_y;
2310 	} else if (m_gv_cursor_gc) {
2311 		// 15 x 15 crosshair
2312 		int cursor_y_top = get_wrapped_scanline(m_gv_cursor_y);
2313 		int int_scanline = (int)video_scanline;
2314 		yw = (int_scanline >= (cursor_y_top + 1) && int_scanline <= (cursor_y_top + 6)) ||
2315 			(int_scanline >= (cursor_y_top + 10) && int_scanline <= (cursor_y_top + 15));
2316 		yc = int_scanline == cursor_y_top + 8;
2317 	} else {
2318 		// 9-pixel blinking line
2319 		int cursor_y_top = get_wrapped_scanline(m_gv_cursor_y);
2320 		int int_scanline = (int)video_scanline;
2321 		yw = false;
2322 		yc = int_scanline == cursor_y_top + 8;
2323 	}
2324 
2325 	unsigned mem_idx = get_gv_mem_addr(0 , video_scanline);
2326 	for (unsigned i = 0; i < GVIDEO_HPIXELS; i += 16) {
2327 		word0 = m_graphic_mem[ 0 ][ mem_idx ];
2328 		word1 = m_graphic_mem[ 1 ][ mem_idx ];
2329 		word2 = m_graphic_mem[ 2 ][ mem_idx ];
2330 		mem_idx++;
2331 		unsigned x = i;
2332 		for (uint16_t mask = 0x8000; mask != 0; mask >>= 1) {
2333 			bool cursor = false;
2334 			unsigned pixel;
2335 
2336 			if (m_gv_cursor_fs) {
2337 				// Full-screen cursor
2338 				cursor = yc || (x + 111) == m_gv_cursor_x;
2339 			} else if (m_gv_cursor_gc) {
2340 				bool xc = (x + 103) == m_gv_cursor_x;
2341 				bool xw = ((x + 96) <= m_gv_cursor_x && (x + 101) >= m_gv_cursor_x) ||
2342 					((x + 105) <= m_gv_cursor_x && (x + 110) >= m_gv_cursor_x);
2343 
2344 				// 15 x 15 crosshair
2345 				cursor = (yc && xw) || (yw && xc);
2346 			} else if (BIT(m_screen->frame_number() , 3)) {
2347 				// 9-pixel blinking line
2348 				cursor = yc && (x + 107) >= m_gv_cursor_x && (x + 99) <= m_gv_cursor_x;
2349 			}
2350 			if (cursor) {
2351 				// Cursor
2352 				pixel = pen_cursor(m_gv_cursor_color);
2353 			} else {
2354 				// Normal pixel
2355 				pixel = pen_graphic(((word0 & mask) ? pen0 : 0) | ((word1 & mask) ? pen1 : 0) | ((word2 & mask) ? pen2 : 0));
2356 			}
2357 			m_bitmap.pix(video_scanline , VIDEO_770_ALPHA_L_LIM + x++) = pen[ pixel ];
2358 		}
2359 	}
2360 }
2361 
plot(uint16_t x,uint16_t y,bool draw_erase)2362 void hp9845c_state::plot(uint16_t x, uint16_t y, bool draw_erase)
2363 {
2364 	uint16_t addr, pixel_mask;
2365 	bool do_draw, do_erase, dominance;
2366 
2367 	pixel_mask = 0x8000 >> (x & 0xf);
2368 
2369 	addr = get_gv_mem_addr(x >> 4 , y);
2370 	dominance = BIT(m_gv_memory_control, 6);
2371 	if (BIT(m_gv_memory_control, 0)) {
2372 		do_erase = dominance;
2373 		do_draw = draw_erase;
2374 		if (!BIT(m_gv_memory_control, 3) && draw_erase) {
2375 			do_draw = false;
2376 			do_erase = true;
2377 		}
2378 		if (do_draw)
2379 			m_graphic_mem[0][ addr ] |= pixel_mask;
2380 		else if (do_erase)
2381 			m_graphic_mem[0][ addr ] &= ~pixel_mask;
2382 	}
2383 	if (BIT(m_gv_memory_control, 1)) {
2384 		do_erase = dominance;
2385 		do_draw = draw_erase;
2386 		if (!BIT(m_gv_memory_control, 4) && draw_erase) {
2387 			do_draw = false;
2388 			do_erase = true;
2389 		}
2390 		if (do_draw)
2391 			m_graphic_mem[1][ addr ] |= pixel_mask;
2392 		else if (do_erase)
2393 			m_graphic_mem[1][ addr ] &= ~pixel_mask;
2394 	}
2395 	if (BIT(m_gv_memory_control, 2)) {
2396 		do_erase = dominance;
2397 		do_draw = draw_erase;
2398 		if (!BIT(m_gv_memory_control, 5) && draw_erase) {
2399 			do_draw = false;
2400 			do_erase = true;
2401 		}
2402 		if (do_draw)
2403 			m_graphic_mem[2][ addr ] |= pixel_mask;
2404 		else if (do_erase)
2405 			m_graphic_mem[2][ addr ] &= ~pixel_mask;
2406 	}
2407 }
2408 
advance_io_counter()2409 void hp9845c_state::advance_io_counter()
2410 {
2411 	m_gv_plane++;
2412 	if (m_gv_plane > 2) {
2413 		if (m_gv_io_counter < GVIDEO_ADDR_MASK) {
2414 			m_gv_plane = 0;
2415 			m_gv_io_counter++;
2416 		} else {
2417 			m_gv_plane = 2;
2418 		}
2419 	}
2420 }
2421 
advance_gv_fsm(bool ds,bool trigger)2422 void hp9845c_state::advance_gv_fsm(bool ds , bool trigger)
2423 {
2424 	if (!m_gv_gr_en) {
2425 		return;
2426 	}
2427 
2428 	bool get_out = false;
2429 
2430 	attotime time_mem_av;
2431 
2432 	do {
2433 		// U73 on vector generator board
2434 		bool act_trig = trigger || m_gv_int_en || !BIT(m_gv_cmd , 0);
2435 
2436 		switch (m_gv_fsm_state) {
2437 		case GV_STAT_WAIT_DS_0:
2438 			// inital state (same as GV_STAT_RESET), command received
2439 			if (m_gv_cmd == 0x1) {
2440 				// read words command
2441 				LOG("read words\n");
2442 				m_gv_fsm_state = GV_STAT_WAIT_TRIG_0;
2443 			} else if (ds) {
2444 				if ((m_gv_cmd == 0x0) || (m_gv_cmd == 0x2)) {
2445 					// write words & clear/set words commands
2446 					if (m_gv_cmd == 0x2) LOG("clear/set words\n");
2447 					else LOG("write words\n");
2448 					m_gv_fsm_state = GV_STAT_WAIT_TRIG_1;   // -> write stream
2449 				} else {
2450 					// any other command
2451 					m_gv_fsm_state = GV_STAT_WAIT_TRIG_0;   // -> wait for trigger
2452 				}
2453 			} else {
2454 				get_out = true;
2455 			}
2456 			break;
2457 
2458 		case GV_STAT_WAIT_TRIG_0:
2459 			// process data on R4 or R6
2460 			if (act_trig) {
2461 				switch (m_gv_cmd) {
2462 				case 1: // read words command
2463 					break;
2464 				case 0x8:   // load X I/O address
2465 					m_gv_word_x_position = ~m_gv_data_w & 0x3f;     // 0..34
2466 					LOG("load X I/O adress = %04x\n", m_gv_word_x_position);
2467 					m_gv_io_counter = get_gv_mem_addr(m_gv_word_x_position , m_gv_word_y_position);
2468 					m_gv_plane = 0;
2469 					break;
2470 				case 0x9:   // load Y I/O address
2471 					m_gv_word_y_position = ~m_gv_data_w & 0x1ff;    // 0..454
2472 					LOG("load Y I/O adress = %04x\n", m_gv_word_y_position);
2473 					m_gv_io_counter = get_gv_mem_addr(m_gv_word_x_position , m_gv_word_y_position);
2474 					break;
2475 				case 0xa:   // load memory control
2476 					m_gv_memory_control = m_gv_data_w & 0x7f;
2477 					LOG("load memory control = %04x\n", m_gv_memory_control);
2478 					break;
2479 				case 0xb:   // set line type/area fill
2480 					m_gv_line_type_area_fill =  m_gv_data_w & 0x1ff;
2481 					if (BIT(m_gv_line_type_area_fill, 4)) {
2482 						m_gv_line_type_mask = m_line_type[ m_gv_line_type_area_fill & 0x7 ];
2483 						m_gv_repeat_count = 0;
2484 					}
2485 					LOG("set line type = %04x\n", m_gv_line_type_area_fill);
2486 					break;
2487 				case 0xc:   // load color mask
2488 					m_gv_music_memory = m_gv_data_w & 0x1ff;
2489 					LOG("load color mask = %04x\n", m_gv_music_memory);
2490 					break;
2491 				case 0xd:   // load end points
2492 					m_gv_ypt = ~m_gv_data_w & 0x1ff;
2493 					LOG("load end points y = %d\n", m_gv_ypt);
2494 					break;
2495 				case 0xe:   // Y cursor position & color
2496 					m_gv_lyc = m_gv_data_w;
2497 					break;
2498 				case 0xf:   // X cursor position & type
2499 					m_gv_lxc = m_gv_data_w;
2500 					break;
2501 				default:
2502 					logerror("unknown 98770A command = %d, parm = 0x%04x\n", m_gv_cmd, m_gv_data_w);
2503 				}
2504 				if (m_gv_cmd == 1) {    // Read words
2505 					m_gv_fsm_state = GV_STAT_WAIT_MEM_0;
2506 				} else if (m_gv_cmd == 0xd) {
2507 					m_gv_fsm_state = GV_STAT_WAIT_DS_2;     // -> get second data word
2508 				} else {
2509 					get_out = true;
2510 					m_gv_fsm_state = GV_STAT_WAIT_DS_0;     // -> done
2511 				}
2512 			} else {
2513 				get_out = true;
2514 			}
2515 			break;
2516 
2517 		case GV_STAT_WAIT_MEM_0:
2518 			// process data during read transfer
2519 			time_mem_av = time_to_gv_mem_availability();
2520 			if (time_mem_av.is_zero()) {
2521 				// Read a word from graphic memory
2522 				m_gv_data_r = m_graphic_mem[ m_gv_plane ][ m_gv_io_counter ];
2523 				LOG("read words @%04x = %04x, plane #%d\n" , m_gv_io_counter , m_gv_data_r, m_gv_plane + 1);
2524 				advance_io_counter();
2525 				m_gv_fsm_state = GV_STAT_WAIT_DS_1;     // -> proceed with read stream
2526 			} else {
2527 				m_gv_timer->adjust(time_mem_av);
2528 				get_out = true;
2529 			}
2530 			break;
2531 
2532 		case GV_STAT_WAIT_DS_1:
2533 			// wait for data word to be read
2534 			if (ds) {
2535 				// -- next word
2536 				m_gv_fsm_state = GV_STAT_WAIT_TRIG_0;    // -> process data word
2537 			} else {
2538 				// -- done
2539 				get_out = true;
2540 			}
2541 			break;
2542 
2543 		case GV_STAT_WAIT_DS_2:
2544 			// wait for data word to be written
2545 			if (ds) {
2546 				// -- next word
2547 				m_gv_fsm_state = GV_STAT_WAIT_TRIG_1;   // -> process data word
2548 			} else {
2549 				// done
2550 				get_out = true;
2551 			}
2552 			break;
2553 
2554 		case GV_STAT_WAIT_TRIG_1:
2555 			// process multi-word parameters & data during write transfer
2556 			if (act_trig) {
2557 				if (m_gv_cmd == 0xd) {
2558 					// load endpoints command
2559 					m_gv_xpt = ~m_gv_data_w & 0x3ff;
2560 					if (BIT(m_gv_data_w, 10)) {
2561 						// draw vector
2562 						LOG("load end points x = %d (draw)\n", m_gv_xpt);
2563 						m_gv_fsm_state = GV_STAT_WAIT_MEM_2;    // -> proceed with draw vector
2564 					} else {
2565 						LOG("load end points x = %d (move)\n", m_gv_xpt);
2566 						m_gv_last_xpt = m_gv_xpt;
2567 						m_gv_last_ypt = m_gv_ypt;
2568 						m_gv_fsm_state = GV_STAT_WAIT_DS_0;     // -> proceed with next word pair
2569 					}
2570 				} else if (m_gv_cmd == 0x2) {
2571 					// clear/set words command
2572 					m_gv_data_w = BIT(m_gv_memory_control, m_gv_plane + 3) ? 0xffff : 0;
2573 					m_gv_fsm_state = GV_STAT_WAIT_MEM_1;        // -> proceed with next word
2574 				} else if (m_gv_cmd == 0x0) {
2575 					// write words command
2576 					m_gv_fsm_state = GV_STAT_WAIT_MEM_1;        // -> proceed with next word
2577 				}
2578 			} else {
2579 				// done
2580 				get_out = true;
2581 			}
2582 			break;
2583 
2584 		case GV_STAT_WAIT_MEM_1:
2585 			// -- transfer from bus to graphics memory to bus within write transfer
2586 			time_mem_av = time_to_gv_mem_availability();
2587 			if (time_mem_av.is_zero()) {
2588 				// Write a full word to graphic memory
2589 				LOG("write words @%04x = %04x, plane #%d\n" , m_gv_io_counter , m_gv_data_w, m_gv_plane + 1);
2590 				if ((m_gv_cmd == 0x0) || BIT(m_gv_memory_control, m_gv_plane)) {
2591 					m_graphic_mem[ m_gv_plane ][ m_gv_io_counter ] = m_gv_data_w;
2592 				}
2593 				advance_io_counter();
2594 				m_gv_fsm_state = GV_STAT_WAIT_DS_2;             // -> proceed with write stream
2595 			} else {
2596 				m_gv_timer->adjust(time_mem_av);
2597 				get_out = true;
2598 			}
2599 			break;
2600 
2601 		case GV_STAT_WAIT_MEM_2:
2602 			// vector generator
2603 			time_mem_av = time_to_gv_mem_availability();
2604 			if (time_mem_av.is_zero()) {
2605 				if (BIT (m_gv_line_type_area_fill, 4)) {
2606 					unsigned x0;
2607 					unsigned x1;
2608 					unsigned y0;
2609 					unsigned y1;
2610 
2611 					// vector generator uses normalization
2612 					if (m_gv_xpt > m_gv_last_xpt) {
2613 						x0 = m_gv_last_xpt;
2614 						y0 = m_gv_last_ypt;
2615 						x1 = m_gv_xpt;
2616 						y1 = m_gv_ypt;
2617 					} else {
2618 						x0 = m_gv_xpt;
2619 						y0 = m_gv_ypt;
2620 						x1 = m_gv_last_xpt;
2621 						y1 = m_gv_last_ypt;
2622 					}
2623 					draw_line(x0 , y0 , x1 , y1);
2624 				} else {
2625 					// fill area with pattern
2626 					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);
2627 
2628 					pattern_fill(m_gv_xpt , m_gv_ypt , m_gv_last_xpt , m_gv_last_ypt , m_gv_line_type_area_fill & 0xf);
2629 				}
2630 				m_gv_last_xpt = m_gv_xpt;
2631 				m_gv_last_ypt = m_gv_ypt;
2632 				m_gv_fsm_state = GV_STAT_WAIT_DS_0;     // -> proceed with next word pair
2633 			} else {
2634 				m_gv_timer->adjust(time_mem_av);
2635 				get_out = true;
2636 			}
2637 			break;
2638 
2639 		default:
2640 			logerror("Invalid state reached %d\n" , m_gv_fsm_state);
2641 			m_gv_fsm_state = GV_STAT_RESET;
2642 		}
2643 
2644 		ds = false;
2645 		trigger = false;
2646 	} while (!get_out);
2647 
2648 	update_graphic_bits();
2649 }
2650 
update_graphic_bits()2651 void hp9845c_state::update_graphic_bits()
2652 {
2653 	bool lp_int = m_gv_lp_int_en && m_gv_lp_status;
2654 	bool sk_int = m_gv_sk_status && m_gv_sk_en;
2655 
2656 	if (lp_int && !m_gv_sk_int_latched) {
2657 		m_gv_lp_int_latched = true;
2658 	} else if (sk_int && !m_gv_lp_int_latched) {
2659 		m_gv_sk_int_latched = true;
2660 	} else if (!lp_int && !sk_int) {
2661 		m_gv_lp_int_latched = false;
2662 		m_gv_sk_int_latched = false;
2663 	}
2664 
2665 	bool gv_ready = m_gv_lp_int_latched || m_gv_sk_int_latched;
2666 
2667 	if (m_gv_gr_en && !gv_ready) {
2668 		gv_ready = m_gv_fsm_state == GV_STAT_WAIT_DS_0 ||
2669 			m_gv_fsm_state == GV_STAT_WAIT_TRIG_0 ||
2670 			m_gv_fsm_state == GV_STAT_WAIT_DS_1 ||
2671 			m_gv_fsm_state == GV_STAT_WAIT_DS_2 ||
2672 			m_gv_fsm_state == GV_STAT_WAIT_TRIG_1;
2673 	}
2674 
2675 	m_io_sys->set_flg(GVIDEO_PA , gv_ready);
2676 
2677 	bool irq = m_gv_int_en && !m_gv_dma_en && gv_ready;
2678 
2679 	m_io_sys->set_irq(GVIDEO_PA , irq);
2680 
2681 	bool dmar = gv_ready && m_gv_dma_en;
2682 
2683 	m_io_sys->set_dmar(GVIDEO_PA , dmar);
2684 }
2685 
update_gcursor()2686 void hp9845c_state::update_gcursor()
2687 {
2688 	m_gv_cursor_color = ~m_gv_lyc & 0x7;
2689 	m_gv_cursor_y = (~m_gv_lyc >> 6) & 0x1ff;
2690 	m_gv_cursor_fs = BIT(m_gv_lxc, 0);
2691 	m_gv_cursor_gc = BIT(m_gv_lxc, 1);
2692 	m_gv_cursor_x = (m_gv_lxc >> 6) & 0x3ff;
2693 }
2694 
2695 // ***************
2696 //  hp9845t_state
2697 // ***************
2698 class hp9845t_state : public hp9845ct_base_state
2699 {
2700 public:
2701 	hp9845t_state(const machine_config &mconfig, device_type type, const char *tag);
2702 
2703 	void hp9845t(machine_config &config);
2704 
2705 private:
2706 	virtual void machine_start() override;
2707 	virtual void machine_reset() override;
2708 
2709 	virtual uint16_t graphic_r(offs_t offset) override;
2710 	virtual void graphic_w(offs_t offset, uint16_t data) override;
2711 
2712 	TIMER_DEVICE_CALLBACK_MEMBER(scanline_timer);
2713 
2714 	virtual void set_graphic_mode(bool graphic , bool alpha) override;
2715 	void video_render_buff(unsigned video_scanline , unsigned line_in_row, bool buff_idx);
2716 	void graphic_video_render(unsigned video_scanline);
2717 	virtual void plot(uint16_t x, uint16_t y, bool draw_erase) override;
2718 	void draw_arc(uint16_t x0, uint16_t y0, int xstart, int ystart, uint16_t radius, int quadrant, int& draw_counter);
2719 	void draw_full_arc(int x0 , int y0 , int dx , int dy , int draw_counter);
2720 
2721 	virtual void advance_gv_fsm(bool ds , bool trigger) override;
2722 	virtual void update_graphic_bits() override;
2723 
2724 	virtual void update_gcursor() override;
2725 
2726 	std::vector<uint16_t> m_graphic_mem;
2727 
2728 	bool m_gv_stat;
2729 	bool m_gv_increment_to_next_row;
2730 
2731 	uint16_t m_gv_scan_start_x;
2732 	uint16_t m_gv_scan_start_y;
2733 	uint8_t m_gv_rb_control;
2734 	uint16_t m_gv_rb_counter;
2735 	uint16_t m_gv_rb_memory[ 256 ];
2736 	uint16_t m_gv_arc[ 4 ];
2737 	uint8_t m_gv_arc_parm;
2738 	bool m_back_arrow_cursor;
2739 
2740 	static const uint8_t m_back_arrow_shape[];
2741 };
2742 
hp9845t_state(const machine_config & mconfig,device_type type,const char * tag)2743 hp9845t_state::hp9845t_state(const machine_config &mconfig, device_type type, const char *tag)
2744 	: hp9845ct_base_state(mconfig , type , tag)
2745 {
2746 }
2747 
machine_start()2748 void hp9845t_state::machine_start()
2749 {
2750 	// Common part first
2751 	hp9845ct_base_state::machine_start();
2752 
2753 	m_graphic_mem.resize(GVIDEO_MEM_SIZE);
2754 
2755 	// initialize palette
2756 	m_palette->set_pen_color(PEN_BLACK  , 0x00, 0x00, 0x00);    // black
2757 	m_palette->set_pen_color(PEN_GRAPHIC, 0x00, I_GR, 0x00);    // graphics
2758 	m_palette->set_pen_color(PEN_ALPHA  , 0x00, I_AL, 0x00);    // alpha
2759 	m_palette->set_pen_color(PEN_CURSOR , 0x00, I_CU, 0x00);    // graphics cursor
2760 	m_palette->set_pen_color(PEN_LP     , 0x00, I_LP, 0x00);    // lightpen cursor
2761 }
2762 
machine_reset()2763 void hp9845t_state::machine_reset()
2764 {
2765 	// Common part first
2766 	hp9845ct_base_state::machine_reset();
2767 
2768 	m_gv_stat = false;
2769 	m_gv_increment_to_next_row = false;
2770 	m_gv_scan_start_x = 0;
2771 	m_gv_scan_start_y = 0;
2772 	m_gv_rb_control = 0;
2773 	m_gv_rb_counter = 0;
2774 	memset(m_gv_rb_memory, 0, sizeof(m_gv_rb_memory));
2775 	m_back_arrow_cursor = false;
2776 
2777 	set_video_mar(0);
2778 }
2779 
graphic_r(offs_t offset)2780 uint16_t hp9845t_state::graphic_r(offs_t offset)
2781 {
2782 	uint16_t res = 0;
2783 
2784 	switch (offset) {
2785 	case 2:
2786 		// R6: data register with DMA TC
2787 		m_gv_dma_en = false;
2788 		// Intentional fall-through
2789 
2790 	case 0:
2791 		// R4: data register
2792 		if (m_gv_lp_en) {
2793 			res = lp_r4_r();
2794 		} else if (m_gv_sk_en) {
2795 			res = m_gv_softkey;
2796 			m_gv_sk_status = false;
2797 		} else {
2798 			res = m_gv_data_r;
2799 		}
2800 		advance_gv_fsm(true , false);
2801 		update_graphic_bits();
2802 		break;
2803 
2804 	case 1:
2805 		// R5: status register
2806 		if (m_gv_int_en) {
2807 			BIT_SET(res, 7);
2808 		}
2809 		if (m_gv_dma_en) {
2810 			BIT_SET(res, 6);
2811 		}
2812 		if (m_gv_lp_status && m_gv_lp_int_en) {
2813 			BIT_SET(res, 0);    // Lightpen service request
2814 		}
2815 		// TODO: gsr/
2816 		if (m_gv_sk_status) {
2817 			BIT_SET(res, 1);    // Softkey service request
2818 		}
2819 		BIT_SET(res, 9);        // ID
2820 		BIT_SET(res, 11);       // ID
2821 		if (m_gv_stat) {
2822 			BIT_SET(res, 13);   // error indication
2823 		}
2824 		break;
2825 
2826 	case 3:
2827 		// R7: not mapped
2828 		break;
2829 	}
2830 
2831 	LOG("rd gv R%u = %04x\n", 4 + offset , res);
2832 
2833 	return res;
2834 }
2835 
graphic_w(offs_t offset,uint16_t data)2836 void hp9845t_state::graphic_w(offs_t offset, uint16_t data)
2837 {
2838 	LOG("wr gv R%u = %04x\n", 4 + offset , data);
2839 
2840 	switch (offset) {
2841 	case 0:
2842 		// R4: data register
2843 		m_gv_data_w = data;
2844 		advance_gv_fsm(true , false);
2845 		lp_r4_w(data);
2846 		if (m_gv_lp_int_en) {
2847 			m_gv_lp_fullbright = BIT(data , 1);
2848 		}
2849 		break;
2850 
2851 	case 1:
2852 		// R5: command register
2853 		m_gv_cmd = (uint8_t)(data & 0xf);
2854 		m_gv_dma_en = BIT(data , 6) != 0;
2855 		m_gv_int_en = BIT(data , 7) != 0;
2856 		m_gv_gr_en = BIT(data , 8);       // enables graphics controller & vector generator command processing and IRQs
2857 		m_gv_sk_en = (data & 0xb00) == 0x200;       // enables reads on R4 to return SK keycode, also enables SK IRQs
2858 		m_gv_opt_en = BIT(data , 11);     // not really used
2859 		m_gv_dsa_en = BIT(data , 12);     // for factory use only (function unknown)
2860 		m_gv_fsm_state = GV_STAT_RESET;     // command/reset state machine
2861 		lp_r5_w(data);
2862 		advance_gv_fsm(false , false);
2863 		break;
2864 
2865 	case 2:
2866 		// R6: data register with DMA TC
2867 		m_gv_dma_en = false;
2868 		m_gv_data_w = data;
2869 		lp_r4_w(data);
2870 		if (m_gv_lp_int_en) {
2871 			m_gv_lp_fullbright = BIT(data , 1);
2872 		}
2873 		advance_gv_fsm(true , false);
2874 		break;
2875 
2876 	case 3:
2877 		// R7: not mapped
2878 		break;
2879 	}
2880 }
2881 
TIMER_DEVICE_CALLBACK_MEMBER(hp9845t_state::scanline_timer)2882 TIMER_DEVICE_CALLBACK_MEMBER(hp9845t_state::scanline_timer)
2883 {
2884 	unsigned video_scanline = param;
2885 
2886 	if (video_scanline >= VIDEO_780_VBEND && video_scanline < VIDEO_780_VBSTART) {
2887 		if (m_graphic_sel) {
2888 			graphic_video_render(video_scanline - VIDEO_780_VBEND);
2889 		}
2890 		unsigned row = (video_scanline - VIDEO_780_VBEND) / VIDEO_CHAR_HEIGHT;
2891 		unsigned line_in_row = (video_scanline - VIDEO_780_VBEND) - row * VIDEO_CHAR_HEIGHT;
2892 
2893 		if (line_in_row == 0) {
2894 			// Start of new row, swap buffers
2895 			m_video_buff_idx = !m_video_buff_idx;
2896 			video_fill_buff(!m_video_buff_idx);
2897 		}
2898 		video_render_buff(video_scanline , line_in_row , m_video_buff_idx);
2899 		// Lightpen cursor
2900 		if (m_graphic_sel) {
2901 			render_lp_cursor(video_scanline - VIDEO_780_VBEND , PEN_LP);
2902 		}
2903 	}
2904 	lp_scanline_update(video_scanline - VIDEO_780_VBEND);
2905 }
2906 
set_graphic_mode(bool graphic,bool alpha)2907 void hp9845t_state::set_graphic_mode(bool graphic , bool alpha)
2908 {
2909 	m_back_arrow_cursor = graphic;      // triggers back arrow cursor, 98780A uses video on/off command for enabling/disabling graphics
2910 	m_alpha_sel = alpha;
2911 }
2912 
video_render_buff(unsigned video_scanline,unsigned line_in_row,bool buff_idx)2913 void hp9845t_state::video_render_buff(unsigned video_scanline , unsigned line_in_row, bool buff_idx)
2914 {
2915 	if (!m_video_buff[ buff_idx ].full) {
2916 		m_video_blanked = true;
2917 	}
2918 
2919 	const pen_t *pen = m_palette->pens();
2920 
2921 	if (m_video_blanked || !m_alpha_sel) {
2922 		// Blank scanline
2923 		for (unsigned i = 0; i < VIDEO_780_ALPHA_L_LIM; i++) {
2924 			m_bitmap.pix(video_scanline , i) = pen[ PEN_BLACK ];
2925 		}
2926 		if (!m_graphic_sel) {
2927 			for (unsigned i = VIDEO_780_ALPHA_L_LIM; i < VIDEO_780_ALPHA_R_LIM; i++) {
2928 				m_bitmap.pix(video_scanline , i) = pen[ PEN_BLACK ];
2929 			}
2930 		}
2931 		for (unsigned i = VIDEO_780_ALPHA_R_LIM; i < VIDEO_TOT_HPIXELS; i++) {
2932 			m_bitmap.pix(video_scanline , i) = pen[ PEN_BLACK ];
2933 		}
2934 	} else {
2935 		bool cursor_line = line_in_row == 12;
2936 		bool ul_line = line_in_row == 14;
2937 		unsigned video_frame = (unsigned)m_screen->frame_number();
2938 		bool cursor_blink = BIT(video_frame , 3);
2939 		bool char_blink = BIT(video_frame , 4);
2940 
2941 		for (unsigned i = 0; i < 80; i++) {
2942 			uint8_t attrs = m_video_buff[ buff_idx ].attrs[ i ];
2943 			uint16_t pixels;
2944 
2945 			if (!BIT(attrs , 2) || char_blink) {
2946 				if (ul_line && BIT(attrs , 3)) {
2947 					pixels = ~0;
2948 				} else {
2949 					// The 98780A uses two identical 4KB ROMs interlaced to keep up with the speed of
2950 					// the video circuit. Each of the 4K ROMs contains the full character set.
2951 					// The 98780A fills row 0 (space between characters) controlled by row 1 and 2 from
2952 					// the character ROM, thereby providing line drawing characters for continuous lines
2953 					uint8_t charcode = m_video_buff[ buff_idx ].chars[ i ];
2954 					uint16_t chrgen_addr = ((uint16_t)(charcode ^ 0xff) << 4) | (line_in_row + 1);
2955 					pixels = (uint16_t)m_chargen[ chrgen_addr ] << 1;
2956 					if ((charcode & 0xe0) == 0xe0 && (pixels & 0x6) == 0x6) {
2957 						pixels |= 1;
2958 					}
2959 				}
2960 			} else {
2961 				pixels = 0;
2962 			}
2963 
2964 			if (cursor_blink && BIT(attrs , 0)) {
2965 				if (m_back_arrow_cursor) {
2966 					// back arrow cursor (HP's hardware easter egg)
2967 					pixels |= m_back_arrow_shape[ line_in_row ];
2968 				} else if (cursor_line) {
2969 					pixels = ~0;
2970 				}
2971 			}
2972 
2973 			if (BIT(attrs , 1)) {
2974 				pixels = ~pixels;
2975 			}
2976 
2977 			for (unsigned j = 0; j < 9; j++) {
2978 				bool pixel = (pixels & (1U << j)) != 0;
2979 				unsigned x = i * 9 + j;
2980 
2981 				if (m_graphic_sel && x >= VIDEO_780_ALPHA_L_LIM && x < VIDEO_780_ALPHA_R_LIM) {
2982 					// alpha overlays graphics (non-dominating)
2983 					if (pixel) {
2984 						m_bitmap.pix(video_scanline , x) = pen[ PEN_ALPHA ];
2985 					}
2986 				} else {
2987 					// Graphics disabled or alpha-only zone
2988 					m_bitmap.pix(video_scanline , x) = pen[ pixel ? PEN_ALPHA : PEN_BLACK ];
2989 				}
2990 			}
2991 		}
2992 	}
2993 }
2994 
graphic_video_render(unsigned video_scanline)2995 void hp9845t_state::graphic_video_render(unsigned video_scanline)
2996 {
2997 	// video_scanline is 0-based, i.e. the topmost visible line of graphic screen is 0
2998 	const pen_t *pen = m_palette->pens();
2999 	bool yc, yw;
3000 	uint16_t word;
3001 
3002 	if (m_gv_cursor_fs) {
3003 		// Full-screen cursor
3004 		yw = false;
3005 		yc = video_scanline == m_gv_cursor_y;
3006 	} else if (m_gv_cursor_gc) {
3007 		// 9 x 9 crosshair
3008 		int cursor_y_top = get_wrapped_scanline(m_gv_cursor_y);
3009 		int int_scanline = (int)video_scanline;
3010 		yw = int_scanline >= cursor_y_top && int_scanline <= (cursor_y_top + 8);
3011 		yc = int_scanline == cursor_y_top + 4;
3012 	} else {
3013 		// 9-pixel blinking line
3014 		int cursor_y_top = get_wrapped_scanline(m_gv_cursor_y);
3015 		int int_scanline = (int)video_scanline;
3016 		yw = false;
3017 		yc = int_scanline == cursor_y_top + 4;
3018 	}
3019 
3020 	unsigned mem_idx = get_gv_mem_addr(m_gv_scan_start_x >> 4, video_scanline + m_gv_scan_start_y);
3021 	for (unsigned i = 0; i < GVIDEO_HPIXELS; i += 16) {
3022 		word = m_graphic_mem[ mem_idx ];
3023 		mem_idx++;
3024 		// Is wraparound better?
3025 		if (mem_idx > GVIDEO_ADDR_MASK) return;
3026 		unsigned x = i;
3027 		for (uint16_t mask = 0x8000; mask != 0; mask >>= 1) {
3028 			bool cursor = false;
3029 			unsigned pixel;
3030 			if (m_gv_cursor_fs) {
3031 				// Full-screen cursor
3032 				cursor = yc || (x + 184) == m_gv_cursor_x;
3033 			} else if (m_gv_cursor_gc) {
3034 				bool xc = (x + 184) == m_gv_cursor_x;
3035 				bool xw = (x + 180) <= m_gv_cursor_x && (x + 188) >= m_gv_cursor_x;
3036 
3037 				// 9 x 9 crosshair
3038 				cursor = (yc && xw) || (yw && xc);
3039 			} else if (BIT(m_screen->frame_number() , 3)) {
3040 				// 9-pixel blinking line
3041 				cursor = yc && (x + 188) >= m_gv_cursor_x && (x + 180) <= m_gv_cursor_x;
3042 			}
3043 			if (cursor) {
3044 				pixel = PEN_CURSOR;
3045 			} else {
3046 				// Normal pixel
3047 				if (m_gv_lp_fullbright)
3048 					pixel = word & mask ? PEN_LP : PEN_BLACK;
3049 				else
3050 					pixel = word & mask ? PEN_GRAPHIC : PEN_BLACK;
3051 			}
3052 			m_bitmap.pix(video_scanline , VIDEO_780_ALPHA_L_LIM + x++) = pen[ pixel ];
3053 		}
3054 	}
3055 }
3056 
plot(uint16_t x,uint16_t y,bool draw_erase)3057 void hp9845t_state::plot(uint16_t x, uint16_t y, bool draw_erase)
3058 {
3059 	uint16_t addr, pixel_mask;
3060 	bool do_draw;
3061 
3062 	pixel_mask = 0x8000 >> (x & 0xf);
3063 	addr = get_gv_mem_addr(x >> 4 , y);
3064 	if (BIT(m_gv_rb_control, 1)) {
3065 		// save graphics memory to rubber band memory
3066 		if (m_graphic_mem[ addr ] & pixel_mask)
3067 			m_gv_rb_memory[m_gv_rb_counter/16] |= 0x1 << (m_gv_rb_counter % 16);        // set
3068 		else
3069 			m_gv_rb_memory[m_gv_rb_counter/16] &= ~(0x1 << (m_gv_rb_counter % 16));     // clear
3070 		m_gv_rb_counter++;
3071 		if (m_gv_rb_counter > 4095) {
3072 			m_gv_stat = true;   // we might prevent data corruption here, but the original hardware doesn't
3073 			m_gv_rb_counter = 0;
3074 		}
3075 	} else if (BIT(m_gv_rb_control, 0)) {
3076 		// restore graphics memory from rubber band memory
3077 		if (BIT(m_gv_rb_memory[m_gv_rb_counter / 16], m_gv_rb_counter % 16))
3078 			m_graphic_mem[ addr ] |= pixel_mask;        // set
3079 		else
3080 			m_graphic_mem[ addr ] &= ~pixel_mask;   // clear
3081 		m_gv_rb_counter++;
3082 		if (m_gv_rb_counter > 4095) {
3083 			m_gv_stat = true;
3084 			m_gv_rb_counter = 0;
3085 		}
3086 	} else {
3087 		// draw/erase pixel
3088 		do_draw = m_gv_memory_control ? draw_erase : !draw_erase;
3089 		if (do_draw)
3090 			m_graphic_mem[ addr ] |= pixel_mask;
3091 		else
3092 			m_graphic_mem[ addr ] &= ~pixel_mask;
3093 	}
3094 }
3095 
draw_arc(uint16_t x0,uint16_t y0,int xstart,int ystart,uint16_t radius,int quadrant,int & draw_counter)3096 void hp9845t_state::draw_arc(uint16_t x0, uint16_t y0, int xstart, int ystart, uint16_t radius, int quadrant, int& draw_counter)
3097 {
3098 	int i, x1, y1, d;
3099 	bool draw_erase;
3100 	bool do_plot = (xstart < 0) || (ystart < 0);
3101 	int save_x1[560];
3102 
3103 	/* quadrants:
3104 	 *
3105 	 *    1 | 0
3106 	 *   ---+---
3107 	 *    2 | 3
3108 	 *
3109 	 *  Note: we are using Horn's algorithm here, however it is not 100% pixel
3110 	 *  accurate with the original vector generator
3111 	 */
3112 
3113 	d = -radius;
3114 	x1 = radius;
3115 	y1 = 0;
3116 	i = 0;
3117 	while ((x1 > 0) && (draw_counter > 0)) {
3118 		// check for plot start
3119 		if  (!do_plot) {
3120 			if ((quadrant % 2) == 0) {
3121 				if ((x1 <= xstart) && (y1 >= ystart)) do_plot = true;
3122 			} else {
3123 				if ((y1 >= xstart) && (x1 <= ystart)) do_plot = true;
3124 			}
3125 		}
3126 
3127 		// draw pixels in quadrants
3128 		draw_erase = BIT(m_gv_line_type_mask, 15);
3129 		if (do_plot) {
3130 			switch (quadrant) {
3131 			case 0:
3132 				plot(x0 + x1, y0 - y1, draw_erase);     // quadrant 0
3133 				break;
3134 			case 1:
3135 				plot(x0 - y1, y0 - x1, draw_erase);     // quadrant 1
3136 				break;
3137 			case 2:
3138 				plot(x0 - x1, y0 + y1, draw_erase);     // quadrant 2
3139 				break;
3140 			case 3:
3141 				plot(x0 + y1, y0 + x1, draw_erase);     // quadrant 3
3142 				break;
3143 			}
3144 		}
3145 
3146 		// update coordinates
3147 		if (x1 > y1) {
3148 			save_x1[i++] = x1;
3149 			d += 2 * y1 + 1;
3150 			y1++;
3151 			if (do_plot) draw_counter--;
3152 			if (d > 0) {
3153 				x1--;
3154 				if (do_plot) draw_counter--;
3155 				d -= 2 * x1;
3156 			}
3157 			if (x1 <= y1) draw_counter++;
3158 		}
3159 		else {
3160 			x1--;
3161 			y1 = save_x1[--i];
3162 			if (do_plot) {
3163 				draw_counter--;
3164 				if (save_x1[i] != save_x1[i+1])
3165 					draw_counter--;
3166 			}
3167 		}
3168 		update_line_pattern();
3169 	}
3170 }
3171 
draw_full_arc(int x0,int y0,int dx,int dy,int draw_counter)3172 void hp9845t_state::draw_full_arc(int x0 , int y0 , int dx , int dy , int draw_counter)
3173 {
3174 	// radius
3175 	int radius = sqrt(dx * dx + dy * dy);
3176 
3177 	LOG("midpoint = (%d,%d) radius = %d ctrl = %d count = %d\n", x0, y0, radius, m_gv_memory_control, draw_counter);
3178 
3179 	/* quadrants:
3180 	 *
3181 	 *    1 | 0
3182 	 *   ---+---
3183 	 *    2 | 3
3184 	 */
3185 
3186 	int quadrant = 0;
3187 
3188 	// determine the start quadrant
3189 	if (dx > 0)
3190 		quadrant = (dy < 0) ? 1 : 2;
3191 	else
3192 		quadrant = (dy <= 0) ? 0 : 3;
3193 
3194 	draw_arc(x0, y0, abs(dx), abs(dy), radius, quadrant, draw_counter);
3195 	while (draw_counter > 0) {
3196 		quadrant++;
3197 		if (quadrant > 3) {
3198 			quadrant = 0;
3199 		}
3200 		draw_arc(x0, y0, -1, -1, radius, quadrant, draw_counter);
3201 	}
3202 }
3203 
advance_gv_fsm(bool ds,bool trigger)3204 void hp9845t_state::advance_gv_fsm(bool ds , bool trigger)
3205 {
3206 	if (!m_gv_gr_en) {
3207 		return;
3208 	}
3209 
3210 	bool get_out = false;
3211 
3212 	attotime time_mem_av;
3213 
3214 	do {
3215 		switch (m_gv_fsm_state) {
3216 		case GV_STAT_WAIT_DS_0:
3217 			// inital state (same as GV_STAT_RESET), command received
3218 			if (m_gv_cmd == 0x9) {
3219 				// read words command
3220 				if (m_gv_last_cmd != m_gv_cmd) {
3221 					m_gv_io_counter = get_gv_mem_addr(m_gv_word_x_position , m_gv_word_y_position);
3222 				}
3223 				LOG("read words, last = %x\n", m_gv_last_cmd);
3224 				m_gv_fsm_state = GV_STAT_WAIT_MEM_0;    // -> read stream
3225 				m_gv_last_cmd = m_gv_cmd;
3226 			} else if (m_gv_cmd == 0xd) {
3227 				// fast clear/set command
3228 				m_gv_fsm_state = GV_STAT_WAIT_MEM_2;
3229 				m_gv_last_cmd = m_gv_cmd;
3230 			} else if (ds) {
3231 				if (m_gv_cmd == 0x8) {
3232 					// write words command
3233 					if (m_gv_last_cmd != m_gv_cmd) {
3234 						m_gv_io_counter = get_gv_mem_addr(m_gv_word_x_position , m_gv_word_y_position);
3235 					}
3236 					LOG("write words\n");
3237 					m_gv_fsm_state = GV_STAT_WAIT_TRIG_1;   // -> write stream
3238 				} else {
3239 					// any other command
3240 					m_gv_fsm_state = GV_STAT_WAIT_TRIG_0;   // -> wait for trigger
3241 				}
3242 				m_gv_last_cmd = m_gv_cmd;
3243 			} else {
3244 				get_out = true;
3245 			}
3246 			break;
3247 
3248 		case GV_STAT_WAIT_TRIG_0:
3249 			// process data on R4 or R6
3250 			switch (m_gv_cmd) {
3251 			case 0x1:   // load end points
3252 				m_gv_ypt = m_gv_data_w & 0x3ff;
3253 				LOG("load end points y = %d\n", m_gv_ypt);
3254 				break;
3255 			case 0x3:   // load arc
3256 				m_gv_arc_parm = 0;
3257 				m_gv_arc[ m_gv_arc_parm ] = m_gv_data_w;
3258 				LOG("load arc parm%d = %04x\n", m_gv_arc_parm, m_gv_arc[m_gv_arc_parm]);
3259 				m_gv_arc_parm++;
3260 				break;
3261 			case 0x5:   // load scan
3262 				m_gv_scan_start_x = m_gv_data_w & 0x3ff;    // 0..559
3263 				LOG("load scan x = %d\n", m_gv_scan_start_x);
3264 				break;
3265 			case 0x6:   // set line type/area fill
3266 				m_gv_line_type_area_fill = m_gv_data_w & 0x1ff;
3267 				if (BIT(m_gv_line_type_area_fill, 4)) {
3268 					m_gv_line_type_mask = m_line_type[ m_gv_line_type_area_fill & 0x7 ];
3269 					m_gv_repeat_count = 0;
3270 				}
3271 				LOG("set line type = %04x\n", m_gv_line_type_area_fill);
3272 				break;
3273 			case 0x7:   // load X/Y I/O address
3274 				m_gv_word_y_position = m_gv_data_w & 0x1ff; // 0..454
3275 				LOG("load X/Y I/O adress y = %04x\n", m_gv_word_y_position);
3276 				break;
3277 			case 0xa:   // load memory control
3278 				// A single bit is saved (InvBit)
3279 				m_gv_memory_control = (m_gv_data_w & 0x9) == 9 || (m_gv_data_w & 0x12) == 0x12 || (m_gv_data_w & 0x24) == 0x24;
3280 				LOG("load memory control = %04x\n", m_gv_memory_control);
3281 				break;
3282 			case 0xb:   // video on/off - enable graphics video output (1=on 2=off)
3283 				m_graphic_sel = BIT(m_gv_data_w, 0);
3284 				LOG("video on/off parm = %d\n", m_gv_data_w & 0x3);
3285 				break;
3286 			case 0xc:   // load color mask (no effect, just for compatibility with 9845c), takes a single word as parameter
3287 				break;
3288 			case 0xe:   // Y cursor position
3289 				m_gv_lyc = m_gv_data_w;
3290 				break;
3291 			case 0xf:   // X cursor position
3292 				m_gv_lxc = m_gv_data_w;
3293 				break;
3294 			default:
3295 				LOG("unknown 98780A command = %d, parm = 0x%04x\n", m_gv_cmd, m_gv_data_w);
3296 			}
3297 			if ((m_gv_cmd == 0x1) || (m_gv_cmd == 0x3) || (m_gv_cmd == 0x5) || (m_gv_cmd == 0x7)) {
3298 				m_gv_fsm_state = GV_STAT_WAIT_DS_2;     // -> get second data word
3299 			} else {
3300 				get_out = true;
3301 				m_gv_fsm_state = GV_STAT_WAIT_DS_0;     // -> done
3302 			}
3303 			break;
3304 
3305 		case GV_STAT_WAIT_MEM_0:
3306 			// process data during read transfer
3307 			time_mem_av = time_to_gv_mem_availability();
3308 			if (time_mem_av.is_zero()) {
3309 				// Read a word from graphic memory
3310 				m_gv_data_r = m_graphic_mem[ m_gv_io_counter ];
3311 				LOG("read words @%04x = %04x\n" , m_gv_io_counter , m_gv_data_r);
3312 				m_gv_io_counter = (m_gv_io_counter + 1) & GVIDEO_ADDR_MASK;
3313 				m_gv_fsm_state = GV_STAT_WAIT_DS_1;     // -> proceed with read stream
3314 			} else {
3315 				m_gv_timer->adjust(time_mem_av);
3316 				get_out = true;
3317 			}
3318 			break;
3319 
3320 		case GV_STAT_WAIT_DS_1:
3321 			// wait for data word to be read
3322 			if (ds) {
3323 				// -- next word
3324 				m_gv_fsm_state = GV_STAT_WAIT_MEM_0;    // -> process data word
3325 			} else {
3326 				// -- done
3327 				get_out = true;
3328 			}
3329 			break;
3330 
3331 		case GV_STAT_WAIT_DS_2:
3332 			// wait for data word to be written
3333 			if (ds) {
3334 				// -- next word
3335 				m_gv_fsm_state = GV_STAT_WAIT_TRIG_1;   // -> process data word
3336 			} else {
3337 				// done
3338 				get_out = true;
3339 			}
3340 			break;
3341 
3342 		case GV_STAT_WAIT_TRIG_1:
3343 			// process multi-word parameters & data during write transfer
3344 			switch (m_gv_cmd) {
3345 			case 0x1:
3346 				// load endpoints command
3347 				m_gv_xpt = m_gv_data_w & 0x3ff;
3348 				// RB control is actually set whenever a data word is written into R4/R6 register, not just here
3349 				m_gv_rb_control = (m_gv_data_w >> 13) & 0x7;
3350 				if (BIT(m_gv_rb_control, 2)) {
3351 					m_gv_rb_counter = 0;
3352 					m_gv_stat = false;
3353 				}
3354 				if (BIT(m_gv_data_w, 11)) {
3355 					// draw vector
3356 					LOG("load end points x = %d, rb = %d (draw)\n", m_gv_xpt, m_gv_rb_control);
3357 					m_gv_fsm_state = GV_STAT_WAIT_MEM_2;    // -> proceed with draw vector
3358 				} else {
3359 					LOG("load end points x = %d, rb = %d (move)\n", m_gv_xpt, m_gv_rb_control);
3360 					m_gv_last_xpt = m_gv_xpt;
3361 					m_gv_last_ypt = m_gv_ypt;
3362 					m_gv_fsm_state = GV_STAT_WAIT_DS_0;     // -> proceed with next word pair
3363 				}
3364 				break;
3365 
3366 			case 0x3:
3367 				// load arc
3368 				m_gv_arc[ m_gv_arc_parm ] = m_gv_data_w;
3369 				LOG("load arc parm%d = %04x\n", m_gv_arc_parm, m_gv_arc[m_gv_arc_parm]);
3370 				m_gv_arc_parm++;
3371 				if (m_gv_arc_parm < 4) {
3372 					m_gv_fsm_state = GV_STAT_WAIT_DS_2;     // -> proceed with next word
3373 				} else {
3374 					m_gv_fsm_state = GV_STAT_WAIT_MEM_2;    // -> proceed with draw vector
3375 				}
3376 				break;
3377 
3378 			case 0x5:
3379 				// load scan
3380 				m_gv_scan_start_y = m_gv_data_w & 0x3ff;    // 0..454
3381 				LOG("load scan y = %d\n", m_gv_scan_start_y);
3382 				m_gv_fsm_state = GV_STAT_WAIT_DS_0;
3383 				break;
3384 
3385 			case 0x7:
3386 				// load X/Y I/O address
3387 				m_gv_word_x_position = (m_gv_data_w & 0x3f0) >> 4;  // 0..34
3388 				m_gv_increment_to_next_row = BIT(m_gv_data_w, 11);
3389 				m_gv_io_counter = get_gv_mem_addr(m_gv_word_x_position , m_gv_word_y_position);
3390 				LOG("load X/Y I/O adress x = %04x increment = %d\n", m_gv_word_x_position, m_gv_increment_to_next_row);
3391 				m_gv_fsm_state = GV_STAT_WAIT_DS_0;
3392 				break;
3393 
3394 			case 0x8:
3395 				// write words command
3396 				m_gv_fsm_state = GV_STAT_WAIT_MEM_1;        // -> proceed with next word
3397 				break;
3398 			}
3399 			break;
3400 
3401 		case GV_STAT_WAIT_MEM_1:
3402 			// -- transfer from bus to graphics memory to bus within write transfer
3403 			time_mem_av = time_to_gv_mem_availability();
3404 			if (time_mem_av.is_zero()) {
3405 				// Write a full word to graphic memory
3406 				LOG("write words @%04x = %04x\n" , m_gv_io_counter , m_gv_data_w);
3407 				m_graphic_mem[ m_gv_io_counter ] = m_gv_data_w;
3408 				if (!m_gv_increment_to_next_row || (m_gv_word_x_position < 34)) {
3409 					m_gv_io_counter = (m_gv_io_counter + 1) & GVIDEO_ADDR_MASK;
3410 				}
3411 				m_gv_fsm_state = GV_STAT_WAIT_DS_2;             // -> proceed with write stream
3412 			} else {
3413 				m_gv_timer->adjust(time_mem_av);
3414 				get_out = true;
3415 			}
3416 			break;
3417 
3418 		case GV_STAT_WAIT_MEM_2:
3419 			// vector generator
3420 			time_mem_av = time_to_gv_mem_availability();
3421 			if (time_mem_av.is_zero()) {
3422 				if (m_gv_cmd == 0xd) {
3423 					// fast clear/set command
3424 					if (m_gv_memory_control) {
3425 						LOG("fast clear/set (set)\n");
3426 						for (auto& el : m_graphic_mem) {
3427 							el = 0xffff;
3428 						}
3429 					} else {
3430 						LOG("fast clear/set (clear)\n");
3431 						for (auto& el : m_graphic_mem) {
3432 							el = 0;
3433 						}
3434 					}
3435 				} else {
3436 					if (m_gv_cmd == 0x3) {
3437 						// draw arc/circle
3438 						// drawing is performed counter-clockwise by using Horn's algorithm
3439 						// m_gv_arc[0] is the delta last load endpoint y coordinate minus the midpoint y coordinate
3440 						// m_gv_arc[1] is the delta last load endpoint x coordinate minus the midpoint x coordinate
3441 						// m_gv_arc[2] is the (probably) the start count (?), actually ignored
3442 						// 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)
3443 						LOG("arc draw\n");
3444 
3445 						// midpoint
3446 						int dx = BIT(m_gv_arc[ 1 ] , 15) ? (int)m_gv_arc[ 1 ] - 65536 : m_gv_arc[ 1 ] & 0x7ff;
3447 						int dy = BIT(m_gv_arc[ 0 ] , 15) ? (int)m_gv_arc[ 0 ] - 65536 : m_gv_arc[ 0 ];
3448 						int x0 = m_gv_xpt + dx;
3449 						int y0 = m_gv_ypt - dy;
3450 
3451 						draw_full_arc(x0 , y0 , dx , dy , m_gv_arc[ 3 ]);
3452 					} else if (BIT (m_gv_line_type_area_fill, 4)) {
3453 						unsigned x0;
3454 						unsigned x1;
3455 						unsigned y0;
3456 						unsigned y1;
3457 
3458 						LOG("line draw (%d,%d)->(%d,%d)\n", m_gv_last_xpt, m_gv_last_ypt, m_gv_xpt, m_gv_ypt);
3459 
3460 						// vector generator uses normalization
3461 						if (m_gv_xpt > m_gv_last_xpt) {
3462 							x0 = m_gv_last_xpt;
3463 							y0 = m_gv_last_ypt;
3464 							x1 = m_gv_xpt;
3465 							y1 = m_gv_ypt;
3466 						} else {
3467 							x0 = m_gv_xpt;
3468 							y0 = m_gv_ypt;
3469 							x1 = m_gv_last_xpt;
3470 							y1 = m_gv_last_ypt;
3471 						}
3472 						draw_line(x0 , y0 , x1 , y1);
3473 					} else {
3474 						// fill area with pattern
3475 						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);
3476 
3477 						pattern_fill(m_gv_xpt , m_gv_ypt , m_gv_last_xpt , m_gv_last_ypt , 15 - (m_gv_line_type_area_fill & 0xf));
3478 					}
3479 					m_gv_last_xpt = m_gv_xpt;
3480 					m_gv_last_ypt = m_gv_ypt;
3481 				}
3482 				m_gv_fsm_state = GV_STAT_WAIT_DS_0;
3483 			} else {
3484 				m_gv_timer->adjust(time_mem_av);
3485 			}
3486 			get_out = true;
3487 			break;
3488 
3489 		default:
3490 			logerror("Invalid state reached %d\n" , m_gv_fsm_state);
3491 			m_gv_fsm_state = GV_STAT_RESET;
3492 		}
3493 
3494 		ds = false;
3495 	} while (!get_out);
3496 
3497 	update_graphic_bits();
3498 }
3499 
update_graphic_bits()3500 void hp9845t_state::update_graphic_bits()
3501 {
3502 	bool gv_ready = (m_gv_lp_int_en && m_gv_lp_status) || (m_gv_sk_en && m_gv_sk_status);
3503 
3504 	if (m_gv_gr_en && !gv_ready) {
3505 		gv_ready = m_gv_fsm_state == GV_STAT_WAIT_DS_0 ||
3506 			m_gv_fsm_state == GV_STAT_WAIT_DS_1 ||
3507 			m_gv_fsm_state == GV_STAT_WAIT_DS_2;
3508 	}
3509 
3510 	// WARNING! Race conditions here!
3511 	// FLG and IRQ are raised together. In enhgfxb ROM a SFC instruction
3512 	// that spins on itself waiting for FLG to be true was getting
3513 	// stuck because the interrupt always took precedence (and the ISR
3514 	// cleared the FLG bit).
3515 	// In real hw there was a non-zero chance that SFC exited the loop before
3516 	// interrupt was serviced. In case SFC stayed in the loop, it got another
3517 	// chance at the next interrupt.
3518 	// Fix for this problem is in commit 27004d00
3519 	// My apologies to Tony Duell for doubting at one point the correctness
3520 	// of his 98780A schematics.. :)
3521 	m_io_sys->set_flg(GVIDEO_PA , gv_ready);
3522 
3523 	bool irq = m_gv_int_en && !m_gv_dma_en && gv_ready;
3524 
3525 	m_io_sys->set_irq(GVIDEO_PA , irq);
3526 
3527 	bool dmar = gv_ready && m_gv_dma_en;
3528 
3529 	m_io_sys->set_dmar(GVIDEO_PA , dmar);
3530 }
3531 
update_gcursor()3532 void hp9845t_state::update_gcursor()
3533 {
3534 	m_gv_cursor_fs = (m_gv_lyc & 0x3) == 0;
3535 	m_gv_cursor_gc = !BIT(m_gv_lyc , 1);
3536 	m_gv_cursor_y = (~m_gv_lyc >> 7) & 0x1ff;
3537 	m_gv_cursor_x = (m_gv_lxc >> 6) & 0x3ff;
3538 }
3539 
3540 const uint8_t hp9845t_state::m_back_arrow_shape[] = {
3541 	0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xfc,
3542 	0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00, 0x00
3543 };
3544 
hp9845a(machine_config & config)3545 void hp9845_state::hp9845a(machine_config &config)
3546 {
3547 	//HP_5061_3010(config, m_lpu, XTAL(11'400'000));
3548 	//HP_5061_3011(config, m_ppu, XTAL(11'400'000));
3549 
3550 	// video hardware
3551 	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
3552 	screen.set_screen_update(FUNC(hp9845_state::screen_update));
3553 	screen.set_refresh_hz(60);
3554 	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500));
3555 	screen.set_size(560, 455);
3556 	screen.set_visarea(0, 560-1, 0, 455-1);
3557 
3558 	SOFTWARE_LIST(config, "optrom_list").set_original("hp9845a_rom");
3559 }
3560 
hp9835a(machine_config & config)3561 void hp9845_state::hp9835a(machine_config &config)
3562 {
3563 	//HP_5061_3001(config, m_lpu, XTAL(11'400'000));
3564 	//HP_5061_301(1config, m_ppu, XTAL(11'400'000));
3565 
3566 	// video hardware
3567 	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
3568 	screen.set_screen_update(FUNC(hp9845_state::screen_update));
3569 	screen.set_refresh_hz(60);
3570 	screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500));
3571 	screen.set_size(560, 455);
3572 	screen.set_visarea(0, 560-1, 0, 455-1);
3573 
3574 	SOFTWARE_LIST(config, "optrom_list").set_original("hp9835a_rom");
3575 }
3576 
3577 /*
3578     Global memory map in blocks of 32 kwords / 64 kbytes each:
3579 
3580     block  0: 0x000000 - 0x007fff (LPU RAM)
3581     block  1: 0x008000 - 0x00ffff (PPU RAM, only 0x00c000 - 0x00ffff used)
3582     block  2: 0x010000 - 0x017fff (unused)
3583     block  3: 0x018000 - 0x01ffff (LPU system ROM)
3584     block  4: 0x020000 - 0x027fff (LPU RAM)
3585     block  5: 0x028000 - 0x02ffff (PPU system ROM)
3586     block  6: 0x030000 - 0x037fff (LPU RAM)
3587     block  7: 0x038000 - 0x03ffff (LPU option ROM)
3588     block 10: 0x040000 - 0x047fff (LPU RAM)
3589     block 11: 0x048000 - 0x04ffff (PPU option ROM)
3590     block 12: 0x050000 - 0x057fff (LPU RAM)
3591     block 13: 0x058000 - 0x05ffff (LPU option ROM)
3592     block 14: 0x060000 - 0x067fff (LPU RAM)
3593     block 15: 0x068000 - 0x06ffff (PPU option ROM)
3594     block 16: 0x070000 - 0x077fff (LPU RAM)
3595     block 17: 0x078000 - 0x07ffff (unused)
3596 
3597     notes:
3598     - all block numbers are octal
3599     - blocks 20 to 76 are reserved for 512 kbyte RAM boards (p/n 09845-66590)
3600     - block 45 is reserved for the Test ROM
3601     - memory addresses are continuous (for convenience, the mapping below uses block numbers as
3602       address part above 0xffff, so there are gaps between 0x8000 and 0xffff which are masked out).
3603     - all LPU RAM is dynamically mapped at machine start according to -ramsize option
3604 */
3605 
global_mem_map(address_map & map)3606 void hp9845_base_state::global_mem_map(address_map &map)
3607 {
3608 	map.global_mask(0x3f7fff);
3609 	map.unmap_value_low();
3610 	map(0x014000, 0x017fff).ram().share("ppu_ram");
3611 	map(0x030000, 0x037fff).rom().region("lpu", 0);
3612 	map(0x050000, 0x057fff).rom().region("ppu", 0);
3613 }
3614 
ppu_io_map(address_map & map)3615 void hp9845_base_state::ppu_io_map(address_map &map)
3616 {
3617 	map.unmap_value_low();
3618 	// PA = 0, IC = 0..1
3619 	// Internal printer
3620 	map(HP_MAKE_IOADDR(PRINTER_PA, 0), HP_MAKE_IOADDR(PRINTER_PA, 1)).rw("printer", FUNC(hp9845_printer_device::printer_r), FUNC(hp9845_printer_device::printer_w));
3621 	// PA = 0, IC = 2
3622 	// Keyboard scancode input
3623 	map(HP_MAKE_IOADDR(0, 2), HP_MAKE_IOADDR(0, 2)).r(FUNC(hp9845_base_state::kb_scancode_r));
3624 	// PA = 0, IC = 3
3625 	// Keyboard status input & keyboard interrupt clear
3626 	map(HP_MAKE_IOADDR(0, 3), HP_MAKE_IOADDR(0, 3)).rw(FUNC(hp9845_base_state::kb_status_r), FUNC(hp9845_base_state::kb_irq_clear_w));
3627 	// PA = 13, IC = 0..3
3628 	// Graphic video
3629 	map(HP_MAKE_IOADDR(GVIDEO_PA, 0), HP_MAKE_IOADDR(GVIDEO_PA, 3)).rw(FUNC(hp9845_base_state::graphic_r), FUNC(hp9845_base_state::graphic_w));
3630 	// PA = 14, IC = 0..3
3631 	// Left-hand side tape drive (T14)
3632 	map(HP_MAKE_IOADDR(T14_PA, 0), HP_MAKE_IOADDR(T14_PA, 3)).rw(m_t14, FUNC(hp_taco_device::reg_r), FUNC(hp_taco_device::reg_w));
3633 	// PA = 15, IC = 0..3
3634 	// Right-hand side tape drive (T15)
3635 	map(HP_MAKE_IOADDR(T15_PA, 0), HP_MAKE_IOADDR(T15_PA, 3)).rw(m_t15, FUNC(hp_taco_device::reg_r), FUNC(hp_taco_device::reg_w));
3636 }
3637 
hp9845_base(machine_config & config)3638 void hp9845_base_state::hp9845_base(machine_config &config)
3639 {
3640 	HP_5061_3001(config , m_lpu , 5700000);
3641 	m_lpu->set_addrmap(AS_PROGRAM , &hp9845_base_state::global_mem_map);
3642 	m_lpu->set_9845_boot_mode(true);
3643 	m_lpu->set_rw_cycles(6 , 6);
3644 	m_lpu->set_relative_mode(true);
3645 	HP_5061_3001(config , m_ppu , 5700000);
3646 	m_ppu->set_addrmap(AS_PROGRAM , &hp9845_base_state::global_mem_map);
3647 	m_ppu->set_addrmap(AS_IO , &hp9845_base_state::ppu_io_map);
3648 	m_ppu->set_9845_boot_mode(true);
3649 	m_ppu->set_rw_cycles(6 , 6);
3650 	m_ppu->set_relative_mode(true);
3651 	m_ppu->int_cb().set(m_io_sys , FUNC(hp98x5_io_sys_device::int_r));
3652 	m_ppu->pa_changed_cb().set(m_io_sys , FUNC(hp98x5_io_sys_device::pa_w));
3653 
3654 	HP98X5_IO_SYS(config , m_io_sys , 0);
3655 	m_io_sys->irl().set_inputline(m_ppu, HPHYBRID_IRL);
3656 	m_io_sys->irh().set_inputline(m_ppu, HPHYBRID_IRH);
3657 	m_io_sys->sts().set(m_ppu , FUNC(hp_5061_3001_cpu_device::status_w));
3658 	m_io_sys->flg().set(m_ppu , FUNC(hp_5061_3001_cpu_device::flag_w));
3659 	m_io_sys->dmar().set(m_ppu , FUNC(hp_5061_3001_cpu_device::dmar_w));
3660 
3661 	// video hardware
3662 	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
3663 
3664 	TIMER(config, m_gv_timer).configure_generic(FUNC(hp9845_base_state::gv_timer));
3665 
3666 	// Actual keyboard refresh rate should be KEY_SCAN_OSCILLATOR / 128 (2560 Hz)
3667 	TIMER(config, "kb_timer").configure_periodic(FUNC(hp9845_base_state::kb_scan), attotime::from_hz(100));
3668 
3669 	// Beeper
3670 	SPEAKER(config, "mono").front_center();
3671 	BEEP(config, m_beeper , KEY_SCAN_OSCILLATOR / 512).add_route(ALL_OUTPUTS , "mono" , 0.50);
3672 
3673 	TIMER(config, m_beep_timer).configure_generic(FUNC(hp9845_base_state::beeper_off));
3674 
3675 	// Tape drives
3676 	HP_TACO(config , m_t15 , 4000000);
3677 	m_t15->irq().set([this](int state) { m_io_sys->set_irq(T15_PA , state); });
3678 	m_t15->flg().set([this](int state) { m_io_sys->set_flg(T15_PA , state); });
3679 	m_t15->sts().set([this](int state) { m_io_sys->set_sts(T15_PA , state); });
3680 	HP_TACO(config , m_t14 , 4000000);
3681 	m_t14->irq().set([this](int state) { m_io_sys->set_irq(T14_PA , state); });
3682 	m_t14->flg().set([this](int state) { m_io_sys->set_flg(T14_PA , state); });
3683 	m_t14->sts().set([this](int state) { m_io_sys->set_sts(T14_PA , state); });
3684 
3685 	// In real machine there were 8 slots for LPU ROMs and 8 slots for PPU ROMs in
3686 	// right-hand side and left-hand side drawers, respectively.
3687 	// Here we do away with the distinction between LPU & PPU ROMs: in the end they
3688 	// are visible to both CPUs at the same addresses.
3689 	HP9845_OPTROM(config, "drawer1", 0);
3690 	HP9845_OPTROM(config, "drawer2", 0);
3691 	HP9845_OPTROM(config, "drawer3", 0);
3692 	HP9845_OPTROM(config, "drawer4", 0);
3693 	HP9845_OPTROM(config, "drawer5", 0);
3694 	HP9845_OPTROM(config, "drawer6", 0);
3695 	HP9845_OPTROM(config, "drawer7", 0);
3696 	HP9845_OPTROM(config, "drawer8", 0);
3697 
3698 	// I/O slots
3699 	for (unsigned slot = 0; slot < 4; slot++) {
3700 		auto& finder = m_io_slot[ slot ];
3701 		hp9845_io_slot_device& tmp( HP9845_IO_SLOT(config , finder , 0) );
3702 		tmp.irq().set([this , slot](int state) { set_irq_slot(slot , state); });
3703 		tmp.sts().set([this , slot](int state) { set_sts_slot(slot , state); });
3704 		tmp.flg().set([this , slot](int state) { set_flg_slot(slot , state); });
3705 		tmp.irq_nextsc().set([this , slot](int state) { set_irq_nextsc_slot(slot , state); });
3706 		tmp.sts_nextsc().set([this , slot](int state) { set_sts_nextsc_slot(slot , state); });
3707 		tmp.flg_nextsc().set([this , slot](int state) { set_flg_nextsc_slot(slot , state); });
3708 		tmp.dmar().set([this , slot](int state) { set_dmar_slot(slot , state); });
3709 	}
3710 
3711 	// LPU memory options
3712 	RAM(config, RAM_TAG).set_default_size("192K").set_extra_options("64K, 320K, 448K");
3713 
3714 	// Internal printer
3715 	hp9845_printer_device& prt{ HP9845_PRINTER(config , "printer" , 0) };
3716 	prt.irq().set(FUNC(hp9845_base_state::prt_irl_w));
3717 	prt.flg().set([this](int state) { m_io_sys->set_flg(PRINTER_PA , state); });
3718 	prt.sts().set([this](int state) { m_io_sys->set_sts(PRINTER_PA , state); });
3719 }
3720 
hp9845b(machine_config & config)3721 void hp9845b_state::hp9845b(machine_config &config)
3722 {
3723 	hp9845_base(config);
3724 	// video hardware
3725 	m_screen->set_screen_update(FUNC(hp9845b_state::screen_update));
3726 	m_screen->screen_vblank().set(FUNC(hp9845b_state::vblank_w));
3727 	m_screen->set_color(rgb_t::green());
3728 	// These parameters are for alpha video
3729 	m_screen->set_raw(VIDEO_PIXEL_CLOCK , VIDEO_HTOTAL , 0 , VIDEO_HBSTART , VIDEO_VTOTAL , 0 , VIDEO_ACTIVE_SCANLINES);
3730 	PALETTE(config, m_palette).set_entries(4);
3731 	TIMER(config, "scantimer").configure_scanline(FUNC(hp9845b_state::scanline_timer), "screen", 0, 1);
3732 
3733 	config.set_default_layout(layout_hp9845b);
3734 
3735 	SOFTWARE_LIST(config, "optrom_list").set_original("hp9845b_rom");
3736 }
3737 
hp9845c(machine_config & config)3738 void hp9845c_state::hp9845c(machine_config &config)
3739 {
3740 	hp9845_base(config);
3741 	// video hardware
3742 	m_screen->set_screen_update(FUNC(hp9845c_state::screen_update));
3743 	m_screen->screen_vblank().set(FUNC(hp9845c_state::vblank_w));
3744 	m_screen->set_raw(VIDEO_770_PIXEL_CLOCK , VIDEO_770_HTOTAL , VIDEO_770_HBEND , VIDEO_770_HBSTART , VIDEO_770_VTOTAL , VIDEO_770_VBEND , VIDEO_770_VBSTART);
3745 	PALETTE(config, m_palette).set_entries(24);
3746 	TIMER(config, "scantimer").configure_scanline(FUNC(hp9845c_state::scanline_timer), "screen", 0, 1);
3747 
3748 	SOFTWARE_LIST(config, "optrom_list").set_original("hp9845b_rom");
3749 }
3750 
hp9845t(machine_config & config)3751 void hp9845t_state::hp9845t(machine_config &config)
3752 {
3753 	hp9845_base(config);
3754 	// video hardware
3755 	m_screen->set_screen_update(FUNC(hp9845t_state::screen_update));
3756 	m_screen->screen_vblank().set(FUNC(hp9845t_state::vblank_w));
3757 	m_screen->set_color(rgb_t::green());
3758 	m_screen->set_raw(VIDEO_780_PIXEL_CLOCK , VIDEO_780_HTOTAL , VIDEO_780_HBEND , VIDEO_780_HBSTART , VIDEO_780_VTOTAL , VIDEO_780_VBEND , VIDEO_780_VBSTART);
3759 	PALETTE(config, m_palette).set_entries(5);
3760 	TIMER(config, "scantimer").configure_scanline(FUNC(hp9845t_state::scanline_timer), "screen", 0, 1);
3761 
3762 	SOFTWARE_LIST(config, "optrom_list").set_original("hp9845b_rom");
3763 }
3764 
3765 	ROM_START( hp9845a )
3766 	ROM_REGION( 0200000, "lpu", ROMREGION_16BIT | ROMREGION_BE )
3767 	ROM_LOAD( "09845-65544-65547-03-system_lpu.bin", 0000000, 0200000, CRC(47beb87f) SHA1(456caefacafcf19435e1e7e68b1c1e4010841664) )
3768 
3769 	ROM_REGION( 0200000, "ppu", ROMREGION_16BIT | ROMREGION_BE )
3770 	ROM_LOAD( "09845-65540-65543-01-system_ppu.bin", 0000000, 0160000, CRC(bc0a34cc) SHA1(9ff215f4ba32ad85f144845d15f762a71e35588b) )
3771 ROM_END
3772 
3773 #define rom_hp9845s rom_hp9845a
3774 
3775 ROM_START( hp9835a )
3776 	ROM_REGION( 0200000, "lpu", ROMREGION_16BIT | ROMREGION_BE )
3777 	ROM_LOAD( "1818-2800-03_00-system-lpu.bin", 0000000, 020000, CRC(e0b0977a) SHA1(5afdc6c725abff70b674e46688d8ab38ccf8f3c1) )
3778 	ROM_LOAD( "1818-2801-03_10-system-lpu.bin", 0020000, 020000, CRC(c51c1e3a) SHA1(798964fa2e7a1fc149ce4400b694630049293119) )
3779 	ROM_LOAD( "1818-2802-03_20-system-lpu.bin", 0040000, 020000, CRC(bba70a7e) SHA1(2d488594493f8dfcd753e462414cc51c24596a2c) )
3780 	ROM_LOAD( "1818-2803-03_30-system-lpu.bin", 0060000, 020000, CRC(65e9eba6) SHA1(a11f5d37e8ed14a428335c43e785d635b02d1129) )
3781 	ROM_LOAD( "1818-2804-03_40-system-lpu.bin", 0100000, 020000, CRC(ef83b695) SHA1(8ca2914609ece2c9c59ebba6ece3fcbc8929aeaf) )
3782 	ROM_LOAD( "1818-2805-03_50-system-lpu.bin", 0120000, 020000, CRC(401d539f) SHA1(00bda59f71632c4d4fc3268c04262bb81ef0eeba) )
3783 	ROM_LOAD( "1818-2806-03_60-system-lpu.bin", 0140000, 020000, CRC(fe353db5) SHA1(0fb52d82d3743008cdebebb20c488e34ce2fca4b) )
3784 	ROM_LOAD( "1818-2807-03_70-system-lpu.bin", 0160000, 020000, CRC(45a3cc5e) SHA1(35c9959331acf7c98ab6a880915b03e3e783a656) )
3785 
3786 	ROM_REGION( 0200000, "ppu", ROMREGION_16BIT | ROMREGION_BE )
3787 	ROM_LOAD( "1818-2808-05_00-system-ppu.bin", 0000000, 020000, CRC(d0c96276) SHA1(cc578d586c4eda81469f29eb7cab7f667e0d5977) )
3788 	ROM_LOAD( "1818-2809-05_30-system-ppu.bin", 0060000, 020000, CRC(ccdb7171) SHA1(1d24596bc1219983e7cb81f6987af094f2ca7d81) )
3789 	ROM_LOAD( "1818-2810-05_40-system-ppu.bin", 0100000, 020000, CRC(97487d24) SHA1(823cd16671de8e6ff2c245060c99778acb6ff79c) )
3790 	ROM_LOAD( "1818-2811-05_50-system-ppu.bin", 0120000, 020000, CRC(18aee6fd) SHA1(388d3b2a063ea2cfdfe9fb9f864fa5f08af817b0) )
3791 	ROM_LOAD( "1818-2812-05_60-system-ppu.bin", 0140000, 020000, CRC(c0beeeae) SHA1(a5db36a7f7bad84c1013bd3ec4813c355f72427d) )
3792 	ROM_LOAD( "1818-2813-05_70-system-ppu.bin", 0160000, 020000, CRC(75361bbf) SHA1(40f499c597da5c8c9a55a2a891976d946a54926b) )
3793 ROM_END
3794 
3795 #define rom_hp9835b rom_hp9835a
3796 
3797 ROM_START( hp9845b )
3798 	ROM_REGION(0x800 , "chargen" , 0)
3799 	ROM_LOAD("chrgen.bin" , 0 , 0x800 , CRC(fe9e844f) SHA1(0c45ae00766ceba94a19bd5e154bd6d23e208cca))
3800 
3801 	ROM_REGION(0x800 , "optional_chargen" , 0)
3802 	ROM_LOAD("optional_chrgen.bin" , 0 , 0x800 , CRC(0ecfa63b) SHA1(c295e6393d1503d903c1d2ce576fa597df9746bf))
3803 
3804 	ROM_REGION(0x10000, "lpu", ROMREGION_16BIT | ROMREGION_BE)
3805 	ROM_LOAD("9845-lpu-standard-processor.bin", 0, 0x10000, CRC(dc266c1b) SHA1(1cf3267f13872fbbfc035b70f8b4ec6b5923f182))
3806 
3807 	ROM_REGION(0x10000, "ppu", ROMREGION_16BIT | ROMREGION_BE)
3808 	ROM_LOAD("9845-ppu-standard-graphics.bin", 0, 0x10000, CRC(f866510f) SHA1(3e22cd2072e3a5f3603a1eb8477b6b4a198d184d))
3809 
3810 #if 0
3811 	ROM_REGION( 0200000, "lpu", ROMREGION_16BIT | ROMREGION_BE )
3812 	ROM_LOAD( "1818-0823-0827-03_00-revb-system_lpu.bin", 0000000, 020000, CRC(7e49c781) SHA1(866c9ebd98d94bb6f99692e29d2d83f55b38c4b6) )
3813 	ROM_LOAD( "1818-0823-0827-03_10-revb-system_lpu.bin", 0020000, 020000, CRC(2f819e3d) SHA1(250886378c3ce2253229997007c7bf0be80a8d1d) )
3814 	ROM_LOAD( "1818-0824-0828-03_20-reva-system_lpu.bin", 0040000, 020000, CRC(834f7063) SHA1(5c390ed74671e4663cc80d899d07b69fd1fb4be6) )
3815 	ROM_LOAD( "1818-0824-0828-03_20-revb-system_lpu.bin", 0040000, 020000, CRC(aa221deb) SHA1(7878643405ee45405dc5269c3b6dc9459f39437b) )
3816 	ROM_LOAD( "1818-0824-0828-03_30-reva-system_lpu.bin", 0060000, 020000, CRC(0ebafdb2) SHA1(80733bfb7026d39a294841221d80ec40eafffe34) )
3817 	ROM_LOAD( "1818-0824-0828-03_30-revb-system_lpu.bin", 0060000, 020000, CRC(0ebafdb2) SHA1(80733bfb7026d39a294841221d80ec40eafffe34) )
3818 	ROM_LOAD( "1818-0825-0829-03_40-revb-system_lpu.bin", 0100000, 020000, CRC(beb09a57) SHA1(b832b995fa21c219673f0c7cf215dee70698f4f1) )
3819 	ROM_LOAD( "1818-0825-0829-03_50-revb-system_lpu.bin", 0120000, 020000, CRC(bbb06222) SHA1(b0bfe1b48fac61eb955e27e0ddfbea020e09e0eb) )
3820 	ROM_LOAD( "1818-0826-0830-03_60-revc-system_lpu.bin", 0140000, 020000, CRC(5c1c3abe) SHA1(fa9f99bf7c8a6df5c71e9fd8c807f0a2ff06640d) )
3821 	ROM_LOAD( "1818-0826-0830-03_70-revc-system_lpu.bin", 0160000, 020000, CRC(0c61a266) SHA1(0cfbf482e7f8e99c87b97c77cf178682cd7af7d6) )
3822 
3823 	ROM_REGION( 0200000, "lpu_fast", ROMREGION_16BIT | ROMREGION_BE )
3824 	ROM_LOAD( "1818-1506-1502-03_00-reva-system_fast_lpu.bin", 0000000, 020000, CRC(b77194d8) SHA1(6feec8605331783e6f5a2ab6d6cbd9285036e863) )
3825 	ROM_LOAD( "1818-1506-1502-03_10-reva-system_fast_lpu.bin", 0020000, 020000, CRC(bc5557a5) SHA1(282237e561c3f2304cdeb45efa2432748581af45) )
3826 	ROM_LOAD( "1818-1507-1503-03_20-reva-system_fast_lpu.bin", 0040000, 020000, CRC(2ebc71e2) SHA1(a2d39fb24d565465304833dfd0ff87dd5ef26fb3) )
3827 	ROM_LOAD( "1818-1507-1503-03_30-reva-system_fast_lpu.bin", 0060000, 020000, CRC(82e56bc4) SHA1(36201f343382e533c248ddd123507a2e195cca39) )
3828 	ROM_LOAD( "1818-1508-1504-03_40-reva-system_fast_lpu.bin", 0100000, 020000, CRC(70b0fcb0) SHA1(3f7ce60cad0ffec8344f33d584869492c7f73026) )
3829 	ROM_LOAD( "1818-1508-1504-03_50-reva-system_fast_lpu.bin", 0120000, 020000, CRC(935fab96) SHA1(ecb1da2a0bd46e8c0da2875a1af8cf71d8f4bb56) )
3830 	ROM_LOAD( "1818-1509-1505-03_60-reva-system_fast_lpu.bin", 0140000, 020000, CRC(f4119af7) SHA1(72a3e8b8d7d306e55f8adf0e23225bb81bc2b4ba) )
3831 	ROM_LOAD( "1818-1509-1505-03_70-reva-system_fast_lpu.bin", 0160000, 020000, CRC(22fb0864) SHA1(4e1dce32e84ba216dbbd4116f3b22ca7f254f529) )
3832 
3833 	ROM_REGION( 0200000, "ppu", ROMREGION_16BIT | ROMREGION_BE )
3834 	ROM_LOAD( "1818-0833-0837-05_40-revc-system_ppu.bin", 0100000, 020000, CRC(d790795c) SHA1(7ba1e245a98379a34833a780898a784049e33b86) )
3835 	ROM_LOAD( "1818-0833-0837-05_40-revd-system_ppu.bin", 0100000, 020000, CRC(49897e40) SHA1(780a9973ff26d40f470e2004fccceb1019f8ba7f) )
3836 	ROM_LOAD( "1818-0833-0837-05_50-revc-system_ppu.bin", 0120000, 020000, CRC(ef8acde4) SHA1(e68648543aac2b841b08d7758949ba1339a83701) )
3837 	ROM_LOAD( "1818-0833-0837-05_50-revd-system_ppu.bin", 0120000, 020000, CRC(54f61d07) SHA1(f807fb8a59cd9cd221f63907e6a86948a0bf7c1d) )
3838 	ROM_LOAD( "1818-0834-0838-05_60-revc-system_ppu.bin", 0140000, 020000, CRC(20f2100a) SHA1(9304f0b069de9233d697588328f9657dbeabc254) )
3839 	ROM_LOAD( "1818-0834-0838-05_60-revd-system_ppu.bin", 0140000, 020000, CRC(454af601) SHA1(54b56e67e855fd2d699a0dbef0b4d2e8c150c39b) )
3840 	ROM_LOAD( "1818-0834-0838-05_70-revc-system_ppu.bin", 0160000, 020000, CRC(43f62491) SHA1(a9489b37b3fa8768ca6e503f346bd023833ae3ac) )
3841 	ROM_LOAD( "1818-0834-0838-05_70-revd-system_ppu.bin", 0160000, 020000, CRC(43f62491) SHA1(a9489b37b3fa8768ca6e503f346bd023833ae3ac) )
3842 	ROM_LOAD( "1818-1899-1898-05_60-reva-system_ppu.bin", 0140000, 020000, CRC(454af601) SHA1(54b56e67e855fd2d699a0dbef0b4d2e8c150c39b) )
3843 	ROM_LOAD( "1818-1899-1898-05_70-reva-system_ppu.bin", 0160000, 020000, CRC(049604f2) SHA1(89bfd8e086bc9365f156966b0a62c3ac720fc627) )
3844 
3845 	ROM_REGION( 0200000, "ppu_tops", ROMREGION_16BIT | ROMREGION_BE )
3846 	ROM_LOAD( "1818-0831-0835-05_00-reva-tops_ppu.bin", 0000000, 020000, CRC(7ddce706) SHA1(746e34d3de52a17372af9a9eb1ed4974a4eae656) )
3847 	ROM_LOAD( "1818-0831-0835-05_10-reva-tops_ppu.bin", 0020000, 020000, CRC(d7fc3d47) SHA1(a3d723fe62f047cb0c17d405d07bb0b08d08e830) )
3848 	ROM_LOAD( "1818-1209-1208-05_00-revb-tops_ppu.bin", 0000000, 020000, CRC(0dc90614) SHA1(94c07553a62b2c86414bc95314601f90eb4e4022) )
3849 	ROM_LOAD( "1818-1209-1208-05_10-revb-tops_ppu.bin", 0020000, 020000, CRC(4e362657) SHA1(b09098c0acd56b11ec3b72ff3e8b5a1e14ef3ae8) )
3850 	ROM_LOAD( "1818-1592-1591-05_00-revb-tops_ppu.bin", 0000000, 020000, CRC(8cfe29a8) SHA1(f1007b6b1d3f2b603653880c44cec48b23701263) )
3851 	ROM_LOAD( "1818-1592-1591-05_10-revb-tops_ppu.bin", 0020000, 020000, CRC(95048264) SHA1(36cfddef9d1289fdaf69596e10d95f88a520feae) )
3852 
3853 	ROM_REGION( 0200000, "ppu_kbd_us", ROMREGION_16BIT | ROMREGION_BE )
3854 	ROM_LOAD( "1818-0832-0836-05_20-revc-keyboard_us.bin", 0040000, 020000, CRC(3bf6268a) SHA1(65d7dfeaf34c74dbc86ebe5d3bb65c6bd10163cb) )
3855 	ROM_LOAD( "1818-0832-0836-05_30-revc-keyboard_us.bin", 0060000, 020000, CRC(2dfc619c) SHA1(5c54ff502d1344907817210bfdfcab7f8d6b61bd) )
3856 
3857 	ROM_REGION( 0200000, "ppu_kbd_de", ROMREGION_16BIT | ROMREGION_BE )
3858 	ROM_LOAD( "1818-0841-0846-05_20-revc-keyboard_german.bin", 0040000, 020000, CRC(76667eca) SHA1(ac63e5d584d1f2da5668d8a9560f927f48e25e03) )
3859 	ROM_LOAD( "1818-0841-0846-05_20-revd-keyboard_german.bin", 0060000, 020000, CRC(3bf6268a) SHA1(65d7dfeaf34c74dbc86ebe5d3bb65c6bd10163cb) )
3860 	ROM_LOAD( "1818-0841-0846-05_30-revc-keyboard_german.bin", 0040000, 020000, CRC(2b83db22) SHA1(6eda714ce05d2d75f4c041e36b6b6df40697d94a) )
3861 	ROM_LOAD( "1818-0841-0846-05_30-revd-keyboard_german.bin", 0060000, 020000, CRC(b4006959) SHA1(584a85f746a3b0c262fdf9e4be8e696c80cfd429) )
3862 #endif
3863 ROM_END
3864 
3865 ROM_START( hp9845c )
3866 	ROM_REGION(0x800 , "chargen" , 0)
3867 	ROM_LOAD("chrgen.bin" , 0 , 0x800 , CRC(fe9e844f) SHA1(0c45ae00766ceba94a19bd5e154bd6d23e208cca))
3868 
3869 	ROM_REGION(0x800 , "optional_chargen" , 0)
3870 	ROM_LOAD("optional_chrgen.bin" , 0 , 0x800 , CRC(0ecfa63b) SHA1(c295e6393d1503d903c1d2ce576fa597df9746bf))
3871 
3872 	ROM_REGION(0x10000, "lpu", ROMREGION_16BIT | ROMREGION_BE)
3873 	ROM_LOAD("9845-lpu-standard-processor.bin", 0, 0x10000, CRC(dc266c1b) SHA1(1cf3267f13872fbbfc035b70f8b4ec6b5923f182))
3874 
3875 	ROM_REGION(0x10000, "ppu", ROMREGION_16BIT | ROMREGION_BE)
3876 	ROM_LOAD("9845-ppu-color-enhanced-graphics.bin", 0, 0x10000, CRC(96e11edc) SHA1(3f1da50edb35dfc57ec2ecfd816a8c8230e110bd))
3877 ROM_END
3878 
3879 ROM_START( hp9845t )
3880 	ROM_REGION(0x1000 , "chargen" , 0)
3881 	ROM_LOAD("1818-1395.bin" , 0 , 0x1000 , CRC(7b555edf) SHA1(3b08e094635ef02aef9a2e37b049c61bcf1ec037))
3882 
3883 	ROM_REGION(0x10000, "lpu", ROMREGION_16BIT | ROMREGION_BE)
3884 	ROM_LOAD("9845-lpu-standard-processor.bin", 0, 0x10000, CRC(dc266c1b) SHA1(1cf3267f13872fbbfc035b70f8b4ec6b5923f182))
3885 
3886 	ROM_REGION(0x10000, "ppu", ROMREGION_16BIT | ROMREGION_BE)
3887 	ROM_LOAD("9845-ppu-color-enhanced-graphics.bin", 0, 0x10000, CRC(96e11edc) SHA1(3f1da50edb35dfc57ec2ecfd816a8c8230e110bd))
3888 ROM_END
3889 
3890 ROM_START( hp9845b_de )
3891 	ROM_REGION(0x800 , "chargen" , 0)
3892 	ROM_LOAD("chrgen.bin" , 0 , 0x800 , CRC(fe9e844f) SHA1(0c45ae00766ceba94a19bd5e154bd6d23e208cca))
3893 
3894 	ROM_REGION(0x800 , "optional_chargen" , 0)
3895 	ROM_LOAD("optional_chrgen.bin" , 0 , 0x800 , CRC(0ecfa63b) SHA1(c295e6393d1503d903c1d2ce576fa597df9746bf))
3896 
3897 	ROM_REGION(0x10000, "lpu", ROMREGION_16BIT | ROMREGION_BE)
3898 	ROM_LOAD("9845-lpu-standard-processor.bin", 0, 0x10000, CRC(dc266c1b) SHA1(1cf3267f13872fbbfc035b70f8b4ec6b5923f182))
3899 
3900 	ROM_REGION(0x10000, "ppu", ROMREGION_16BIT | ROMREGION_BE)
3901 	ROM_LOAD("9845-ppu-standard-graphics-ger.bin", 0, 0x10000, CRC(c968363d) SHA1(bc6805403371ca49d1a137f22cd254e3b0e0dbb4))
3902 ROM_END
3903 
3904 ROM_START( hp9845c_de )
3905 	ROM_REGION(0x800 , "chargen" , 0)
3906 	ROM_LOAD("chrgen.bin" , 0 , 0x800 , CRC(fe9e844f) SHA1(0c45ae00766ceba94a19bd5e154bd6d23e208cca))
3907 
3908 	ROM_REGION(0x800 , "optional_chargen" , 0)
3909 	ROM_LOAD("optional_chrgen.bin" , 0 , 0x800 , CRC(0ecfa63b) SHA1(c295e6393d1503d903c1d2ce576fa597df9746bf))
3910 
3911 	ROM_REGION(0x10000, "lpu", ROMREGION_16BIT | ROMREGION_BE)
3912 	ROM_LOAD("9845-lpu-standard-processor.bin", 0, 0x10000, CRC(dc266c1b) SHA1(1cf3267f13872fbbfc035b70f8b4ec6b5923f182))
3913 
3914 	ROM_REGION(0x10000, "ppu", ROMREGION_16BIT | ROMREGION_BE)
3915 	ROM_LOAD("9845-ppu-color-enhanced-graphics-ger.bin", 0, 0x10000, CRC(a7ef79ee) SHA1(637742ed8fc8201a8e7bac62654f21c5409dfb76))
3916 ROM_END
3917 
3918 ROM_START( hp9845t_de )
3919 	ROM_REGION(0x1000 , "chargen" , 0)
3920 	ROM_LOAD("1818-1395.bin" , 0 , 0x1000 , CRC(7b555edf) SHA1(3b08e094635ef02aef9a2e37b049c61bcf1ec037))
3921 
3922 	ROM_REGION(0x10000, "lpu", ROMREGION_16BIT | ROMREGION_BE)
3923 	ROM_LOAD("9845-lpu-standard-processor.bin", 0, 0x10000, CRC(dc266c1b) SHA1(1cf3267f13872fbbfc035b70f8b4ec6b5923f182))
3924 
3925 	ROM_REGION(0x10000, "ppu", ROMREGION_16BIT | ROMREGION_BE)
3926 	ROM_LOAD("9845-ppu-color-enhanced-graphics-ger.bin", 0, 0x10000, CRC(a7ef79ee) SHA1(637742ed8fc8201a8e7bac62654f21c5409dfb76))
3927 ROM_END
3928 
3929 //    YEAR  NAME        PARENT   COMPAT  MACHINE  INPUT           CLASS          INIT        COMPANY            FULLNAME  FLAGS
3930 COMP( 1977, hp9845a,    0,       0,      hp9845a, hp9845,         hp9845_state,  empty_init, "Hewlett-Packard", "9845A",  MACHINE_IS_SKELETON )
3931 COMP( 1977, hp9845s,    hp9845a, 0,      hp9845a, hp9845,         hp9845_state,  empty_init, "Hewlett-Packard", "9845S",  MACHINE_IS_SKELETON )
3932 COMP( 1979, hp9835a,    0,       0,      hp9835a, hp9845,         hp9845_state,  empty_init, "Hewlett-Packard", "9835A",  MACHINE_IS_SKELETON )
3933 COMP( 1979, hp9835b,    hp9835a, 0,      hp9835a, hp9845,         hp9845_state,  empty_init, "Hewlett-Packard", "9835B",  MACHINE_IS_SKELETON )
3934 COMP( 1979, hp9845b,    0,       0,      hp9845b, hp9845_base,    hp9845b_state, empty_init, "Hewlett-Packard", "9845B",  0 )
3935 COMP( 1982, hp9845t,    0,       0,      hp9845t, hp9845ct,       hp9845t_state, empty_init, "Hewlett-Packard", "9845T",  0 )
3936 COMP( 1980, hp9845c,    0,       0,      hp9845c, hp9845ct,       hp9845c_state, empty_init, "Hewlett-Packard", "9845C",  0 )
3937 COMP( 1979, hp9845b_de, hp9845b, 0,      hp9845b, hp9845_base_de, hp9845b_state, empty_init, "Hewlett-Packard", "9845B (Germany)", 0 )
3938 COMP( 1982, hp9845t_de, hp9845t, 0,      hp9845t, hp9845ct_de,    hp9845t_state, empty_init, "Hewlett-Packard", "9845T (Germany)", 0 )
3939 COMP( 1980, hp9845c_de, hp9845c, 0,      hp9845c, hp9845ct_de,    hp9845c_state, empty_init, "Hewlett-Packard", "9845C (Germany)", 0 )
3940