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