/[gxemul]/upstream/0.3.1/devices/dev_bt459.c
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /upstream/0.3.1/devices/dev_bt459.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3 - (show annotations)
Mon Oct 8 16:17:52 2007 UTC (16 years, 7 months ago) by dpavlin
File MIME type: text/plain
File size: 17051 byte(s)
0.3.1
1 /*
2 * Copyright (C) 2003-2005 Anders Gavare. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. The name of the author may not be used to endorse or promote products
13 * derived from this software without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 *
28 * $Id: dev_bt459.c,v 1.59 2005/02/22 12:15:29 debug Exp $
29 *
30 * Brooktree 459 vdac, used by TURBOchannel graphics cards.
31 */
32
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36
37 #include "cpu.h"
38 #include "devices.h"
39 #include "machine.h"
40 #include "memory.h"
41 #include "misc.h"
42 #include "x11.h"
43
44 #include "bt459.h"
45
46
47 #ifdef WITH_X11
48 #include <X11/Xlib.h>
49 #include <X11/Xutil.h>
50 #endif
51
52 extern int quiet_mode;
53
54
55 /* #define BT459_DEBUG */
56 /* #define WITH_CURSOR_DEBUG */
57 #define BT459_TICK_SHIFT 14
58
59 struct bt459_data {
60 uint32_t bt459_reg[DEV_BT459_NREGS];
61
62 unsigned char cur_addr_hi;
63 unsigned char cur_addr_lo;
64
65 int planes;
66 int type;
67
68 int irq_nr;
69 int interrupts_enable;
70 int interrupt_time;
71 int interrupt_time_reset_value;
72
73 int cursor_x_add;
74 int cursor_y_add;
75
76 int need_to_redraw_whole_screen;
77
78 int need_to_update_cursor_shape;
79 int cursor_on;
80 int cursor_x;
81 int cursor_y;
82 int cursor_xsize;
83 int cursor_ysize;
84
85 int palette_sub_offset; /* 0, 1, or 2 */
86
87 struct vfb_data *vfb_data;
88
89 /*
90 * There is one pointer to the framebuffer's RGB palette,
91 * and then a local copy of the palette. 256 * 3 bytes (r,g,b).
92 * The reason for this is that when we need to blank the screen
93 * (ie video_on = 0), we can set the framebuffer's palette to all
94 * zeroes, but keep our own copy intact, to be reused later again
95 * when the screen is unblanked.
96 */
97 int video_on;
98 unsigned char *rgb_palette; /* 256 * 3 (r,g,b) */
99 unsigned char local_rgb_palette[256 * 3];
100 };
101
102
103 /*
104 * bt459_state():
105 */
106 int bt459_state(struct cpu *cpu, struct memory *mem, void *extra, int wf,
107 int nr, int *type, char **namep, void **data, size_t *len)
108 {
109 struct bt459_data *d = (struct bt459_data *) extra;
110
111 switch (nr) {
112 case 0: if (wf) {
113 memcpy(&d->cursor_on, *data, *len);
114 } else {
115 (*namep) = "cursor_on";
116 (*type) = DEVICE_STATE_TYPE_INT;
117 *len = sizeof(d->cursor_on);
118 *data = &d->cursor_on;
119 }
120 break;
121 case 1: if (wf) {
122 memcpy(&d->cursor_x, *data, *len);
123 } else {
124 (*namep) = "cursor_x";
125 (*type) = DEVICE_STATE_TYPE_INT;
126 *len = sizeof(d->cursor_x);
127 *data = &d->cursor_x;
128 }
129 break;
130 case 2: if (wf) {
131 memcpy(&d->cursor_y, *data, *len);
132 } else {
133 (*namep) = "cursor_y";
134 (*type) = DEVICE_STATE_TYPE_INT;
135 *len = sizeof(d->cursor_y);
136 *data = &d->cursor_y;
137 }
138 break;
139 case 3: if (wf) {
140 memcpy(&d->cursor_xsize, *data, *len);
141 } else {
142 (*namep) = "cursor_xsize";
143 (*type) = DEVICE_STATE_TYPE_INT;
144 *len = sizeof(d->cursor_xsize);
145 *data = &d->cursor_xsize;
146 }
147 break;
148 case 4: if (wf) {
149 memcpy(&d->cursor_ysize, *data, *len);
150 } else {
151 (*namep) = "cursor_ysize";
152 (*type) = DEVICE_STATE_TYPE_INT;
153 *len = sizeof(d->cursor_ysize);
154 *data = &d->cursor_ysize;
155 }
156 break;
157 default:
158 return 0;
159 }
160
161 return 1;
162 }
163
164
165 /*
166 * bt459_update_X_cursor():
167 *
168 * This routine takes the color values in the cursor RAM area, and put them
169 * in the framebuffer window's cursor_pixels.
170 *
171 * d->cursor_xsize and ysize are also updated.
172 */
173 static void bt459_update_X_cursor(struct cpu *cpu, struct bt459_data *d)
174 {
175 int i, x,y, xmax=0, ymax=0;
176 int bw_only = 1;
177
178 /* First, let's calculate the size of the cursor: */
179 for (y=0; y<64; y++)
180 for (x=0; x<64; x+=4) {
181 int reg = BT459_REG_CRAM_BASE + y*16 + x/4;
182 unsigned char data = d->bt459_reg[reg];
183
184 if (data)
185 ymax = y;
186
187 for (i=0; i<4; i++) {
188 int color = (data >> (6-2*i)) & 3;
189 if (color != 0)
190 xmax = x + i;
191 if (color != 0 && color != 3)
192 bw_only = 0;
193 }
194 }
195
196 d->cursor_xsize = xmax + 1;
197 d->cursor_ysize = ymax + 1;
198
199 /*
200 * The 'bw_only' hack is because it is nicer to have the b/w
201 * text cursor invert whatever it is standing on, not just overwrite
202 * it with a big white box.
203 *
204 * The following seems to work with NetBSD/OpenBSD/Ultrix/Sprite:
205 * 0 = transparent, 1 and 2 = use the color specified by
206 * BT459_REG_CCOLOR_2, 3 = reverse of color 1/2.
207 */
208
209 #ifdef WITH_X11
210 if (cpu->machine->use_x11 && d->vfb_data->fb_window != NULL) {
211 for (y=0; y<=ymax; y++) {
212 for (x=0; x<=xmax; x+=4) {
213 struct fb_window *win = d->vfb_data->fb_window;
214 int reg = BT459_REG_CRAM_BASE + y*16 + x/4;
215 unsigned char data = d->bt459_reg[reg];
216
217 for (i=0; i<4; i++) {
218 int color = (data >> (6-2*i)) & 3;
219 int pixelvalue;
220
221 if (bw_only) {
222 if (color)
223 pixelvalue =
224 CURSOR_COLOR_INVERT;
225 else
226 pixelvalue = 0;
227 } else {
228 pixelvalue =
229 CURSOR_COLOR_TRANSPARENT;
230 switch (color) {
231 case 1:
232 case 2: pixelvalue = (d->
233 bt459_reg[
234 BT459_REG_CCOLOR_2]
235 >> 4) & 0xf;
236 break;
237 case 3: pixelvalue = 15 -
238 ((d->bt459_reg[
239 BT459_REG_CCOLOR_2]
240 >> 4) & 0xf);
241 break;
242 }
243 }
244
245 win->cursor_pixels[y][x+i] =
246 pixelvalue;
247 #ifdef WITH_CURSOR_DEBUG
248 printf("%i", color);
249 #endif
250 }
251 }
252 #ifdef WITH_CURSOR_DEBUG
253 printf("\n");
254 #endif
255 }
256 #ifdef WITH_CURSOR_DEBUG
257 printf("color 1,2,3 = 0x%02x, 0x%02x, 0x%02x\n",
258 d->bt459_reg[BT459_REG_CCOLOR_1],
259 d->bt459_reg[BT459_REG_CCOLOR_2],
260 d->bt459_reg[BT459_REG_CCOLOR_3]);
261 printf("\n");
262 #endif
263 /*
264 * Make sure the cursor is redrawn, if it is on:
265 *
266 * How does this work? Well, 0 is off, and non-zero is on,
267 * but if the old and new differ, the cursor is redrawn.
268 * (Hopefully this will "never" overflow.)
269 */
270 if (d->cursor_on)
271 d->cursor_on ++;
272 }
273 #endif
274 }
275
276
277 /*
278 * bt459_update_cursor_position():
279 */
280 static void bt459_update_cursor_position(struct bt459_data *d,
281 int old_cursor_on)
282 {
283 int new_cursor_x = (d->bt459_reg[BT459_REG_CXLO] & 255) +
284 ((d->bt459_reg[BT459_REG_CXHI] & 255) << 8) - d->cursor_x_add;
285 int new_cursor_y = (d->bt459_reg[BT459_REG_CYLO] & 255) +
286 ((d->bt459_reg[BT459_REG_CYHI] & 255) << 8) - d->cursor_y_add;
287
288 if (new_cursor_x != d->cursor_x || new_cursor_y != d->cursor_y ||
289 d->cursor_on != old_cursor_on) {
290 int on;
291
292 d->cursor_x = new_cursor_x;
293 d->cursor_y = new_cursor_y;
294
295 if (!quiet_mode)
296 debug("[ bt459: cursor = %03i,%03i ]\n",
297 d->cursor_x, d->cursor_y);
298
299 on = d->cursor_on;
300 if (d->cursor_xsize == 0 || d->cursor_ysize == 0)
301 on = 0;
302
303 dev_fb_setcursor(d->vfb_data, d->cursor_x, d->cursor_y,
304 on, d->cursor_xsize, d->cursor_ysize);
305 }
306 }
307
308
309 /*
310 * dev_bt459_tick():
311 */
312 void dev_bt459_tick(struct cpu *cpu, void *extra)
313 {
314 struct bt459_data *d = extra;
315 int old_cursor_on = d->cursor_on;
316
317 if (d->need_to_update_cursor_shape) {
318 d->need_to_update_cursor_shape = 0;
319 bt459_update_X_cursor(cpu, d);
320 bt459_update_cursor_position(d, old_cursor_on);
321 }
322
323 if (d->need_to_redraw_whole_screen) {
324 d->vfb_data->update_x1 = 0;
325 d->vfb_data->update_x2 = d->vfb_data->xsize - 1;
326 d->vfb_data->update_y1 = 0;
327 d->vfb_data->update_y2 = d->vfb_data->ysize - 1;
328 d->need_to_redraw_whole_screen = 0;
329 }
330
331 /*
332 * Vertical retrace interrupts. (This hack is kind of ugly.)
333 * Once every 'interrupt_time_reset_value', the interrupt is
334 * asserted. It is acked either manually (by someone reading
335 * a normal BT459 register or the Interrupt ack register),
336 * or after another tick has passed. (This is to prevent
337 * lockups from unhandled interrupts.)
338 */
339 if (d->type != BT459_PX && d->interrupts_enable && d->irq_nr > 0) {
340 d->interrupt_time --;
341 if (d->interrupt_time < 0) {
342 d->interrupt_time = d->interrupt_time_reset_value;
343 cpu_interrupt(cpu, d->irq_nr);
344 } else
345 cpu_interrupt_ack(cpu, d->irq_nr);
346 }
347 }
348
349
350 /*
351 * dev_bt459_irq_access():
352 */
353 int dev_bt459_irq_access(struct cpu *cpu, struct memory *mem,
354 uint64_t relative_addr, unsigned char *data, size_t len,
355 int writeflag, void *extra)
356 {
357 struct bt459_data *d = (struct bt459_data *) extra;
358 uint64_t idata = 0, odata = 0;
359
360 idata = memory_readmax64(cpu, data, len);
361
362 #ifdef BT459_DEBUG
363 fatal("[ bt459: IRQ ack ]\n");
364 #endif
365
366 d->interrupts_enable = 1;
367 if (d->irq_nr > 0)
368 cpu_interrupt_ack(cpu, d->irq_nr);
369
370 if (writeflag == MEM_READ)
371 memory_writemax64(cpu, data, len, odata);
372
373 return 1;
374 }
375
376
377 /*
378 * dev_bt459_access():
379 */
380 int dev_bt459_access(struct cpu *cpu, struct memory *mem,
381 uint64_t relative_addr, unsigned char *data, size_t len,
382 int writeflag, void *extra)
383 {
384 struct bt459_data *d = (struct bt459_data *) extra;
385 uint64_t idata = 0, odata = 0;
386 int btaddr, old_cursor_on = d->cursor_on, modified;
387
388 idata = memory_readmax64(cpu, data, len);
389
390 #ifdef BT459_DEBUG
391 if (writeflag == MEM_WRITE)
392 fatal("[ bt459: write to addr 0x%02x: %08x ]\n",
393 (int)relative_addr, (int)idata);
394 #endif
395
396 /*
397 * Vertical retrace interrupts are acked either by
398 * accessing a normal BT459 register, or the irq register,
399 * or by simply "missing" it.
400 */
401 if (d->irq_nr > 0)
402 cpu_interrupt_ack(cpu, d->irq_nr);
403
404 /* ID register is read-only, should always be 0x4a or 0x4a4a4a: */
405 if (d->planes == 24)
406 d->bt459_reg[BT459_REG_ID] = 0x4a4a4a;
407 else {
408 /*
409 * TODO: Is it really 0x4a, or 0x4a0000?
410 * Ultrix panics with a "bad VDAC ID" message if 0x4a
411 * is returned.
412 */
413 d->bt459_reg[BT459_REG_ID] = 0x4a0000;
414 }
415
416 btaddr = ((d->cur_addr_hi << 8) + d->cur_addr_lo) % DEV_BT459_NREGS;
417
418 /* Read from/write to the bt459: */
419 switch (relative_addr) {
420 case 0x00: /* Low byte of address: */
421 if (writeflag == MEM_WRITE) {
422 if (!quiet_mode)
423 debug("[ bt459: write to Low Address Byte, "
424 "0x%02x ]\n", (int)idata);
425 d->cur_addr_lo = idata;
426 d->palette_sub_offset = 0;
427 } else {
428 odata = d->cur_addr_lo;
429 if (!quiet_mode)
430 debug("[ bt459: read from Low Address Byte: "
431 "0x%0x ]\n", (int)odata);
432 }
433 break;
434 case 0x04: /* High byte of address: */
435 if (writeflag == MEM_WRITE) {
436 if (!quiet_mode)
437 debug("[ bt459: write to High Address Byte, "
438 "0x%02x ]\n", (int)idata);
439 d->cur_addr_hi = idata;
440 d->palette_sub_offset = 0;
441 } else {
442 odata = d->cur_addr_hi;
443 if (!quiet_mode)
444 debug("[ bt459: read from High Address Byte: "
445 "0x%0x ]\n", (int)odata);
446 }
447 break;
448 case 0x08: /* Register access: */
449 if (writeflag == MEM_WRITE) {
450 if (!quiet_mode)
451 debug("[ bt459: write to BT459 register "
452 "0x%04x, value 0x%02x ]\n", btaddr,
453 (int)idata);
454 modified = (d->bt459_reg[btaddr] != idata);
455 d->bt459_reg[btaddr] = idata;
456
457 switch (btaddr) {
458 case BT459_REG_CCOLOR_1:
459 case BT459_REG_CCOLOR_2:
460 case BT459_REG_CCOLOR_3:
461 if (modified)
462 d->need_to_update_cursor_shape = 1;
463 break;
464 case BT459_REG_PRM:
465 /*
466 * NetBSD writes 0x00 to this register to
467 * blank the screen (video off), and 0xff
468 * to turn the screen on.
469 */
470 switch (idata & 0xff) {
471 case 0: d->video_on = 0;
472 memset(d->rgb_palette, 0, 256*3);
473 d->need_to_redraw_whole_screen = 1;
474 debug("[ bt459: video OFF ]\n");
475 break;
476 default:d->video_on = 1;
477 memcpy(d->rgb_palette,
478 d->local_rgb_palette, 256*3);
479 d->need_to_redraw_whole_screen = 1;
480 debug("[ bt459: video ON ]\n");
481 }
482 break;
483 case BT459_REG_CCR:
484 /* Cursor control register: */
485 switch (idata & 0xff) {
486 case 0x00: d->cursor_on = 0; break;
487 case 0xc0:
488 case 0xc1: d->cursor_on = 1; break;
489 default:
490 fatal("[ bt459: unimplemented CCR "
491 "value 0x%08x ]\n", (int)idata);
492 }
493 if (modified)
494 d->need_to_update_cursor_shape = 1;
495 break;
496 default:
497 if (btaddr < 0x100)
498 fatal("[ bt459: write to BT459 "
499 "register 0x%04x, value 0x%02x ]\n",
500 btaddr, (int)idata);
501 }
502
503 /* Write to cursor bitmap: */
504 if (btaddr >= BT459_REG_CRAM_BASE && modified)
505 d->need_to_update_cursor_shape = 1;
506 } else {
507 odata = d->bt459_reg[btaddr];
508
509 /* Perhaps this hack is not necessary: */
510 if (btaddr == BT459_REG_ID && len==1)
511 odata = (odata >> 16) & 255;
512
513 if (!quiet_mode)
514 debug("[ bt459: read from BT459 register "
515 "0x%04x, value 0x%02x ]\n", btaddr,
516 (int)odata);
517 }
518
519 /* Go to next register: */
520 d->cur_addr_lo ++;
521 if (d->cur_addr_lo == 0)
522 d->cur_addr_hi ++;
523 break;
524 case 0xc: /* Color map: */
525 if (writeflag == MEM_WRITE) {
526 idata &= 255;
527 if (!quiet_mode)
528 debug("[ bt459: write to BT459 colormap "
529 "0x%04x subaddr %i, value 0x%02x ]\n",
530 btaddr, d->palette_sub_offset, (int)idata);
531
532 if (btaddr < 0x100) {
533 if (d->video_on &&
534 d->local_rgb_palette[(btaddr & 0xff) * 3
535 + d->palette_sub_offset] != idata)
536 d->need_to_redraw_whole_screen = 1;
537
538 /*
539 * Actually, the palette should only be
540 * updated after the third write,
541 * but this should probably work fine too:
542 */
543 d->local_rgb_palette[(btaddr & 0xff) * 3
544 + d->palette_sub_offset] = idata;
545
546 if (d->video_on)
547 d->rgb_palette[(btaddr & 0xff) * 3
548 + d->palette_sub_offset] = idata;
549 }
550 } else {
551 if (btaddr < 0x100)
552 odata = d->local_rgb_palette[(btaddr & 0xff)
553 * 3 + d->palette_sub_offset];
554 if (!quiet_mode)
555 debug("[ bt459: read from BT459 colormap "
556 "0x%04x subaddr %i, value 0x%02x ]\n",
557 btaddr, d->palette_sub_offset, (int)odata);
558 }
559
560 d->palette_sub_offset ++;
561 if (d->palette_sub_offset >= 3) {
562 d->palette_sub_offset = 0;
563
564 d->cur_addr_lo ++;
565 if (d->cur_addr_lo == 0)
566 d->cur_addr_hi ++;
567 }
568
569 break;
570 default:
571 if (writeflag == MEM_WRITE) {
572 debug("[ bt459: unimplemented write to address 0x%x, "
573 "data=0x%02x ]\n", (int)relative_addr, (int)idata);
574 } else {
575 debug("[ bt459: unimplemented read from address "
576 "0x%x ]\n", (int)relative_addr);
577 }
578 }
579
580
581 bt459_update_cursor_position(d, old_cursor_on);
582
583 if (writeflag == MEM_READ)
584 memory_writemax64(cpu, data, len, odata);
585
586 #ifdef BT459_DEBUG
587 if (writeflag == MEM_READ)
588 fatal("[ bt459: read from addr 0x%02x: %08x ]\n",
589 (int)relative_addr, (int)idata);
590 #endif
591
592 return 1;
593 }
594
595
596 /*
597 * dev_bt459_init():
598 */
599 void dev_bt459_init(struct machine *machine, struct memory *mem,
600 uint64_t baseaddr, uint64_t baseaddr_irq, struct vfb_data *vfb_data,
601 int planes, int irq_nr, int type)
602 {
603 struct bt459_data *d = malloc(sizeof(struct bt459_data));
604 if (d == NULL) {
605 fprintf(stderr, "out of memory\n");
606 exit(1);
607 }
608
609 memset(d, 0, sizeof(struct bt459_data));
610
611 d->vfb_data = vfb_data;
612 d->rgb_palette = vfb_data->rgb_palette;
613 d->planes = planes;
614 d->irq_nr = irq_nr;
615 d->type = type;
616 d->cursor_x = -1;
617 d->cursor_y = -1;
618 d->cursor_xsize = d->cursor_ysize = 0; /* anything */
619 d->video_on = 1;
620
621 /*
622 * These offsets are based on those mentioned in NetBSD,
623 * and then adjusted to look good with both NetBSD and
624 * Ultrix:
625 */
626 switch (d->type) {
627 case BT459_PX:
628 d->cursor_x_add = 370;
629 d->cursor_y_add = 37;
630 break;
631 case BT459_BA:
632 d->cursor_x_add = 220;
633 d->cursor_y_add = 35;
634 break;
635 case BT459_BBA:
636 if (vfb_data->xsize == 1280) {
637 /* 1280x1024: */
638 d->cursor_x_add = 368;
639 d->cursor_y_add = 38;
640 } else {
641 /* 1024x864: */
642 d->cursor_x_add = 220;
643 d->cursor_y_add = 35;
644 }
645 break;
646 }
647
648 d->interrupt_time_reset_value = 500;
649
650 memory_device_register(mem, "bt459", baseaddr, DEV_BT459_LENGTH,
651 dev_bt459_access, (void *)d, MEM_DEFAULT, NULL);
652
653 if (baseaddr_irq != 0)
654 memory_device_register(mem, "bt459_irq", baseaddr_irq, 0x10000,
655 dev_bt459_irq_access, (void *)d, MEM_DEFAULT, NULL);
656
657 machine_add_tickfunction(machine, dev_bt459_tick, d, BT459_TICK_SHIFT);
658
659 memory_device_register_statefunction(mem, d, bt459_state);
660 }
661

  ViewVC Help
Powered by ViewVC 1.1.26