uxn

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

screen.c (8428B)


      1 #include <stdlib.h>
      2 
      3 #include "../uxn.h"
      4 #include "screen.h"
      5 
      6 /*
      7 Copyright (c) 2021-2024 Devine Lu Linvega, Andrew Alderwick
      8 
      9 Permission to use, copy, modify, and distribute this software for any
     10 purpose with or without fee is hereby granted, provided that the above
     11 copyright notice and this permission notice appear in all copies.
     12 
     13 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     14 WITH REGARD TO THIS SOFTWARE.
     15 */
     16 
     17 UxnScreen uxn_screen;
     18 
     19 #define MAR(x) (x + 0x08)
     20 #define MAR2(x) (x + 0x10)
     21 
     22 /* c = !ch ? (color % 5 ? color >> 2 : 0) : color % 4 + ch == 1 ? 0 : (ch - 2 + (color & 3)) % 3 + 1; */
     23 
     24 static Uint8 blending[4][16] = {
     25 	{0, 0, 0, 0, 1, 0, 1, 1, 2, 2, 0, 2, 3, 3, 3, 0},
     26 	{0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3},
     27 	{1, 2, 3, 1, 1, 2, 3, 1, 1, 2, 3, 1, 1, 2, 3, 1},
     28 	{2, 3, 1, 2, 2, 3, 1, 2, 2, 3, 1, 2, 2, 3, 1, 2}};
     29 
     30 int
     31 screen_changed(void)
     32 {
     33 	if(uxn_screen.x1 < 0)
     34 		uxn_screen.x1 = 0;
     35 	else if(uxn_screen.x1 >= uxn_screen.width)
     36 		uxn_screen.x1 = uxn_screen.width;
     37 	if(uxn_screen.y1 < 0)
     38 		uxn_screen.y1 = 0;
     39 	else if(uxn_screen.y1 >= uxn_screen.height)
     40 		uxn_screen.y1 = uxn_screen.height;
     41 	if(uxn_screen.x2 < 0)
     42 		uxn_screen.x2 = 0;
     43 	else if(uxn_screen.x2 >= uxn_screen.width)
     44 		uxn_screen.x2 = uxn_screen.width;
     45 	if(uxn_screen.y2 < 0)
     46 		uxn_screen.y2 = 0;
     47 	else if(uxn_screen.y2 >= uxn_screen.height)
     48 		uxn_screen.y2 = uxn_screen.height;
     49 	return uxn_screen.x2 > uxn_screen.x1 && uxn_screen.y2 > uxn_screen.y1;
     50 }
     51 
     52 void
     53 screen_change(int x1, int y1, int x2, int y2)
     54 {
     55 	if(x1 < uxn_screen.x1) uxn_screen.x1 = x1;
     56 	if(y1 < uxn_screen.y1) uxn_screen.y1 = y1;
     57 	if(x2 > uxn_screen.x2) uxn_screen.x2 = x2;
     58 	if(y2 > uxn_screen.y2) uxn_screen.y2 = y2;
     59 }
     60 
     61 void
     62 screen_fill(Uint8 *layer, int color)
     63 {
     64 	int i, len = MAR2(uxn_screen.width) * uxn_screen.height;
     65 	for(i = 0; i < len; i++)
     66 		layer[i] = color;
     67 }
     68 
     69 void
     70 screen_palette(void)
     71 {
     72 	int i, shift;
     73 	for(i = 0, shift = 4; i < 4; ++i, shift ^= 4) {
     74 		Uint8
     75 			r = (uxn.dev[0x8 + i / 2] >> shift) & 0xf,
     76 			g = (uxn.dev[0xa + i / 2] >> shift) & 0xf,
     77 			b = (uxn.dev[0xc + i / 2] >> shift) & 0xf;
     78 		uxn_screen.palette[i] = 0x0f000000 | r << 16 | g << 8 | b;
     79 		uxn_screen.palette[i] |= uxn_screen.palette[i] << 4;
     80 	}
     81 	screen_change(0, 0, uxn_screen.width, uxn_screen.height);
     82 }
     83 
     84 void
     85 screen_resize(Uint16 width, Uint16 height, int scale)
     86 {
     87 	Uint8 *bg, *fg;
     88 	Uint32 *pixels = NULL;
     89 	int dim_change = uxn_screen.width != width || uxn_screen.height != height;
     90 	if(width < 0x8 || height < 0x8 || width >= 0x800 || height >= 0x800 || scale < 1 || scale >= 4)
     91 		return;
     92 	if(uxn_screen.width == width && uxn_screen.height == height && uxn_screen.scale == scale)
     93 		return;
     94 	if(dim_change) {
     95 		int wmar = MAR2(width), hmar = MAR2(height);
     96 		bg = malloc(wmar * hmar), fg = malloc(wmar * hmar);
     97 		if(bg && fg)
     98 			pixels = realloc(uxn_screen.pixels, width * height * sizeof(Uint32) * scale * scale);
     99 		if(!bg || !fg || !pixels) {
    100 			free(bg), free(fg);
    101 			return;
    102 		}
    103 		free(uxn_screen.bg), free(uxn_screen.fg);
    104 	} else {
    105 		bg = uxn_screen.bg, fg = uxn_screen.fg;
    106 		pixels = realloc(uxn_screen.pixels, width * height * sizeof(Uint32) * scale * scale);
    107 		if(!pixels)
    108 			return;
    109 	}
    110 	uxn_screen.bg = bg, uxn_screen.fg = fg;
    111 	uxn_screen.pixels = pixels;
    112 	uxn_screen.width = width, uxn_screen.height = height, uxn_screen.scale = scale;
    113 	if(dim_change)
    114 		screen_fill(uxn_screen.bg, 0), screen_fill(uxn_screen.fg, 0);
    115 	emu_resize(width, height);
    116 	screen_change(0, 0, width, height);
    117 }
    118 
    119 void
    120 screen_redraw(void)
    121 {
    122 	int i, x, y, k, l;
    123 	int x1 = uxn_screen.x1 < 0 ? 0 : uxn_screen.x1, y1 = uxn_screen.y1 < 0 ? 0 : uxn_screen.y1;
    124 	int x2 = uxn_screen.x2 > uxn_screen.width ? uxn_screen.width : uxn_screen.x2;
    125 	int y2 = uxn_screen.y2 > uxn_screen.height ? uxn_screen.height : uxn_screen.y2;
    126 	Uint32 palette[16], *pixels = uxn_screen.pixels;
    127 	uxn_screen.x1 = uxn_screen.y1 = 9000;
    128 	uxn_screen.x2 = uxn_screen.y2 = 0;
    129 	for(i = 0; i < 16; i++)
    130 		palette[i] = uxn_screen.palette[(i >> 2) ? (i >> 2) : (i & 3)];
    131 	for(y = y1; y < y2; y++) {
    132 		int ys = y * uxn_screen.scale;
    133 		for(x = x1, i = MAR(x) + MAR(y) * MAR2(uxn_screen.width); x < x2; x++, i++) {
    134 			int c = palette[uxn_screen.fg[i] << 2 | uxn_screen.bg[i]];
    135 			for(k = 0; k < uxn_screen.scale; k++) {
    136 				int oo = ((ys + k) * uxn_screen.width + x) * uxn_screen.scale;
    137 				for(l = 0; l < uxn_screen.scale; l++)
    138 					pixels[oo + l] = c;
    139 			}
    140 		}
    141 	}
    142 }
    143 
    144 /* screen registers */
    145 
    146 static int rX, rY, rA, rMX, rMY, rMA, rML, rDX, rDY;
    147 
    148 Uint8
    149 screen_dei(Uint8 addr)
    150 {
    151 	switch(addr) {
    152 	case 0x22: return uxn_screen.width >> 8;
    153 	case 0x23: return uxn_screen.width;
    154 	case 0x24: return uxn_screen.height >> 8;
    155 	case 0x25: return uxn_screen.height;
    156 	case 0x28: return rX >> 8;
    157 	case 0x29: return rX;
    158 	case 0x2a: return rY >> 8;
    159 	case 0x2b: return rY;
    160 	case 0x2c: return rA >> 8;
    161 	case 0x2d: return rA;
    162 	default: return uxn.dev[addr];
    163 	}
    164 }
    165 
    166 void
    167 screen_deo(Uint8 addr)
    168 {
    169 	switch(addr) {
    170 	case 0x21: uxn_screen.vector = PEEK2(&uxn.dev[0x20]); return;
    171 	case 0x23: screen_resize(PEEK2(&uxn.dev[0x22]), uxn_screen.height, uxn_screen.scale); return;
    172 	case 0x25: screen_resize(uxn_screen.width, PEEK2(&uxn.dev[0x24]), uxn_screen.scale); return;
    173 	case 0x26: rMX = uxn.dev[0x26] & 0x1, rMY = uxn.dev[0x26] & 0x2, rMA = uxn.dev[0x26] & 0x4, rML = uxn.dev[0x26] >> 4, rDX = rMX << 3, rDY = rMY << 2; return;
    174 	case 0x28:
    175 	case 0x29: rX = (uxn.dev[0x28] << 8) | uxn.dev[0x29], rX = twos(rX); return;
    176 	case 0x2a:
    177 	case 0x2b: rY = (uxn.dev[0x2a] << 8) | uxn.dev[0x2b], rY = twos(rY); return;
    178 	case 0x2c:
    179 	case 0x2d: rA = (uxn.dev[0x2c] << 8) | uxn.dev[0x2d]; return;
    180 	case 0x2e: {
    181 		int ctrl = uxn.dev[0x2e];
    182 		int color = ctrl & 0x3;
    183 		int len = MAR2(uxn_screen.width);
    184 		Uint8 *layer = ctrl & 0x40 ? uxn_screen.fg : uxn_screen.bg;
    185 		/* fill mode */
    186 		if(ctrl & 0x80) {
    187 			int x1, y1, x2, y2, ax, bx, ay, by, hor, ver;
    188 			if(ctrl & 0x10)
    189 				x1 = MAR(0), x2 = MAR(rX);
    190 			else
    191 				x1 = MAR(rX), x2 = MAR(uxn_screen.width);
    192 			if(ctrl & 0x20)
    193 				y1 = MAR(0), y2 = MAR(rY);
    194 			else
    195 				y1 = MAR(rY), y2 = MAR(uxn_screen.height);
    196 			hor = x2 - x1, ver = y2 - y1;
    197 			for(ay = y1 * len, by = ay + ver * len; ay < by; ay += len)
    198 				for(ax = ay + x1, bx = ax + hor; ax < bx; ax++)
    199 					layer[ax] = color;
    200 			screen_change(x1, y1, x2, y2);
    201 		}
    202 		/* pixel mode */
    203 		else {
    204 			if(rX >= 0 && rY >= 0 && rX < len && rY < uxn_screen.height)
    205 				layer[MAR(rX) + MAR(rY) * len] = color;
    206 			screen_change(rX, rY, rX + 1, rY + 1);
    207 			if(rMX) rX++;
    208 			if(rMY) rY++;
    209 		}
    210 		return;
    211 	}
    212 	case 0x2f: {
    213 		int ctrl = uxn.dev[0x2f];
    214 		int twobpp = !!(ctrl & 0x80);
    215 		int blend = ctrl & 0xf;
    216 		int fx = ctrl & 0x10 ? -1 : 1, fy = ctrl & 0x20 ? -1 : 1;
    217 		int i, x1, x2, y1, y2, ax, ay, qx, qy, x = rX, y = rY;
    218 		int dxy = rDX * fy, dyx = rDY * fx, addr_incr = rMA << (1 + twobpp);
    219 		int wmar = MAR(uxn_screen.width), wmar2 = MAR2(uxn_screen.width);
    220 		int hmar2 = MAR2(uxn_screen.height);
    221 		Uint8 *layer = ctrl & 0x40 ? uxn_screen.fg : uxn_screen.bg;
    222 		if(twobpp)
    223 			for(i = 0; i <= rML; i++, x += dyx, y += dxy, rA += addr_incr) {
    224 				Uint16 xmar = MAR(x), ymar = MAR(y);
    225 				Uint16 xmar2 = MAR2(x), ymar2 = MAR2(y);
    226 				if(xmar < wmar && ymar2 < hmar2) {
    227 					Uint8 *sprite = &uxn.ram[rA];
    228 					int opaque = blend % 5, by = ymar2 * wmar2;
    229 					for(ay = ymar * wmar2, qy = fy < 0 ? 7 : 0; ay < by; ay += wmar2, qy += fy) {
    230 						int ch1 = sprite[qy], ch2 = sprite[qy + 8] << 1, bx = xmar2 + ay;
    231 						for(ax = xmar + ay, qx = fx > 0 ? 7 : 0; ax < bx; ax++, qx -= fx) {
    232 							int color = ((ch1 >> qx) & 1) | ((ch2 >> qx) & 2);
    233 							if(opaque || color) layer[ax] = blending[color][blend];
    234 						}
    235 					}
    236 				}
    237 			}
    238 		else
    239 			for(i = 0; i <= rML; i++, x += dyx, y += dxy, rA += addr_incr) {
    240 				Uint16 xmar = MAR(x), ymar = MAR(y);
    241 				Uint16 xmar2 = MAR2(x), ymar2 = MAR2(y);
    242 				if(xmar < wmar && ymar2 < hmar2) {
    243 					Uint8 *sprite = &uxn.ram[rA];
    244 					int opaque = blend % 5, by = ymar2 * wmar2;
    245 					for(ay = ymar * wmar2, qy = fy < 0 ? 7 : 0; ay < by; ay += wmar2, qy += fy) {
    246 						int ch1 = sprite[qy], bx = xmar2 + ay;
    247 						for(ax = xmar + ay, qx = fx > 0 ? 7 : 0; ax < bx; ax++, qx -= fx) {
    248 							int color = (ch1 >> qx) & 1;
    249 							if(opaque || color) layer[ax] = blending[color][blend];
    250 						}
    251 					}
    252 				}
    253 			}
    254 		if(fx < 0)
    255 			x1 = x, x2 = rX;
    256 		else
    257 			x1 = rX, x2 = x;
    258 		if(fy < 0)
    259 			y1 = y, y2 = rY;
    260 		else
    261 			y1 = rY, y2 = y;
    262 		screen_change(x1 - 8, y1 - 8, x2 + 8, y2 + 8);
    263 		if(rMX) rX += rDX * fx;
    264 		if(rMY) rY += rDY * fy;
    265 		return;
    266 	}
    267 	}
    268 }