1 /*
2  *  Copyright (C) 2004-2020  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: TURBOchannel Pixelstamp graphics card
29  *
30  *	PMAG-CA = PX
31  *	PMAG-DA = PXG
32  *	PMAG-EA = PXG+
33  *	PMAG-FA = PXG+ TURBO
34  *
35  *  See include/pxreg.h (and NetBSD's arch/pmax/dev/px.c) for more information.
36  *
37  *  The emulation of this device is far from complete. Different pixelstamp
38  *  boards are recognizes under different names depending on operating system:
39  *
40  *	NetBSD/pmax:  (works fine both with and without console on framebuffer)
41  *		PMAG-CA:	px0 at tc0 slot 0 offset 0x0: 2D, 4x1 stamp,
42  *				  8 plane
43  *		PMAG-DA:	px0 at tc0 slot 0 offset 0x0: 3D, 4x1 stamp,
44  *				  8 plane, 128KB SRAM
45  *		PMAG-EA:	(not supported)
46  *		PMAG-FA:	px0 at tc0 slot 0 offset 0x0: 3D, 5x2 stamp,
47  *				  24 plane, 128KB SRAM
48  *
49  *	Ultrix 4.2A rev 47:  (usually crashes if the device is installed, but
50  *						serial console is used)
51  *		PMAG-CA:	px0 at ibus0, pa0 (5x1 8+8+0+0)
52  *		PMAG-DA:	px0 at ibus0, pq0 (5x1 16+16+16+0 128KB)
53  *				    or (5x1 0+0+16+0 128KB)
54  *		PMAG-EA:	(not supported)
55  *		PMAG-FA:	px0 at ibus0, pq0 (5x2 24+24+16+16 128KB)
56  *
57  *	Ultrix 4.2 rev 85:  (usually crashes if the device is installed,
58  *					 but serial console is used)
59  *		PMAG-CA:	ga0 at ibus0, ga0 ( 8 planes 4x1 stamp )
60  *		PMAG-DA:	gq0 at ibus0, gq0 ( 8+8+16Z+0X plane 4x1 stamp )
61  *		PMAG-EA:	(not supported)
62  *		PMAG-FA:	gq0 at ibus0, gq0 ( 24+24+24Z+24X plane
63  *				 5x2 stamp )  (crashes in serial console mode)
64  *
65  *  TODO:  A lot of stuff:
66  *
67  *	Read http://www.mit.edu/afs/athena/system/pmax_ul3/srvd.73/sys/
68  *		io/tc/gq.h
69  *	and try to figure out the interrupt and memory management stuff.
70  *
71  *	Color support: foreground, background, 8-bit palette?
72  *	2D and 3D stuff: polygons? shading?
73  *	Don't use so many hardcoded values.
74  *	Actually interpret the values in each command, don't just
75  *		assume NetBSD/Ultrix usage.
76  *	Factor out the DMA read (main memory vs sram).
77  *	Interrupts?
78  *	Make sure that everything works with both NetBSD and Ultrix.
79  */
80 
81 #include <stdio.h>
82 #include <stdlib.h>
83 #include <string.h>
84 
85 #include "cpu.h"
86 #include "devices.h"
87 #include "machine.h"
88 #include "memory.h"
89 #include "misc.h"
90 
91 #include "thirdparty/pxreg.h"
92 
93 #define	PX_XSIZE	1280
94 #define	PX_YSIZE	1024
95 
96 static bool px_debug = false;
97 
98 
DEVICE_TICK(px)99 DEVICE_TICK(px)
100 {
101 #if 0
102 	struct px_data *d = extra;
103 
104 	if (d->intr & STIC_INT_P_EN)		/*  or _WE ?  */
105 		INTERRUPT_ASSERT(d->irq);
106 #endif
107 }
108 
109 
110 /*
111  *  px_readword():
112  *
113  *  Helper function to read 32-bit words from DMA memory,
114  *  to allow both little and big endian accesses.
115  *  (DECstations probably only use little endian access,
116  *  but endianness-independance is probably nice to have anyway.)
117  */
px_readword(struct cpu * cpu,unsigned char * dma_buf,int ofs)118 uint32_t px_readword(struct cpu *cpu, unsigned char *dma_buf, int ofs)
119 {
120 	if (cpu->byte_order == EMUL_LITTLE_ENDIAN)
121 		return dma_buf[ofs+0] + (dma_buf[ofs+1] << 8) +
122 		    (dma_buf[ofs+2] << 16) + (dma_buf[ofs+3] << 24);
123 	else
124 		return dma_buf[ofs+3] + (dma_buf[ofs+2] << 8) +
125 		    (dma_buf[ofs+1] << 16) + (dma_buf[ofs+0] << 24);
126 }
127 
128 
129 /*
130  *  dev_px_dma():
131  *
132  *  This routine performs a (fake) DMA transfer of STAMP commands
133  *  and executes them.
134  *
135  *  For the "PX" board, read from main memory (cpu->mem). For all other
136  *  boards, read from the i860 SRAM portion of the device (d->sram).
137  */
dev_px_dma(struct cpu * cpu,uint32_t sys_addr,struct px_data * d)138 void dev_px_dma(struct cpu *cpu, uint32_t sys_addr, struct px_data *d)
139 {
140 	unsigned char dma_buf[32768];
141 	size_t dma_len = sizeof(dma_buf);
142 	int bytesperpixel;
143 	uint32_t cmdword;
144 
145 	bytesperpixel = d->bitdepth >> 3;
146 
147 	dma_len = 56 * 4;	/*  TODO: this is just enough for NetBSD's
148 					 putchar  */
149 
150 	if (d->type == DEV_PX_TYPE_PX) {
151 		cpu->memory_rw(cpu, cpu->mem, sys_addr, dma_buf,
152 		    dma_len, MEM_READ, NO_EXCEPTIONS | PHYSICAL);
153 	} else {
154 		/*  TODO:  past end of sram?  */
155 		memmove(dma_buf, &d->sram[sys_addr & 0x1ffff], dma_len);
156 	}
157 
158 	if (cpu->byte_order == EMUL_LITTLE_ENDIAN)
159 		cmdword = dma_buf[0] + (dma_buf[1] << 8) +
160 		    (dma_buf[2] << 16) + (dma_buf[3] << 24);
161 	else
162 		cmdword = dma_buf[3] + (dma_buf[2] << 8) +
163 		    (dma_buf[1] << 16) + (dma_buf[0] << 24);
164 
165 	if (px_debug) {
166 		debug("[ px: dma from 0x%08x: ", (int)sys_addr);
167 
168 		debug("cmd=");
169 		switch (cmdword & 0xf) {
170 		case STAMP_CMD_POINTS:		debug("points");	break;
171 		case STAMP_CMD_LINES:		debug("lines");		break;
172 		case STAMP_CMD_TRIANGLES:	debug("triangles");	break;
173 		case STAMP_CMD_COPYSPANS:	debug("copyspans");	break;
174 		case STAMP_CMD_READSPANS:	debug("readspans");	break;
175 		case STAMP_CMD_WRITESPANS:	debug("writespans");	break;
176 		case STAMP_CMD_VIDEO:		debug("video");		break;
177 		default:
178 			debug("0x%x (?)", cmdword & 0xf);
179 		}
180 
181 		debug(",rgb=");
182 		switch (cmdword & 0x30) {
183 		case STAMP_RGB_NONE:	debug("none");		break;
184 		case STAMP_RGB_CONST:	debug("const");		break;
185 		case STAMP_RGB_FLAT:	debug("flat");		break;
186 		case STAMP_RGB_SMOOTH:	debug("smooth");	break;
187 		default:
188 			debug("0x%x (?)", cmdword & 0x30);
189 		}
190 
191 		debug(",z=");
192 		switch (cmdword & 0xc0) {
193 		case STAMP_Z_NONE:	debug("none");		break;
194 		case STAMP_Z_CONST:	debug("const");		break;
195 		case STAMP_Z_FLAT:	debug("flat");		break;
196 		case STAMP_Z_SMOOTH:	debug("smooth");	break;
197 		default:
198 			debug("0x%x (?)", cmdword & 0xc0);
199 		}
200 
201 		debug(",xy=");
202 		switch (cmdword & 0x300) {
203 		case STAMP_XY_NONE:		debug("none");		break;
204 		case STAMP_XY_PERPACKET:	debug("perpacket");	break;
205 		case STAMP_XY_PERPRIMATIVE:	debug("perprimative");	break;
206 		default:
207 			debug("0x%x (?)", cmdword & 0x300);
208 		}
209 
210 		debug(",lw=");
211 		switch (cmdword & 0xc00) {
212 		case STAMP_LW_NONE:		debug("none");		break;
213 		case STAMP_LW_PERPACKET:	debug("perpacket");	break;
214 		case STAMP_LW_PERPRIMATIVE:	debug("perprimative");	break;
215 		default:
216 			debug("0x%x (?)", cmdword & 0xc00);
217 		}
218 
219 		if (cmdword & STAMP_CLIPRECT)
220 			debug(",CLIPRECT");
221 		if (cmdword & STAMP_MESH)
222 			debug(",MESH");
223 		if (cmdword & STAMP_AALINE)
224 			debug(",AALINE");
225 		if (cmdword & STAMP_HS_EQUALS)
226 			debug(",HS_EQUALS");
227 
228 		for (size_t i=0; i<dma_len; i++)
229 			debug(" %02x", dma_buf[i]);
230 
231 		debug(" ]\n");
232 	}
233 
234 	/*  NetBSD and Ultrix copyspans  */
235 	if (cmdword == 0x405) {
236 		uint32_t nspans, lw;
237 		uint32_t spannr, ofs;
238 		uint32_t span_len, span_src, span_dst;
239 		/*  unsigned char pixels[PX_XSIZE * 3];  */
240 
241 		if (cpu->byte_order == EMUL_LITTLE_ENDIAN)
242 			nspans = dma_buf[4] + (dma_buf[5] << 8) +
243 			    (dma_buf[6] << 16) + (dma_buf[7] << 24);
244 		else
245 			nspans = dma_buf[7] + (dma_buf[6] << 8) +
246 			    (dma_buf[5] << 16) + (dma_buf[4] << 24);
247 
248 		if (cpu->byte_order == EMUL_LITTLE_ENDIAN)
249 			lw = dma_buf[16] + (dma_buf[17] << 8) +
250 			    (dma_buf[18] << 16) + (dma_buf[19] << 24);
251 		else
252 			lw = dma_buf[19] + (dma_buf[18] << 8) +
253 			    (dma_buf[17] << 16) + (dma_buf[16] << 24);
254 
255 		nspans >>= 24;
256 		/*  Why not this?  lw = (lw + 1) >> 2;  */
257 
258 		if (px_debug)
259 			debug("[ px: copyspans:  nspans = %i, lw = %i ]\n", nspans, lw);
260 
261 		/*  Reread copyspans command if it wasn't completely read:  */
262 		if (dma_len < 4*(5 + nspans*3)) {
263 			dma_len = 4 * (5+nspans*3);
264 			if (d->type == DEV_PX_TYPE_PX)
265 				cpu->memory_rw(cpu, cpu->mem, sys_addr,
266 				    dma_buf, dma_len, MEM_READ,
267 				    NO_EXCEPTIONS | PHYSICAL);
268 			else
269 				memmove(dma_buf, &d->sram[sys_addr & 0x1ffff],
270 				    dma_len);	/*  TODO:  past end of sram?  */
271 		}
272 
273 		ofs = 4*5;
274 		for (spannr=0; spannr<nspans; spannr++) {
275 			if (cpu->byte_order == EMUL_LITTLE_ENDIAN)
276 				span_len = dma_buf[ofs+0] + (dma_buf[ofs+1] <<
277 				    8) + (dma_buf[ofs+2] << 16) +
278 				    (dma_buf[ofs+3] << 24);
279 			else
280 				span_len = dma_buf[ofs+3] + (dma_buf[ofs+2] <<
281 				    8) + (dma_buf[ofs+1] << 16) +
282 				    (dma_buf[ofs+0] << 24);
283 			ofs += 4;
284 
285 			if (cpu->byte_order == EMUL_LITTLE_ENDIAN)
286 				span_src = dma_buf[ofs+0] + (dma_buf[ofs+1] <<
287 				    8) + (dma_buf[ofs+2] << 16) +
288 				    (dma_buf[ofs+3] << 24);
289 			else
290 				span_src = dma_buf[ofs+3] + (dma_buf[ofs+2] <<
291 				    8) + (dma_buf[ofs+1] << 16) +
292 				    (dma_buf[ofs+0] << 24);
293 			ofs += 4;
294 
295 			if (cpu->byte_order == EMUL_LITTLE_ENDIAN)
296 				span_dst = dma_buf[ofs+0] + (dma_buf[ofs+1] <<
297 				    8) + (dma_buf[ofs+2] << 16) +
298 				    (dma_buf[ofs+3] << 24);
299 			else
300 				span_dst = dma_buf[ofs+3] + (dma_buf[ofs+2] <<
301 				    8) + (dma_buf[ofs+1] << 16) +
302 				    (dma_buf[ofs+0] << 24);
303 			ofs += 4;
304 
305 			span_len >>= 3;
306 			span_dst >>= 3;
307 			span_src >>= 3;
308 
309 			if (span_len > PX_XSIZE)
310 				span_len = PX_XSIZE;
311 
312 			/*  debug("  span %i: len=%i src=%i dst=%i\n",
313 			    spannr, span_len, span_src, span_dst);  */
314 
315 			memmove(d->vfb_data->framebuffer + span_dst *
316 			    PX_XSIZE * bytesperpixel, d->vfb_data->framebuffer
317 			    + span_src * PX_XSIZE * bytesperpixel, span_len *
318 			    bytesperpixel);
319 
320 			d->vfb_data->update_x1 = 0; d->vfb_data->update_x2 =
321 			    PX_XSIZE-1;
322 			if ((int32_t)span_dst < d->vfb_data->update_y1)
323 				d->vfb_data->update_y1 = span_dst;
324 			if ((int32_t)span_dst > d->vfb_data->update_y2)
325 				d->vfb_data->update_y2 = span_dst;
326 			if ((int32_t)span_src < d->vfb_data->update_y1)
327 				d->vfb_data->update_y1 = span_src;
328 			if ((int32_t)span_src > d->vfb_data->update_y2)
329 				d->vfb_data->update_y2 = span_src;
330 		}
331 	}
332 
333 	/*  NetBSD and Ultrix erasecols/eraserows  */
334 	if (cmdword == 0x411) {
335 		uint32_t v1, v2, attr;
336 		int32_t lw;
337 		int x,y,x2,y2;
338 		int fb_y;
339 		int bg_r, bg_g, bg_b;
340 		unsigned char pixels[PX_XSIZE * 3];
341 
342 		lw = px_readword(cpu, dma_buf, 16);
343 		attr = px_readword(cpu, dma_buf, 20);
344 		v1 = px_readword(cpu, dma_buf, 24);
345 		v2 = px_readword(cpu, dma_buf, 28);
346 #if 0
347 		if (cpu->byte_order == EMUL_LITTLE_ENDIAN)
348 			lw = dma_buf[16] + (dma_buf[17] << 8) +
349 			    (dma_buf[18] << 16) + (dma_buf[19] << 24);
350 		else
351 			lw = dma_buf[19] + (dma_buf[18] << 8) +
352 			    (dma_buf[17] << 16) + (dma_buf[16] << 24);
353 
354 		if (cpu->byte_order == EMUL_LITTLE_ENDIAN)
355 			v1 = dma_buf[24] + (dma_buf[25] << 8) +
356 			    (dma_buf[26] << 16) + (dma_buf[27] << 24);
357 		else
358 			v1 = dma_buf[27] + (dma_buf[26] << 8) +
359 			    (dma_buf[25] << 16) + (dma_buf[24] << 24);
360 
361 		if (cpu->byte_order == EMUL_LITTLE_ENDIAN)
362 			v2 = dma_buf[28] + (dma_buf[29] << 8) +
363 			    (dma_buf[30] << 16) + (dma_buf[31] << 24);
364 		else
365 			v2 = dma_buf[31] + (dma_buf[30] << 8) +
366 			    (dma_buf[29] << 16) + (dma_buf[28] << 24);
367 #endif
368 		bg_r = (attr >> 16) & 255;
369 		bg_g = (attr >> 8) & 255;
370 		bg_b = attr & 255;
371 		if (bg_r == 0)
372 			bg_r = bg_g = bg_b = 0;
373 		else
374 		if (bg_r == 7)
375 			bg_r = bg_g = bg_b = 192;
376 		else
377 			bg_r = bg_g = bg_b = 255;
378 
379 		v1 -= lw;
380 		v2 -= lw;
381 
382 		x = (v1 >> 19) & 2047;
383 		y = (v1 >> 3) & 1023;
384 		x2 = (v2 >> 19) & 2047;
385 		y2 = (v2 >> 3) & 1023;
386 
387 		lw = (lw + 1) >> 2;
388 
389 		if (x2 - x > PX_XSIZE)
390 			x2 = PX_XSIZE;
391 
392 		if (px_debug)
393 			debug("[ px: clear/fill: v1 = 0x%08x  v2 = 0x%08x "
394 			    "lw=%i x=%i y=%i x2=%i y2=%i ]\n", (int)v1, (int)v2,
395 			    lw, x,y, x2,y2);
396 
397 		if (bytesperpixel == 3) {
398 			int xi;
399 			for (xi=0; xi<x2-x; xi++) {
400 				/*  TODO:  rgb order?  */
401 				pixels[xi*3 + 0] = bg_r;
402 				pixels[xi*3 + 1] = bg_g;
403 				pixels[xi*3 + 2] = bg_b;
404 			}
405 		} else
406 			memset(pixels, attr, (x2 - x) * bytesperpixel);
407 
408 		if (x < d->vfb_data->update_x1)
409 			d->vfb_data->update_x1 = x;
410 		if (x2 > d->vfb_data->update_x2)
411 			d->vfb_data->update_x2 = x2;
412 
413 		for (fb_y=y; fb_y < y2 + lw; fb_y ++) {
414 			memcpy(d->vfb_data->framebuffer + (fb_y * PX_XSIZE + x)
415 			    * bytesperpixel, pixels, (x2-x)*bytesperpixel);
416 
417 			if (fb_y < d->vfb_data->update_y1)
418 				d->vfb_data->update_y1 = fb_y;
419 			if (fb_y > d->vfb_data->update_y2)
420 				d->vfb_data->update_y2 = fb_y;
421 		}
422 	}
423 
424 	/*  NetBSD and Ultrix putchar  */
425 	if (cmdword == 0xa21) {
426 		/*  Ugly test code:  */
427 		unsigned char pixels[16 * 3];
428 		int pixels_len = 16;
429 		uint32_t v1, v2, fgcolor, bgcolor;
430 		int x, y, x2,y2, i, maxi;
431 		int xbit;
432 		int suby;
433 		int fg_r, fg_g, fg_b;
434 		int bg_r, bg_g, bg_b;
435 
436 		v1 = px_readword(cpu, dma_buf, 52);
437 		v2 = px_readword(cpu, dma_buf, 56);
438 		fgcolor = px_readword(cpu, dma_buf, 16 * 4);
439 		bgcolor = px_readword(cpu, dma_buf, 29 * 4);
440 
441 		/*
442 		 *  TODO:  Which one is r, which one is g, and which one is b?
443 		 *  TODO 2:  Use the BT459 palette, these values are hardcoded
444 		 *  for NetBSD and Ultrix grayscale only.
445 		 */
446 		fg_r = (fgcolor >> 16) & 255;
447 		fg_g = (fgcolor >> 8) & 255;
448 		fg_b = fgcolor & 255;
449 		if (fg_r == 0)
450 			fg_r = fg_g = fg_b = 0;
451 		else
452 		if (fg_r == 7)
453 			fg_r = fg_g = fg_b = 192;
454 		else
455 			fg_r = fg_g = fg_b = 255;
456 
457 		bg_r = (bgcolor >> 16) & 255;
458 		bg_g = (bgcolor >> 8) & 255;
459 		bg_b = bgcolor & 255;
460 		if (bg_r == 0)
461 			bg_r = bg_g = bg_b = 0;
462 		else
463 		if (bg_r == 7)
464 			bg_r = bg_g = bg_b = 192;
465 		else
466 			bg_r = bg_g = bg_b = 255;
467 
468 		x = (v1 >> 19) & 2047;
469 		y = ((v1 - 63) >> 3) & 1023;
470 		x2 = (v2 >> 19) & 2047;
471 		y2 = ((v2 - 63) >> 3) & 1023;
472 
473 		if (px_debug)
474 			debug("[ px putchar: v1 = 0x%08x  v2 = 0x%08x x=%i y=%i ]\n",
475 			    (int)v1, (int)v2, x,y, x2,y2);
476 
477 		x %= PX_XSIZE;
478 		y %= PX_YSIZE;
479 		x2 %= PX_XSIZE;
480 		y2 %= PX_YSIZE;
481 
482 		pixels_len = x2 - x;
483 
484 		suby = 0;
485 		maxi = 12;
486 		maxi = 33;
487 
488 		for (i=4; i<maxi; i++) {
489 			int j;
490 
491 			if (i == 12)
492 				i = 30;
493 
494 			for (j=0; j<2; j++) {
495 				for (xbit = 0; xbit < 8; xbit ++) {
496 					if (bytesperpixel == 3) {
497 						/*  24-bit:  */
498 						/*  TODO:  Which one is r,
499 						    which one is g, and b?  */
500 						pixels[xbit * 3 + 0] =
501 						    (dma_buf[i*4 + j*2 + 0] &
502 						    (1 << xbit))? fg_r : bg_r;
503 						pixels[xbit * 3 + 1] =
504 						    (dma_buf[i*4 + j*2 + 0] &
505 						    (1 << xbit))? fg_g : bg_g;
506 						pixels[xbit * 3 + 2] =
507 						    (dma_buf[i*4 + j*2 + 0] &
508 						    (1 << xbit))? fg_b : bg_b;
509 						pixels[(xbit + 8) * 3 + 0] =
510 						    (dma_buf[i*4 + j*2 + 1] &
511 						    (1 << xbit))? fg_r : bg_r;
512 						pixels[(xbit + 8) * 3 + 1] =
513 						    (dma_buf[i*4 + j*2 + 1] &
514 						    (1 << xbit))? fg_g : bg_g;
515 						pixels[(xbit + 8) * 3 + 2] =
516 						    (dma_buf[i*4 + j*2 + 1] &
517 						    (1 << xbit))? fg_b : bg_b;
518 					} else {
519 						/*  8-bit:  */
520 						pixels[xbit] = (dma_buf[i*4 +
521 						    j*2 + 0] & (1 << xbit))?
522 						    (fgcolor & 255) :
523 						    (bgcolor & 255);
524 						pixels[xbit + 8] = (dma_buf[i*4
525 						    + j*2 + 1] & (1 << xbit))?
526 						    (fgcolor & 255) :
527 						    (bgcolor & 255);
528 					}
529 				}
530 
531 				memcpy(d->vfb_data->framebuffer + ((y+suby)
532 				    * PX_XSIZE + x) * bytesperpixel,
533 				    pixels, pixels_len * bytesperpixel);
534 
535 				if (y+suby < d->vfb_data->update_y1)
536 					d->vfb_data->update_y1 = y+suby;
537 				if (y+suby > d->vfb_data->update_y2)
538 					d->vfb_data->update_y2 = y+suby;
539 
540 				suby ++;
541 			}
542 
543 		if (x < d->vfb_data->update_x1)
544 			d->vfb_data->update_x1 = x;
545 		if (x2 > d->vfb_data->update_x2)
546 			d->vfb_data->update_x2 = x2;
547 		}
548 	}
549 }
550 
551 
DEVICE_ACCESS(px)552 DEVICE_ACCESS(px)
553 {
554 	uint64_t idata = 0, odata = 0;
555 	struct px_data *d = (struct px_data *) extra;
556 	size_t i;
557 
558 	if (writeflag == MEM_WRITE)
559 		idata = memory_readmax64(cpu, data, len);
560 
561 	if (relative_addr < 0x0c0000) {
562 		/*
563 		 *  DMA poll:  a read from this address should start a DMA
564 		 *  transfer, and return 1 in odata while the DMA is in
565 		 *  progress (STAMP_BUSY), and then 0 (STAMP_OK) once we're
566 		 *  done.
567 		 *
568 		 *  According to NetBSD's pxreg.h, the following formula gets
569 		 *  us from system address to DMA address:  (v is the system
570 		 *  address)
571 		 *
572 		 *	dma_addr = ( ( ((v & ~0x7fff) << 3) |
573 		 *		(v & 0x7fff) ) & 0x1ffff800) >> 9;
574 		 *
575 		 *  Hopefully, this is a good enough reversal of that formula:
576 		 *
577 		 *	sys_addr = ((dma_addr << 9) & 0x7800) +
578 		 *		   ((dma_addr << 6) & 0xffff8000);
579 		 *
580 		 *  If the board type is "PX" then the system address is an
581 		 *  address in host memory.  Otherwise, it is relative to
582 		 *  0x200000 (the i860's memory space on the board).
583 		 */
584 		uint32_t sys_addr;	/*  system address for DMA transfers  */
585 		sys_addr = ((relative_addr << 9) & 0x7800) +
586 		    ((relative_addr << 6) & 0xffff8000);
587 
588 		/*
589 		 *  If the system address is sane enough, then start a DMA
590 		 *  transfer:  (for the "PX" board type, don't allow obviously
591 		 *  too-low physical addresses)
592 		 */
593 		if (sys_addr >= 0x4000 || d->type != DEV_PX_TYPE_PX)
594 			dev_px_dma(cpu, sys_addr, d);
595 
596 		/*  Pretend that it was always OK:  */
597 		odata = STAMP_OK;
598 	}
599 
600 	/*  N10 sram:  */
601 	if (relative_addr >= 0x200000 && relative_addr < 0x280000) {
602 		if (d->type == DEV_PX_TYPE_PX)
603 			fatal("WARNING: the vdac should be at this "
604 			    "address. overlap problems?\n");
605 
606 		if (writeflag == MEM_WRITE) {
607 			for (i=0; i<len; i++)
608 				d->sram[relative_addr - 0x200000 + i] = data[i];
609 			/*  NOTE:  this return here supresses debug output
610 			    (which would be printed if we continue)  */
611 			return 1;
612 		} else {
613 			/*
614 			 *  Huh? Why have I commented out this? TODO
615 			 */
616 			/*  for (i=0; i<len; i++)
617 				data[i] = d->sram[relative_addr - 0x200000
618 			    + i];  */
619 			odata = 1;
620 		}
621 	}
622 
623 	/*  TODO:  Most of these aren't implemented yet.  */
624 
625 	switch (relative_addr) {
626 
627 	case 0x180008:		/*  hsync  */
628 		if (writeflag==MEM_READ) {
629 			debug("[ px: read from hsync: 0x%08llx ]\n",
630 			    (long long)odata);
631 		} else {
632 			debug("[ px: write to hsync: 0x%08llx ]\n",
633 			    (long long)idata);
634 		}
635 		break;
636 
637 	case 0x18000c:		/*  hsync2  */
638 		if (writeflag==MEM_READ) {
639 			debug("[ px: read from hsync2: 0x%08llx ]\n",
640 			    (long long)odata);
641 		} else {
642 			debug("[ px: write to hsync2: 0x%08llx ]\n",
643 			    (long long)idata);
644 		}
645 		break;
646 
647 	case 0x180010:		/*  hblank  */
648 		if (writeflag==MEM_READ) {
649 			debug("[ px: read from hblank: 0x%08llx ]\n",
650 			    (long long)odata);
651 		} else {
652 			debug("[ px: write to hblank: 0x%08llx ]\n",
653 			    (long long)idata);
654 		}
655 		break;
656 
657 	case 0x180014:		/*  vsync  */
658 		if (writeflag==MEM_READ) {
659 			debug("[ px: read from vsync: 0x%08llx ]\n",
660 			    (long long)odata);
661 		} else {
662 			debug("[ px: write to vsync: 0x%08llx ]\n",
663 			    (long long)idata);
664 		}
665 		break;
666 
667 	case 0x180018:		/*  vblank  */
668 		if (writeflag==MEM_READ) {
669 			debug("[ px: read from vblank: 0x%08llx ]\n",
670 			    (long long)odata);
671 		} else {
672 			debug("[ px: write to vblank: 0x%08llx ]\n",
673 			    (long long)idata);
674 		}
675 		break;
676 
677 	case 0x180020:		/*  ipdvint  */
678 		if (writeflag==MEM_READ) {
679 			odata = d->intr;
680 
681 /*  TODO:  how do interrupts work on the pixelstamp boards?  */
682 odata = random();
683 
684 			debug("[ px: read from ipdvint: 0x%08llx ]\n",
685 			    (long long)odata);
686 		} else {
687 			d->intr = idata;
688 			if (idata & STIC_INT_E_WE)
689 				d->intr &= ~STIC_INT_E;
690 			if (idata & STIC_INT_V_WE)
691 				d->intr &= ~STIC_INT_V;
692 			if (idata & STIC_INT_P_WE)
693 				d->intr &= ~STIC_INT_P;
694 			debug("[ px: write to ipdvint: 0x%08llx ]\n",
695 			    (long long)idata);
696 		}
697 		break;
698 
699 	case 0x180028:		/*  sticsr  */
700 		if (writeflag==MEM_READ) {
701 			debug("[ px: read from sticsr: 0x%08llx ]\n",
702 			    (long long)odata);
703 		} else {
704 			debug("[ px: write to sticsr: 0x%08llx ]\n",
705 			    (long long)idata);
706 		}
707 		break;
708 
709 	case 0x180038:		/*  buscsr  */
710 		if (writeflag==MEM_READ) {
711 			debug("[ px: read from buscsr: 0x%08llx ]\n",
712 			    (long long)odata);
713 		} else {
714 			debug("[ px: write to buscsr: 0x%08llx ]\n",
715 			    (long long)idata);
716 		}
717 		break;
718 
719 	case 0x18003c:		/*  modcl  */
720 		if (writeflag==MEM_READ) {
721 			odata = (d->type << 12) + (d->xconfig << 11) +
722 			    (d->yconfig << 9);
723 			debug("[ px: read from modcl: 0x%llx ]\n",
724 			    (long long)odata);
725 		} else {
726 			debug("[ px: write to modcl: 0x%llx ]\n",
727 			    (long long)idata);
728 		}
729 		break;
730 
731 	default:
732 		if (writeflag==MEM_READ) {
733 			debug("[ px: read from addr 0x%x: 0x%llx ]\n",
734 			    (int)relative_addr, (long long)odata);
735 		} else {
736 			debug("[ px: write to addr 0x%x: 0x%llx ]\n",
737 			    (int)relative_addr, (long long)idata);
738 		}
739 	}
740 
741 	if (writeflag == MEM_READ)
742 		memory_writemax64(cpu, data, len, odata);
743 
744 	return 1;
745 }
746 
747 
dev_px_init(struct machine * machine,struct memory * mem,uint64_t baseaddr,int px_type,const char * irq_path)748 void dev_px_init(struct machine *machine, struct memory *mem,
749 	uint64_t baseaddr, int px_type, const char *irq_path)
750 {
751 	struct px_data *d;
752 
753 	CHECK_ALLOCATION(d = (struct px_data *) malloc(sizeof(struct px_data)));
754 	memset(d, 0, sizeof(struct px_data));
755 
756 	d->type = px_type;
757 
758 	INTERRUPT_CONNECT(irq_path, d->irq);
759 
760 	d->xconfig = d->yconfig = 0;	/*  4x1  */
761 
762 	d->bitdepth = 24;
763 	d->px_name = "(invalid)";
764 
765 	switch (d->type) {
766 	case DEV_PX_TYPE_PX:
767 		d->bitdepth = 8;
768 		d->px_name = "PX";
769 		break;
770 	case DEV_PX_TYPE_PXG:
771 		d->bitdepth = 8;
772 		d->px_name = "PXG";
773 		break;
774 	case DEV_PX_TYPE_PXGPLUS:
775 		d->px_name = "PXG+";
776 		break;
777 	case DEV_PX_TYPE_PXGPLUSTURBO:
778 		d->px_name = "PXG+ TURBO";
779 		d->xconfig = d->yconfig = 1;	/*  5x2  */
780 		break;
781 	default:
782 		fatal("dev_px_init(): unimplemented px_type\n");
783 	}
784 
785 	d->fb_mem = memory_new(PX_XSIZE * PX_YSIZE * d->bitdepth / 8,
786 	    machine->arch);
787 	if (d->fb_mem == NULL) {
788 		fprintf(stderr, "dev_px_init(): out of memory (1)\n");
789 		exit(1);
790 	}
791 
792 	d->vfb_data = dev_fb_init(machine, d->fb_mem, 0, VFB_GENERIC,
793 	    PX_XSIZE, PX_YSIZE, PX_XSIZE, PX_YSIZE, d->bitdepth, d->px_name);
794 	if (d->vfb_data == NULL) {
795 		fprintf(stderr, "dev_px_init(): out of memory (2)\n");
796 		exit(2);
797 	}
798 
799 	switch (d->type) {
800 	case DEV_PX_TYPE_PX:
801 		dev_bt459_init(machine, mem, baseaddr + 0x200000, 0,
802 		    d->vfb_data, 8, irq_path, BT459_PX);
803 		break;
804 	case DEV_PX_TYPE_PXG:
805 	case DEV_PX_TYPE_PXGPLUS:
806 	case DEV_PX_TYPE_PXGPLUSTURBO:
807 		dev_bt459_init(machine, mem, baseaddr + 0x300000, 0,
808 		    d->vfb_data, d->bitdepth, irq_path, BT459_PX);
809 		break;
810 	default:
811 		fatal("dev_px_init(): unimplemented px_type\n");
812 	}
813 
814 	memory_device_register(mem, "px", baseaddr, DEV_PX_LENGTH,
815 	    dev_px_access, d, DM_DEFAULT, NULL);
816 	machine_add_tickfunction(machine, dev_px_tick, d, 14);
817 }
818 
819