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