/[gxemul]/upstream/0.4.4/src/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

Annotation of /upstream/0.4.4/src/devices/dev_bt459.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 35 - (hide annotations)
Mon Oct 8 16:21:26 2007 UTC (16 years, 8 months ago) by dpavlin
File MIME type: text/plain
File size: 15329 byte(s)
0.4.4
1 dpavlin 4 /*
2 dpavlin 34 * Copyright (C) 2003-2007 Anders Gavare. All rights reserved.
3 dpavlin 4 *
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 dpavlin 34 * $Id: dev_bt459.c,v 1.67 2006/12/30 13:30:57 debug Exp $
29 dpavlin 4 *
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 dpavlin 34 struct interrupt irq;
69 dpavlin 4 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_update_X_cursor():
105     *
106     * This routine takes the color values in the cursor RAM area, and put them
107     * in the framebuffer window's cursor_pixels.
108     *
109     * d->cursor_xsize and ysize are also updated.
110     */
111     static void bt459_update_X_cursor(struct cpu *cpu, struct bt459_data *d)
112     {
113     int i, x,y, xmax=0, ymax=0;
114     int bw_only = 1;
115    
116     /* First, let's calculate the size of the cursor: */
117     for (y=0; y<64; y++)
118     for (x=0; x<64; x+=4) {
119     int reg = BT459_REG_CRAM_BASE + y*16 + x/4;
120     unsigned char data = d->bt459_reg[reg];
121    
122     if (data)
123     ymax = y;
124    
125     for (i=0; i<4; i++) {
126     int color = (data >> (6-2*i)) & 3;
127     if (color != 0)
128     xmax = x + i;
129     if (color != 0 && color != 3)
130     bw_only = 0;
131     }
132     }
133    
134     d->cursor_xsize = xmax + 1;
135     d->cursor_ysize = ymax + 1;
136    
137     /*
138     * The 'bw_only' hack is because it is nicer to have the b/w
139     * text cursor invert whatever it is standing on, not just overwrite
140     * it with a big white box.
141     *
142     * The following seems to work with NetBSD/OpenBSD/Ultrix/Sprite:
143     * 0 = transparent, 1 and 2 = use the color specified by
144     * BT459_REG_CCOLOR_2, 3 = reverse of color 1/2.
145     */
146    
147     #ifdef WITH_X11
148     if (cpu->machine->use_x11 && d->vfb_data->fb_window != NULL) {
149     for (y=0; y<=ymax; y++) {
150     for (x=0; x<=xmax; x+=4) {
151     struct fb_window *win = d->vfb_data->fb_window;
152     int reg = BT459_REG_CRAM_BASE + y*16 + x/4;
153     unsigned char data = d->bt459_reg[reg];
154    
155     for (i=0; i<4; i++) {
156     int color = (data >> (6-2*i)) & 3;
157     int pixelvalue;
158    
159     if (bw_only) {
160     if (color)
161     pixelvalue =
162     CURSOR_COLOR_INVERT;
163     else
164     pixelvalue = 0;
165     } else {
166     pixelvalue =
167     CURSOR_COLOR_TRANSPARENT;
168     switch (color) {
169     case 1:
170     case 2: pixelvalue = (d->
171     bt459_reg[
172     BT459_REG_CCOLOR_2]
173     >> 4) & 0xf;
174     break;
175     case 3: pixelvalue = 15 -
176     ((d->bt459_reg[
177     BT459_REG_CCOLOR_2]
178     >> 4) & 0xf);
179     break;
180     }
181     }
182    
183     win->cursor_pixels[y][x+i] =
184     pixelvalue;
185     #ifdef WITH_CURSOR_DEBUG
186     printf("%i", color);
187     #endif
188     }
189     }
190     #ifdef WITH_CURSOR_DEBUG
191     printf("\n");
192     #endif
193     }
194     #ifdef WITH_CURSOR_DEBUG
195     printf("color 1,2,3 = 0x%02x, 0x%02x, 0x%02x\n",
196     d->bt459_reg[BT459_REG_CCOLOR_1],
197     d->bt459_reg[BT459_REG_CCOLOR_2],
198     d->bt459_reg[BT459_REG_CCOLOR_3]);
199     printf("\n");
200     #endif
201     /*
202     * Make sure the cursor is redrawn, if it is on:
203     *
204     * How does this work? Well, 0 is off, and non-zero is on,
205     * but if the old and new differ, the cursor is redrawn.
206     * (Hopefully this will "never" overflow.)
207     */
208     if (d->cursor_on)
209     d->cursor_on ++;
210     }
211     #endif
212     }
213    
214    
215     /*
216     * bt459_update_cursor_position():
217     */
218     static void bt459_update_cursor_position(struct bt459_data *d,
219     int old_cursor_on)
220     {
221     int new_cursor_x = (d->bt459_reg[BT459_REG_CXLO] & 255) +
222     ((d->bt459_reg[BT459_REG_CXHI] & 255) << 8) - d->cursor_x_add;
223     int new_cursor_y = (d->bt459_reg[BT459_REG_CYLO] & 255) +
224     ((d->bt459_reg[BT459_REG_CYHI] & 255) << 8) - d->cursor_y_add;
225    
226     if (new_cursor_x != d->cursor_x || new_cursor_y != d->cursor_y ||
227     d->cursor_on != old_cursor_on) {
228     int on;
229    
230     d->cursor_x = new_cursor_x;
231     d->cursor_y = new_cursor_y;
232    
233     if (!quiet_mode)
234     debug("[ bt459: cursor = %03i,%03i ]\n",
235     d->cursor_x, d->cursor_y);
236    
237     on = d->cursor_on;
238     if (d->cursor_xsize == 0 || d->cursor_ysize == 0)
239     on = 0;
240    
241     dev_fb_setcursor(d->vfb_data, d->cursor_x, d->cursor_y,
242     on, d->cursor_xsize, d->cursor_ysize);
243     }
244     }
245    
246    
247 dpavlin 30 DEVICE_TICK(bt459)
248 dpavlin 4 {
249     struct bt459_data *d = extra;
250     int old_cursor_on = d->cursor_on;
251    
252     if (d->need_to_update_cursor_shape) {
253     d->need_to_update_cursor_shape = 0;
254     bt459_update_X_cursor(cpu, d);
255     bt459_update_cursor_position(d, old_cursor_on);
256     }
257    
258     if (d->need_to_redraw_whole_screen) {
259     d->vfb_data->update_x1 = 0;
260     d->vfb_data->update_x2 = d->vfb_data->xsize - 1;
261     d->vfb_data->update_y1 = 0;
262     d->vfb_data->update_y2 = d->vfb_data->ysize - 1;
263     d->need_to_redraw_whole_screen = 0;
264     }
265    
266     /*
267     * Vertical retrace interrupts. (This hack is kind of ugly.)
268     * Once every 'interrupt_time_reset_value', the interrupt is
269     * asserted. It is acked either manually (by someone reading
270     * a normal BT459 register or the Interrupt ack register),
271     * or after another tick has passed. (This is to prevent
272     * lockups from unhandled interrupts.)
273     */
274 dpavlin 34 if (d->type != BT459_PX && d->interrupts_enable) {
275 dpavlin 4 d->interrupt_time --;
276     if (d->interrupt_time < 0) {
277     d->interrupt_time = d->interrupt_time_reset_value;
278 dpavlin 34 INTERRUPT_ASSERT(d->irq);
279 dpavlin 4 } else
280 dpavlin 34 INTERRUPT_DEASSERT(d->irq);
281 dpavlin 4 }
282     }
283    
284    
285 dpavlin 22 DEVICE_ACCESS(bt459_irq)
286 dpavlin 4 {
287     struct bt459_data *d = (struct bt459_data *) extra;
288     uint64_t idata = 0, odata = 0;
289    
290 dpavlin 18 if (writeflag == MEM_WRITE)
291     idata = memory_readmax64(cpu, data, len);
292 dpavlin 4
293     #ifdef BT459_DEBUG
294     fatal("[ bt459: IRQ ack ]\n");
295     #endif
296    
297     d->interrupts_enable = 1;
298    
299 dpavlin 34 INTERRUPT_DEASSERT(d->irq);
300    
301 dpavlin 4 if (writeflag == MEM_READ)
302     memory_writemax64(cpu, data, len, odata);
303    
304     return 1;
305     }
306    
307    
308     /*
309     * dev_bt459_access():
310     */
311 dpavlin 22 DEVICE_ACCESS(bt459)
312 dpavlin 4 {
313     struct bt459_data *d = (struct bt459_data *) extra;
314     uint64_t idata = 0, odata = 0;
315     int btaddr, old_cursor_on = d->cursor_on, modified;
316    
317     idata = memory_readmax64(cpu, data, len);
318    
319     #ifdef BT459_DEBUG
320     if (writeflag == MEM_WRITE)
321     fatal("[ bt459: write to addr 0x%02x: %08x ]\n",
322     (int)relative_addr, (int)idata);
323     #endif
324    
325     /*
326     * Vertical retrace interrupts are acked either by
327     * accessing a normal BT459 register, or the irq register,
328     * or by simply "missing" it.
329     */
330 dpavlin 34 INTERRUPT_DEASSERT(d->irq);
331 dpavlin 4
332     /* ID register is read-only, should always be 0x4a or 0x4a4a4a: */
333     if (d->planes == 24)
334     d->bt459_reg[BT459_REG_ID] = 0x4a4a4a;
335     else {
336     /*
337     * TODO: Is it really 0x4a, or 0x4a0000?
338     * Ultrix panics with a "bad VDAC ID" message if 0x4a
339     * is returned.
340     */
341     d->bt459_reg[BT459_REG_ID] = 0x4a0000;
342     }
343    
344     btaddr = ((d->cur_addr_hi << 8) + d->cur_addr_lo) % DEV_BT459_NREGS;
345    
346     /* Read from/write to the bt459: */
347     switch (relative_addr) {
348     case 0x00: /* Low byte of address: */
349     if (writeflag == MEM_WRITE) {
350     if (!quiet_mode)
351     debug("[ bt459: write to Low Address Byte, "
352     "0x%02x ]\n", (int)idata);
353     d->cur_addr_lo = idata;
354     d->palette_sub_offset = 0;
355     } else {
356     odata = d->cur_addr_lo;
357     if (!quiet_mode)
358     debug("[ bt459: read from Low Address Byte: "
359     "0x%0x ]\n", (int)odata);
360     }
361     break;
362     case 0x04: /* High byte of address: */
363     if (writeflag == MEM_WRITE) {
364     if (!quiet_mode)
365     debug("[ bt459: write to High Address Byte, "
366     "0x%02x ]\n", (int)idata);
367     d->cur_addr_hi = idata;
368     d->palette_sub_offset = 0;
369     } else {
370     odata = d->cur_addr_hi;
371     if (!quiet_mode)
372     debug("[ bt459: read from High Address Byte: "
373     "0x%0x ]\n", (int)odata);
374     }
375     break;
376     case 0x08: /* Register access: */
377     if (writeflag == MEM_WRITE) {
378     if (!quiet_mode)
379     debug("[ bt459: write to BT459 register "
380     "0x%04x, value 0x%02x ]\n", btaddr,
381     (int)idata);
382     modified = (d->bt459_reg[btaddr] != idata);
383     d->bt459_reg[btaddr] = idata;
384    
385     switch (btaddr) {
386     case BT459_REG_CCOLOR_1:
387     case BT459_REG_CCOLOR_2:
388     case BT459_REG_CCOLOR_3:
389     if (modified)
390     d->need_to_update_cursor_shape = 1;
391     break;
392     case BT459_REG_PRM:
393     /*
394     * NetBSD writes 0x00 to this register to
395     * blank the screen (video off), and 0xff
396     * to turn the screen on.
397     */
398     switch (idata & 0xff) {
399     case 0: d->video_on = 0;
400     memset(d->rgb_palette, 0, 256*3);
401     d->need_to_redraw_whole_screen = 1;
402     debug("[ bt459: video OFF ]\n");
403     break;
404     default:d->video_on = 1;
405     memcpy(d->rgb_palette,
406     d->local_rgb_palette, 256*3);
407     d->need_to_redraw_whole_screen = 1;
408     debug("[ bt459: video ON ]\n");
409     }
410     break;
411     case BT459_REG_CCR:
412     /* Cursor control register: */
413     switch (idata & 0xff) {
414     case 0x00: d->cursor_on = 0; break;
415     case 0xc0:
416     case 0xc1: d->cursor_on = 1; break;
417     default:
418     fatal("[ bt459: unimplemented CCR "
419     "value 0x%08x ]\n", (int)idata);
420     }
421     if (modified)
422     d->need_to_update_cursor_shape = 1;
423     break;
424     default:
425     if (btaddr < 0x100)
426     fatal("[ bt459: write to BT459 "
427     "register 0x%04x, value 0x%02x ]\n",
428     btaddr, (int)idata);
429     }
430    
431     /* Write to cursor bitmap: */
432     if (btaddr >= BT459_REG_CRAM_BASE && modified)
433     d->need_to_update_cursor_shape = 1;
434     } else {
435     odata = d->bt459_reg[btaddr];
436    
437     /* Perhaps this hack is not necessary: */
438     if (btaddr == BT459_REG_ID && len==1)
439     odata = (odata >> 16) & 255;
440    
441     if (!quiet_mode)
442     debug("[ bt459: read from BT459 register "
443     "0x%04x, value 0x%02x ]\n", btaddr,
444     (int)odata);
445     }
446    
447     /* Go to next register: */
448     d->cur_addr_lo ++;
449     if (d->cur_addr_lo == 0)
450     d->cur_addr_hi ++;
451     break;
452     case 0xc: /* Color map: */
453     if (writeflag == MEM_WRITE) {
454     idata &= 255;
455     if (!quiet_mode)
456     debug("[ bt459: write to BT459 colormap "
457     "0x%04x subaddr %i, value 0x%02x ]\n",
458     btaddr, d->palette_sub_offset, (int)idata);
459    
460     if (btaddr < 0x100) {
461     if (d->video_on &&
462     d->local_rgb_palette[(btaddr & 0xff) * 3
463     + d->palette_sub_offset] != idata)
464     d->need_to_redraw_whole_screen = 1;
465    
466     /*
467     * Actually, the palette should only be
468     * updated after the third write,
469     * but this should probably work fine too:
470     */
471     d->local_rgb_palette[(btaddr & 0xff) * 3
472     + d->palette_sub_offset] = idata;
473    
474     if (d->video_on)
475     d->rgb_palette[(btaddr & 0xff) * 3
476     + d->palette_sub_offset] = idata;
477     }
478     } else {
479     if (btaddr < 0x100)
480     odata = d->local_rgb_palette[(btaddr & 0xff)
481     * 3 + d->palette_sub_offset];
482     if (!quiet_mode)
483     debug("[ bt459: read from BT459 colormap "
484     "0x%04x subaddr %i, value 0x%02x ]\n",
485     btaddr, d->palette_sub_offset, (int)odata);
486     }
487    
488     d->palette_sub_offset ++;
489     if (d->palette_sub_offset >= 3) {
490     d->palette_sub_offset = 0;
491    
492     d->cur_addr_lo ++;
493     if (d->cur_addr_lo == 0)
494     d->cur_addr_hi ++;
495     }
496    
497     break;
498     default:
499     if (writeflag == MEM_WRITE) {
500     debug("[ bt459: unimplemented write to address 0x%x, "
501     "data=0x%02x ]\n", (int)relative_addr, (int)idata);
502     } else {
503     debug("[ bt459: unimplemented read from address "
504     "0x%x ]\n", (int)relative_addr);
505     }
506     }
507    
508    
509     bt459_update_cursor_position(d, old_cursor_on);
510    
511     if (writeflag == MEM_READ)
512     memory_writemax64(cpu, data, len, odata);
513    
514     #ifdef BT459_DEBUG
515     if (writeflag == MEM_READ)
516     fatal("[ bt459: read from addr 0x%02x: %08x ]\n",
517     (int)relative_addr, (int)idata);
518     #endif
519    
520     return 1;
521     }
522    
523    
524     /*
525     * dev_bt459_init():
526     */
527     void dev_bt459_init(struct machine *machine, struct memory *mem,
528     uint64_t baseaddr, uint64_t baseaddr_irq, struct vfb_data *vfb_data,
529 dpavlin 34 int planes, char *irq_path, int type)
530 dpavlin 4 {
531     struct bt459_data *d = malloc(sizeof(struct bt459_data));
532     if (d == NULL) {
533     fprintf(stderr, "out of memory\n");
534     exit(1);
535     }
536    
537     memset(d, 0, sizeof(struct bt459_data));
538    
539 dpavlin 34 INTERRUPT_CONNECT(irq_path, d->irq);
540    
541 dpavlin 4 d->vfb_data = vfb_data;
542     d->rgb_palette = vfb_data->rgb_palette;
543     d->planes = planes;
544     d->type = type;
545     d->cursor_x = -1;
546     d->cursor_y = -1;
547     d->cursor_xsize = d->cursor_ysize = 0; /* anything */
548     d->video_on = 1;
549    
550     /*
551     * These offsets are based on those mentioned in NetBSD,
552     * and then adjusted to look good with both NetBSD and
553     * Ultrix:
554     */
555     switch (d->type) {
556     case BT459_PX:
557     d->cursor_x_add = 370;
558     d->cursor_y_add = 37;
559     break;
560     case BT459_BA:
561     d->cursor_x_add = 220;
562     d->cursor_y_add = 35;
563     break;
564     case BT459_BBA:
565     if (vfb_data->xsize == 1280) {
566     /* 1280x1024: */
567     d->cursor_x_add = 368;
568     d->cursor_y_add = 38;
569     } else {
570     /* 1024x864: */
571     d->cursor_x_add = 220;
572     d->cursor_y_add = 35;
573     }
574     break;
575     }
576    
577     d->interrupt_time_reset_value = 500;
578    
579     memory_device_register(mem, "bt459", baseaddr, DEV_BT459_LENGTH,
580 dpavlin 20 dev_bt459_access, (void *)d, DM_DEFAULT, NULL);
581 dpavlin 4
582     if (baseaddr_irq != 0)
583     memory_device_register(mem, "bt459_irq", baseaddr_irq, 0x10000,
584 dpavlin 20 dev_bt459_irq_access, (void *)d, DM_DEFAULT, NULL);
585 dpavlin 4
586 dpavlin 24 machine_add_tickfunction(machine, dev_bt459_tick, d,
587     BT459_TICK_SHIFT, 0.0);
588 dpavlin 4 }
589    

  ViewVC Help
Powered by ViewVC 1.1.26