diff options
Diffstat (limited to 'src/devices.c')
-rw-r--r-- | src/devices.c | 303 |
1 files changed, 303 insertions, 0 deletions
diff --git a/src/devices.c b/src/devices.c new file mode 100644 index 0000000..6a1ed28 --- /dev/null +++ b/src/devices.c | |||
@@ -0,0 +1,303 @@ | |||
1 | static time_t seconds = 1693475007; | ||
2 | |||
3 | #define RAM_PAGES 0x10 | ||
4 | |||
5 | void | ||
6 | deo_console(u8 *dev, u8 port) { | ||
7 | switch(port) { | ||
8 | case 0x8: | ||
9 | txt_putc(dev[port]); | ||
10 | return; | ||
11 | case 0x9: | ||
12 | txt_printf("ERROR: %c"); | ||
13 | txt_putc(dev[port]); | ||
14 | return; | ||
15 | } | ||
16 | } | ||
17 | |||
18 | u16 | ||
19 | dei_screen(u8 *dev, u8 port) { | ||
20 | switch(port) { | ||
21 | case 0x0: | ||
22 | case 0x8: | ||
23 | case 0xa: | ||
24 | case 0xc: return PEEK2(dev + port); | ||
25 | case 0x2: return SCREEN_WIDTH; | ||
26 | case 0x4: return SCREEN_HEIGHT; | ||
27 | default: return dev[port]; | ||
28 | } | ||
29 | } | ||
30 | |||
31 | u16 | ||
32 | dei_mouse(u8 *dev, u8 port) { | ||
33 | switch(port) { | ||
34 | case 0x0: | ||
35 | case 0x2: | ||
36 | case 0x4: | ||
37 | case 0xa: | ||
38 | case 0xc: return PEEK2(dev + port); | ||
39 | default: return dev[port]; | ||
40 | } | ||
41 | } | ||
42 | |||
43 | void | ||
44 | deo_screen(u8 *dev, u8 port) { | ||
45 | switch(port) { | ||
46 | case 0xe: { | ||
47 | u8 ctrl = dev[0xe]; | ||
48 | u8 color = ctrl & 0x3; | ||
49 | u16 x0 = PEEK2(dev + 0x8); | ||
50 | u16 y0 = PEEK2(dev + 0xa); | ||
51 | u8 *layer = (ctrl & 0x40) ? FG_BACK : BG_BACK; | ||
52 | if(ctrl & 0x80) { | ||
53 | u16 x1 = SCREEN_WIDTH - 1; | ||
54 | u16 y1 = SCREEN_HEIGHT - 1; | ||
55 | if(ctrl & 0x10) x1 = x0, x0 = 0; | ||
56 | if(ctrl & 0x20) y1 = y0, y0 = 0; | ||
57 | PROF(screen_fill(layer, x0, y0, x1, y1, color), ppu_fill_cycles); | ||
58 | } else { | ||
59 | PROF(ppu_pixel(layer, x0, y0, color), ppu_pixel_cycles); | ||
60 | if(dev[0x6] & 0x1) POKE2(dev + 0x8, x0 + 1); /* auto x+1 */ | ||
61 | if(dev[0x6] & 0x2) POKE2(dev + 0xa, y0 + 1); /* auto y+1 */ | ||
62 | } | ||
63 | break; | ||
64 | } | ||
65 | case 0xf: { | ||
66 | u8 ctrl = dev[0xf]; | ||
67 | u8 move = dev[0x6]; | ||
68 | u8 length = move >> 4; | ||
69 | u8 twobpp = !!(ctrl & 0x80); | ||
70 | u8 *layer = (dev[0xf] & 0x40) ? FG_BACK : BG_BACK; | ||
71 | u8 color = ctrl & 0xf; | ||
72 | u16 x = PEEK2(dev + 0x8), dx = (move & 0x1) << 3; | ||
73 | u16 y = PEEK2(dev + 0xa), dy = (move & 0x2) << 2; | ||
74 | u16 addr = PEEK2(dev + 0xc), addr_incr = (move & 0x4) << (1 + twobpp); | ||
75 | int flipx = (ctrl & 0x10), fx = flipx ? -1 : 1; | ||
76 | int flipy = (ctrl & 0x20), fy = flipy ? -1 : 1; | ||
77 | for(size_t i = 0; i <= length; i++) { | ||
78 | u8 *sprite = &uxn_ram[addr]; | ||
79 | if (twobpp) { | ||
80 | PROF(ppu_2bpp(layer, | ||
81 | x + dy * i * fx, | ||
82 | y + dx * i * fy, | ||
83 | sprite, | ||
84 | color, | ||
85 | flipx, flipy), ppu_chr_cycles); | ||
86 | } else { | ||
87 | PROF(ppu_1bpp(layer, | ||
88 | x + dy * i * fx, | ||
89 | y + dx * i * fy, | ||
90 | sprite, | ||
91 | color, | ||
92 | flipx, flipy), ppu_icn_cycles); | ||
93 | } | ||
94 | addr += addr_incr; | ||
95 | } | ||
96 | if(move & 0x1) POKE2(dev + 0x8, x + dx * fx); /* auto x+8 */ | ||
97 | if(move & 0x2) POKE2(dev + 0xa, y + dy * fy); /* auto y+8 */ | ||
98 | if(move & 0x4) POKE2(dev + 0xc, addr); /* auto addr+length */ | ||
99 | break; | ||
100 | } | ||
101 | } | ||
102 | } | ||
103 | |||
104 | void | ||
105 | deo_system(u8 *dev, u8 port) { | ||
106 | switch(port) { | ||
107 | case 0x2: { | ||
108 | // Rom bank switching. | ||
109 | u16 addr = PEEK2(dev + port); | ||
110 | if(uxn_ram[addr] == 0x01) { | ||
111 | u16 length = PEEK2(uxn_ram + addr + 1); | ||
112 | u16 a_page = PEEK2(uxn_ram + addr + 1 + 2); | ||
113 | u16 a_addr = PEEK2(uxn_ram + addr + 1 + 4); | ||
114 | u16 b_page = PEEK2(uxn_ram + addr + 1 + 6); | ||
115 | u16 b_addr = PEEK2(uxn_ram + addr + 1 + 8); | ||
116 | u8 *ram = uxn_ram + (b_page % RAM_PAGES) * 0x10000; | ||
117 | u8 *rom = uxn_rom + (a_page % RAM_PAGES) * 0x10000 - PAGE_PROGRAM; | ||
118 | for(size_t i = 0; i < length; i++) { | ||
119 | ram[b_addr + i] = rom[a_addr + i]; | ||
120 | } | ||
121 | } | ||
122 | } break; | ||
123 | case 0x4: { | ||
124 | // TODO: Set wst_ptr, but is it the offset instead? | ||
125 | } break; | ||
126 | case 0x5: { | ||
127 | // TODO: Set rst_ptr, but is it the offset instead? | ||
128 | } break; | ||
129 | case 0x8: | ||
130 | case 0x9: | ||
131 | case 0xa: | ||
132 | case 0xb: | ||
133 | case 0xc: | ||
134 | case 0xd: { | ||
135 | // Setup RGB palette. | ||
136 | putcolors(&dev[0x8]); | ||
137 | } break; | ||
138 | case 0xe: { | ||
139 | // TODO: System inspect. | ||
140 | } break; | ||
141 | } | ||
142 | } | ||
143 | |||
144 | u16 | ||
145 | dei_system(u8 *dev, u8 port) { | ||
146 | switch (port) { | ||
147 | case 0x0: | ||
148 | case 0x2: | ||
149 | case 0x6: | ||
150 | case 0x8: | ||
151 | case 0xa: | ||
152 | case 0xc: return PEEK2(dev + port); | ||
153 | case 0x4: { | ||
154 | // TODO: Return wst_ptr, but is it the offset instead? | ||
155 | } break; | ||
156 | case 0x5: { | ||
157 | // TODO: Return rst_ptr, but is it the offset instead? | ||
158 | } break; | ||
159 | } | ||
160 | return dev[port]; | ||
161 | } | ||
162 | |||
163 | u16 | ||
164 | dei_datetime(u8 *dev, u8 port) { | ||
165 | struct tm *t = gmtime(&seconds); | ||
166 | switch(port) { | ||
167 | case 0x0: return (t->tm_year + 1900); | ||
168 | case 0x1: return (t->tm_year + 1900) >> 8; | ||
169 | case 0x2: return t->tm_mon; | ||
170 | case 0x3: return t->tm_mday; | ||
171 | case 0x4: return t->tm_hour; | ||
172 | case 0x5: return t->tm_min; | ||
173 | case 0x6: return t->tm_sec; | ||
174 | case 0x7: return t->tm_wday; | ||
175 | case 0x8: return t->tm_yday; | ||
176 | case 0x9: return t->tm_yday >> 8; | ||
177 | case 0xa: return t->tm_isdst; | ||
178 | } | ||
179 | return dev[port]; | ||
180 | } | ||
181 | |||
182 | u16 | ||
183 | dei_audio(u8 *dev, u8 port) { | ||
184 | size_t idx = (dev - (device_data + 0x30)) / 16; | ||
185 | AudioChannel *c = &channels[idx]; | ||
186 | switch(port) { | ||
187 | case 0x0: | ||
188 | case 0x8: | ||
189 | case 0xa: | ||
190 | case 0x2: // TODO: return the position | ||
191 | case 0xc: return PEEK2(dev + port); | ||
192 | // case 0x2: { | ||
193 | // POKE2(d + 0x2, c->pos); | ||
194 | // c->pos <<= 12; // fixed point. | ||
195 | // break; | ||
196 | // } | ||
197 | // case 0x4: return apu_get_vu(idx); | ||
198 | // TODO: return the current envelope loudness (0x00-0xff). | ||
199 | default: return dev[port]; | ||
200 | } | ||
201 | return dev[port]; | ||
202 | } | ||
203 | |||
204 | void | ||
205 | deo_audio(u8 *dev, u8 port) { | ||
206 | size_t idx = (dev - (device_data + 0x30)) / 16; | ||
207 | txt_printf("IDX: %d\n", idx); | ||
208 | AudioChannel *c = &channels[idx]; | ||
209 | if (port == 0xf) { | ||
210 | u16 length = 0; | ||
211 | u16 adsr = 0; | ||
212 | u16 addr = 0; | ||
213 | u8 pitch = dev[0xf] & 0x7f; | ||
214 | adsr = PEEK2(dev + 0x8); | ||
215 | length = PEEK2(dev + 0xa); | ||
216 | addr = PEEK2(dev + 0xc); | ||
217 | u8 *data = &uxn_ram[addr]; | ||
218 | u32 vol = MAX(dev[0xe] >> 4, dev[0xe] & 0xf) * 4 / 3; | ||
219 | bool loop = !(dev[0xf] & 0x80); | ||
220 | update_channel(c, data, length, pitch, adsr, vol, loop); | ||
221 | } | ||
222 | } | ||
223 | |||
224 | u16 | ||
225 | dei_file(u8 *dev, u8 port) { | ||
226 | size_t idx = (dev - (device_data + 0xa0)) / 16; | ||
227 | UxnFile *c = &uxn_file[idx]; | ||
228 | switch(port) { | ||
229 | case 0x0: | ||
230 | case 0x2: | ||
231 | case 0x4: | ||
232 | case 0x8: | ||
233 | case 0xa: | ||
234 | case 0xe: return PEEK2(dev + port); | ||
235 | case 0xc: | ||
236 | case 0xd: { | ||
237 | u16 res = file_read(c, &dev[port], 1); | ||
238 | POKE2(dev + 0x2, res); | ||
239 | return res; | ||
240 | } | ||
241 | } | ||
242 | return dev[port]; | ||
243 | } | ||
244 | |||
245 | void | ||
246 | deo_file(u8 *dev, u8 port) { | ||
247 | size_t idx = (dev - (device_data + 0xa0)) / 16; | ||
248 | u16 a, b, res; | ||
249 | UxnFile *f = &uxn_file[idx]; | ||
250 | switch(port) { | ||
251 | case 0x5: { | ||
252 | a = PEEK2(dev + 0x4); | ||
253 | b = PEEK2(dev + 0xa); | ||
254 | if(b > 0x10000 - a) { | ||
255 | b = 0x10000 - a; | ||
256 | } | ||
257 | res = file_stat(f, &uxn_ram[a], b); | ||
258 | POKE2(dev + 0x2, res); | ||
259 | } break; | ||
260 | case 0x6: { | ||
261 | // TODO: no file deletion for now | ||
262 | // res = file_delete(); | ||
263 | // POKE2(dev + 0x2, res); | ||
264 | } break; | ||
265 | case 0x9: { | ||
266 | a = PEEK2(dev + 0x8); | ||
267 | res = file_init(f, &uxn_ram[a]); | ||
268 | POKE2(dev + 0x2, res); | ||
269 | } break; | ||
270 | case 0xd: { | ||
271 | a = PEEK2(dev + 0xc); | ||
272 | b = PEEK2(dev + 0xa); | ||
273 | if(b > 0x10000 - a) { | ||
274 | b = 0x10000 - a; | ||
275 | } | ||
276 | res = file_read(f, &uxn_ram[a], b); | ||
277 | POKE2(dev + 0x2, res); | ||
278 | } break; | ||
279 | case 0xf: { | ||
280 | a = PEEK2(dev + 0xe); | ||
281 | b = PEEK2(dev + 0xa); | ||
282 | if(b > 0x10000 - a) { | ||
283 | b = 0x10000 - a; | ||
284 | } | ||
285 | res = file_write(f, &uxn_ram[a], b, dev[0x7]); | ||
286 | POKE2(dev + 0x2, res); | ||
287 | } break; | ||
288 | } | ||
289 | } | ||
290 | |||
291 | void | ||
292 | deo_stub(u8 *dev, u8 port) { | ||
293 | (void)dev; | ||
294 | (void)port; | ||
295 | } | ||
296 | |||
297 | u16 | ||
298 | dei_stub(u8 *dev, u8 port) { | ||
299 | if (port == 0) { | ||
300 | return PEEK2(dev); | ||
301 | } | ||
302 | return dev[port]; | ||
303 | } | ||