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