diff options
author | Bad Diode <bd@badd10de.dev> | 2022-10-16 15:44:12 +0200 |
---|---|---|
committer | Bad Diode <bd@badd10de.dev> | 2022-10-16 15:44:12 +0200 |
commit | 9322f2c413b4ea22c386d3725ffa79bc54d5596c (patch) | |
tree | bb8f57bbe638a65a9de4ab4bd041c4bc5a6bffab | |
parent | 40fe8dfc9f9e6e1d69d442fa9e6b15e555d27650 (diff) | |
download | uxn64-9322f2c413b4ea22c386d3725ffa79bc54d5596c.tar.gz uxn64-9322f2c413b4ea22c386d3725ffa79bc54d5596c.zip |
Add uxn core and initial implementation of interpreter
-rw-r--r-- | .gitmodules | 3 | ||||
-rw-r--r-- | src/main.c | 464 | ||||
-rw-r--r-- | src/ppu.c | 349 | ||||
m--------- | src/uxn | 0 | ||||
-rw-r--r-- | src/uxn_screen_rom.c | 41 |
5 files changed, 589 insertions, 268 deletions
diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..c925178 --- /dev/null +++ b/.gitmodules | |||
@@ -0,0 +1,3 @@ | |||
1 | [submodule "src/uxn"] | ||
2 | path = src/uxn | ||
3 | url = https://git.sr.ht/~rabbits/uxn | ||
@@ -2,10 +2,6 @@ | |||
2 | 2 | ||
3 | #include "types.h" | 3 | #include "types.h" |
4 | 4 | ||
5 | // Screen size. | ||
6 | #define SCREEN_WIDTH 320 | ||
7 | #define SCREEN_HEIGHT 240 | ||
8 | |||
9 | // Get a pointer to the start (top) of a stack. | 5 | // Get a pointer to the start (top) of a stack. |
10 | #define STACK_START(stack) ((stack) + sizeof((stack))) | 6 | #define STACK_START(stack) ((stack) + sizeof((stack))) |
11 | 7 | ||
@@ -16,308 +12,240 @@ | |||
16 | #define STACK_SIZE 8 * 1024 | 12 | #define STACK_SIZE 8 * 1024 |
17 | static OSThread idle_thread; | 13 | static OSThread idle_thread; |
18 | static OSThread main_thread; | 14 | static OSThread main_thread; |
19 | static u8 idle_thread_stack[STACK_SIZE] __attribute__((aligned(8))); | 15 | static u8 idle_thread_stack[STACK_SIZE] __attribute__((aligned(8))); |
20 | static u8 main_thread_stack[STACK_SIZE] __attribute__((aligned(8))); | 16 | static u8 main_thread_stack[STACK_SIZE] __attribute__((aligned(8))); |
21 | static u64 dram_stack[SP_DRAM_STACK_SIZE64] __attribute__((aligned(16))); | 17 | u64 boot_stack[STACK_SIZE / sizeof(u64)] __attribute__((aligned(8))); |
22 | u64 boot_stack[STACK_SIZE / sizeof(u64)] __attribute__((aligned(8))); | ||
23 | 18 | ||
24 | // | 19 | // |
25 | // Framebuffers. | 20 | // Message buffers and queues. |
26 | // | 21 | // |
27 | 22 | ||
28 | u16 framebuffers[2][SCREEN_WIDTH * SCREEN_HEIGHT] __attribute__((aligned(16))); | 23 | #define NUM_PI_MSGS 8 |
24 | static OSMesg pi_msg[NUM_PI_MSGS]; | ||
25 | |||
26 | // Handle for rom memory. | ||
27 | OSPiHandle *rom_handle; | ||
29 | 28 | ||
30 | // | 29 | // |
31 | // Statics. | 30 | // UXN. |
32 | // | 31 | // |
33 | 32 | ||
34 | static int current_fb = 0; | 33 | #include "ppu.c" |
35 | static u16 pixels[SCREEN_WIDTH * SCREEN_HEIGHT] __attribute__((aligned(16))); | 34 | #include "uxn/src/uxn.c" |
36 | static Gfx glist[2048 * 8]; | ||
37 | 35 | ||
38 | // | 36 | #define CLAMP(X, MIN, MAX) ((X) <= (MIN) ? (MIN) : (X) > (MAX) ? (MAX): (X)) |
39 | // Message buffers and queues. | ||
40 | // | ||
41 | 37 | ||
42 | #define NUM_PI_MSGS 8 | 38 | static Uxn u; |
43 | static OSMesg pi_msg[NUM_PI_MSGS]; | 39 | static Device *devscreen; |
44 | static OSMesg rdp_msg_buf, retrace_msg_buf; | 40 | static Device *devctrl; |
45 | static OSMesgQueue pi_msg_queue; | 41 | static Device *devmouse; |
46 | static OSMesgQueue rdp_msg_queue; | ||
47 | static OSMesgQueue retrace_msg_queue; | ||
48 | 42 | ||
49 | // Handle for rom memory. | 43 | int |
50 | OSPiHandle *rom_handle; | 44 | uxn_halt(Uxn *u, Uint8 error, Uint16 addr) { |
45 | (void)u; | ||
46 | (void)error; | ||
47 | (void)addr; | ||
48 | for (;;) {} | ||
49 | } | ||
51 | 50 | ||
52 | // DEBUG: animations | 51 | int |
53 | static int t = 0; | 52 | uxn_interrupt(void) { |
53 | return 1; | ||
54 | } | ||
54 | 55 | ||
55 | void | 56 | static u8 |
56 | fb_write_test(void) { | 57 | nil_dei(Device *d, u8 port) { |
57 | for (size_t j = 0; j < SCREEN_HEIGHT; j++) { | 58 | return d->dat[port]; |
58 | for (size_t i = 0; i < SCREEN_WIDTH; i++) { | 59 | } |
59 | // u16 shade_x = (float)i / (float)SCREEN_WIDTH * 32; | 60 | |
60 | // u16 shade_y = (float)j / (float)SCREEN_HEIGHT * 32; | 61 | static void |
61 | // u16 color = shade_x << 11 | shade_y << 1; | 62 | nil_deo(Device *d, u8 port) { |
62 | u16 shade_x = (float)i / (float)SCREEN_WIDTH * 255; | 63 | (void)d; |
63 | u16 shade_y = (float)j / (float)SCREEN_HEIGHT * 255; | 64 | (void)port; |
64 | u16 color = GPACK_RGBA5551(shade_x, t, shade_y, 1); | 65 | } |
65 | // u16 color = t; | 66 | |
66 | framebuffers[current_fb][i + j * SCREEN_WIDTH] = color; | 67 | static void |
67 | } | 68 | screen_palette(Device *d) { |
69 | for(size_t i = 0; i < 4; ++i) { | ||
70 | u8 r = ((d->dat[0x8 + i / 2] >> (!(i % 2) << 2)) & 0x0f) * 0x11; | ||
71 | u8 g = ((d->dat[0xa + i / 2] >> (!(i % 2) << 2)) & 0x0f) * 0x11; | ||
72 | u8 b = ((d->dat[0xc + i / 2] >> (!(i % 2) << 2)) & 0x0f) * 0x11; | ||
73 | |||
74 | palette[i] = GPACK_RGBA5551(r, g, b, 1); | ||
75 | } | ||
76 | for(size_t i = 4; i < 16; ++i) { | ||
77 | palette[i] = palette[i / 4]; | ||
68 | } | 78 | } |
79 | |||
80 | // Redraw the screen if we change the color palette. | ||
81 | reqdraw = 1; | ||
82 | redraw_screen(); | ||
83 | } | ||
84 | |||
85 | u8 | ||
86 | system_dei(Device *d, u8 port) { | ||
87 | switch(port) { | ||
88 | case 0x2: return d->u->wst.ptr; | ||
89 | case 0x3: return d->u->rst.ptr; | ||
90 | default: return d->dat[port]; | ||
91 | } | ||
69 | } | 92 | } |
70 | 93 | ||
71 | void | 94 | void |
72 | fb_copy_test(void) { | 95 | system_deo(Device *d, u8 port) { |
73 | for (size_t i = 0; i < SCREEN_HEIGHT * SCREEN_WIDTH; i++) { | 96 | switch(port) { |
74 | framebuffers[current_fb][i] = pixels[i]; | 97 | case 0x2: d->u->wst.ptr = d->dat[port]; break; |
98 | case 0x3: d->u->rst.ptr = d->dat[port]; break; | ||
99 | case 0xe: break; | ||
100 | default: { | ||
101 | if(port > 0x7 && port < 0xe) { | ||
102 | screen_palette(d); | ||
103 | } | ||
104 | } break; | ||
105 | } | ||
106 | } | ||
107 | |||
108 | static void | ||
109 | console_deo(Device *d, u8 port) { | ||
110 | (void)d; | ||
111 | (void)port; | ||
112 | } | ||
113 | |||
114 | u8 | ||
115 | screen_dei(Device *d, u8 port) { | ||
116 | switch(port) { | ||
117 | case 0x2: return screen_width >> 8; | ||
118 | case 0x3: return screen_width; | ||
119 | case 0x4: return screen_height >> 8; | ||
120 | case 0x5: return screen_height; | ||
121 | default: return d->dat[port]; | ||
75 | } | 122 | } |
76 | } | 123 | } |
77 | 124 | ||
78 | void | 125 | void |
79 | rdp_init(void) { | 126 | screen_deo(Device *d, u8 port) { |
80 | Gfx *glistp = glist; | 127 | switch(port) { |
81 | 128 | // case 0x3: | |
82 | static const Vp viewport = {{ | 129 | // if(!FIXED_SIZE) { |
83 | .vscale = {SCREEN_WIDTH * 2, SCREEN_HEIGHT * 2, G_MAXZ / 2, 0}, | 130 | // Uint16 w; |
84 | .vtrans = {SCREEN_WIDTH * 2, SCREEN_HEIGHT * 2, G_MAXZ / 2, 0}, | 131 | // DEVPEEK16(w, 0x2); |
85 | }}; | 132 | // screen_resize(&uxn_screen, clamp(w, 1, 1024), uxn_screen.height); |
86 | 133 | // } | |
87 | // Initialize the RSP. | 134 | // break; |
88 | Gfx rspinit_dl[] = { | 135 | // case 0x5: |
89 | gsSPViewport(&viewport), | 136 | // if(!FIXED_SIZE) { |
90 | gsSPClearGeometryMode(G_SHADE | G_SHADING_SMOOTH | G_CULL_BOTH | | 137 | // Uint16 h; |
91 | G_FOG | G_LIGHTING | G_TEXTURE_GEN | | 138 | // DEVPEEK16(h, 0x4); |
92 | G_TEXTURE_GEN_LINEAR | G_LOD), | 139 | // screen_resize(&uxn_screen, uxn_screen.width, clamp(h, 1, 1024)); |
93 | gsSPTexture(0, 0, 0, 0, G_OFF), | 140 | // } |
94 | }; | 141 | // break; |
95 | 142 | case 0xe: { | |
96 | // Initialize the RDP. | 143 | u16 x, y; |
97 | Gfx rdpinit_dl[] = { | 144 | u8 layer = d->dat[0xe] & 0x40; |
98 | gsDPSetCycleType(G_CYC_1CYCLE), | 145 | DEVPEEK16(x, 0x8); |
99 | gsDPSetScissor(G_SC_NON_INTERLACE, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT), | 146 | DEVPEEK16(y, 0xa); |
100 | gsDPSetCombineKey(G_CK_NONE), | 147 | ppu_pixel(layer ? pixels_fg : pixels_bg, x, y, d->dat[0xe] & 0x3); |
101 | gsDPSetAlphaCompare(G_AC_NONE), | 148 | if(d->dat[0x6] & 0x01) DEVPOKE16(0x8, x + 1); /* auto x+1 */ |
102 | gsDPSetRenderMode(G_RM_NOOP, G_RM_NOOP2), | 149 | if(d->dat[0x6] & 0x02) DEVPOKE16(0xa, y + 1); /* auto y+1 */ |
103 | gsDPSetColorDither(G_CD_DISABLE), | 150 | } break; |
104 | gsDPPipeSync(), | 151 | case 0xf: { |
105 | }; | 152 | u16 x, y, dx, dy, addr; |
106 | 153 | u8 twobpp = !!(d->dat[0xf] & 0x80); | |
107 | gSPDisplayList(glistp++, rspinit_dl); | 154 | DEVPEEK16(x, 0x8); |
108 | gSPDisplayList(glistp++, rdpinit_dl); | 155 | DEVPEEK16(y, 0xa); |
109 | gDPFullSync(glistp++); | 156 | DEVPEEK16(addr, 0xc); |
110 | gSPEndDisplayList(glistp++); | 157 | u8 n = d->dat[0x6] >> 4; |
111 | 158 | dx = (d->dat[0x6] & 0x01) << 3; | |
112 | // Start up the task list. | 159 | dy = (d->dat[0x6] & 0x02) << 2; |
113 | OSTask tlist = (OSTask){ | 160 | if(addr > 0x10000 - ((n + 1) << (3 + twobpp))) { |
114 | { | 161 | return; |
115 | .type = M_GFXTASK, | 162 | } |
116 | .flags = OS_TASK_DP_WAIT, | 163 | u8 *layer = (d->dat[0xf] & 0x40) ? pixels_fg : pixels_bg; |
117 | .ucode_boot = (u64*)rspbootTextStart, | 164 | u8 color = d->dat[0xf] & 0xf; |
118 | .ucode_boot_size = (u32)rspbootTextEnd - (u32)rspbootTextStart, | 165 | u8 flipx = d->dat[0xf] & 0x10; |
119 | .ucode = (u64*)gspF3DEX2_xbusTextStart, | 166 | u8 flipy = d->dat[0xf] & 0x20; |
120 | .ucode_size = SP_UCODE_SIZE, | 167 | for(size_t i = 0; i <= n; i++) { |
121 | .ucode_data = (u64*)gspF3DEX2_xbusDataStart, | 168 | u8 *sprite = &d->u->ram[addr]; |
122 | .ucode_data_size = SP_UCODE_DATA_SIZE, | 169 | if (twobpp) { |
123 | .data_ptr = (u64*)glist, | 170 | ppu_2bpp(layer, x + dy * i, y + dx * i, sprite, color, flipx, flipy); |
124 | .data_size = (u32)((glistp - glist) * sizeof(Gfx)), | 171 | } else { |
125 | .dram_stack = dram_stack, | 172 | ppu_1bpp(layer, x + dy * i, y + dx * i, sprite, color, flipx, flipy); |
126 | .dram_stack_size = SP_DRAM_STACK_SIZE8, | 173 | } |
127 | }, | 174 | addr += (d->dat[0x6] & 0x04) << (1 + twobpp); |
128 | }; | 175 | } |
129 | osSpTaskStart(&tlist); | 176 | DEVPOKE16(0xc, addr); /* auto addr+length */ |
130 | 177 | DEVPOKE16(0x8, x + dx); /* auto x+8 */ | |
131 | // Wait for RDP completion. | 178 | DEVPOKE16(0xa, y + dy); /* auto y+8 */ |
132 | osRecvMesg(&rdp_msg_queue, NULL, OS_MESG_BLOCK); | 179 | } break; |
180 | } | ||
181 | reqdraw = 1; | ||
133 | } | 182 | } |
134 | 183 | ||
135 | void | 184 | void |
136 | rdp_clearfb(u8 r, u8 g, u8 b) { | 185 | poll_input() { |
137 | Gfx *glistp = glist; | 186 | // STUB... |
138 | Gfx clearcfb_dl[] = { | ||
139 | gsDPSetCycleType(G_CYC_FILL), | ||
140 | gsDPSetColorImage( | ||
141 | G_IM_FMT_RGBA, G_IM_SIZ_16b, SCREEN_WIDTH, framebuffers[current_fb]), | ||
142 | gsDPSetFillColor( | ||
143 | GPACK_RGBA5551(r, g, b, 1) << 16 | GPACK_RGBA5551(r, g, b, 1)), | ||
144 | gsDPFillRectangle(0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1), | ||
145 | gsDPPipeSync(), | ||
146 | }; | ||
147 | gSPDisplayList(glistp++, clearcfb_dl); | ||
148 | gDPFullSync(glistp++); | ||
149 | gSPEndDisplayList(glistp++); | ||
150 | |||
151 | // Start up the task list. | ||
152 | OSTask tlist = (OSTask){ | ||
153 | { | ||
154 | .type = M_GFXTASK, | ||
155 | .flags = OS_TASK_DP_WAIT, | ||
156 | .ucode_boot = (u64*)rspbootTextStart, | ||
157 | .ucode_boot_size = (u32)rspbootTextEnd - (u32)rspbootTextStart, | ||
158 | .ucode = (u64*)gspF3DEX2_xbusTextStart, | ||
159 | .ucode_size = SP_UCODE_SIZE, | ||
160 | .ucode_data = (u64*)gspF3DEX2_xbusDataStart, | ||
161 | .ucode_data_size = SP_UCODE_DATA_SIZE, | ||
162 | .data_ptr = (u64*)glist, | ||
163 | .data_size = (u32)((glistp - glist) * sizeof(Gfx)), | ||
164 | .dram_stack = dram_stack, | ||
165 | .dram_stack_size = SP_DRAM_STACK_SIZE8, | ||
166 | }, | ||
167 | }; | ||
168 | osSpTaskStart(&tlist); | ||
169 | |||
170 | // Wait for RDP completion. | ||
171 | osRecvMesg(&rdp_msg_queue, NULL, OS_MESG_BLOCK); | ||
172 | } | 187 | } |
173 | 188 | ||
174 | void | 189 | void |
175 | rdp_texture_copy(void) { | 190 | handle_input() { |
176 | Gfx *glistp = glist; | 191 | // STUB... |
177 | Gfx dl[] = { | 192 | } |
178 | gsDPSetTexturePersp(G_TP_NONE), | 193 | |
179 | gsDPSetCycleType(G_CYC_COPY), | 194 | static u8 uxn_ram[0x10000]; |
180 | gsDPSetRenderMode(G_RM_NOOP, G_RM_NOOP2), | 195 | |
181 | gsSPClearGeometryMode(G_SHADE | G_SHADING_SMOOTH), | 196 | #include "uxn_screen_rom.c" |
182 | gsSPTexture(0x2000, 0x2000, 0, G_TX_RENDERTILE, G_ON), | 197 | |
183 | gsDPSetCombineMode(G_CC_DECALRGB, G_CC_DECALRGB), | 198 | void |
184 | gsDPSetTexturePersp(G_TP_NONE), | 199 | init_uxn(Uxn *u) { |
185 | gsDPSetTextureFilter(G_TF_POINT), | 200 | // Setup UXN memory. |
186 | }; | 201 | for (size_t i = 0; i < sizeof(uxn_ram); i++) { |
187 | gSPDisplayList(glistp++, dl); | 202 | uxn_ram[i] = 0; |
188 | for (size_t i = 0; i < 240; i++) { | 203 | } |
189 | gDPLoadTextureBlock( | 204 | uxn_boot(u, uxn_ram); |
190 | glistp++, | 205 | |
191 | &pixels[320 * 1 * i], // Texture data. | 206 | // Copy rom to VM. |
192 | G_IM_FMT_RGBA, // Image format. | 207 | // memcpy(u->ram + PAGE_PROGRAM, uxn_rom, sizeof(uxn_rom)); |
193 | G_IM_SIZ_16b, // Pixel component size. | 208 | u8 *dst = u->ram + PAGE_PROGRAM; |
194 | 320, // Width. | 209 | u8 *src = (u8*)uxn_rom; |
195 | 1, // Height. | 210 | for (size_t i = 0; i < sizeof(uxn_rom); i++) { |
196 | 0, // Palette location. | 211 | dst[i] = src[i]; |
197 | G_TX_CLAMP, // S-axis mirror/wrap/clamp. | ||
198 | G_TX_CLAMP, // T-axis mirror/wrap/clamp. | ||
199 | 0, // S-axis mask. | ||
200 | 0, // T-axis mask. | ||
201 | G_TX_NOLOD, // S-axis shift. | ||
202 | G_TX_NOLOD); // T-axis shift. | ||
203 | gSPTextureRectangle( | ||
204 | glistp++, | ||
205 | 0, // ulx | ||
206 | (1 * i) << 2, // uly | ||
207 | (320 * (i + 1)) << 2, // lrx | ||
208 | (1 * (i + 1)) << 2, // lry | ||
209 | 0, | ||
210 | 0, | ||
211 | 0, | ||
212 | 4 << 10, | ||
213 | 1 << 10); | ||
214 | } | 212 | } |
215 | gDPFullSync(glistp++); | 213 | |
216 | gSPEndDisplayList(glistp++); | 214 | // Prepare devices. |
217 | 215 | /* system */ uxn_port(u, 0x0, system_dei, system_deo); | |
218 | // Start up the task list. | 216 | /* console */ uxn_port(u, 0x1, nil_dei, console_deo); |
219 | OSTask tlist = (OSTask){ | 217 | /* screen */ devscreen = uxn_port(u, 0x2, screen_dei, screen_deo); |
220 | { | 218 | /* audio0 */ uxn_port(u, 0x3, nil_dei, nil_deo); |
221 | .type = M_GFXTASK, | 219 | /* audio1 */ uxn_port(u, 0x4, nil_dei, nil_deo); |
222 | .flags = OS_TASK_DP_WAIT, | 220 | /* audio2 */ uxn_port(u, 0x5, nil_dei, nil_deo); |
223 | .ucode_boot = (u64*)rspbootTextStart, | 221 | /* audio3 */ uxn_port(u, 0x6, nil_dei, nil_deo); |
224 | .ucode_boot_size = (u32)rspbootTextEnd - (u32)rspbootTextStart, | 222 | /* unused */ uxn_port(u, 0x7, nil_dei, nil_deo); |
225 | .ucode = (u64*)gspF3DEX2_xbusTextStart, | 223 | /* control */ devctrl = uxn_port(u, 0x8, nil_dei, nil_deo); |
226 | .ucode_size = SP_UCODE_SIZE, | 224 | /* mouse */ devmouse = uxn_port(u, 0x9, nil_dei, nil_deo); |
227 | .ucode_data = (u64*)gspF3DEX2_xbusDataStart, | 225 | /* file0 */ uxn_port(u, 0xa, nil_dei, nil_deo); |
228 | .ucode_data_size = SP_UCODE_DATA_SIZE, | 226 | /* file1 */ uxn_port(u, 0xb, nil_dei, nil_deo); |
229 | .data_ptr = (u64*)glist, | 227 | /* datetime */ uxn_port(u, 0xc, nil_dei, nil_deo); |
230 | .data_size = (u32)((glistp - glist) * sizeof(Gfx)), | 228 | /* unused */ uxn_port(u, 0xd, nil_dei, nil_deo); |
231 | .dram_stack = dram_stack, | 229 | /* unused */ uxn_port(u, 0xe, nil_dei, nil_deo); |
232 | .dram_stack_size = SP_DRAM_STACK_SIZE8, | 230 | /* unused */ uxn_port(u, 0xf, nil_dei, nil_deo); |
233 | }, | 231 | uxn_eval(u, PAGE_PROGRAM); |
234 | }; | ||
235 | osSpTaskStart(&tlist); | ||
236 | |||
237 | // Wait for RDP completion. | ||
238 | osRecvMesg(&rdp_msg_queue, NULL, OS_MESG_BLOCK); | ||
239 | } | 232 | } |
240 | 233 | ||
241 | static void | 234 | static void |
242 | main_proc(void *arg) { | 235 | main_proc(void *arg) { |
243 | (void)arg; | 236 | (void)arg; |
244 | 237 | ||
245 | // Setup the message queues | 238 | init_ppu(); |
246 | osCreateMesgQueue(&rdp_msg_queue, &rdp_msg_buf, 1); | ||
247 | osSetEventMesg(OS_EVENT_DP, &rdp_msg_queue, NULL); | ||
248 | osCreateMesgQueue(&retrace_msg_queue, &retrace_msg_buf, 1); | ||
249 | osViSetEvent(&retrace_msg_queue, NULL, 1); | ||
250 | |||
251 | // NOTE: Graphics drawing pipeline: | ||
252 | // 1. Transfer gfx data from ROM to RDRAM (CPU -- N64 OS) | ||
253 | // 2. Create display list in RDRAM from img data (CPU -- Game application) | ||
254 | // 3. Transfer display list and graphics microcode to the RSP (CPU -- N64 OS) | ||
255 | // 4. Conversion process (RSP -- GFX microcode) | ||
256 | // 5. Transfer converted data to RDP (RSP -- GFX microcode) | ||
257 | // 6. Manipulation process (RDP) | ||
258 | // 7. Transfer manipulated data to framebuffer (RDP) | ||
259 | // 8. Transfer manipulated data to the video interface (CPU -- N64 OS) | ||
260 | // 9. Trasfer digital data to DAC (Video Interface) | ||
261 | // 10. Video signal is produced. | ||
262 | |||
263 | // NOTE: Test image. | ||
264 | for (size_t j = 0; j < SCREEN_HEIGHT; j++) { | ||
265 | for (size_t i = 0; i < SCREEN_WIDTH; i++) { | ||
266 | u16 shade_x = (float)i / (float)SCREEN_WIDTH * 255; | ||
267 | u16 shade_y = (float)j / (float)SCREEN_HEIGHT * 255; | ||
268 | u16 color = GPACK_RGBA5551(shade_x, 0, shade_y, 1); | ||
269 | pixels[i + j * SCREEN_WIDTH] = color; | ||
270 | } | ||
271 | } | ||
272 | 239 | ||
273 | // Clear the framebuffer with rdp. | 240 | init_uxn(&u); |
274 | rdp_init(); | ||
275 | 241 | ||
276 | // Main loop. | 242 | // Main loop. |
277 | int increment = 4; | ||
278 | int direction = increment; | ||
279 | while (true) { | 243 | while (true) { |
280 | if (t >= 255 - increment) { | 244 | poll_input(); |
281 | direction = -increment; | 245 | handle_input(); |
282 | } if (t <= 0 + increment) { | 246 | uxn_eval(&u, GETVECTOR(devscreen)); |
283 | direction = +increment; | 247 | blit_framebuffer(); |
284 | } | 248 | swap_buffers(); |
285 | t += direction; | ||
286 | |||
287 | // A | ||
288 | // rdp_clearfb(t, t, t); | ||
289 | // osWritebackDCacheAll(); | ||
290 | // B | ||
291 | // fb_write_test(); | ||
292 | // osWritebackDCacheAll(); | ||
293 | // C | ||
294 | for (size_t j = 0; j < SCREEN_HEIGHT; j++) { | ||
295 | for (size_t i = 0; i < SCREEN_WIDTH; i++) { | ||
296 | u16 shade_x = (float)i / (float)SCREEN_WIDTH * 255; | ||
297 | u16 shade_y = (float)j / (float)SCREEN_HEIGHT * 255; | ||
298 | u16 color = GPACK_RGBA5551(shade_x, t, shade_y, 1); | ||
299 | pixels[i + j * SCREEN_WIDTH] = color; | ||
300 | } | ||
301 | } | ||
302 | // osWritebackDCacheAll(); | ||
303 | // fb_copy_test(); | ||
304 | // D | ||
305 | rdp_texture_copy(); | ||
306 | // osWritebackDCacheAll(); | ||
307 | // osWritebackDCacheAll(); | ||
308 | |||
309 | // Swap buffers. | ||
310 | osViSwapBuffer(framebuffers[current_fb]); | ||
311 | |||
312 | // Make sure there isn't an old retrace in queue | ||
313 | // (assumes queue has a depth of 1). | ||
314 | if (MQ_IS_FULL(&retrace_msg_queue)) { | ||
315 | osRecvMesg(&retrace_msg_queue, NULL, OS_MESG_BLOCK); | ||
316 | } | ||
317 | |||
318 | // Wait for Vertical retrace to finish swap buffers. | ||
319 | osRecvMesg(&retrace_msg_queue, NULL, OS_MESG_BLOCK); | ||
320 | current_fb ^= 1; | ||
321 | } | 249 | } |
322 | } | 250 | } |
323 | 251 | ||
diff --git a/src/ppu.c b/src/ppu.c new file mode 100644 index 0000000..e614f9b --- /dev/null +++ b/src/ppu.c | |||
@@ -0,0 +1,349 @@ | |||
1 | // Screen size. | ||
2 | #define SCREEN_WIDTH 320 | ||
3 | #define SCREEN_HEIGHT 240 | ||
4 | |||
5 | static OSMesg rdp_msg_buf, retrace_msg_buf; | ||
6 | static OSMesgQueue pi_msg_queue; | ||
7 | static OSMesgQueue rdp_msg_queue; | ||
8 | static OSMesgQueue retrace_msg_queue; | ||
9 | |||
10 | // | ||
11 | // Framebuffers. | ||
12 | // | ||
13 | |||
14 | u16 framebuffers[2][SCREEN_WIDTH * SCREEN_HEIGHT] __attribute__((aligned(16))); | ||
15 | |||
16 | // | ||
17 | // Statics. | ||
18 | // | ||
19 | |||
20 | static u64 dram_stack[SP_DRAM_STACK_SIZE64] __attribute__((aligned(16))); | ||
21 | static u16 pixels[SCREEN_WIDTH * SCREEN_HEIGHT] __attribute__((aligned(16))); | ||
22 | static int current_fb = 0; | ||
23 | static Gfx glist[2048 * 8]; | ||
24 | |||
25 | static size_t screen_width = SCREEN_WIDTH; | ||
26 | static size_t screen_height = SCREEN_HEIGHT; | ||
27 | static u16 palette[16]; | ||
28 | |||
29 | static u8 pixels_fg[SCREEN_WIDTH * SCREEN_HEIGHT] __attribute__((aligned(16))); | ||
30 | static u8 pixels_bg[SCREEN_WIDTH * SCREEN_HEIGHT] __attribute__((aligned(16))); | ||
31 | static u8 dirty_lines[SCREEN_HEIGHT]; | ||
32 | static u8 reqdraw = 0; | ||
33 | |||
34 | static u8 blending[5][16] = { | ||
35 | {0, 0, 0, 0, 1, 0, 1, 1, 2, 2, 0, 2, 3, 3, 3, 0}, | ||
36 | {0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3}, | ||
37 | {1, 2, 3, 1, 1, 2, 3, 1, 1, 2, 3, 1, 1, 2, 3, 1}, | ||
38 | {2, 3, 1, 2, 2, 3, 1, 2, 2, 3, 1, 2, 2, 3, 1, 2}, | ||
39 | {1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0}}; | ||
40 | |||
41 | u16 | ||
42 | rgb565(u32 rgba) { | ||
43 | u16 r = (rgba >> 16 & 0xFF); | ||
44 | u16 g = (rgba >> 8 & 0xFF); | ||
45 | u16 b = (rgba >> 0 & 0xFF); | ||
46 | r = r >> 3; | ||
47 | g = g >> 2; | ||
48 | b = b >> 3; | ||
49 | return (r << 11) | (g << 5) | b; | ||
50 | } | ||
51 | |||
52 | void | ||
53 | redraw_screen(void) { | ||
54 | for (size_t j = 0; j < screen_height; j++) { | ||
55 | dirty_lines[j] = 1; | ||
56 | } | ||
57 | } | ||
58 | |||
59 | void | ||
60 | ppu_pixel(u8 *layer, u16 x, u16 y, u8 color) { | ||
61 | if (x >= screen_width || y >= screen_height) { | ||
62 | return; | ||
63 | } | ||
64 | u32 i = x + y *screen_width; | ||
65 | if(color != layer[i]) { | ||
66 | layer[i] = color; | ||
67 | } | ||
68 | dirty_lines[y] |= 1; | ||
69 | } | ||
70 | |||
71 | void | ||
72 | ppu_1bpp(u8 *layer, u16 x, u16 y, u8 *sprite, u8 color, u8 flipx, u8 flipy) { | ||
73 | u16 v, h; | ||
74 | for(v = 0; v < 8; v++) | ||
75 | for(h = 0; h < 8; h++) { | ||
76 | u8 ch1 = (sprite[v] >> (7 - h)) & 0x1; | ||
77 | if(ch1 || blending[4][color]) | ||
78 | ppu_pixel(layer, | ||
79 | x + (flipx ? 7 - h : h), | ||
80 | y + (flipy ? 7 - v : v), | ||
81 | blending[ch1][color]); | ||
82 | } | ||
83 | } | ||
84 | |||
85 | void | ||
86 | ppu_2bpp(u8 *layer, u16 x, u16 y, u8 *sprite, u8 color, u8 flipx, u8 flipy) { | ||
87 | u16 v, h; | ||
88 | for(v = 0; v < 8; v++) | ||
89 | for(h = 0; h < 8; h++) { | ||
90 | u8 ch1 = ((sprite[v] >> (7 - h)) & 0x1); | ||
91 | u8 ch2 = ((sprite[v + 8] >> (7 - h)) & 0x1); | ||
92 | u8 ch = ch1 + ch2 * 2; | ||
93 | if(ch || blending[4][color]) | ||
94 | ppu_pixel(layer, | ||
95 | x + (flipx ? 7 - h : h), | ||
96 | y + (flipy ? 7 - v : v), | ||
97 | blending[ch][color]); | ||
98 | } | ||
99 | } | ||
100 | |||
101 | void | ||
102 | fb_write_test(void) { | ||
103 | for (size_t j = 0; j < SCREEN_HEIGHT; j++) { | ||
104 | for (size_t i = 0; i < SCREEN_WIDTH; i++) { | ||
105 | u16 shade_x = (float)i / (float)SCREEN_WIDTH * 255; | ||
106 | u16 shade_y = (float)j / (float)SCREEN_HEIGHT * 255; | ||
107 | u16 color = GPACK_RGBA5551(shade_x, 0, shade_y, 1); | ||
108 | framebuffers[current_fb][i + j * SCREEN_WIDTH] = color; | ||
109 | } | ||
110 | } | ||
111 | } | ||
112 | |||
113 | void | ||
114 | fb_copy_test(void) { | ||
115 | for (size_t i = 0; i < SCREEN_HEIGHT * SCREEN_WIDTH; i++) { | ||
116 | framebuffers[current_fb][i] = pixels[i]; | ||
117 | } | ||
118 | } | ||
119 | |||
120 | void | ||
121 | rdp_init(void) { | ||
122 | Gfx *glistp = glist; | ||
123 | |||
124 | static const Vp viewport = {{ | ||
125 | .vscale = {SCREEN_WIDTH * 2, SCREEN_HEIGHT * 2, G_MAXZ / 2, 0}, | ||
126 | .vtrans = {SCREEN_WIDTH * 2, SCREEN_HEIGHT * 2, G_MAXZ / 2, 0}, | ||
127 | }}; | ||
128 | |||
129 | // Initialize the RSP. | ||
130 | Gfx rspinit_dl[] = { | ||
131 | gsSPViewport(&viewport), | ||
132 | gsSPClearGeometryMode(G_SHADE | G_SHADING_SMOOTH | G_CULL_BOTH | | ||
133 | G_FOG | G_LIGHTING | G_TEXTURE_GEN | | ||
134 | G_TEXTURE_GEN_LINEAR | G_LOD), | ||
135 | gsSPTexture(0, 0, 0, 0, G_OFF), | ||
136 | }; | ||
137 | |||
138 | // Initialize the RDP. | ||
139 | Gfx rdpinit_dl[] = { | ||
140 | gsDPSetCycleType(G_CYC_1CYCLE), | ||
141 | gsDPSetScissor(G_SC_NON_INTERLACE, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT), | ||
142 | gsDPSetCombineKey(G_CK_NONE), | ||
143 | gsDPSetAlphaCompare(G_AC_NONE), | ||
144 | gsDPSetRenderMode(G_RM_NOOP, G_RM_NOOP2), | ||
145 | gsDPSetColorDither(G_CD_DISABLE), | ||
146 | gsDPPipeSync(), | ||
147 | }; | ||
148 | |||
149 | gSPDisplayList(glistp++, rspinit_dl); | ||
150 | gSPDisplayList(glistp++, rdpinit_dl); | ||
151 | gDPFullSync(glistp++); | ||
152 | gSPEndDisplayList(glistp++); | ||
153 | |||
154 | // Start up the task list. | ||
155 | OSTask tlist = (OSTask){ | ||
156 | { | ||
157 | .type = M_GFXTASK, | ||
158 | .flags = OS_TASK_DP_WAIT, | ||
159 | .ucode_boot = (u64*)rspbootTextStart, | ||
160 | .ucode_boot_size = (u32)rspbootTextEnd - (u32)rspbootTextStart, | ||
161 | .ucode = (u64*)gspF3DEX2_xbusTextStart, | ||
162 | .ucode_size = SP_UCODE_SIZE, | ||
163 | .ucode_data = (u64*)gspF3DEX2_xbusDataStart, | ||
164 | .ucode_data_size = SP_UCODE_DATA_SIZE, | ||
165 | .data_ptr = (u64*)glist, | ||
166 | .data_size = (u32)((glistp - glist) * sizeof(Gfx)), | ||
167 | .dram_stack = dram_stack, | ||
168 | .dram_stack_size = SP_DRAM_STACK_SIZE8, | ||
169 | }, | ||
170 | }; | ||
171 | osSpTaskStart(&tlist); | ||
172 | |||
173 | // Wait for RDP completion. | ||
174 | osRecvMesg(&rdp_msg_queue, NULL, OS_MESG_BLOCK); | ||
175 | } | ||
176 | |||
177 | void | ||
178 | rdp_clearfb(u8 r, u8 g, u8 b) { | ||
179 | Gfx *glistp = glist; | ||
180 | Gfx clearcfb_dl[] = { | ||
181 | gsDPSetCycleType(G_CYC_FILL), | ||
182 | gsDPSetColorImage( | ||
183 | G_IM_FMT_RGBA, G_IM_SIZ_16b, SCREEN_WIDTH, framebuffers[current_fb]), | ||
184 | gsDPSetFillColor( | ||
185 | GPACK_RGBA5551(r, g, b, 1) << 16 | GPACK_RGBA5551(r, g, b, 1)), | ||
186 | gsDPFillRectangle(0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1), | ||
187 | gsDPPipeSync(), | ||
188 | }; | ||
189 | gSPDisplayList(glistp++, clearcfb_dl); | ||
190 | gDPFullSync(glistp++); | ||
191 | gSPEndDisplayList(glistp++); | ||
192 | |||
193 | // Start up the task list. | ||
194 | OSTask tlist = (OSTask){ | ||
195 | { | ||
196 | .type = M_GFXTASK, | ||
197 | .flags = OS_TASK_DP_WAIT, | ||
198 | .ucode_boot = (u64*)rspbootTextStart, | ||
199 | .ucode_boot_size = (u32)rspbootTextEnd - (u32)rspbootTextStart, | ||
200 | .ucode = (u64*)gspF3DEX2_xbusTextStart, | ||
201 | .ucode_size = SP_UCODE_SIZE, | ||
202 | .ucode_data = (u64*)gspF3DEX2_xbusDataStart, | ||
203 | .ucode_data_size = SP_UCODE_DATA_SIZE, | ||
204 | .data_ptr = (u64*)glist, | ||
205 | .data_size = (u32)((glistp - glist) * sizeof(Gfx)), | ||
206 | .dram_stack = dram_stack, | ||
207 | .dram_stack_size = SP_DRAM_STACK_SIZE8, | ||
208 | }, | ||
209 | }; | ||
210 | osSpTaskStart(&tlist); | ||
211 | |||
212 | // Wait for RDP completion. | ||
213 | osRecvMesg(&rdp_msg_queue, NULL, OS_MESG_BLOCK); | ||
214 | } | ||
215 | |||
216 | void | ||
217 | rdp_texture_copy(void) { | ||
218 | Gfx *glistp = glist; | ||
219 | // gSPSegment(glistp++, 0, 0x0); /* Physical address segment */ | ||
220 | // gSPSegment(glistp++, 1, OS_K0_TO_PHYSICAL(framebuffers[current_fb])); | ||
221 | |||
222 | Gfx dl[] = { | ||
223 | gsDPSetColorImage( | ||
224 | G_IM_FMT_RGBA, G_IM_SIZ_16b, SCREEN_WIDTH, framebuffers[current_fb]), | ||
225 | gsDPSetFillColor(0), | ||
226 | gsDPFillRectangle(0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1), | ||
227 | gsDPPipeSync(), | ||
228 | gsDPSetTexturePersp(G_TP_NONE), | ||
229 | gsDPSetCycleType(G_CYC_COPY), | ||
230 | gsDPSetRenderMode(G_RM_NOOP, G_RM_NOOP2), | ||
231 | gsSPClearGeometryMode(G_SHADE | G_SHADING_SMOOTH), | ||
232 | gsSPTexture(0x2000, 0x2000, 0, G_TX_RENDERTILE, G_ON), | ||
233 | gsDPSetCombineMode(G_CC_DECALRGB, G_CC_DECALRGB), | ||
234 | gsDPSetTexturePersp(G_TP_NONE), | ||
235 | gsDPSetTextureFilter(G_TF_POINT), | ||
236 | }; | ||
237 | gSPDisplayList(glistp++, dl); | ||
238 | for (size_t i = 0; i < 240; i++) { | ||
239 | gDPLoadTextureBlock( | ||
240 | glistp++, | ||
241 | &pixels[320 * 1 * i], // Texture data. | ||
242 | G_IM_FMT_RGBA, // Image format. | ||
243 | G_IM_SIZ_16b, // Pixel component size. | ||
244 | 320, // Width. | ||
245 | 1, // Height. | ||
246 | 0, // Palette location. | ||
247 | G_TX_CLAMP, // S-axis mirror/wrap/clamp. | ||
248 | G_TX_CLAMP, // T-axis mirror/wrap/clamp. | ||
249 | 0, // S-axis mask. | ||
250 | 0, // T-axis mask. | ||
251 | G_TX_NOLOD, // S-axis shift. | ||
252 | G_TX_NOLOD); // T-axis shift. | ||
253 | gSPTextureRectangle( | ||
254 | glistp++, | ||
255 | 0, // ulx | ||
256 | (1 * i) << 2, // uly | ||
257 | (320 * (i + 1)) << 2, // lrx | ||
258 | (1 * (i + 1)) << 2, // lry | ||
259 | 0, | ||
260 | 0, | ||
261 | 0, | ||
262 | 4 << 10, | ||
263 | 1 << 10); | ||
264 | } | ||
265 | gDPFullSync(glistp++); | ||
266 | gSPEndDisplayList(glistp++); | ||
267 | osWritebackDCache(&glist, sizeof(glist)); | ||
268 | |||
269 | // Start up the task list. | ||
270 | OSTask tlist = (OSTask){ | ||
271 | { | ||
272 | .type = M_GFXTASK, | ||
273 | .flags = OS_TASK_DP_WAIT, | ||
274 | .ucode_boot = (u64*)rspbootTextStart, | ||
275 | .ucode_boot_size = (u32)rspbootTextEnd - (u32)rspbootTextStart, | ||
276 | .ucode = (u64*)gspF3DEX2_xbusTextStart, | ||
277 | .ucode_size = SP_UCODE_SIZE, | ||
278 | .ucode_data = (u64*)gspF3DEX2_xbusDataStart, | ||
279 | .ucode_data_size = SP_UCODE_DATA_SIZE, | ||
280 | .data_ptr = (u64*)glist, | ||
281 | .data_size = (u32)((glistp - glist) * sizeof(Gfx)), | ||
282 | .dram_stack = dram_stack, | ||
283 | .dram_stack_size = SP_DRAM_STACK_SIZE8, | ||
284 | }, | ||
285 | }; | ||
286 | osSpTaskStart(&tlist); | ||
287 | |||
288 | // Wait for RDP completion. | ||
289 | osRecvMesg(&rdp_msg_queue, NULL, OS_MESG_BLOCK); | ||
290 | } | ||
291 | |||
292 | void | ||
293 | init_ppu(void) { | ||
294 | // NOTE: Test image. | ||
295 | for (size_t j = 0; j < SCREEN_HEIGHT; j++) { | ||
296 | for (size_t i = 0; i < SCREEN_WIDTH; i++) { | ||
297 | u16 shade_x = (float)i / (float)SCREEN_WIDTH * 255; | ||
298 | u16 shade_y = (float)j / (float)SCREEN_HEIGHT * 255; | ||
299 | u16 color = GPACK_RGBA5551(shade_x, 0, shade_y, 1); | ||
300 | pixels[i + j * SCREEN_WIDTH] = color; | ||
301 | } | ||
302 | } | ||
303 | // TODO: clear pixel buffers and dirty lines | ||
304 | |||
305 | // Setup the message queues | ||
306 | osCreateMesgQueue(&rdp_msg_queue, &rdp_msg_buf, 1); | ||
307 | osSetEventMesg(OS_EVENT_DP, &rdp_msg_queue, NULL); | ||
308 | osCreateMesgQueue(&retrace_msg_queue, &retrace_msg_buf, 1); | ||
309 | osViSetEvent(&retrace_msg_queue, NULL, 1); | ||
310 | } | ||
311 | |||
312 | void | ||
313 | swap_buffers(void) { | ||
314 | osViSwapBuffer(framebuffers[current_fb]); | ||
315 | |||
316 | // Wait for Vertical retrace to finish swap buffers. | ||
317 | if (MQ_IS_FULL(&retrace_msg_queue)) { | ||
318 | osRecvMesg(&retrace_msg_queue, NULL, OS_MESG_BLOCK); | ||
319 | } | ||
320 | osRecvMesg(&retrace_msg_queue, NULL, OS_MESG_BLOCK); | ||
321 | |||
322 | current_fb ^= 1; | ||
323 | } | ||
324 | |||
325 | void | ||
326 | blit_framebuffer(void) { | ||
327 | if (reqdraw == 0) { | ||
328 | return; | ||
329 | } | ||
330 | |||
331 | for (size_t j = 0; j < screen_height; j++) { | ||
332 | if (dirty_lines[j] != 0) { | ||
333 | for (size_t i = 0; i < screen_width; i++) { | ||
334 | size_t idx = i + j * screen_width; | ||
335 | pixels[idx] = palette[pixels_fg[idx] << 2 | pixels_bg[idx]]; | ||
336 | } | ||
337 | } | ||
338 | dirty_lines[j] = 0; | ||
339 | } | ||
340 | |||
341 | // TODO: Find out how to properly draw to the framebuffer using either the | ||
342 | // RDP or DMA. Maybe just drawing directly with the CPU is fine for our | ||
343 | // purposes though. | ||
344 | // rdp_clearfb(t, t, t); | ||
345 | // fb_write_test(); | ||
346 | fb_copy_test(); // CPU blit | ||
347 | // rdp_texture_copy(); // RDP texture blit | ||
348 | reqdraw = 0; | ||
349 | } | ||
diff --git a/src/uxn b/src/uxn new file mode 160000 | |||
Subproject 5496712ae1b231ea31be68151d2cfd10e933969 | |||
diff --git a/src/uxn_screen_rom.c b/src/uxn_screen_rom.c new file mode 100644 index 0000000..4e00d83 --- /dev/null +++ b/src/uxn_screen_rom.c | |||
@@ -0,0 +1,41 @@ | |||
1 | // NOTE: MIPS is a MSB system, make sure the bytes are in the right order. | ||
2 | const u16 uxn_rom[] = { | ||
3 | 0xa001, 0x4780, 0x2037, 0xa0f0, 0x7f80, 0x0837, 0xa0f0, 0xe080, | ||
4 | 0x0a37, 0xa0f0, 0xc080, 0x0c37, 0xa000, 0xf080, 0x2237, 0xa000, | ||
5 | 0xa080, 0x2437, 0x8022, 0x3680, 0x013f, 0x8002, 0x3180, 0x2436, | ||
6 | 0x8001, 0x3f80, 0x0431, 0xa001, 0x6c2e, 0xa001, 0xf52e, 0xa002, | ||
7 | 0x2f2e, 0xa000, 0x2617, 0x00a0, 0x0126, 0x1780, 0x0030, 0x2126, | ||
8 | 0x8000, 0x3180, 0x0230, 0xa000, 0x5038, 0x8028, 0x3780, 0x0430, | ||
9 | 0xa000, 0x4839, 0x802a, 0x37a0, 0x02a3, 0x2e00, 0x8002, 0x30a0, | ||
10 | 0x0060, 0x3980, 0x2837, 0x8004, 0x30a0, 0x0048, 0x3980, 0x2a37, | ||
11 | 0xa002, 0xd380, 0x2c37, 0xa0f6, 0x2617, 0xa001, 0x2f17, 0x8002, | ||
12 | 0x30a0, 0x0070, 0x3980, 0x2837, 0x8004, 0x30a0, 0x0038, 0x3980, | ||
13 | 0x2a37, 0xa002, 0xd380, 0x2c37, 0xa0f5, 0x2617, 0xa001, 0x2f17, | ||
14 | 0xa000, 0x2617, 0xa002, 0xc380, 0x2c37, 0xa000, 0x0006, 0x800f, | ||
15 | 0x1c80, 0x401f, 0x8001, 0x1f80, 0x0004, 0x8002, 0x30a0, 0x0060, | ||
16 | 0x3938, 0x8028, 0x3706, 0x80f0, 0x1c80, 0x011f, 0x8000, 0x0480, | ||
17 | 0x0430, 0xa000, 0x3839, 0x3880, 0x2a37, 0x0680, 0x2f17, 0x0189, | ||
18 | 0x80ca, 0x0d22, 0x6ca0, 0x1000, 0x8f06, 0x8002, 0x1f80, 0x0004, | ||
19 | 0x8040, 0x3f80, 0x0430, 0xa000, 0x3839, 0x382f, 0x0680, 0x031c, | ||
20 | 0x8000, 0x0480, 0x403f, 0xa000, 0x4038, 0x8002, 0x30a0, 0x0010, | ||
21 | 0x3938, 0x6f4f, 0xa000, 0x410e, 0x018a, 0x80cb, 0x0d22, 0x6ca0, | ||
22 | 0x1000, 0x8f06, 0x8002, 0x1f80, 0x0004, 0x8040, 0x3f80, 0x0430, | ||
23 | 0xa000, 0x0838, 0x382f, 0x0680, 0x031c, 0x8000, 0x0480, 0x403f, | ||
24 | 0xa000, 0x4038, 0x8002, 0x30a0, 0x0010, 0x3938, 0x6f4f, 0xa080, | ||
25 | 0x070e, 0x018a, 0x80cb, 0x0d22, 0x6c18, 0x0fa0, 0x02c3, 0x802c, | ||
26 | 0x3780, 0x2a37, 0x8028, 0x37a0, 0x0126, 0x174f, 0x802f, 0x97a0, | ||
27 | 0x0226, 0x1704, 0x8010, 0x1804, 0x9780, 0x28b6, 0xa000, 0x0839, | ||
28 | 0x0537, 0xa001, 0x2617, 0x0480, 0x1018, 0x0497, 0x0480, 0x1018, | ||
29 | 0x0417, 0x6c04, 0x8000, 0x0e06, 0x8004, 0x1f80, 0x030e, 0x800f, | ||
30 | 0x1c80, 0x0004, 0x8030, 0x3fa0, 0x02d3, 0x3880, 0x2c37, 0xa001, | ||
31 | 0x2f17, 0x6c0f, 0x3867, 0x5fdf, 0xbfbf, 0xbf00, 0x0718, 0x2023, | ||
32 | 0x4448, 0x4800, 0x7c82, 0x8282, 0x8282, 0x7c00, 0x3010, 0x1010, | ||
33 | 0x1010, 0x1000, 0x7c82, 0x027c, 0x8080, 0xfe00, 0x7c82, 0x021c, | ||
34 | 0x0282, 0x7c00, 0x0c14, 0x2444, 0x84fe, 0x0400, 0xfe80, 0x807c, | ||
35 | 0x0282, 0x7c00, 0x7c82, 0x80fc, 0x8282, 0x7c00, 0x7c82, 0x021e, | ||
36 | 0x0202, 0x0200, 0x7c82, 0x827c, 0x8282, 0x7c00, 0x7c82, 0x827e, | ||
37 | 0x0282, 0x7c00, 0x7c82, 0x027e, 0x8282, 0x7e00, 0xfc82, 0x82fc, | ||
38 | 0x8282, 0xfc00, 0x7c82, 0x8080, 0x8082, 0x7c00, 0xfc82, 0x8282, | ||
39 | 0x8282, 0xfc00, 0x7c82, 0x80f0, 0x8082, 0x7c00, 0x7c82, 0x80f0, | ||
40 | 0x8080, 0x8080, | ||
41 | }; | ||