1 /*
2 * Copyright (C) 2003-2019 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 * COMMENT: SGI "Graphics Back End", graphics controller + framebuffer
29 *
30 * Guesswork, based on how Linux, NetBSD, and OpenBSD use the graphics on
31 * the SGI O2. Using NetBSD terminology (from crmfbreg.h):
32 *
33 * dev_sgi_re.cc:
34 * 0x15001000 rendering engine (TLBs)
35 * 0x15002000 drawing engine
36 * 0x15003000 memory transfer engine
37 * 0x15004000 status registers for drawing engine
38 *
39 * dev_sgi_gbe.cc:
40 * 0x16000000 crm (or GBE) framebuffer control / video output
41 *
42 * According to https://www.linux-mips.org/wiki/GBE, the GBE is also used in
43 * the SGI Visual Workstation.
44 */
45
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49
50 #include "console.h"
51 #include "cpu.h"
52 #include "devices.h"
53 #include "machine.h"
54 #include "memory.h"
55 #include "misc.h"
56
57 #include "thirdparty/crmfbreg.h"
58
59
60 /* Let's hope nothing is there already... */
61 #define FAKE_GBE_FB_ADDRESS 0x380000000ULL
62
63
64 // #define GBE_DEBUG
65 // #define debug fatal
66
67 #define GBE_DEFAULT_XRES 1280
68 #define GBE_DEFAULT_YRES 1024
69 #define GBE_DEFAULT_BITDEPTH 8
70
71
72 struct sgi_gbe_data {
73 // CRM / GBE registers:
74 uint32_t ctrlstat; /* 0x00000 */
75 uint32_t dotclock; /* 0x00004 */
76 uint32_t i2c; /* 0x00008 */
77 uint32_t i2cfp; /* 0x00010 */
78
79 uint32_t freeze; /* and xy */ /* 0x10000 */
80 uint32_t y_intr01; /* 0x10020 */
81 uint32_t y_intr23; /* 0x10024 */
82
83 uint32_t ovr_tilesize; /* 0x20000 */
84 uint32_t ovr_control; /* 0x2000c */
85
86 uint32_t tilesize; /* 0x30000 */
87 uint32_t frm_control; /* 0x3000c */
88
89 uint32_t palette[32 * 256]; /* 0x50000 */
90
91 uint32_t cursor_pos; /* 0x70000 */
92 uint32_t cursor_control; /* 0x70004 */
93 uint32_t cursor_cmap0; /* 0x70008 */
94 uint32_t cursor_cmap1; /* 0x7000c */
95 uint32_t cursor_cmap2; /* 0x70010 */
96 uint32_t cursor_bitmap[64]; /* 0x78000 */
97
98 // Emulator's representation:
99 int xres, yres;
100 int width_in_tiles;
101 int partial_pixels;
102 int ovr_width_in_tiles;
103 int ovr_partial_pixels;
104 int bitdepth;
105 int color_mode;
106 int cmap_select;
107 uint32_t selected_palette[256];
108 struct vfb_data *fb_data;
109 };
110
111
get_rgb(struct sgi_gbe_data * d,uint32_t color,uint8_t * r,uint8_t * g,uint8_t * b)112 void get_rgb(struct sgi_gbe_data *d, uint32_t color, uint8_t* r, uint8_t* g, uint8_t* b)
113 {
114 // TODO: Don't switch on color_mode. For overlays, this is always
115 // 8-bit index mode!
116 switch (d->color_mode) {
117 case CRMFB_MODE_TYP_I8:
118 color &= 0xff;
119 *r = d->selected_palette[color] >> 24;
120 *g = d->selected_palette[color] >> 16;
121 *b = d->selected_palette[color] >> 8;
122 break;
123 case CRMFB_MODE_TYP_RG3B2: // Used by NetBSD console mode
124 *r = 255 * ((color >> 5) & 7) / 7;
125 *g = 255 * ((color >> 2) & 7) / 7;
126 *b = (color & 3) * 85;
127 break;
128 case CRMFB_MODE_TYP_RGB8: // Used by NetBSD's X11 server
129 *r = color >> 24;
130 *g = color >> 16;
131 *b = color >> 8;
132 break;
133 default:fatal("sgi gbe get_rgb(): unimplemented mode %i\n", d->color_mode);
134 exit(1);
135 }
136 }
137
138
select_palette(struct sgi_gbe_data * d,int palette_nr)139 void select_palette(struct sgi_gbe_data *d, int palette_nr)
140 {
141 memmove(&d->selected_palette[0],
142 &d->palette[256 * palette_nr],
143 256 * sizeof(uint32_t));
144 }
145
146
147 /*
148 * dev_sgi_gbe_tick():
149 *
150 * Every now and then, copy data from the framebuffer in normal ram
151 * to the actual framebuffer (which will then redraw the window).
152 *
153 * NOTE: This is very slow, even slower than the normal emulated framebuffer,
154 * which is already slow as it is.
155 *
156 * frm_control contains a pointer to an array of uint16_t. These numbers
157 * (when shifted 16 bits to the left) are pointers to the tiles. Tiles are
158 * 512x128 in 8-bit mode, 256x128 in 16-bit mode, and 128x128 in 32-bit mode.
159 *
160 * An exception is how Linux/O2 uses the framebuffer, in a "tweaked" mode
161 * which resembles linear mode. This code attempts to support both.
162 */
DEVICE_TICK(sgi_gbe)163 DEVICE_TICK(sgi_gbe)
164 {
165 struct sgi_gbe_data *d = (struct sgi_gbe_data *) extra;
166 uint64_t tiletable;
167 unsigned char buf[16384]; /* must be power of 2, at most 65536 */
168 int bytes_per_pixel = d->bitdepth / 8;
169 int partial_pixels, width_in_tiles;
170
171 if (!cpu->machine->x11_md.in_use)
172 return;
173
174 // If not frozen...
175 if (!(d->freeze & 0x80000000)) {
176 // ... check if the guest OS wants interrupts based on Y:
177 if ((d->y_intr01 & CRMFB_INTR_0_MASK) != 0xfff000 ||
178 (d->y_intr01 & CRMFB_INTR_1_MASK) != 0xfff ||
179 (d->y_intr23 & CRMFB_INTR_2_MASK) != 0xfff000 ||
180 (d->y_intr23 & CRMFB_INTR_3_MASK) != 0xfff) {
181 fatal("[ sgi_gbe: WARNING: Y interrupts not yet implemented. ]\n");
182 }
183 }
184
185 // printf("d->frm_control = %08x d->ovr_control = %08x\n", d->frm_control,d->ovr_control);
186
187 // NetBSD's crmfbreg.h documents the tileptr as having a "9 bit shift",
188 // but IRIX seems to put a value ending in 0x......80 there, and the
189 // last part of that address seems to matter.
190 // TODO: Double-check this with the real hardware.
191
192 if (d->ovr_control & CRMFB_DMA_ENABLE) {
193 tiletable = (d->ovr_control & 0xffffff80);
194 bytes_per_pixel = 1;
195 partial_pixels = d->ovr_partial_pixels;
196 width_in_tiles = d->ovr_width_in_tiles;
197 select_palette(d, 17); // TODO: is it always palette nr 17 for overlays?
198 } else if (d->frm_control & CRMFB_DMA_ENABLE) {
199 tiletable = (d->frm_control & 0xffffff80);
200 partial_pixels = d->partial_pixels;
201 width_in_tiles = d->width_in_tiles;
202 select_palette(d, d->cmap_select);
203 } else {
204 return;
205 }
206
207 #ifdef GBE_DEBUG
208 fatal("[ sgi_gbe: dev_sgi_gbe_tick(): tiletable = 0x%llx, bytes_per_pixel = %i ]\n", (long long)tiletable,
209 bytes_per_pixel);
210 #endif
211
212 if (tiletable == 0)
213 return;
214
215 // Nr of tiles horizontally:
216 int w = width_in_tiles + (partial_pixels > 0 ? 1 : 0);
217
218 // Actually, the number of tiles vertically is usually very few,
219 // but this algorithm will render "up to" 256 and abort as soon
220 // as the screen is filled instead. This makes it work for both
221 // Linux' "tweaked linear" mode and all the other guest OSes.
222 const int max_nr_of_tiles = 256;
223
224 uint32_t tile[max_nr_of_tiles];
225 uint8_t alltileptrs[max_nr_of_tiles * sizeof(uint16_t)];
226
227 cpu->memory_rw(cpu, cpu->mem, tiletable,
228 alltileptrs, sizeof(alltileptrs), MEM_READ,
229 NO_EXCEPTIONS | PHYSICAL);
230
231 for (int i = 0; i < 256; ++i) {
232 tile[i] = (256 * alltileptrs[i*2] + alltileptrs[i*2+1]) << 16;
233 #ifdef GBE_DEBUG
234 if (tile[i] != 0)
235 printf("tile[%i] = 0x%08x\n", i, tile[i]);
236 #endif
237 }
238
239 int screensize = d->xres * d->yres * 3;
240 int x = 0, y = 0;
241
242 for (int tiley = 0; tiley < max_nr_of_tiles; ++tiley) {
243 for (int line = 0; line < 128; ++line) {
244 for (int tilex = 0; tilex < w; ++tilex) {
245 int tilenr = tilex + tiley * w;
246
247 if (tilenr >= max_nr_of_tiles)
248 continue;
249
250 uint32_t base = tile[tilenr];
251
252 if (base == 0)
253 continue;
254
255 // Read one line of up to 512 bytes from the tile.
256 int len = tilex < width_in_tiles ? 512 : (partial_pixels * bytes_per_pixel);
257
258 cpu->memory_rw(cpu, cpu->mem, base + 512 * line,
259 buf, len, MEM_READ, NO_EXCEPTIONS | PHYSICAL);
260
261 int fb_offset = (x + y * d->xres) * 3;
262 int fb_len = (len / bytes_per_pixel) * 3;
263
264 if (fb_offset + fb_len > screensize) {
265 fb_len = screensize - fb_offset;
266 }
267
268 if (fb_len <= 0) {
269 tiley = max_nr_of_tiles; // to break
270 tilex = w;
271 line = 128;
272 }
273
274 uint8_t fb_buf[512 * 3];
275 int fb_i = 0;
276 for (int i = 0; i < 512; i+=bytes_per_pixel) {
277 uint32_t color;
278 if (bytes_per_pixel == 1)
279 color = buf[i];
280 else if (bytes_per_pixel == 2)
281 color = (buf[i]<<8) + buf[i+1];
282 else // if (bytes_per_pixel == 4)
283 color = (buf[i]<<24) + (buf[i+1]<<16)
284 + (buf[i+2]<<8)+buf[i+3];
285 get_rgb(d, color,
286 &fb_buf[fb_i],
287 &fb_buf[fb_i+1],
288 &fb_buf[fb_i+2]);
289 fb_i += 3;
290 }
291
292 dev_fb_access(cpu, cpu->mem, fb_offset,
293 fb_buf, fb_len, MEM_WRITE, d->fb_data);
294
295 x += len / bytes_per_pixel;
296 if (x >= d->xres) {
297 x -= d->xres;
298 ++y;
299 if (y >= d->yres) {
300 tiley = max_nr_of_tiles; // to break
301 tilex = w;
302 line = 128;
303 }
304 }
305 }
306 }
307 }
308
309 if (d->cursor_control & CRMFB_CURSOR_ON) {
310 int16_t cx = d->cursor_pos & 0xffff;
311 int16_t cy = d->cursor_pos >> 16;
312
313 if (d->cursor_control & CRMFB_CURSOR_CROSSHAIR) {
314 uint8_t pixel[3];
315 pixel[0] = d->cursor_cmap0 >> 24;
316 pixel[1] = d->cursor_cmap0 >> 16;
317 pixel[2] = d->cursor_cmap0 >> 8;
318
319 if (cx >= 0 && cx < d->xres) {
320 for (y = 0; y < d->yres; ++y)
321 dev_fb_access(cpu, cpu->mem, (cx + y * d->xres) * 3,
322 pixel, 3, MEM_WRITE, d->fb_data);
323 }
324
325 // TODO: Rewrite as a single framebuffer block write?
326 if (cy >= 0 && cy < d->yres) {
327 for (x = 0; x < d->xres; ++x)
328 dev_fb_access(cpu, cpu->mem, (x + cy * d->xres) * 3,
329 pixel, 3, MEM_WRITE, d->fb_data);
330 }
331 } else {
332 uint8_t pixel[3];
333 int sx, sy;
334
335 for (int dy = 0; dy < 32; ++dy) {
336 for (int dx = 0; dx < 32; ++dx) {
337 sx = cx + dx;
338 sy = cy + dy;
339
340 if (sx < 0 || sx >= d->xres ||
341 sy < 0 || sy >= d->yres)
342 continue;
343
344 int wordindex = dy*2 + (dx>>4);
345 uint32_t word = d->cursor_bitmap[wordindex];
346
347 int color = (word >> ((15 - (dx&15))*2)) & 3;
348
349 if (!color)
350 continue;
351
352 if (color == 1) {
353 pixel[0] = d->cursor_cmap0 >> 24;
354 pixel[1] = d->cursor_cmap0 >> 16;
355 pixel[2] = d->cursor_cmap0 >> 8;
356 } else if (color == 2) {
357 pixel[0] = d->cursor_cmap1 >> 24;
358 pixel[1] = d->cursor_cmap1 >> 16;
359 pixel[2] = d->cursor_cmap1 >> 8;
360 } else {
361 pixel[0] = d->cursor_cmap2 >> 24;
362 pixel[1] = d->cursor_cmap2 >> 16;
363 pixel[2] = d->cursor_cmap2 >> 8;
364 }
365
366 dev_fb_access(cpu, cpu->mem, (sx + sy * d->xres) * 3,
367 pixel, 3, MEM_WRITE, d->fb_data);
368 }
369 }
370 }
371 }
372 }
373
374
DEVICE_ACCESS(sgi_gbe)375 DEVICE_ACCESS(sgi_gbe)
376 {
377 struct sgi_gbe_data *d = (struct sgi_gbe_data *) extra;
378 uint64_t idata = 0, odata = 0;
379
380 if (writeflag == MEM_WRITE) {
381 idata = memory_readmax64(cpu, data, len);
382
383 #ifdef GBE_DEBUG
384 fatal("[ sgi_gbe: DEBUG: write to address 0x%llx, data"
385 "=0x%llx ]\n", (long long)relative_addr, (long long)idata);
386 #endif
387 }
388
389 switch (relative_addr) {
390
391 case CRMFB_CTRLSTAT: // 0x0
392 if (writeflag == MEM_WRITE) {
393 debug("[ sgi_gbe: write to ctrlstat: 0x%08x ]\n", (int)idata);
394 d->ctrlstat = (idata & ~CRMFB_CTRLSTAT_CHIPID_MASK)
395 | (d->ctrlstat & CRMFB_CTRLSTAT_CHIPID_MASK);
396 } else
397 odata = d->ctrlstat;
398 break;
399
400 case CRMFB_DOTCLOCK: // 0x4
401 if (writeflag == MEM_WRITE)
402 d->dotclock = idata;
403 else
404 odata = d->dotclock;
405 break;
406
407 case CRMFB_I2C_VGA: // 0x8
408 /*
409 * "CRT I2C control".
410 *
411 * I'm not sure what this does. It isn't really commented
412 * in the Linux sources. The IP32 PROM writes the values
413 * 0x03, 0x01, and then 0x00 to this address, and then
414 * reads back a value.
415 */
416 if (writeflag == MEM_WRITE) {
417 //if (!(d->i2c & CRMFB_I2C_SCL) &&
418 // (idata & CRMFB_I2C_SCL)) {
419 // fatal("vga i2c data: %i\n", idata & CRMFB_I2C_SDA);
420 //}
421
422 d->i2c = idata;
423 } else {
424 odata = d->i2c;
425 odata |= 1; /* ? The IP32 prom wants this? */
426 }
427 break;
428
429 case CRMFB_I2C_FP: // 0x10, i2cfp, flat panel control
430 if (writeflag == MEM_WRITE) {
431 //if (d->i2c & CRMFB_I2C_SCL &&
432 // !(idata & CRMFB_I2C_SCL)) {
433 // fatal("fp i2c data: %i\n", idata & CRMFB_I2C_SDA);
434 //}
435
436 d->i2cfp = idata;
437 } else {
438 odata = d->i2cfp;
439 odata |= 1; /* ? The IP32 prom wants this? */
440 }
441 break;
442
443 case CRMFB_DEVICE_ID: // 0x14
444 odata = CRMFB_DEVICE_ID_DEF;
445 break;
446
447 case CRMFB_VT_XY: // 0x10000
448 if (writeflag == MEM_WRITE)
449 d->freeze = idata & 0x80000000;
450 else {
451 /*
452 * vt_xy, according to Linux:
453 *
454 * bit 31 = freeze, 23..12 = cury, 11.0 = curx
455 */
456 /* odata = ((random() % (d->yres + 10)) << 12)
457 + (random() % (d->xres + 10)) +
458 d->freeze; */
459
460 /*
461 * Hack for IRIX/IP32. During startup, it waits for
462 * the value to be over 0x400 (in "gbeRun").
463 *
464 * Hack for the IP32 PROM: During startup, it waits
465 * for the value to be above 0x500 (I think).
466 */
467 odata = d->freeze | (random() & 1 ? 0x3ff : 0x501);
468 }
469 break;
470
471 case CRMFB_VT_XYMAX: // 0x10004, vt_xymax, according to Linux & NetBSD
472 odata = ((d->yres-1) << 12) + d->xres-1;
473 /* ... 12 bits maxy, 12 bits maxx. */
474 break;
475
476 case CRMFB_VT_VSYNC: // 0x10008
477 case CRMFB_VT_HSYNC: // 0x1000c
478 case CRMFB_VT_VBLANK: // 0x10010
479 case CRMFB_VT_HBLANK: // 0x10014
480 // TODO
481 break;
482
483 case CRMFB_VT_FLAGS: // 0x10018
484 // OpenBSD/sgi writes to this register.
485 break;
486
487 case CRMFB_VT_FRAMELOCK: // 0x1001c
488 // TODO.
489 break;
490
491 case CRMFB_VT_INTR01: // 0x10020
492 if (writeflag == MEM_WRITE)
493 d->y_intr01 = idata;
494 break;
495
496 case CRMFB_VT_INTR23: // 0x10024
497 if (writeflag == MEM_WRITE)
498 d->y_intr23 = idata;
499 break;
500
501 case 0x10028: // 0x10028
502 case 0x1002c: // 0x1002c
503 case 0x10030: // 0x10030
504 // TODO: Unknown, written to by the PROM?
505 break;
506
507 case CRMFB_VT_HPIX_EN: // 0x10034, vt_hpixen, according to Linux
508 odata = (0 << 12) + d->xres-1;
509 /* ... 12 bits on, 12 bits off. */
510 break;
511
512 case CRMFB_VT_VPIX_EN: // 0x10038, vt_vpixen, according to Linux
513 odata = (0 << 12) + d->yres-1;
514 /* ... 12 bits on, 12 bits off. */
515 break;
516
517 case CRMFB_VT_HCMAP: // 0x1003c
518 if (writeflag == MEM_WRITE) {
519 d->xres = (idata & CRMFB_HCMAP_ON_MASK) >> CRMFB_VT_HCMAP_ON_SHIFT;
520 dev_fb_resize(d->fb_data, d->xres, d->yres);
521 }
522
523 odata = (d->xres << CRMFB_VT_HCMAP_ON_SHIFT) + d->xres + 100;
524 break;
525
526 case CRMFB_VT_VCMAP: // 0x10040
527 if (writeflag == MEM_WRITE) {
528 d->yres = (idata & CRMFB_VCMAP_ON_MASK) >> CRMFB_VT_VCMAP_ON_SHIFT;
529 dev_fb_resize(d->fb_data, d->xres, d->yres);
530 }
531
532 odata = (d->yres << CRMFB_VT_VCMAP_ON_SHIFT) + d->yres + 100;
533 break;
534
535 case CRMFB_VT_DID_STARTXY: // 0x10044
536 case CRMFB_VT_CRS_STARTXY: // 0x10048
537 case CRMFB_VT_VC_STARTXY: // 0x1004c
538 // TODO
539 break;
540
541 case CRMFB_OVR_WIDTH_TILE: // 0x20000
542 if (writeflag == MEM_WRITE) {
543 d->ovr_tilesize = idata;
544
545 d->ovr_width_in_tiles = (idata >> CRMFB_FRM_TILESIZE_WIDTH_SHIFT) & 0xff;
546 d->ovr_partial_pixels = ((idata >> CRMFB_FRM_TILESIZE_RHS_SHIFT) & 0x1f) * 32;
547
548 debug("[ sgi_gbe: OVR setting width in tiles = %i, partial pixels = %i ]\n",
549 d->ovr_width_in_tiles, d->ovr_partial_pixels);
550 } else
551 odata = d->ovr_tilesize;
552 break;
553
554 case CRMFB_OVR_TILE_PTR: // 0x20004
555 odata = d->ovr_control ^ (random() & 1);
556 break;
557
558 case CRMFB_OVR_CONTROL: // 0x20008
559 if (writeflag == MEM_WRITE)
560 d->ovr_control = idata;
561 else
562 odata = d->ovr_control;
563 break;
564
565 case CRMFB_FRM_TILESIZE: // 0x30000:
566 if (writeflag == MEM_WRITE) {
567 d->tilesize = idata;
568
569 d->bitdepth = 8 << ((d->tilesize >> CRMFB_FRM_TILESIZE_DEPTH_SHIFT) & 3);
570 d->width_in_tiles = (idata >> CRMFB_FRM_TILESIZE_WIDTH_SHIFT) & 0xff;
571 d->partial_pixels = ((idata >> CRMFB_FRM_TILESIZE_RHS_SHIFT) & 0x1f) * 32 * 8 / d->bitdepth;
572
573 debug("[ sgi_gbe: setting color depth to %i bits, width in tiles = %i, partial pixels = %i ]\n",
574 d->bitdepth, d->width_in_tiles, d->partial_pixels);
575 } else
576 odata = d->tilesize;
577 break;
578
579 case CRMFB_FRM_PIXSIZE: // 0x30004
580 if (writeflag == MEM_WRITE) {
581 debug("[ sgi_gbe: setting PIXSIZE to 0x%08x ]\n", (int)idata);
582 }
583 break;
584
585 case 0x30008:
586 // TODO: Figure out exactly what the low bits do.
587 // Irix seems to want 0x20 to "sometimes" be on or off here.
588 odata = d->frm_control ^ (random() & 0x20);
589 break;
590
591 case CRMFB_FRM_CONTROL: // 0x3000c
592 /*
593 * Writes to 3000c should be readable back at 30008?
594 * At least bit 0 (dma) ctrl 3.
595 */
596 if (writeflag == MEM_WRITE) {
597 d->frm_control = idata;
598 debug("[ sgi_gbe: frm_control = 0x%08x ]\n", d->frm_control);
599 } else
600 odata = d->frm_control;
601 break;
602
603 case CRMFB_DID_PTR: // 0x40000
604 odata = random(); /* IP32 prom test hack. TODO */
605 /* IRIX wants 0x20, it seems. */
606 if (random() & 1)
607 odata = 0x20;
608 break;
609
610 case CRMFB_DID_CONTROL: // 0x40004
611 // TODO
612 break;
613
614 case CRMFB_CMAP_FIFO: // 0x58000
615 break;
616
617 case CRMFB_CURSOR_POS: // 0x70000
618 if (writeflag == MEM_WRITE)
619 d->cursor_pos = idata;
620 else
621 odata = d->cursor_pos;
622 break;
623
624 case CRMFB_CURSOR_CONTROL: // 0x70004
625 if (writeflag == MEM_WRITE)
626 d->cursor_control = idata;
627 else
628 odata = d->cursor_control;
629 break;
630
631 case CRMFB_CURSOR_CMAP0: // 0x70008
632 if (writeflag == MEM_WRITE)
633 d->cursor_cmap0 = idata;
634 else
635 odata = d->cursor_cmap0;
636 break;
637
638 case CRMFB_CURSOR_CMAP1: // 0x7000c
639 if (writeflag == MEM_WRITE)
640 d->cursor_cmap1 = idata;
641 else
642 odata = d->cursor_cmap1;
643 break;
644
645 case CRMFB_CURSOR_CMAP2: // 0x70010
646 if (writeflag == MEM_WRITE)
647 d->cursor_cmap2 = idata;
648 else
649 odata = d->cursor_cmap2;
650 break;
651
652 /*
653 * Linux/sgimips seems to write color palette data to offset 0x50000
654 * to 0x503xx, and gamma correction data to 0x60000 - 0x603ff, as
655 * 32-bit values at addresses divisible by 4 (formated as 0xrrggbb00).
656 *
657 * "sgio2fb: initializing
658 * sgio2fb: I/O at 0xffffffffb6000000
659 * sgio2fb: tiles at ffffffffa2ef5000
660 * sgio2fb: framebuffer at ffffffffa1000000
661 * sgio2fb: 8192kB memory
662 * Console: switching to colour frame buffer device 80x30"
663 *
664 * NetBSD's crmfb_set_palette, however, uses values in reverse, like this:
665 * val = (r << 8) | (g << 16) | (b << 24);
666 */
667
668 default:
669 /* WID at 0x48000 .. 0x48000 + 4*31: */
670 if (relative_addr >= CRMFB_WID && relative_addr <= CRMFB_WID + 4 * 31) {
671 // TODO: Figure out how this really works. Why are
672 // there 32 such registers?
673 if (writeflag == MEM_WRITE) {
674 d->color_mode = (idata >> CRMFB_MODE_TYP_SHIFT) & 7;
675 d->cmap_select = (idata >> CRMFB_MODE_CMAP_SELECT_SHIFT) & 0x1f;
676 }
677
678 break;
679 }
680
681 /* RGB Palette at 0x50000 .. 0x57fff: */
682 if (relative_addr >= CRMFB_CMAP && relative_addr < CRMFB_CMAP + 256 * 32 * sizeof(uint32_t)) {
683 int color_index = (relative_addr - CRMFB_CMAP) >> 2;
684 if (writeflag == MEM_WRITE) {
685 int cmap = color_index >> 8;
686 d->palette[color_index] = idata;
687 if (cmap == d->cmap_select)
688 d->selected_palette[color_index] = idata;
689 } else {
690 odata = d->palette[color_index];
691 }
692 break;
693 }
694
695 /* Gamma correction at 0x60000 .. 0x603ff: */
696 if (relative_addr >= CRMFB_GMAP && relative_addr <= CRMFB_GMAP + 0x3ff) {
697 /* ignore gamma correction for now */
698 break;
699 }
700
701 /* Cursor bitmap at 0x78000 ..: */
702 if (relative_addr >= CRMFB_CURSOR_BITMAP && relative_addr <= CRMFB_CURSOR_BITMAP + 0xff) {
703 if (len != 4) {
704 printf("unimplemented CRMFB_CURSOR_BITMAP len %i\n", (int)len);
705 }
706
707 int index = (relative_addr & 0xff) / 4;
708 if (writeflag == MEM_WRITE)
709 d->cursor_bitmap[index] = idata;
710 else
711 odata = d->cursor_bitmap[index];
712 break;
713 }
714
715 if (writeflag == MEM_WRITE)
716 fatal("[ sgi_gbe: unimplemented write to address "
717 "0x%llx, data=0x%llx ]\n",
718 (long long)relative_addr, (long long)idata);
719 else
720 fatal("[ sgi_gbe: unimplemented read from address "
721 "0x%llx ]\n", (long long)relative_addr);
722 }
723
724 if (writeflag == MEM_READ) {
725 #ifdef GBE_DEBUG
726 debug("[ sgi_gbe: DEBUG: read from address 0x%llx: 0x%llx ]\n",
727 (long long)relative_addr, (long long)odata);
728 #endif
729 memory_writemax64(cpu, data, len, odata);
730 }
731
732 return 1;
733 }
734
735
dev_sgi_gbe_init(struct machine * machine,struct memory * mem,uint64_t baseaddr)736 void dev_sgi_gbe_init(struct machine *machine, struct memory *mem, uint64_t baseaddr)
737 {
738 struct sgi_gbe_data *d;
739
740 CHECK_ALLOCATION(d = (struct sgi_gbe_data *) malloc(sizeof(struct sgi_gbe_data)));
741 memset(d, 0, sizeof(struct sgi_gbe_data));
742
743 d->xres = GBE_DEFAULT_XRES;
744 d->yres = GBE_DEFAULT_YRES;
745 d->bitdepth = GBE_DEFAULT_BITDEPTH;
746
747 // My O2 says 0x300ae001 here (while running).
748 d->ctrlstat = CRMFB_CTRLSTAT_INTERNAL_PCLK |
749 CRMFB_CTRLSTAT_GPIO6_INPUT |
750 CRMFB_CTRLSTAT_GPIO5_INPUT |
751 CRMFB_CTRLSTAT_GPIO4_INPUT |
752 CRMFB_CTRLSTAT_GPIO4_SENSE |
753 CRMFB_CTRLSTAT_GPIO3_INPUT |
754 (CRMFB_CTRLSTAT_CHIPID_MASK & 1);
755
756 // Set a value in the interrupt register that will "never happen" by default.
757 d->y_intr01 = (0xfff << 12) | 0xfff;
758 d->y_intr23 = (0xfff << 12) | 0xfff;
759
760 // Grayscale palette, most likely overwritten immediately by the
761 // guest operating system.
762 for (int i = 0; i < 256; ++i)
763 d->palette[i] = i * 0x01010100UL;
764
765 d->fb_data = dev_fb_init(machine, mem, FAKE_GBE_FB_ADDRESS,
766 VFB_GENERIC, d->xres, d->yres, d->xres, d->yres, 24, "SGI GBE");
767
768 memory_device_register(mem, "sgi_gbe", baseaddr, DEV_SGI_GBE_LENGTH,
769 dev_sgi_gbe_access, d, DM_DEFAULT, NULL);
770 machine_add_tickfunction(machine, dev_sgi_gbe_tick, d, 19);
771 }
772
773
774