1 // license:BSD-3-Clause
2 // copyright-holders:Sergey Svishchev, F. Ulivi
3 /*
4 HP 1LL3-0005 GPU emulation.
5
6 Used by HP Integral PC, possibly other HP products.
7
8 On IPC, memory is 4 16Kx4bit DRAM chips = 32KB total (16K words),
9 but firmware probes memory size and can work with 128KB memory.
10 Undocumented "_desktop" mode requires this.
11
12 Capabilities:
13 - up to 1024x1024 px on screen
14 - lines
15 - rectangles
16 - area fill with user-defined pattern
17 - 16x16 user-defined proportional font, with automatic cursor
18 - 16x16 user-defined sprite for mouse cursor (not a sprite layer)
19 - windows with blitter (copy, fill and scroll) and clipping
20
21 To do:
22 - interrupt generation
23 - realistic timing?
24 - &c.
25
26 This table summarizes the GPU commands.
27 "Code" is the hex code of command.
28 "Impl" shows whether the command is implemented in this driver or not.
29 "Parameter" reports the I/O data exchanged by the command. Each line
30 is a 16-bit word which is read from/written to the GPU by a pair of
31 8-bit accesses to data register (MSB first).
32 "I" is an input parameter (sent before command code)
33 "D" is an input data word (sent after command code)
34 "O" is an output data word.
35
36 | *Cmd* | *Code* | *Impl.* | *Parameter* | *Description* |
37 |-----------+--------+---------+---------------------+-----------------------------------|
38 | NOP | 00 | Y | - | No operation. Used to close |
39 | | | | | RDMEM/WRMEM/RDWIN/WRWIN commands. |
40 | CONF | 02 | Y | D@0: H. timing 0 | Configure GPU |
41 | | | | D@1: H. timing 1 | |
42 | | | | D@2: H. timing 2 | |
43 | | | | D@3: H. timing 3 | |
44 | | | | D@4: V. timing 0 | |
45 | | | | D@5: V. timing 1 | |
46 | | | | D@6: V. timing 2 | |
47 | | | | D@7: V. timing 3 | |
48 | | | | D@8: Words per line | |
49 | | | | D@9: Lines | |
50 | | | | D@10: Option bits | |
51 | DISVID | 03 | Y | - | Disable video |
52 | ENVID | 04 | Y | - | Enable video |
53 | ? | 06 | Y | - | Unknown |
54 | | | | | My guess is interrupt enable |
55 | | | | | 05 is probably int. disable |
56 | | | | | It's only used by diagb ROM |
57 | WRMEM | 07 | Y | I@0: RAM address | Write video RAM. Keep writing |
58 | | | | D@0: RAM word | words with auto-incremented addr |
59 | | | | ... | until terminated by NOP. |
60 | RDMEM | 08 | Y | I@0: RAM address | Read video RAM. Read words until |
61 | | | | O@0: RAM word | terminated by NOP. |
62 | | | | ... | |
63 | WRSAD | 09 | Y | I@0: Start address | Set screen start address |
64 | WRORG | 0a | Y | I@0: Start address | Set raster start address |
65 | WRDAD | 0b | Y | I@0: Start address | Set data start address |
66 | WRRR | 0c | Y | I@0: RR | Set replacement rule |
67 | MOVEP | 0d | Y | I@0: X | Set pen position |
68 | | | | I@1: Y | |
69 | IMOVEP | 0e | Y | I@0: delta X | Set pen position (incremental) |
70 | | | | I@1: delta Y | |
71 | DRAWP | 0f | Y | I@0: X | Draw a line and move pen |
72 | | | | I@1: Y | |
73 | IDRAWP | 10 | N | I@0: delta X | Draw a line (incremental) |
74 | | | | I@1: delta Y | |
75 | RDP | 11 | Y | O@0: pen X pos | Read current pen position |
76 | | | | O@1: pen Y pos | |
77 | WRUDL | 12 | Y | I@0: UDL | Set user-defined line pattern |
78 | WRWINSIZ | 13 | Y | I@0: Window width | Set window size |
79 | | | | I@1: Window height | |
80 | WRWINORG | 14 | Y | I@0: Origin X | Set window origin (UL corner) |
81 | | | | I@1: Origin Y | |
82 | COPY | 15 | Y | I@0: Dest. origin X | Copy a window. Parameter sets |
83 | | | | I@1: Dest. origin Y | the UL corner of the dest. win. |
84 | FILL | 16 | Y | I@0: Pattern no. | Fill a window |
85 | FRAME | 17 | N | - | Draw a frame (rectangle) |
86 | SCROLUP | 18 | Y | I@0: Fill pattern | Scroll a window upwards |
87 | | | | I@1: Scroll amount | |
88 | SCROLDN | 19 | Y | I@0: Fill pattern | Scroll a window downwards |
89 | | | | I@1: Scroll amount | |
90 | SCROLLF | 1a | N | I@0: Fill pattern | Scroll a window to the left |
91 | | | | I@1: Scroll amount | |
92 | SCROLLF | 1b | N | I@0: Fill pattern | Scroll a window to the right |
93 | | | | I@1: Scroll amount | |
94 | RDWIN | 1c | Y | I@0: bit offset | Read a window. Terminated by NOP. |
95 | | | | O@0: data word | |
96 | | | | ... | |
97 | WRWIN | 1d | Y | I@0: bit offset | Write a window. Terminated by NOP.|
98 | | | | D@0: data word | |
99 | | | | ... | |
100 | RDWINPARM | 1e | Y | O@0: Win. origin X | Read current window parameters |
101 | | | | O@1: Win. origin Y | |
102 | | | | O@2: Window width | |
103 | | | | O@3: Window height | |
104 | CR | 1f | N | - | Carriage return: move pen to |
105 | | | | | start of line |
106 | CRLF | 20 | Y | - | CRLF: move pen to start of next |
107 | | | | | text row |
108 | LABEL | 24 | Y | I@0: character code | Write a character and move pen |
109 | ENSP | 26 | Y | I@0: sprite pattern | Enable sprite |
110 | DISSP | 27 | Y | - | Disable sprite |
111 | MOVESP | 28 | Y | I@0: Sprite X pos | Move sprite |
112 | | | | I@1: Sprite Y pos | |
113 | IMOVESP | 29 | N | I@0: Sprite delta X | Move sprite (incremental) |
114 | | | | I@1: Sprite delta Y | |
115 | RDSP | 2a | Y | O@0: Sprite X pos | Read sprite position |
116 | | | | O@1: Sprite Y pos | |
117 | DRAWPX | 2b | Y | I@0: X | Write a pixel |
118 | | | | I@1: Y | |
119 | WRFAD | 2c | Y | I@0: Font address | Set address of font data |
120 | ENCURS | 2d | Y | I@0: cursor pattern | Enable cursor |
121 | | | | I@1: cursor offset | |
122 | DISCURS | 2e | Y | - | Disable cursor |
123 | ID | 3f | Y | O@0: ID | Read hw ID |
124 */
125
126 #include "emu.h"
127 #include "hp1ll3.h"
128
129 #include "screen.h"
130
131
132 ///*************************************************************************
133 // MACROS / CONSTANTS
134 ///*************************************************************************
135
136 /*
137 * command types (send)
138 *
139 * 0 -- no data
140 * 1 -- write 1 word of data, then command
141 * 2 -- write 2 words of data, then command
142 * 3 -- write command, then 12 words of data (= CONF only)
143 * 4 -- write 1 word of data, then command, then write X words of data, then write NOP
144 *
145 * (read)
146 *
147 * 3 -- write command, then read 2 words of data (X & Y coordinates)
148 * 4 -- write 1 word of data, then command, then read X words of data, then write NOP
149 */
150 #define NOP 0 // type 0
151 #define CONF 2 // type 3, configure GPU (screen size, timings...). 11 words of data.
152 #define DISVID 3 // type 0, disable video
153 #define ENVID 4 // type 0, enable video
154 #define WRMEM 7 // type 4, write GPU memory at offset, terminate by NOP
155 #define RDMEM 8 // type 4, read GPU memory from offset, terminate by NOP
156 #define WRSAD 9 // type 1, set screen area start address
157 #define WRORG 10 // type 1, set raster start address
158 #define WRDAD 11 // type 1, set data area start address (16x16 area fill, sprite and cursor)
159 #define WRRR 12 // type 1, set replacement rule (rasterop)
160 #define MOVEP 13 // type 2, move pointer
161 #define IMOVEP 14 // type 2, incremental move pointer
162 #define DRAWP 15 // type 2, draw line
163 #define IDRAWP 16 // type 2, incremental draw line
164 #define RDP 17 // type 3, read pointer position
165 #define WRUDL 18 // type 1, set user-defined line pattern (16-bit)
166 #define WRWINSIZ 19 // type 2, set window size
167 #define WRWINORG 20 // type 2, set window origin
168 #define COPY 21 // type 2, block copy
169 #define FILL 22 // type 1, fill area
170 #define FRAME 23 // type 0, draw rectangle
171 #define SCROLUP 24 // type 2, scroll up
172 #define SCROLDN 25 // type 2, scroll down
173 #define SCROLLF 26 // type 2, scroll left
174 #define SCROLRT 27 // type 2, scroll right
175 #define RDWIN 28 // type 4, read window raster
176 #define WRWIN 29 // type 4, write window raster
177 #define RDWINPARM 30
178 #define CR 31 // type 0, carriage return
179 #define CRLFx 32 // type 0, CR + LF
180 #define LABEL 36 // type 1, draw text
181 #define ENSP 38 // type 1, enable sprite
182 #define DISSP 39 // type 0, disable sprite
183 #define MOVESP 40 // type 2, move sprite
184 #define IMOVESP 41 // type 2, incremental move sprite
185 #define RDSP 42 // type 3, read sprite position
186 #define DRAWPX 43 // type 2, draw single pixel
187 #define WRFAD 44 // type 1, set font area start address
188 #define ENCURS 45 // type 2, enable cursor
189 #define DISCURS 46 // type 0, disable cursor
190 #define ID 63
191
192
193 /*
194 * Replacement Rules (rops). sources:
195 *
196 * - NetBSD's diofbvar.h (definitions for Topcat chip)
197 * - pdf/hp/9000_300/specs/A-5958-4362-9_Series_300_Display_Color_Card_Theory_of_Operation_Oct85.pdf
198 * refers to TOPCAT documentation p/n A-1FH2-2001-7 (not online)
199 */
200 #define RR_FORCE_ZERO 0x0
201 #define RR_CLEAR RR_FORCE_ZERO
202 #define RR_AND 0x1
203 #define RR_AND_NOT_OLD 0x2
204 #define RR_NEW 0x3
205 #define RR_COPY RR_NEW
206 #define RR_AND_NOT_NEW 0x4
207 #define RR_OLD 0x5
208 #define RR_XOR 0x6
209 #define RR_OR 0x7
210 #define RR_NOR 0x8
211 #define RR_XNOR 0x9
212 #define RR_NOT_OLD 0xa
213 #define RR_INVERT RR_NOT_OLD
214 #define RR_OR_NOT_OLD 0xb
215 #define RR_NOT_NEW 0xc
216 #define RR_COPYINVERTED RR_NOT_NEW
217 #define RR_OR_NOT_NEW 0xd
218 #define RR_NAND 0xe
219 #define RR_FORCE_ONE 0xf
220
221 constexpr unsigned WS = 16; // bits in a word
222 constexpr uint16_t MSB_MASK = 0x8000; // Mask of MSB
223
224 //**************************************************************************
225 // MACROS / CONSTANTS
226 //**************************************************************************
227
228 #define VERBOSE_DBG 2 /* general debug messages */
229
230 #define DBG_LOG(N,M,A) \
231 do { \
232 if(VERBOSE_DBG>=N) \
233 { \
234 if( M ) \
235 logerror("%11.6f at %s: %-16s",machine().time().as_double(),machine().describe_context(),(char*)M ); \
236 logerror A; \
237 } \
238 } while (0)
239
240 // Index of words in configuration vector
241 enum : unsigned
242 {
243 CONF_HOR_0 = 0, // Horizontal timing, 1st word
244 CONF_HOR_1 = 1, // Horizontal timing, 2nd word
245 CONF_HOR_2 = 2, // Horizontal timing, 3rd word
246 CONF_HOR_3 = 3, // Horizontal timing, 4th word
247 CONF_VER_0 = 4, // Vertical timing, 1st word
248 CONF_VER_1 = 5, // Vertical timing, 2nd word
249 CONF_VER_2 = 6, // Vertical timing, 3rd word
250 CONF_VER_3 = 7, // Vertical timing, 4th word
251 CONF_WPL = 8, // Words per line
252 CONF_LINES = 9, // Lines
253 CONF_OPTS = 10 // RAM size or option bits (more likely)
254 };
255
256 // Fields of hor/ver timing words
257 enum : uint16_t
258 {
259 HV_CNT_MASK = 0x3ff, // Mask of counter part
260 HV_ZONE_MASK = 0xc00, // Mask of zone part
261 HV_ZONE_0 = 0x000, // Value for zone 0
262 HV_ZONE_1 = 0x400, // Value for zone 1
263 HV_ZONE_2 = 0x800, // Value for zone 2
264 HV_ZONE_3 = 0xc00 // Value for zone 3
265 };
266
267 //**************************************************************************
268 // GLOBAL VARIABLES
269 //**************************************************************************
270
271 // devices
272 DEFINE_DEVICE_TYPE(HP1LL3, hp1ll3_device, "hp1ll3", "Hewlett-Packard 1LL3-0005 GPU")
273
274
275 //**************************************************************************
276 // LIVE DEVICE
277 //**************************************************************************
278
279 //-------------------------------------------------
280 // hp1ll3_device - constructor
281 //-------------------------------------------------
282
hp1ll3_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)283 hp1ll3_device::hp1ll3_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
284 : device_t(mconfig, HP1LL3, tag, owner, clock)
285 , device_video_interface(mconfig, *this)
286 , m_vram_size(16)
287 {
288 }
289
290 //-------------------------------------------------
291 // device_start - device-specific startup
292 //-------------------------------------------------
293
device_start()294 void hp1ll3_device::device_start()
295 {
296 const rectangle &visarea = screen().visible_area();
297
298 // register for state saving
299 save_item(NAME(m_conf));
300
301 screen().register_screen_bitmap(m_bitmap);
302
303 m_videoram = std::make_unique<uint16_t[]>(m_vram_size);
304
305 save_pointer(NAME(m_videoram) , m_vram_size);
306 m_ram_addr_mask = uint16_t(m_vram_size - 1);
307 m_horiz_pix_total = visarea.max_x + 1;
308 m_vert_pix_total = visarea.max_y + 1;
309 }
310
device_reset()311 void hp1ll3_device::device_reset()
312 {
313 m_rd_ptr = m_wr_ptr = m_command = 0;
314 m_sad = m_fad = m_dad = m_org = m_rr = m_udl = 0;
315 m_enable_video = m_enable_cursor = m_enable_sprite = m_busy = false;
316 }
317
get_pix_addr(uint16_t x,uint16_t y) const318 uint16_t hp1ll3_device::get_pix_addr(uint16_t x, uint16_t y) const
319 {
320 return m_org + y * m_conf[CONF_WPL] + x / WS;
321 }
322
point(int x,int y,bool pix,const uint16_t masks[])323 inline void hp1ll3_device::point(int x, int y, bool pix, const uint16_t masks[])
324 {
325 uint16_t offset = get_pix_addr(x, y);
326 uint16_t pix_mask = MSB_MASK >> (x % WS);
327
328 rmw_rop(offset, pix ? ~0 : 0, pix_mask, masks);
329 }
330
331 // Bresenham algorithm -- from ef9365.cpp
line(int x1,int y1,int x2,int y2)332 void hp1ll3_device::line(int x1, int y1, int x2, int y2)
333 {
334 int dx;
335 int dy,t;
336 int e;
337 int x,y;
338 int incy;
339 int diago,horiz;
340 unsigned char c1;
341
342 uint16_t rop_masks[4];
343 get_rop_masks(m_rr, rop_masks);
344
345 uint16_t udl_scan = MSB_MASK;
346
347 c1 = 0;
348 incy = 1;
349
350 if (x2 > x1)
351 dx = x2 - x1;
352 else
353 dx = x1 - x2;
354
355 if (y2 > y1)
356 dy = y2 - y1;
357 else
358 dy = y1 - y2;
359
360 if (dy > dx)
361 {
362 t = y2;
363 y2 = x2;
364 x2 = t;
365
366 t = y1;
367 y1 = x1;
368 x1 = t;
369
370 t = dx;
371 dx = dy;
372 dy = t;
373
374 c1 = 1;
375 }
376
377 if (x1 > x2)
378 {
379 t = y2;
380 y2 = y1;
381 y1 = t;
382
383 t = x1;
384 x1 = x2;
385 x2 = t;
386 }
387
388 horiz = dy << 1;
389 diago = (dy - dx) << 1;
390 e = (dy << 1) - dx;
391
392 if (y1 <= y2)
393 incy = 1;
394 else
395 incy = -1;
396
397 x = x1;
398 y = y1;
399
400 if (c1)
401 {
402 do
403 {
404 point(y, x, m_udl & udl_scan,rop_masks);
405 udl_scan >>= 1;
406 if (!udl_scan)
407 udl_scan = MSB_MASK;
408
409 if (e > 0)
410 {
411 y = y + incy;
412 e = e + diago;
413 }
414 else
415 {
416 e = e + horiz;
417 }
418
419 x++;
420
421 } while (x <= x2);
422 }
423 else
424 {
425 do
426 {
427 point(x, y, m_udl & udl_scan,rop_masks);
428 udl_scan >>= 1;
429 if (!udl_scan)
430 udl_scan = MSB_MASK;
431
432 if (e > 0)
433 {
434 y = y + incy;
435 e = e + diago;
436 }
437 else
438 {
439 e = e + horiz;
440 }
441
442 x++;
443
444 } while (x <= x2);
445 }
446 }
447
get_font(uint16_t & font_data,uint16_t & font_height) const448 void hp1ll3_device::get_font(uint16_t& font_data, uint16_t& font_height) const
449 {
450 font_data = m_fad + rd_video(m_fad + 1) + 2;
451 font_height = rd_video(m_fad);
452 }
453
label(uint8_t chr,int width)454 void hp1ll3_device::label(uint8_t chr, int width)
455 {
456 draw_cursor_sprite();
457
458 uint16_t font_data;
459 uint16_t font_height;
460 get_font(font_data, font_height);
461
462 Rectangle clip = get_window();
463 Rectangle dst{{ m_cursor_x, m_cursor_y }, { uint16_t(width), font_height }};
464 bitblt(font_data + chr * 16, 16, 16, Point { 0, 0 }, clip, dst, m_rr);
465 m_cursor_x += width;
466 draw_cursor_sprite();
467 }
468
wr_video(uint16_t addr,uint16_t v)469 void hp1ll3_device::wr_video(uint16_t addr, uint16_t v)
470 {
471 m_videoram[addr & m_ram_addr_mask] = v;
472 }
473
rd_video(uint16_t addr) const474 uint16_t hp1ll3_device::rd_video(uint16_t addr) const
475 {
476 return m_videoram[addr & m_ram_addr_mask];
477 }
478
get_rop_masks(uint16_t rop,uint16_t masks[])479 void hp1ll3_device::get_rop_masks(uint16_t rop, uint16_t masks[])
480 {
481 masks[0] = BIT(rop, 0) ? ~0 : 0;
482 masks[1] = BIT(rop, 1) ? ~0 : 0;
483 masks[2] = BIT(rop, 2) ? ~0 : 0;
484 masks[3] = BIT(rop, 3) ? ~0 : 0;
485 }
486
apply_rop(uint16_t old_pix,uint16_t new_pix,uint16_t glob_mask,const uint16_t masks[])487 uint16_t hp1ll3_device::apply_rop(uint16_t old_pix, uint16_t new_pix, uint16_t glob_mask, const uint16_t masks[])
488 {
489 uint16_t diff0 = old_pix & new_pix;
490 uint16_t diff1 = ~old_pix & new_pix;
491 uint16_t diff2 = old_pix & ~new_pix;
492 uint16_t diff3 = ~old_pix & ~new_pix;
493
494 // The idea here is that ROP is forced to be "RR_OLD" for all zero bits in glob_mask
495 // so that the corresponding bits in new_pix are ignored
496 return
497 ((masks[0] | ~glob_mask) & diff0) |
498 (masks[1] & glob_mask & diff1) |
499 ((masks[2] | ~glob_mask) & diff2) |
500 (masks[3] & glob_mask & diff3);
501 }
502
rmw_rop(uint16_t addr,uint16_t new_pix,uint16_t glob_mask,const uint16_t masks[])503 void hp1ll3_device::rmw_rop(uint16_t addr, uint16_t new_pix, uint16_t glob_mask, const uint16_t masks[])
504 {
505 wr_video(addr, apply_rop(rd_video(addr), new_pix, glob_mask, masks));
506 }
507
clip_coord(int size_1,int & p1,int origin_clip,int size_clip,int & origin_2,int & size_2) const508 void hp1ll3_device::clip_coord(int size_1, int &p1, int origin_clip, int size_clip, int &origin_2, int &size_2) const
509 {
510 // origin_1 is implicitly 0
511 int corner_2 = origin_2 + size_2;
512 // Clip origin_2 & p1 w.r.t. clip rectangle
513 int t = origin_2 - origin_clip;
514 if (t < 0)
515 {
516 p1 -= t;
517 origin_2 = origin_clip;
518 }
519 // Clip size_2 w.r.t. clip rectangle
520 int corner_clip = origin_clip + size_clip;
521 if (corner_2 > corner_clip)
522 size_2 = corner_clip - origin_2;
523
524 // Clip size_2 w.r.t. rectangle 1 (whose origin is 0,0)
525 t = p1 + size_2 - size_1;
526 if (t > 0)
527 size_2 -= t;
528 }
529
bitblt(uint16_t src_base_addr,unsigned src_width,unsigned src_height,Point src_p,const Rectangle & clip_rect,const Rectangle & dst_rect,uint16_t rop,bool use_m_org)530 bool hp1ll3_device::bitblt(uint16_t src_base_addr, unsigned src_width, unsigned src_height, Point src_p,
531 const Rectangle &clip_rect, const Rectangle &dst_rect, uint16_t rop, bool use_m_org)
532 {
533 DBG_LOG(3,0,("bitblt %04x,%u,%u,(%u,%u),(%u,%u,%u,%u),(%u,%u,%u,%u),%u\n", src_base_addr,
534 src_width,
535 src_height,
536 src_p.x,
537 src_p.y,
538 clip_rect.origin.x, clip_rect.origin.y, clip_rect.size.x, clip_rect.size.y,
539 dst_rect.origin.x, dst_rect.origin.y, dst_rect.size.x, dst_rect.size.y,
540 rop));
541 int src_x = src_p.x;
542 int dst_x = static_cast<int16_t>(dst_rect.origin.x);
543 int dst_width = dst_rect.size.x;
544 int src_y = src_p.y;
545 int dst_y = static_cast<int16_t>(dst_rect.origin.y);
546 int dst_height = dst_rect.size.y;
547 // Clip x-coordinates
548 clip_coord(src_width, src_x, clip_rect.origin.x, clip_rect.size.x, dst_x, dst_width);
549 // Clip y-coordinates
550 clip_coord(src_height, src_y, clip_rect.origin.y, clip_rect.size.y, dst_y, dst_height);
551
552 DBG_LOG(3,0,("bitblt (%u,%u) (%u,%u,%u,%u)\n", src_x, src_y, dst_x, dst_y, dst_width, dst_height));
553 if (dst_width <= 0 || dst_height <= 0)
554 return false;
555
556 unsigned dst_rounded_width = m_conf[CONF_WPL] * WS;
557
558 // p1_pix & p2_pix point to a uniquely identified pixel in video RAM
559 unsigned p1_pix = unsigned(src_base_addr) * WS + src_x + src_width * src_y;
560 unsigned p2_pix = unsigned(use_m_org ? m_org : m_sad) * WS + dst_x + dst_rounded_width * dst_y;
561
562 DBG_LOG(3,0,("bitblt p1_pix=%u p2_pix=%u\n", p1_pix, p2_pix));
563 uint16_t rop_masks[4];
564 get_rop_masks(rop, rop_masks);
565
566 if (p1_pix < p2_pix)
567 {
568 // Move block going up (decrease y)
569 p1_pix += dst_height * src_width + dst_width - 1;
570 p2_pix += dst_height * dst_rounded_width + dst_width - 1;
571 DBG_LOG(3,0,("bitblt rev p1_pix=%u p2_pix=%u\n", p1_pix, p2_pix));
572 while (dst_height--)
573 {
574 p1_pix -= src_width;
575 p2_pix -= dst_rounded_width;
576 rowbltneg(p1_pix, p2_pix, dst_width, rop_masks);
577 }
578 }
579 else
580 {
581 // Move block going down (increase y)
582 DBG_LOG(3,0,("bitblt fwd\n"));
583 while (dst_height--)
584 {
585 rowbltpos(p1_pix, p2_pix, dst_width, rop_masks);
586 p1_pix += src_width;
587 p2_pix += dst_rounded_width;
588 }
589 }
590
591 return true;
592 }
593
rowbltpos(unsigned p1_pix,unsigned p2_pix,int width,const uint16_t masks[])594 void hp1ll3_device::rowbltpos(unsigned p1_pix, unsigned p2_pix, int width, const uint16_t masks[])
595 {
596 // p1_pix and p2_pix point to the leftmost pixel of the row
597 while (width > 0)
598 {
599 unsigned p1_word = p1_pix / WS;
600 unsigned p1_bit = p1_pix % WS;
601 // Get src pixels and align to MSB
602 uint16_t new_pix = rd_video(p1_word) << p1_bit;
603 if (p1_bit)
604 new_pix |= rd_video(p1_word + 1) >> (WS - p1_bit);
605
606 unsigned p2_word = p2_pix / WS;
607 unsigned p2_bit = p2_pix % WS;
608 uint16_t old_pix = rd_video(p2_word);
609 unsigned w = p2_bit + width;
610 uint16_t glob_mask = ~0;
611 if (p2_bit)
612 {
613 // Left end
614 new_pix >>= p2_bit;
615 glob_mask >>= p2_bit;
616 }
617 if (w < WS)
618 {
619 // Right end
620 glob_mask &= ~0 << (WS - w);
621 }
622 rmw_rop(p2_word, new_pix, glob_mask, masks);
623 DBG_LOG(3,0,("rowbltpos %04x %04x %04x>%04x %u %u %u\n", old_pix, new_pix, glob_mask, rd_video(p2_word), p1_pix, p2_pix, width));
624 w = WS - p2_bit;
625 p1_pix += w;
626 p2_pix += w;
627 width -= w;
628 }
629 }
630
rowbltneg(unsigned p1_pix,unsigned p2_pix,int width,const uint16_t masks[])631 void hp1ll3_device::rowbltneg(unsigned p1_pix, unsigned p2_pix, int width, const uint16_t masks[])
632 {
633 // p1_pix and p2_pix point to the rightmost pixel of the row
634 while (width > 0)
635 {
636 unsigned p1_word = p1_pix / WS;
637 unsigned p1_bit = p1_pix % WS;
638 // Get src pixels and align to LSB
639 uint16_t new_pix = rd_video(p1_word) >> (WS - 1 - p1_bit);
640 if (p1_bit != (WS - 1))
641 new_pix |= rd_video(p1_word - 1) << (p1_bit + 1);
642
643 unsigned p2_word = p2_pix / WS;
644 unsigned p2_bit = p2_pix % WS;
645 uint16_t old_pix = rd_video(p2_word);
646 int w = p2_bit - width;
647 uint16_t glob_mask = ~0;
648 if (p2_bit != (WS - 1))
649 {
650 // Right end
651 new_pix <<= (WS - 1 - p2_bit);
652 glob_mask <<= (WS - 1 - p2_bit);
653 }
654 if (w >= 0)
655 {
656 // Left end
657 uint16_t tmp = ~0;
658 glob_mask &= tmp >> (w + 1);
659 }
660 rmw_rop(p2_word, new_pix, glob_mask, masks);
661 DBG_LOG(3,0,("rowbltneg %04x %04x %04x>%04x %u %u %u\n", old_pix, new_pix, glob_mask, rd_video(p2_word), p1_pix, p2_pix, width));
662 w = p2_bit + 1;
663 p1_pix -= w;
664 p2_pix -= w;
665 width -= w;
666 }
667 }
668
fill(const Rectangle & fill_rect,uint16_t pattern_no)669 void hp1ll3_device::fill(const Rectangle &fill_rect, uint16_t pattern_no)
670 {
671 uint16_t pattern_addr = get_pattern_addr(pattern_no);
672
673 Point src_p{ 0, 0 };
674 // Align to 16x16 tiles in absolute coordinates
675 uint16_t start_x = fill_rect.origin.x & ~0xf;
676 Rectangle dst_rect{{ start_x, uint16_t(fill_rect.origin.y & ~0xf) }, { 16, 16 }};
677
678 // Iterate over vertical span
679 while (bitblt(pattern_addr, 16, 16, src_p, fill_rect, dst_rect, m_rr))
680 {
681 // Iterate over horizontal span
682 do {
683 dst_rect.origin.x += 16;
684 } while (bitblt(pattern_addr, 16, 16, src_p, fill_rect, dst_rect, m_rr));
685 dst_rect.origin.x = start_x;
686 dst_rect.origin.y += 16;
687 }
688 }
689
get_pattern_addr(uint16_t pattern_no) const690 uint16_t hp1ll3_device::get_pattern_addr(uint16_t pattern_no) const
691 {
692 return m_dad + pattern_no * 16;
693 }
694
draw_cursor()695 void hp1ll3_device::draw_cursor()
696 {
697 if (m_enable_cursor)
698 {
699 Rectangle dst{{ uint16_t(m_cursor_x + m_cursor_offset), m_cursor_y }, { 16, 16 }};
700 bitblt(get_pattern_addr(m_cursor_pattern), 16, 16, Point{ 0, 0 }, get_screen(), dst, RR_XOR, false);
701 }
702 }
703
draw_sprite()704 void hp1ll3_device::draw_sprite()
705 {
706 if (m_enable_sprite)
707 {
708 Rectangle dst{{ m_sprite_x, m_sprite_y }, { 16, 16 }};
709 bitblt(get_pattern_addr(m_sprite_pattern), 16, 16, Point{ 0, 0 }, get_screen(), dst, RR_XOR, false);
710 }
711 }
712
draw_cursor_sprite()713 void hp1ll3_device::draw_cursor_sprite()
714 {
715 draw_cursor();
716 draw_sprite();
717 }
718
set_pen_pos(Point p)719 void hp1ll3_device::set_pen_pos(Point p)
720 {
721 if (p.x != m_cursor_x || p.y != m_cursor_y)
722 {
723 draw_cursor();
724 m_cursor_x = p.x;
725 m_cursor_y = p.y;
726 draw_cursor();
727 }
728 }
729
set_sprite_pos(Point p)730 void hp1ll3_device::set_sprite_pos(Point p)
731 {
732 if (p.x != m_sprite_x || p.y != m_sprite_y)
733 {
734 draw_sprite();
735 m_sprite_x = p.x;
736 m_sprite_y = p.y;
737 draw_sprite();
738 }
739 }
740
disable_cursor()741 void hp1ll3_device::disable_cursor()
742 {
743 draw_cursor();
744 m_enable_cursor = false;
745 }
746
disable_sprite()747 void hp1ll3_device::disable_sprite()
748 {
749 draw_sprite();
750 m_enable_sprite = false;
751 }
752
get_window() const753 hp1ll3_device::Rectangle hp1ll3_device::get_window() const
754 {
755 return Rectangle{{ m_window.org_x, m_window.org_y }, { m_window.width, m_window.height }};
756 }
757
get_screen() const758 hp1ll3_device::Rectangle hp1ll3_device::get_screen() const
759 {
760 return Rectangle{{ 0, 0 }, { uint16_t(m_horiz_pix_total), uint16_t(m_vert_pix_total) }};
761 }
762
get_hv_timing(bool vertical,unsigned & total,unsigned & active) const763 void hp1ll3_device::get_hv_timing(bool vertical, unsigned& total, unsigned& active) const
764 {
765 unsigned idx = vertical ? CONF_VER_0 : CONF_HOR_0;
766 // Look for active part (zone 1) & compute total time
767 total = 0;
768 active = 0;
769 for (unsigned i = 0; i < 4; ++i)
770 {
771 unsigned cnt = (m_conf[idx + i] & HV_CNT_MASK) + 1;
772 total += cnt;
773 if ((m_conf[idx + i] & HV_ZONE_MASK) == HV_ZONE_1)
774 {
775 active = cnt;
776 // It seems that if a zone 2 comes immediately before a zone 1, it's
777 // counted in the active part
778 unsigned idx_1 = (i + 3) % 4 + idx;
779 if ((m_conf[idx_1] & HV_ZONE_MASK) == HV_ZONE_2)
780 active += (m_conf[idx_1] & HV_CNT_MASK) + 1;
781 }
782 }
783 DBG_LOG(2, 0, ("H/V=%d total=%u active=%u\n", vertical, total, active));
784 }
785
apply_conf()786 void hp1ll3_device::apply_conf()
787 {
788 unsigned h_total;
789 unsigned h_active;
790 unsigned v_total;
791 unsigned v_active;
792
793 get_hv_timing(false, h_total, h_active);
794 get_hv_timing(true, v_total, v_active);
795
796 // Dot clock is 4 times the GPU clock, H timings are in units of half the GPU clock
797 // I suspect that the GPU has a kind of "double clock" mode that is enabled by option bits in the last conf word.
798 // Without clock doubling, the HP-9808A video has a strange frame rate (~32Hz).
799 h_total *= 8;
800 h_active *= 8;
801
802 attotime frame_rate{clocks_to_attotime(h_total * v_total)};
803 screen().configure(h_total, v_total, rectangle{0, int32_t(h_active - 1), 0, int32_t(v_active - 1)}, frame_rate.attoseconds() / 4);
804
805 m_horiz_pix_total = h_active;
806 m_vert_pix_total = v_active;
807 }
808
screen_update(screen_device & screen,bitmap_ind16 & bitmap,const rectangle & cliprect)809 uint32_t hp1ll3_device::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
810 {
811 if (!m_enable_video)
812 {
813 bitmap.fill(0);
814 return 0;
815 }
816
817 for (int y = 0; y < m_vert_pix_total; y++)
818 {
819 int const offset = m_sad + y*m_conf[CONF_WPL];
820 uint16_t *p = &m_bitmap.pix(y);
821
822 for (int x = offset; x < offset + m_horiz_pix_total / 16; x++)
823 {
824 uint16_t const gfx = m_videoram[x];
825
826 for (int i = 15; i >= 0; i--)
827 {
828 *p++ = BIT(gfx, i);
829 }
830 }
831 }
832
833 copybitmap(bitmap, m_bitmap, 0, 0, 0, 0, cliprect);
834
835 return 0;
836 }
837
838 //-------------------------------------------------
839 // read - register read
840 //-------------------------------------------------
841
842 /*
843 * offset 0: CSR
844 *
845 * bit 0 gpu is busy
846 * bit 1 data is ready
847 * bit 3 vert blank time
848 * bit 7 out of window
849 *
850 * offset 2: data
851 */
852
read(offs_t offset)853 uint8_t hp1ll3_device::read(offs_t offset)
854 {
855 uint8_t data = 0;
856
857 if (offset == 0)
858 {
859 data = m_busy ? 1 : 0;
860 data |= 2;
861 data |= (screen().vblank() ? 8 : 0);
862 }
863 else
864 {
865 if (!(m_rd_ptr & 1))
866 {
867 switch (m_command)
868 {
869 case RDMEM:
870 m_io_word = rd_video(m_memory_ptr++);
871 break;
872
873 case RDWIN:
874 {
875 uint16_t addr = get_pix_addr(m_rw_win_x, m_rw_win_y);
876 unsigned bit = m_rw_win_x % WS;
877 int width = m_window.width + m_window.org_x - m_rw_win_x;
878 if (width <= 0)
879 {
880 m_command = NOP;
881 }
882 else
883 {
884 unsigned discarded_bits = 0;
885 // Insert "m_rw_win_off" dummy bits in the first word of each line
886 if (m_rw_win_x == m_window.org_x)
887 discarded_bits = m_rw_win_off;
888 unsigned w = std::min(WS - discarded_bits, unsigned(width));
889
890 m_io_word = (rd_video(addr) << bit) >> discarded_bits;
891 if ((bit + w) > WS)
892 m_io_word |= rd_video(addr + 1) >> (WS - bit + discarded_bits);
893
894 m_io_word &= ~0 << (WS - w - discarded_bits);
895 m_rw_win_x += w;
896 if (m_rw_win_x >= (m_window.width + m_window.org_x))
897 {
898 m_rw_win_x = m_window.org_x;
899 m_rw_win_y++;
900 if (m_rw_win_y >= (m_window.height + m_window.org_y))
901 m_command = NOP;
902 }
903 }
904 }
905 break;
906
907 case RDP:
908 if (m_rd_ptr == 0)
909 m_io_word = m_cursor_x;
910 else if (m_rd_ptr == 2)
911 m_io_word = m_cursor_y;
912 break;
913
914 case RDSP:
915 if (m_rd_ptr == 0)
916 m_io_word = m_sprite_x;
917 else if (m_rd_ptr == 2)
918 m_io_word = m_sprite_y;
919 break;
920
921 case RDWINPARM:
922 switch (m_rd_ptr)
923 {
924 case 0:
925 m_io_word = m_window.org_x;
926 break;
927 case 2:
928 m_io_word = m_window.org_y;
929 break;
930 case 4:
931 m_io_word = m_window.width;
932 break;
933 case 6:
934 m_io_word = m_window.height;
935 break;
936 default:
937 break;
938 }
939 break;
940
941 case ID:
942 /*
943 * 'diagb' ROM accepts either of these ID values
944 * 0x3003, 0x4004, 0x5005, 0x6006
945 */
946 m_io_word = 0x4004;
947 break;
948 }
949 data = uint8_t(m_io_word >> 8);
950 } else
951 {
952 data = m_io_word & 0xff;
953 }
954 m_rd_ptr++;
955 }
956
957 DBG_LOG(1,"HPGPU", ("R @ %d == %02x\n", offset, data));
958
959 return data;
960 }
961
962
963 //-------------------------------------------------
964 // write - register write
965 //-------------------------------------------------
966
write(offs_t offset,uint8_t data)967 void hp1ll3_device::write(offs_t offset, uint8_t data)
968 {
969 DBG_LOG(1,"HPGPU", ("W @ %d <- %02x\n", offset, data));
970
971 if (offset == 0)
972 {
973 command(data);
974 }
975 else
976 {
977 if (!(m_wr_ptr & 1))
978 {
979 m_io_word = uint16_t(data) << 8;
980 }
981 else
982 {
983 m_io_word |= data;
984 switch (m_command)
985 {
986 case CONF:
987 m_conf[m_wr_ptr >> 1] = m_io_word;
988 DBG_LOG(1,"HPGPU",("CONF data word %d received: %04X\n", m_wr_ptr >> 1, m_io_word));
989 if ((m_wr_ptr >> 1) == 10)
990 {
991 apply_conf();
992 m_wr_ptr = -1;
993 m_command = NOP;
994 }
995 break;
996
997 case WRMEM:
998 wr_video(m_memory_ptr++, m_io_word);
999 break;
1000
1001 case WRWIN:
1002 {
1003 uint16_t rop_masks[4];
1004 get_rop_masks(m_rr, rop_masks);
1005
1006 uint16_t addr = get_pix_addr(m_rw_win_x, m_rw_win_y);
1007 uint16_t glob_mask = ~0;
1008 unsigned bit = m_rw_win_x % WS;
1009 int width = m_window.width + m_window.org_x - m_rw_win_x;
1010 if (width <= 0)
1011 m_command = NOP;
1012 else
1013 {
1014 unsigned discarded_bits = 0;
1015 // Remove "m_rw_win_off" bits from the first word of each line
1016 if (m_rw_win_x == m_window.org_x)
1017 discarded_bits = m_rw_win_off;
1018 unsigned w = std::min(WS - discarded_bits, unsigned(width));
1019
1020 glob_mask >>= bit;
1021 if ((bit + w) < WS)
1022 glob_mask &= ~0 << (WS - (bit + w));
1023
1024 rmw_rop(addr, (m_io_word << discarded_bits) >> bit, glob_mask, rop_masks);
1025 DBG_LOG(3, 0, ("WRWIN (%u,%u) %d %04x %04x->%04x\n", m_rw_win_x, m_rw_win_y, width, (m_io_word << discarded_bits) >> bit, glob_mask, rd_video(addr)));
1026 if ((bit + w) > WS)
1027 {
1028 unsigned pad = WS - bit + discarded_bits;
1029 glob_mask = ~0 << (2 * WS - bit - w);
1030 rmw_rop(addr + 1, uint16_t(m_io_word << pad), glob_mask, rop_masks);
1031 DBG_LOG(3, 0, ("WRWIN %u %04x %04x->%04x\n", pad, m_io_word << pad, glob_mask, rd_video(addr + 1)));
1032 }
1033 m_rw_win_x += w;
1034 if (m_rw_win_x >= (m_window.width + m_window.org_x))
1035 {
1036 m_rw_win_x = m_window.org_x;
1037 m_rw_win_y++;
1038 if (m_rw_win_y >= (m_window.height + m_window.org_y))
1039 m_command = NOP;
1040 }
1041 }
1042 }
1043 break;
1044
1045 default:
1046 if (m_wr_ptr <= 4)
1047 m_input[m_wr_ptr / 2] = m_io_word;
1048 DBG_LOG(2,"HPGPU",("wrote %02x at %d, input buffer is %04X %04X\n", data, m_wr_ptr, m_input[0], m_input[1]));
1049 }
1050 }
1051 m_wr_ptr++;
1052 }
1053 }
1054
1055
command(int command)1056 void hp1ll3_device::command(int command)
1057 {
1058 switch (command)
1059 {
1060
1061 // type 0 commands -- no data
1062
1063 case NOP:
1064 DBG_LOG(2,"HPGPU",("command: NOP [%d, 0x%x]\n", command, command));
1065 switch (m_command)
1066 {
1067 case RDMEM:
1068 DBG_LOG(1,"HPGPU",("RDMEM of %d words at %04X complete\n", m_memory_ptr - m_input[0], m_input[0]));
1069 break;
1070
1071 case WRMEM:
1072 DBG_LOG(1,"HPGPU",("WRMEM of %d words to %04X complete\n", m_memory_ptr - m_input[0], m_input[0]));
1073 break;
1074 }
1075 break;
1076
1077 case DISVID:
1078 DBG_LOG(2,"HPGPU",("command: DISVID [%d, 0x%x]\n", command, command));
1079 m_enable_video = false;
1080 break;
1081
1082 case ENVID:
1083 DBG_LOG(2,"HPGPU",("command: ENVID [%d, 0x%x]\n", command, command));
1084 DBG_LOG(1,"HPGPU",("enable video; SAD %04x FAD %04x DAD %04x ORG %04x UDL %04x RR %04x\n",
1085 m_sad, m_fad, m_dad, m_org, m_udl, m_rr));
1086 m_enable_video = true;
1087 break;
1088
1089 case DISSP:
1090 DBG_LOG(2,"HPGPU",("command: DISSP [%d, 0x%x]\n", command, command));
1091 disable_sprite();
1092 break;
1093
1094 case ENSP:
1095 DBG_LOG(2,"HPGPU",("command: ENSP [%d, 0x%x]\n", command, command));
1096 draw_sprite();
1097 m_sprite_pattern = m_input[0];
1098 m_enable_sprite = true;
1099 draw_sprite();
1100 DBG_LOG(1,"HPGPU",("enable sprite; cursor %d,%d sprite %d,%d\n", m_cursor_x, m_cursor_y, m_sprite_x, m_sprite_y));
1101 break;
1102
1103 case DISCURS:
1104 DBG_LOG(2,"HPGPU",("command: DISCURS [%d, 0x%x]\n", command, command));
1105 disable_cursor();
1106 break;
1107
1108 // type 1 commands -- 1 word of data expected in the buffer
1109
1110 // start of screen memory
1111 case WRSAD:
1112 DBG_LOG(2,"HPGPU",("command: WRSAD [%d, 0x%x] (0x%04x)\n", command, command, m_input[0]));
1113 m_sad = m_input[0];
1114 break;
1115
1116 // start of font memory
1117 case WRFAD:
1118 DBG_LOG(2,"HPGPU",("command: WRFAD [%d, 0x%x] (0x%04x)\n", command, command, m_input[0]));
1119 m_fad = m_input[0];
1120 break;
1121
1122 // ?? used by diagnostic ROM
1123 case 6:
1124 DBG_LOG(1,"HPGPU",("command: 6\n"));
1125 break;
1126
1127 // start of data area
1128 case WRDAD:
1129 DBG_LOG(2,"HPGPU",("command: WRDAD [%d, 0x%x] (0x%04x)\n", command, command, m_input[0]));
1130 m_dad = m_input[0];
1131 break;
1132
1133 // ??
1134 case WRORG:
1135 DBG_LOG(2,"HPGPU",("command: WRORG [%d, 0x%x] (0x%04x)\n", command, command, m_input[0]));
1136 m_org = m_input[0];
1137 break;
1138
1139 // set replacement rule (raster op)
1140 case WRRR:
1141 DBG_LOG(2,"HPGPU",("command: WRRR [%d, 0x%x] (0x%04x)\n", command, command, m_input[0]));
1142 m_rr = m_input[0];
1143 break;
1144
1145 // set user-defined line pattern
1146 case WRUDL:
1147 DBG_LOG(2,"HPGPU",("command: WRUDL [%d, 0x%x] (0x%04x)\n", command, command, m_input[0]));
1148 m_udl = m_input[0];
1149 break;
1150
1151 // area fill
1152 case FILL:
1153 DBG_LOG(2,"HPGPU",("command: FILL [%d, 0x%x] (0x%04x) from (%d,%d) size (%d,%d) rop 0x%x\n",
1154 command, command, m_input[0],
1155 m_window.org_x, m_window.org_y, m_window.width, m_window.height, m_rr));
1156 draw_cursor_sprite();
1157 fill(get_window(), m_input[0]);
1158 draw_cursor_sprite();
1159 break;
1160
1161 case LABEL:
1162 {
1163 DBG_LOG(2,"HPGPU",("command: LABEL [%d, 0x%x] (0x%04x, '%c') at %d,%d\n", command, command, m_input[0],
1164 (m_input[0]<32||m_input[0]>127) ? ' ' : m_input[0], m_cursor_x, m_cursor_y));
1165 int const c = m_input[0] & 255;
1166 int const w = (c & 1) ?
1167 (rd_video(m_fad + 2 + (c>>1)) & 255) :
1168 (rd_video(m_fad + 2 + (c>>1)) >> 8);
1169 label(c, w);
1170 }
1171 break;
1172
1173 // Write window
1174 case WRWIN:
1175 // Read window
1176 case RDWIN:
1177 DBG_LOG(2,"HPGPU",("command: %s [%d, 0x%x] offset=%u size(%d,%d)\n",
1178 command == RDWIN ? "RDWIN":"WRWIN", command, command, m_input[0], m_window.width, m_window.height));
1179 m_rw_win_x = m_window.org_x;
1180 m_rw_win_y = m_window.org_y;
1181 m_rw_win_off = m_input[0] % WS;
1182 disable_cursor();
1183 disable_sprite();
1184 break;
1185
1186 // type 2 commands -- 2 words of data expected in the buffer
1187
1188 case DRAWPX:
1189 {
1190 DBG_LOG(2,"HPGPU",("command: DRAWPX [%d, 0x%x] (%d, %d)\n", command, command, m_input[0], m_input[1]));
1191 uint16_t rop_masks[4];
1192 get_rop_masks(m_rr, rop_masks);
1193 point(m_input[0], m_input[1], true, rop_masks);
1194 }
1195 break;
1196
1197 // set window size
1198 case WRWINSIZ:
1199 DBG_LOG(2,"HPGPU",("command: WRWINSIZ [%d, 0x%x] (%d, %d)\n", command, command, m_input[0], m_input[1]));
1200 m_window.width = m_input[0];
1201 m_window.height = m_input[1];
1202 break;
1203
1204 // set window origin
1205 case WRWINORG:
1206 DBG_LOG(2,"HPGPU",("command: WRWINORG [%d, 0x%x] (%d, %d)\n", command, command, m_input[0], m_input[1]));
1207 m_window.org_x = m_input[0];
1208 m_window.org_y = m_input[1];
1209 break;
1210
1211 // Copy
1212 case COPY:
1213 {
1214 DBG_LOG(2,"HPGPU",("command: COPY [%d, 0x%x] (%d, %d)\n", command, command, m_input[0], m_input[1]));
1215 draw_cursor_sprite();
1216 unsigned rounded_width = m_conf[CONF_WPL] * WS;
1217 Rectangle dst_rect{{ m_input[0], m_input[1] }, { m_window.width, m_window.height }};
1218 // COPY apparently doesn't clip anything
1219 bitblt(m_org, rounded_width, 0xffff, Point{ m_window.org_x, m_window.org_y },
1220 Rectangle{{ 0, 0 }, { uint16_t(rounded_width), 0xffff }}, dst_rect, m_rr);
1221 draw_cursor_sprite();
1222 }
1223 break;
1224
1225 // move pointer absolute
1226 case MOVEP:
1227 DBG_LOG(2,"HPGPU",("command: MOVEP [%d, 0x%x] (%d, %d)\n", command, command, m_input[0], m_input[1]));
1228 set_pen_pos(Point{ m_input[0], m_input[1] });
1229 m_saved_x = m_cursor_x;
1230 break;
1231
1232 // move pointer incremental
1233 case IMOVEP:
1234 DBG_LOG(2,"HPGPU",("command: IMOVEP [%d, 0x%x] (%d, %d)\n", command, command, m_input[0], m_input[1]));
1235 set_pen_pos(Point{ uint16_t(m_cursor_x + m_input[0]), uint16_t(m_cursor_y + m_input[1]) });
1236 m_saved_x = m_cursor_x;
1237 break;
1238
1239 // Scroll UP
1240 case SCROLUP:
1241 {
1242 DBG_LOG(2,"HPGPU",("command: SCROLUP [%d, 0x%x] (%d, %d)\n", command, command, m_input[0], m_input[1]));
1243 draw_cursor_sprite();
1244 Point src_p{ m_window.org_x, uint16_t(m_window.org_y + m_input[1]) };
1245 Rectangle dst_rect = get_window();
1246 dst_rect.size.y -= m_input[1];
1247 unsigned rounded_width = m_conf[CONF_WPL] * WS;
1248 bitblt(m_org, rounded_width, m_vert_pix_total, src_p, get_window(), dst_rect, m_rr);
1249 dst_rect.origin.y += dst_rect.size.y;
1250 dst_rect.size.y = m_input[1];
1251 fill(dst_rect, m_input[0]);
1252 draw_cursor_sprite();
1253 }
1254 break;
1255
1256 // Scroll DOWN
1257 case SCROLDN:
1258 {
1259 DBG_LOG(2,"HPGPU",("command: SCROLDN [%d, 0x%x] (%d, %d)\n", command, command, m_input[0], m_input[1]));
1260 draw_cursor_sprite();
1261 Point src_p{ m_window.org_x, m_window.org_y };
1262 Rectangle dst_rect = get_window();
1263 dst_rect.origin.y += m_input[1];
1264 dst_rect.size.y -= m_input[1];
1265 unsigned rounded_width = m_conf[CONF_WPL] * WS;
1266 bitblt(m_org, rounded_width, m_vert_pix_total, src_p, get_window(), dst_rect, m_rr);
1267 dst_rect.origin.y = m_window.org_y;
1268 dst_rect.size.y = m_input[1];
1269 fill(dst_rect, m_input[0]);
1270 draw_cursor_sprite();
1271 }
1272 break;
1273
1274 case ENCURS:
1275 DBG_LOG(2,"HPGPU",("command: ENCURS [%d, 0x%x]\n", command, command));
1276 draw_cursor();
1277 m_cursor_pattern = m_input[0];
1278 m_cursor_offset = m_input[1];
1279 m_enable_cursor = true;
1280 draw_cursor();
1281 DBG_LOG(1,"HPGPU",("enable cursor; cursor %d,%d sprite %d,%d\n", m_cursor_x, m_cursor_y, m_sprite_x, m_sprite_y));
1282 break;
1283
1284 // carriage return, line feed
1285 case CRLFx:
1286 {
1287 DBG_LOG(2,"HPGPU",("command: CRLF [%d, 0x%x]\n", command, command));
1288
1289 uint16_t font_data;
1290 uint16_t font_height;
1291 get_font(font_data, font_height);
1292
1293 set_pen_pos(Point{ m_saved_x, uint16_t(m_cursor_y + font_height) });
1294 }
1295 break;
1296
1297 // move sprite absolute
1298 case MOVESP:
1299 DBG_LOG(2,"HPGPU",("command: MOVESP [%d, 0x%x] (%d, %d)\n", command, command, m_input[0], m_input[1]));
1300 set_sprite_pos(Point{ m_input[0], m_input[1] });
1301 break;
1302
1303 // draw to ...
1304 case DRAWP:
1305 DBG_LOG(2,"HPGPU",("command: DRAWP [%d, 0x%x] (%d, %d) to (%d, %d)\n",
1306 command, command, m_cursor_x, m_cursor_y, m_input[0], m_input[1]));
1307 line(m_cursor_x, m_cursor_y, m_input[0], m_input[1]);
1308 set_pen_pos(Point{ m_input[0], m_input[1] });
1309 break;
1310
1311 // type 3 command -- CONF -- accept configuration parameters (11 words)
1312
1313 case CONF:
1314 break;
1315
1316 // type 4 commands -- like type 1 plus data is read or written after command, terminated by NOP
1317
1318 case RDMEM:
1319 case WRMEM:
1320 DBG_LOG(2,"HPGPU",("command: %s [%d, 0x%x] (0x%04x)\n",
1321 command == RDMEM?"RDMEM":"WRMEM", command, command, m_input[0]));
1322 m_memory_ptr = m_input[0]; // memory is word-addressable
1323 disable_cursor();
1324 disable_sprite();
1325 break;
1326
1327 // type 3 read commands
1328 case RDP:
1329 DBG_LOG(2,"HPGPU",("command: RDP [%d, 0x%x]\n", command, command));
1330 break;
1331
1332 case RDSP:
1333 DBG_LOG(2,"HPGPU",("command: RDSP [%d, 0x%x]\n", command, command));
1334 break;
1335
1336 case RDWINPARM:
1337 DBG_LOG(2,"HPGPU",("command: RDWINPARM [%d, 0x%x]\n", command, command));
1338 break;
1339
1340 case ID:
1341 DBG_LOG(2,"HPGPU",("command: ID [%d, 0x%x]\n", command, command));
1342 break;
1343
1344 default:
1345 DBG_LOG(1,"HPGPU",("command: UNKNOWN [%d, 0x%x]\n", command, command));
1346 break;
1347 }
1348
1349 m_rd_ptr = 0;
1350 m_wr_ptr = 0;
1351 m_command = command;
1352 }
1353