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 }