uxn

Varvara Ordinator, written in ANSI C(SDL2)
git clone https://git.eamoncaddigan.net/uxn.git
Log | Files | Refs | README | LICENSE

screen.c (8968B)


      1 #include <stdlib.h>
      2 #include <stdio.h>
      3 
      4 #include "../uxn.h"
      5 #include "screen.h"
      6 
      7 /*
      8 Copyright (c) 2021-2023 Devine Lu Linvega, Andrew Alderwick
      9 
     10 Permission to use, copy, modify, and distribute this software for any
     11 purpose with or without fee is hereby granted, provided that the above
     12 copyright notice and this permission notice appear in all copies.
     13 
     14 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     15 WITH REGARD TO THIS SOFTWARE.
     16 */
     17 
     18 UxnScreen uxn_screen;
     19 
     20 /* c = !ch ? (color % 5 ? color >> 2 : 0) : color % 4 + ch == 1 ? 0 : (ch - 2 + (color & 3)) % 3 + 1; */
     21 
     22 static Uint8 blending[][16] = {
     23 	{0, 0, 0, 0, 1, 0, 1, 1, 2, 2, 0, 2, 3, 3, 3, 0},
     24 	{0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3},
     25 	{1, 2, 3, 1, 1, 2, 3, 1, 1, 2, 3, 1, 1, 2, 3, 1},
     26 	{2, 3, 1, 2, 2, 3, 1, 2, 2, 3, 1, 2, 2, 3, 1, 2},
     27 	{0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0}};
     28 
     29 void
     30 screen_change(Uint16 x1, Uint16 y1, Uint16 x2, Uint16 y2)
     31 {
     32 	if(x1 > uxn_screen.width && x2 > x1) return;
     33 	if(y1 > uxn_screen.height && y2 > y1) return;
     34 	if(x1 > x2) x1 = 0;
     35 	if(y1 > y2) y1 = 0;
     36 	if(x1 < uxn_screen.x1) uxn_screen.x1 = x1;
     37 	if(y1 < uxn_screen.y1) uxn_screen.y1 = y1;
     38 	if(x2 > uxn_screen.x2) uxn_screen.x2 = x2;
     39 	if(y2 > uxn_screen.y2) uxn_screen.y2 = y2;
     40 }
     41 
     42 void
     43 screen_fill(Uint8 *layer, int color)
     44 {
     45 	int i, length = uxn_screen.width * uxn_screen.height;
     46 	for(i = 0; i < length; i++)
     47 		layer[i] = color;
     48 }
     49 
     50 void
     51 screen_rect(Uint8 *layer, Uint16 x1, Uint16 y1, Uint16 x2, Uint16 y2, int color)
     52 {
     53 	int row, x, y, w = uxn_screen.width, h = uxn_screen.height;
     54 	for(y = y1; y < y2 && y < h; y++)
     55 		for(x = x1, row = y * w; x < x2 && x < w; x++)
     56 			layer[x + row] = color;
     57 }
     58 
     59 static void
     60 screen_2bpp(Uint8 *layer, Uint8 *addr, Uint16 x1, Uint16 y1, Uint16 color, int fx, int fy)
     61 {
     62 	int w = uxn_screen.width, h = uxn_screen.height, opaque = blending[4][color];
     63 	Uint16 y, ymod = (fy < 0 ? 7 : 0), ymax = y1 + ymod + fy * 8;
     64 	Uint16 x, xmod = (fx > 0 ? 7 : 0), xmax = x1 + xmod - fx * 8;
     65 	for(y = y1 + ymod; y != ymax; y += fy, addr++) {
     66 		int c = addr[0] | (addr[8] << 8), row = y * w;
     67 		if(y < h)
     68 			for(x = x1 + xmod; x != xmax; x -= fx, c >>= 1) {
     69 				Uint8 ch = (c & 1) | ((c >> 7) & 2);
     70 				if(x < w && (opaque || ch))
     71 					layer[x + row] = blending[ch][color];
     72 			}
     73 	}
     74 }
     75 
     76 static void
     77 screen_1bpp(Uint8 *layer, Uint8 *addr, Uint16 x1, Uint16 y1, Uint16 color, int fx, int fy)
     78 {
     79 	int w = uxn_screen.width, h = uxn_screen.height, opaque = blending[4][color];
     80 	Uint16 y, ymod = (fy < 0 ? 7 : 0), ymax = y1 + ymod + fy * 8;
     81 	Uint16 x, xmod = (fx > 0 ? 7 : 0), xmax = x1 + xmod - fx * 8;
     82 	for(y = y1 + ymod; y != ymax; y += fy) {
     83 		int c = *addr++, row = y * w;
     84 		if(y < h)
     85 			for(x = x1 + xmod; x != xmax; x -= fx, c >>= 1) {
     86 				Uint8 ch = c & 1;
     87 				if(x < w && (opaque || ch))
     88 					layer[x + row] = blending[ch][color];
     89 			}
     90 	}
     91 }
     92 
     93 /* clang-format off */
     94 
     95 static Uint8 icons[] = {
     96 	0x00, 0x7c, 0x82, 0x82, 0x82, 0x82, 0x82, 0x7c, 0x00, 0x30, 0x10, 0x10, 0x10, 0x10, 0x10, 
     97 	0x10, 0x00, 0x7c, 0x82, 0x02, 0x7c, 0x80, 0x80, 0xfe, 0x00, 0x7c, 0x82, 0x02, 0x1c, 0x02, 
     98 	0x82, 0x7c, 0x00, 0x0c, 0x14, 0x24, 0x44, 0x84, 0xfe, 0x04, 0x00, 0xfe, 0x80, 0x80, 0x7c, 
     99 	0x02, 0x82, 0x7c, 0x00, 0x7c, 0x82, 0x80, 0xfc, 0x82, 0x82, 0x7c, 0x00, 0x7c, 0x82, 0x02, 
    100 	0x1e, 0x02, 0x02, 0x02, 0x00, 0x7c, 0x82, 0x82, 0x7c, 0x82, 0x82, 0x7c, 0x00, 0x7c, 0x82, 
    101 	0x82, 0x7e, 0x02, 0x82, 0x7c, 0x00, 0x7c, 0x82, 0x02, 0x7e, 0x82, 0x82, 0x7e, 0x00, 0xfc, 
    102 	0x82, 0x82, 0xfc, 0x82, 0x82, 0xfc, 0x00, 0x7c, 0x82, 0x80, 0x80, 0x80, 0x82, 0x7c, 0x00, 
    103 	0xfc, 0x82, 0x82, 0x82, 0x82, 0x82, 0xfc, 0x00, 0x7c, 0x82, 0x80, 0xf0, 0x80, 0x82, 0x7c,
    104 	0x00, 0x7c, 0x82, 0x80, 0xf0, 0x80, 0x80, 0x80 };
    105 static Uint8 arrow[] = {
    106 	0x00, 0x00, 0x00, 0xfe, 0x7c, 0x38, 0x10, 0x00 };
    107 
    108 /* clang-format on */
    109 
    110 static void
    111 draw_byte(Uint8 b, Uint16 x, Uint16 y, Uint8 color)
    112 {
    113 	screen_1bpp(uxn_screen.fg, &icons[(b >> 4) << 3], x, y, color, 1, 1);
    114 	screen_1bpp(uxn_screen.fg, &icons[(b & 0xf) << 3], x + 8, y, color, 1, 1);
    115 	screen_change(x, y, x + 0x10, y + 0x8);
    116 }
    117 
    118 static void
    119 screen_debugger(Uxn *u)
    120 {
    121 	int i;
    122 	for(i = 0; i < 0x08; i++) {
    123 		Uint8 pos = u->wst.ptr - 4 + i;
    124 		Uint8 color = i > 4 ? 0x01 : !pos ? 0xc
    125 			: i == 4                      ? 0x8
    126 										  : 0x2;
    127 		draw_byte(u->wst.dat[pos], i * 0x18 + 0x8, uxn_screen.height - 0x18, color);
    128 	}
    129 	for(i = 0; i < 0x08; i++) {
    130 		Uint8 pos = u->rst.ptr - 4 + i;
    131 		Uint8 color = i > 4 ? 0x01 : !pos ? 0xc
    132 			: i == 4                      ? 0x8
    133 										  : 0x2;
    134 		draw_byte(u->rst.dat[pos], i * 0x18 + 0x8, uxn_screen.height - 0x10, color);
    135 	}
    136 	screen_1bpp(uxn_screen.fg, &arrow[0], 0x68, uxn_screen.height - 0x20, 3, 1, 1);
    137 	for(i = 0; i < 0x20; i++)
    138 		draw_byte(u->ram[i], (i & 0x7) * 0x18 + 0x8, ((i >> 3) << 3) + 0x8, 1 + !!u->ram[i]);
    139 }
    140 
    141 void
    142 screen_palette(Uint8 *addr)
    143 {
    144 	int i, shift;
    145 	for(i = 0, shift = 4; i < 4; ++i, shift ^= 4) {
    146 		Uint8
    147 			r = (addr[0 + i / 2] >> shift) & 0xf,
    148 			g = (addr[2 + i / 2] >> shift) & 0xf,
    149 			b = (addr[4 + i / 2] >> shift) & 0xf;
    150 		uxn_screen.palette[i] = 0x0f000000 | r << 16 | g << 8 | b;
    151 		uxn_screen.palette[i] |= uxn_screen.palette[i] << 4;
    152 	}
    153 	screen_change(0, 0, uxn_screen.width, uxn_screen.height);
    154 }
    155 
    156 void
    157 screen_resize(Uint16 width, Uint16 height)
    158 {
    159 	Uint8 *bg, *fg;
    160 	Uint32 *pixels = NULL;
    161 	if(width < 0x8 || height < 0x8 || width >= 0x800 || height >= 0x800)
    162 		return;
    163 	if(uxn_screen.width == width && uxn_screen.height == height)
    164 		return;
    165 	bg = malloc(width * height), fg = malloc(width * height);
    166 	if(bg && fg)
    167 		pixels = realloc(uxn_screen.pixels, width * height * sizeof(Uint32));
    168 	if(!bg || !fg || !pixels) {
    169 		free(bg), free(fg);
    170 		return;
    171 	}
    172 	free(uxn_screen.bg), free(uxn_screen.fg);
    173 	uxn_screen.bg = bg, uxn_screen.fg = fg;
    174 	uxn_screen.pixels = pixels;
    175 	uxn_screen.width = width, uxn_screen.height = height;
    176 	screen_fill(uxn_screen.bg, 0), screen_fill(uxn_screen.fg, 0);
    177 	emu_resize(width, height);
    178 	screen_change(0, 0, width, height);
    179 }
    180 
    181 void
    182 screen_redraw(Uxn *u)
    183 {
    184 	int i, j, o, y;
    185 	Uint8 *fg = uxn_screen.fg, *bg = uxn_screen.bg;
    186 	Uint16 w = uxn_screen.width, h = uxn_screen.height;
    187 	Uint16 x1 = uxn_screen.x1, y1 = uxn_screen.y1;
    188 	Uint16 x2 = uxn_screen.x2 > w ? w : uxn_screen.x2, y2 = uxn_screen.y2 > h ? h : uxn_screen.y2;
    189 	Uint32 palette[16], *pixels = uxn_screen.pixels;
    190 	uxn_screen.x1 = uxn_screen.y1 = 0xffff;
    191 	uxn_screen.x2 = uxn_screen.y2 = 0;
    192 	if(u->dev[0x0e])
    193 		screen_debugger(u);
    194 	for(i = 0; i < 16; i++)
    195 		palette[i] = uxn_screen.palette[(i >> 2) ? (i >> 2) : (i & 3)];
    196 	for(y = y1; y < y2; y++)
    197 		for(o = y * w, i = x1 + o, j = x2 + o; i < j; i++)
    198 			pixels[i] = palette[fg[i] << 2 | bg[i]];
    199 }
    200 
    201 /* screen registers */
    202 
    203 static Uint16 rX, rY, rA, rMX, rMY, rMA, rML, rDX, rDY;
    204 
    205 Uint8
    206 screen_dei(Uxn *u, Uint8 addr)
    207 {
    208 	switch(addr) {
    209 	case 0x22: return uxn_screen.width >> 8;
    210 	case 0x23: return uxn_screen.width;
    211 	case 0x24: return uxn_screen.height >> 8;
    212 	case 0x25: return uxn_screen.height;
    213 	case 0x28: return rX >> 8;
    214 	case 0x29: return rX;
    215 	case 0x2a: return rY >> 8;
    216 	case 0x2b: return rY;
    217 	case 0x2c: return rA >> 8;
    218 	case 0x2d: return rA;
    219 	default: return u->dev[addr];
    220 	}
    221 }
    222 
    223 void
    224 screen_deo(Uint8 *ram, Uint8 *d, Uint8 port)
    225 {
    226 	switch(port) {
    227 	case 0x3: screen_resize(PEEK2(d + 2), uxn_screen.height); return;
    228 	case 0x5: screen_resize(uxn_screen.width, PEEK2(d + 4)); return;
    229 	case 0x6: rMX = d[0x6] & 0x1, rMY = d[0x6] & 0x2, rMA = d[0x6] & 0x4, rML = d[0x6] >> 4, rDX = rMX << 3, rDY = rMY << 2; return;
    230 	case 0x8:
    231 	case 0x9: rX = (d[0x8] << 8) | d[0x9]; return;
    232 	case 0xa:
    233 	case 0xb: rY = (d[0xa] << 8) | d[0xb]; return;
    234 	case 0xc:
    235 	case 0xd: rA = (d[0xc] << 8) | d[0xd]; return;
    236 	case 0xe: {
    237 		Uint8 ctrl = d[0xe];
    238 		Uint8 color = ctrl & 0x3;
    239 		Uint8 *layer = ctrl & 0x40 ? uxn_screen.fg : uxn_screen.bg;
    240 		/* fill mode */
    241 		if(ctrl & 0x80) {
    242 			Uint16 x1, y1, x2, y2;
    243 			if(ctrl & 0x10)
    244 				x1 = 0, x2 = rX;
    245 			else
    246 				x1 = rX, x2 = uxn_screen.width;
    247 			if(ctrl & 0x20)
    248 				y1 = 0, y2 = rY;
    249 			else
    250 				y1 = rY, y2 = uxn_screen.height;
    251 			screen_rect(layer, x1, y1, x2, y2, color);
    252 			screen_change(x1, y1, x2, y2);
    253 		}
    254 		/* pixel mode */
    255 		else {
    256 			Uint16 w = uxn_screen.width;
    257 			if(rX < w && rY < uxn_screen.height)
    258 				layer[rX + rY * w] = color;
    259 			screen_change(rX, rY, rX + 1, rY + 1);
    260 			if(rMX) rX++;
    261 			if(rMY) rY++;
    262 		}
    263 		return;
    264 	}
    265 	case 0xf: {
    266 		Uint8 i;
    267 		Uint8 ctrl = d[0xf];
    268 		Uint8 twobpp = !!(ctrl & 0x80);
    269 		Uint8 color = ctrl & 0xf;
    270 		Uint8 *layer = ctrl & 0x40 ? uxn_screen.fg : uxn_screen.bg;
    271 		int fx = ctrl & 0x10 ? -1 : 1;
    272 		int fy = ctrl & 0x20 ? -1 : 1;
    273 		Uint16 dxy = rDX * fy, dyx = rDY * fx, addr_incr = rMA << (1 + twobpp);
    274 		if(twobpp)
    275 			for(i = 0; i <= rML; i++, rA += addr_incr)
    276 				screen_2bpp(layer, &ram[rA], rX + dyx * i, rY + dxy * i, color, fx, fy);
    277 		else
    278 			for(i = 0; i <= rML; i++, rA += addr_incr)
    279 				screen_1bpp(layer, &ram[rA], rX + dyx * i, rY + dxy * i, color, fx, fy);
    280 		screen_change(rX, rY, rX + dyx * rML + 8, rY + dxy * rML + 8);
    281 		if(rMX) rX += rDX * fx;
    282 		if(rMY) rY += rDY * fy;
    283 		return;
    284 	}
    285 	}
    286 }