1 #include "spi.h"
2 #include "emu.h"
3 #include "bus.h"
4 #include "schedule.h"
5
6 #include <assert.h>
7 #include <stdlib.h>
8 #include <string.h>
9
10 spi_state_t spi;
11
spi_scan_line(uint16_t row)12 static bool spi_scan_line(uint16_t row) {
13 if (unlikely(row > SPI_LAST_ROW)) {
14 spi.mode |= SPI_MODE_IGNORE;
15 return false;
16 }
17 spi.mode &= ~SPI_MODE_IGNORE;
18 if (unlikely(spi.mode & SPI_MODE_PARTIAL) &&
19 spi.partialStart > spi.partialEnd ?
20 spi.partialStart > row && row > spi.partialEnd :
21 spi.partialStart > row || row > spi.partialEnd) {
22 spi.mode |= SPI_MODE_BLANK;
23 } else {
24 spi.mode &= ~SPI_MODE_BLANK;
25 }
26 spi.row = spi.dstRow = spi.srcRow = row;
27 if (unlikely(spi.mode & SPI_MODE_SCROLL)) {
28 uint16_t top = spi.topArea, bot = SPI_LAST_ROW - spi.bottomArea;
29 if (row >= top && row <= bot) {
30 spi.srcRow += spi.scrollStart - top;
31 if (spi.srcRow > bot) {
32 spi.srcRow -= SPI_NUM_ROWS - spi.topArea - spi.bottomArea;
33 }
34 spi.srcRow &= 0x1FF;
35 }
36 }
37 if (unlikely(spi.mac & SPI_MAC_VRO)) {
38 spi.dstRow = SPI_LAST_ROW - spi.dstRow;
39 spi.srcRow = SPI_LAST_ROW - spi.srcRow;
40 }
41 if (unlikely(spi.mac & SPI_MAC_HRO)) {
42 spi.col = SPI_LAST_COL;
43 spi.colDir = -1;
44 } else {
45 spi.col = 0;
46 spi.colDir = 1;
47 }
48 return true;
49 }
50
spi_hsync(void)51 bool spi_hsync(void) {
52 return spi_scan_line(spi.row + 1);
53 }
54
spi_reset_mregs(void)55 static void spi_reset_mregs(void) {
56 if (unlikely(spi.mac & SPI_MAC_RCX)) {
57 spi.rowReg = spi.rowStart;
58 spi.colReg = spi.colStart;
59 } else {
60 spi.rowReg = spi.colStart;
61 spi.colReg = spi.rowStart;
62 }
63 }
64
spi_vsync(void)65 bool spi_vsync(void) {
66 if (likely(spi.ifCtl & SPI_IC_CTRL_DATA)) {
67 spi_reset_mregs();
68 }
69 return spi_scan_line(0);
70 }
71
spi_refresh_pixel(void)72 bool spi_refresh_pixel(void) {
73 uint8_t *pixel, red, green, blue;
74 if (unlikely(spi.mode & SPI_MODE_IGNORE)) {
75 return false;
76 }
77 if (unlikely(spi.mode & (SPI_MODE_SLEEP | SPI_MODE_OFF | SPI_MODE_BLANK))) {
78 red = green = blue = ~0;
79 } else {
80 if (unlikely(spi.srcRow > SPI_LAST_ROW)) {
81 red = bus_rand();
82 green = bus_rand();
83 blue = bus_rand();
84 } else {
85 pixel = spi.frame[spi.srcRow][spi.col];
86 red = pixel[SPI_RED];
87 green = pixel[SPI_GREEN];
88 blue = pixel[SPI_BLUE];
89 }
90 if (!likely(spi.mac & SPI_MAC_BGR)) { /* eor */
91 uint8_t temp = red;
92 red = blue;
93 blue = temp;
94 }
95 if (unlikely(spi.mode & SPI_MODE_INVERT)) {
96 red = ~red;
97 green = ~green;
98 blue = ~blue;
99 }
100 if (unlikely(spi.mode & SPI_MODE_IDLE)) {
101 red = (int8_t)red >> 7;
102 green = (int8_t)green >> 7;
103 blue = (int8_t)blue >> 7;
104 }
105 }
106 pixel = spi.display[spi.col][spi.dstRow];
107 pixel[SPI_RED] = red;
108 pixel[SPI_GREEN] = green;
109 pixel[SPI_BLUE] = blue;
110 pixel[SPI_ALPHA] = ~0;
111 spi.col += spi.colDir;
112 if (unlikely(spi.col > SPI_LAST_COL)) {
113 spi.mode |= SPI_MODE_IGNORE;
114 return false;
115 }
116 return true;
117 }
118
spi_update_pixel(uint8_t red,uint8_t green,uint8_t blue)119 static void spi_update_pixel(uint8_t red, uint8_t green, uint8_t blue) {
120 if (likely(spi.rowReg < 320 && spi.colReg < 240)) {
121 uint8_t *pixel = spi.frame[spi.rowReg][spi.colReg];
122 pixel[SPI_RED] = red;
123 pixel[SPI_GREEN] = green;
124 pixel[SPI_BLUE] = blue;
125 }
126 if (unlikely(spi.mac & SPI_MAC_RCX)) {
127 if (unlikely(spi.colReg == spi.colEnd)) {
128 if (unlikely(spi.rowReg == spi.rowEnd && spi.rowStart <= spi.rowEnd)) {
129 spi.rowReg = spi.colReg = ~0;
130 } else {
131 spi.colReg = spi.colStart;
132 spi.rowReg = (spi.rowReg + 1 - (spi.mac >> 6 & 2)) & 0x1FF;
133 }
134 } else if (spi.colReg < 0x100) {
135 spi.colReg = (spi.colReg + 1 - (spi.mac >> 5 & 2)) & 0xFF;
136 }
137 } else {
138 if (unlikely(spi.rowReg == spi.colEnd)) {
139 if (unlikely(spi.colReg == spi.rowEnd && spi.rowStart <= spi.rowEnd)) {
140 spi.rowReg = spi.colReg = ~0;
141 } else {
142 spi.rowReg = spi.colStart;
143 spi.colReg = (spi.colReg + 1 - (spi.mac >> 5 & 2)) & 0xFF;
144 }
145 } else if (spi.rowReg < 0x200) {
146 spi.rowReg = (spi.rowReg + 1 - (spi.mac >> 6 & 2)) & 0x1FF;
147 }
148 }
149 }
150
spi_update_pixel_18bpp(uint8_t red,uint8_t green,uint8_t blue)151 void spi_update_pixel_18bpp(uint8_t red, uint8_t green, uint8_t blue) {
152 assert(red < 64 && green < 64 && blue < 64);
153 spi_update_pixel(red << 2 | red >> 4, green << 2 | green >> 4, blue << 2 | blue >> 4);
154 }
155
spi_update_pixel_16bpp(uint8_t red,uint8_t green,uint8_t blue)156 void spi_update_pixel_16bpp(uint8_t red, uint8_t green, uint8_t blue) {
157 assert(red < 32 && green < 64 && blue < 32);
158 spi_update_pixel(spi.lut[red + 0], spi.lut[green + 32], spi.lut[blue + 96]);
159 }
160
spi_update_pixel_12bpp(uint8_t red,uint8_t green,uint8_t blue)161 void spi_update_pixel_12bpp(uint8_t red, uint8_t green, uint8_t blue) {
162 assert(red < 16 && green < 16 && blue < 16);
163 spi_update_pixel(spi.lut[(red << 1) + 0], spi.lut[(green << 2) + 32], spi.lut[(blue << 1) + 96]);
164 }
165
spi_sw_reset(void)166 static void spi_sw_reset(void) {
167 spi.cmd = 0;
168 spi.fifo = 1;
169 spi.param = 0;
170 spi.gamma = 1;
171 spi.mode = SPI_MODE_SLEEP | SPI_MODE_OFF;
172 spi.colStart = 0;
173 spi.colEnd = spi.mac & SPI_MAC_RCX ? SPI_LAST_COL : SPI_LAST_ROW;
174 spi.rowStart = 0;
175 spi.rowEnd = spi.mac & SPI_MAC_RCX ? SPI_LAST_ROW : SPI_LAST_COL;
176 spi.topArea = 0;
177 spi.scrollArea = SPI_NUM_ROWS;
178 spi.bottomArea = 0;
179 spi.partialStart = 0;
180 spi.partialEnd = SPI_LAST_ROW;
181 spi.scrollStart = 0;
182 spi.tear = false;
183 }
184
spi_hw_reset(void)185 static void spi_hw_reset(void) {
186 spi.mac = 0;
187 spi_sw_reset();
188 }
189
spi_write_cmd(uint8_t value)190 static void spi_write_cmd(uint8_t value) {
191 spi.cmd = value;
192 spi.param = 0;
193
194 switch (spi.cmd) {
195 case 0x00:
196 break;
197 case 0x01:
198 spi_sw_reset();
199 break;
200 case 0x10:
201 spi.mode |= SPI_MODE_SLEEP;
202 break;
203 case 0x11:
204 spi.mode &= ~SPI_MODE_SLEEP;
205 break;
206 case 0x12:
207 spi.mode |= SPI_MODE_PARTIAL;
208 spi.scrollStart = 0;
209 break;
210 case 0x13:
211 spi.mode &= ~(SPI_MODE_PARTIAL | SPI_MODE_SCROLL);
212 spi.scrollStart = 0;
213 break;
214 case 0x20:
215 spi.mode &= ~SPI_MODE_INVERT;
216 break;
217 case 0x21:
218 spi.mode |= SPI_MODE_INVERT;
219 break;
220 case 0x28:
221 spi.mode |= SPI_MODE_OFF;
222 break;
223 case 0x29:
224 spi.mode &= ~SPI_MODE_OFF;
225 break;
226 case 0x2C:
227 spi_reset_mregs();
228 break;
229 case 0x34:
230 spi.tear = false;
231 break;
232 case 0x35:
233 spi.tear = true;
234 break;
235 case 0x38:
236 spi.mode &= ~SPI_MODE_IDLE;
237 break;
238 case 0x39:
239 spi.mode |= SPI_MODE_IDLE;
240 break;
241 default:
242 break;
243 }
244 }
245
spi_write_param(uint8_t value)246 static void spi_write_param(uint8_t value) {
247 uint8_t word_param = spi.param >> 1;
248 uint8_t bit_offset = ~spi.param << 3 & 8;
249
250 switch (spi.cmd) {
251 case 0x26:
252 if (spi.param == 0) {
253 spi.gamma = value;
254 }
255 break;
256 case 0x2A:
257 switch (word_param) {
258 case 0:
259 write8(spi.colStart, bit_offset, value & 0x1FF >> bit_offset);
260 break;
261 case 1:
262 write8(spi.colEnd, bit_offset, value & 0x1FF >> bit_offset);
263 break;
264 default:
265 break;
266 }
267 break;
268 case 0x2B:
269 switch (word_param) {
270 case 0:
271 write8(spi.rowStart, bit_offset, value & 0x1FF >> bit_offset);
272 break;
273 case 1:
274 write8(spi.rowEnd, bit_offset, value & 0x1FF >> bit_offset);
275 break;
276 default:
277 break;
278 }
279 break;
280 case 0x2C:
281 case 0x3C:
282 if (unlikely(!(spi.ifCtl & SPI_IC_CTRL_DATA))) {
283 switch (spi.ifBpp & 7) {
284 default:
285 case 6: /* 18bpp */
286 switch (spi.param % 3) {
287 case 0:
288 spi.ifBlue = value >> 2;
289 break;
290 case 1:
291 spi.ifGreen = value >> 2;
292 break;
293 case 2:
294 spi.ifRed = value >> 2;
295 spi_update_pixel_18bpp(spi.ifRed, spi.ifGreen, spi.ifBlue);
296 break;
297 }
298 break;
299 case 5: /* 16bpp */
300 switch (spi.param % 2) {
301 case 0:
302 spi.ifBlue = value >> 3;
303 spi.ifGreen = value << 3 & 0x38;
304 break;
305 case 1:
306 spi.ifGreen |= value >> 5;
307 spi.ifRed = value & 0x1F;
308 spi_update_pixel_16bpp(spi.ifRed, spi.ifGreen, spi.ifBlue);
309 break;
310 }
311 break;
312 case 3: /* 12bpp */
313 switch (spi.param % 3) {
314 case 0:
315 spi.ifBlue = value >> 4;
316 spi.ifGreen = value & 0xF;
317 break;
318 case 1:
319 spi.ifRed = value >> 4;
320 spi_update_pixel_12bpp(spi.ifRed, spi.ifGreen, spi.ifBlue);
321 spi.ifBlue = value & 0xF;
322 break;
323 case 2:
324 spi.ifGreen = value >> 4;
325 spi.ifRed = value & 0xF;
326 spi_update_pixel_12bpp(spi.ifRed, spi.ifGreen, spi.ifBlue);
327 break;
328 }
329 break;
330 }
331 }
332 break;
333 case 0x2D:
334 break;
335 case 0x30:
336 switch (word_param) {
337 case 0:
338 write8(spi.partialStart, bit_offset, value & 0x1FF >> bit_offset);
339 break;
340 case 1:
341 write8(spi.partialEnd, bit_offset, value & 0x1FF >> bit_offset);
342 break;
343 default:
344 break;
345 }
346 break;
347 case 0x33:
348 switch (word_param) {
349 case 0:
350 write8(spi.topArea, bit_offset, value & 0x1FF >> bit_offset);
351 break;
352 case 1:
353 write8(spi.scrollArea, bit_offset, value & 0x1FF >> bit_offset);
354 break;
355 case 2:
356 write8(spi.bottomArea, bit_offset, value & 0x1FF >> bit_offset);
357 break;
358 default:
359 break;
360 }
361 break;
362 case 0x36:
363 if (spi.param == 0) {
364 spi.mac = value;
365 }
366 break;
367 case 0x37:
368 switch (word_param) {
369 case 0:
370 write8(spi.scrollStart, bit_offset, value & 0x1FF >> bit_offset);
371 spi.mode |= SPI_MODE_SCROLL;
372 break;
373 default:
374 break;
375 }
376 break;
377 case 0x3A:
378 switch (word_param) {
379 case 0:
380 spi.ifBpp = value;
381 break;
382 default:
383 break;
384 }
385 break;
386 case 0xB0:
387 switch (word_param) {
388 case 0:
389 spi.ifCtl = value;
390 break;
391 case 1:
392 break;
393 default:
394 break;
395 }
396 break;
397 case 0xE0:
398 spi.gammaCorrection[0][spi.param] = value;
399 break;
400 case 0xE1:
401 spi.gammaCorrection[1][spi.param] = value;
402 break;
403 default:
404 break;
405 }
406
407 spi.param++;
408 }
409
410 /* Read from the SPI range of ports */
spi_read(const uint16_t pio,bool peek)411 static uint8_t spi_read(const uint16_t pio, bool peek) {
412 (void)peek;
413 (void)pio;
414 return 0;
415 }
416
417 /* Write to the SPI range of ports */
spi_write(const uint16_t pio,const uint8_t byte,bool poke)418 static void spi_write(const uint16_t pio, const uint8_t byte, bool poke) {
419 (void)poke;
420
421 if (pio == 0x18) {
422 spi.fifo = spi.fifo << 3 | (byte & 7);
423 if (spi.fifo & 0x200) {
424 if (spi.fifo & 0x100) {
425 spi_write_param(spi.fifo);
426 } else {
427 spi_write_cmd(spi.fifo);
428 }
429 spi.fifo = 1;
430 }
431 }
432 }
433
434 static const eZ80portrange_t pspi = {
435 .read = spi_read,
436 .write = spi_write
437 };
438
439
spi_reset(void)440 void spi_reset(void) {
441 uint8_t i = 0, c;
442 memset(&spi, 0, sizeof(spi));
443 spi_hw_reset();
444 for (c = 0; c < 1 << 5; c++) {
445 spi.lut[i++] = c << 3 | c >> 2;
446 }
447 for (c = 0; c < 1 << 6; c++) {
448 spi.lut[i++] = c << 2 | c >> 4;
449 }
450 for (c = 0; c < 1 << 5; c++) {
451 spi.lut[i++] = c << 3 | c >> 2;
452 }
453 }
454
init_spi(void)455 eZ80portrange_t init_spi(void) {
456 spi_reset();
457 gui_console_printf("[CEmu] Initialized Serial Peripheral Interface...\n");
458 return pspi;
459 }
460
spi_save(FILE * image)461 bool spi_save(FILE *image) {
462 return fwrite(&spi, sizeof(spi), 1, image) == 1;
463 }
464
spi_restore(FILE * image)465 bool spi_restore(FILE *image) {
466 return fread(&spi, sizeof(spi), 1, image) == 1;
467 }
468