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 }