1 #include <stdint.h>
2 #include <string.h>
3 
4 #include "emulator.h"
5 #include "portability.h"
6 #include "dbvz.h"
7 #include "sed1376.h"
8 #include "flx68000.h"//for flx68000GetPc()
9 
10 
11 //the SED1376 has only 16 address lines(17 if you count the line that switches between registers and framebuffer) and 16 data lines, the most you can read at once is 16 bits, registers are 8 bits
12 
13 //the actions described below are just my best guesses after reading the datasheet, I have not tested with actual hardware
14 //you read and write the register on the address lines set
15 //8 bit register access works normal
16 //16 bit register reads will result in you getting (0x00 << 8 | register)(this is unverified)
17 //16 bit register writes will result in you writing the lower 8 bits(this is unverified)
18 //32 bit register reads will result in doing 2 16 bit reads
19 //32 bit register writes will result in doing 2 16 bit writes
20 
21 //The LCD power-on sequence is activated by programming the Power Save Mode Enable bit (REG[A0h] bit 0) to 0.
22 //The LCD power-off sequence is activated by programming the Power Save Mode Enable bit (REG[A0h] bit 0) to 1.
23 
24 
25 #include "sed1376RegisterNames.c.h"
26 
27 
28 uint16_t* sed1376Framebuffer;
29 uint16_t  sed1376FramebufferWidth;
30 uint16_t  sed1376FramebufferHeight;
31 uint8_t   sed1376Ram[0x20000];
32 
33 static uint8_t  sed1376Registers[0xB4];
34 static uint8_t  sed1376RLut[0x100];
35 static uint8_t  sed1376GLut[0x100];
36 static uint8_t  sed1376BLut[0x100];
37 static uint16_t sed1376OutputLut[0x100];//used to speed up pixel conversion
38 static uint32_t sed1376ScreenStartAddress;
39 static uint16_t sed1376LineSize;
40 static uint16_t (*sed1376RenderPixel)(uint16_t x, uint16_t y);
41 
42 
43 #include "sed1376Accessors.c.h"
44 
sed1376GetBufferStartAddress(void)45 static uint32_t sed1376GetBufferStartAddress(void){
46    uint32_t sed1376ScreenStartAddress = sed1376Registers[DISP_ADDR_2] << 16 | sed1376Registers[DISP_ADDR_1] << 8 | sed1376Registers[DISP_ADDR_0];
47    switch((sed1376Registers[SPECIAL_EFFECT] & 0x03) * 90){
48       case 0:
49          //desired byte address / 4.
50          sed1376ScreenStartAddress *= 4;
51          break;
52 
53       case 90:
54          //((desired byte address + (panel height * bpp / 8)) / 4) - 1.
55          sed1376ScreenStartAddress += 1;
56          sed1376ScreenStartAddress *= 4;
57          //sed1376ScreenStartAddress - (panelHeight * bpp / 8);
58          break;
59 
60       case 180:
61          //((desired byte address + (panel width * panel height * bpp / 8)) / 4) - 1.
62          sed1376ScreenStartAddress += 1;
63          sed1376ScreenStartAddress *= 4;
64          //sed1376ScreenStartAddress - (panelWidth * panelHeight * bpp / 8);
65          break;
66 
67       case 270:
68          //(desired byte address + ((panel width - 1) * panel height * bpp / 8)) / 4.
69          sed1376ScreenStartAddress *= 4;
70          //sed1376ScreenStartAddress -= ((panelWidth - 1) * panelHeight * bpp / 8);
71          break;
72    }
73 
74    return sed1376ScreenStartAddress;
75 }
76 
sed1376GetPipStartAddress(void)77 static uint32_t sed1376GetPipStartAddress(void){
78    uint32_t pipStartAddress = sed1376Registers[PIP_ADDR_2] << 16 | sed1376Registers[PIP_ADDR_1] << 8 | sed1376Registers[PIP_ADDR_0];
79    switch((sed1376Registers[SPECIAL_EFFECT] & 0x03) * 90){
80       case 0:
81          //desired byte address / 4.
82          pipStartAddress *= 4;
83          break;
84 
85       case 90:
86          //((desired byte address + (panel height * bpp / 8)) / 4) - 1.
87          pipStartAddress += 1;
88          pipStartAddress *= 4;
89          //pipStartAddress - (panelHeight * bpp / 8);
90          break;
91 
92       case 180:
93          //((desired byte address + (panel width * panel height * bpp / 8)) / 4) - 1.
94          pipStartAddress += 1;
95          pipStartAddress *= 4;
96          //pipStartAddress - (panelWidth * panelHeight * bpp / 8);
97          break;
98 
99       case 270:
100          //(desired byte address + ((panel width - 1) * panel height * bpp / 8)) / 4.
101          pipStartAddress *= 4;
102          //pipStartAddress -= ((panelWidth - 1) * panelHeight * bpp / 8);
103          break;
104    }
105 
106    return pipStartAddress;
107 }
108 
sed1376Reset(void)109 void sed1376Reset(void){
110    memset(sed1376Registers, 0x00, sizeof(sed1376Registers));
111    memset(sed1376OutputLut, 0x00, sizeof(sed1376OutputLut));
112    memset(sed1376RLut, 0x00, sizeof(sed1376RLut));
113    memset(sed1376GLut, 0x00, sizeof(sed1376GLut));
114    memset(sed1376BLut, 0x00, sizeof(sed1376BLut));
115    memset(sed1376Ram, 0x00, sizeof(sed1376Ram));
116 
117    palmMisc.backlightLevel = 0;
118    palmMisc.lcdOn = false;
119 
120    sed1376RenderPixel = NULL;
121 
122    sed1376Registers[REV_CODE] = 0x28;
123    sed1376Registers[DISP_BUFF_SIZE] = 0x14;
124 
125    //timing hack
126    sed1376Registers[PWR_SAVE_CFG] = 0x80;
127 }
128 
sed1376StateSize(void)129 uint32_t sed1376StateSize(void){
130    uint32_t size = 0;
131 
132    size += sizeof(sed1376Registers);
133    size += sizeof(sed1376RLut);
134    size += sizeof(sed1376GLut);
135    size += sizeof(sed1376BLut);
136    size += sizeof(sed1376Ram);
137 
138    return size;
139 }
140 
sed1376SaveState(uint8_t * data)141 void sed1376SaveState(uint8_t* data){
142    uint32_t offset = 0;
143 
144    memcpy(data + offset, sed1376Registers, sizeof(sed1376Registers));
145    offset += sizeof(sed1376Registers);
146    memcpy(data + offset, sed1376RLut, sizeof(sed1376RLut));
147    offset += sizeof(sed1376RLut);
148    memcpy(data + offset, sed1376GLut, sizeof(sed1376GLut));
149    offset += sizeof(sed1376GLut);
150    memcpy(data + offset, sed1376BLut, sizeof(sed1376BLut));
151    offset += sizeof(sed1376BLut);
152    memcpy(data + offset, sed1376Ram, sizeof(sed1376Ram));
153    offset += sizeof(sed1376Ram);
154 }
155 
sed1376LoadState(uint8_t * data)156 void sed1376LoadState(uint8_t* data){
157    uint32_t offset = 0;
158    uint16_t index;
159 
160    memcpy(sed1376Registers, data + offset, sizeof(sed1376Registers));
161    offset += sizeof(sed1376Registers);
162    memcpy(sed1376RLut, data + offset, sizeof(sed1376RLut));
163    offset += sizeof(sed1376RLut);
164    memcpy(sed1376GLut, data + offset, sizeof(sed1376GLut));
165    offset += sizeof(sed1376GLut);
166    memcpy(sed1376BLut, data + offset, sizeof(sed1376BLut));
167    offset += sizeof(sed1376BLut);
168    memcpy(sed1376Ram, data + offset, sizeof(sed1376Ram));
169    offset += sizeof(sed1376Ram);
170 
171    //refresh LUT
172    MULTITHREAD_LOOP(index) for(index = 0; index < 0x100; index++)
173       sed1376OutputLut[index] = makeRgb16FromSed666(sed1376RLut[index], sed1376GLut[index], sed1376BLut[index]);
174 }
175 
sed1376PowerSaveEnabled(void)176 bool sed1376PowerSaveEnabled(void){
177    return sed1376Registers[PWR_SAVE_CFG] & 0x01;
178 }
179 
sed1376GetRegister(uint8_t address)180 uint8_t sed1376GetRegister(uint8_t address){
181    //returning 0x00 on power save mode is done in the sed1376ReadXX functions
182    switch(address){
183       case LUT_READ_LOC:
184       case LUT_WRITE_LOC:
185       case LUT_B_WRITE:
186       case LUT_G_WRITE:
187       case LUT_R_WRITE:
188          //write only
189          return 0x00;
190 
191       case PWR_SAVE_CFG:
192       case SPECIAL_EFFECT:
193       case DISP_MODE:
194       case LINE_SIZE_0:
195       case LINE_SIZE_1:
196       case PIP_ADDR_0:
197       case PIP_ADDR_1:
198       case PIP_ADDR_2:
199       case SCRATCH_0:
200       case SCRATCH_1:
201       case GPIO_CONF_0:
202       case GPIO_CONT_0:
203       case GPIO_CONF_1:
204       case GPIO_CONT_1:
205       case MEM_CLK:
206       case PIXEL_CLK:
207          //simple read, no actions needed
208          return sed1376Registers[address];
209 
210       default:
211          debugLog("SED1376 unknown register read 0x%02X, PC 0x%08X.\n", address, flx68000GetPc());
212          return 0x00;
213    }
214 }
215 
sed1376SetRegister(uint8_t address,uint8_t value)216 void sed1376SetRegister(uint8_t address, uint8_t value){
217    switch(address){
218       case PWR_SAVE_CFG:
219          //bit 7 must always be set, timing hack
220          sed1376Registers[address] = (value & 0x01) | 0x80;
221          return;
222 
223       case DISP_MODE:
224          sed1376Registers[address] = value & 0xF7;
225          return;
226 
227       case PANEL_TYPE:
228          sed1376Registers[address] = value & 0xFB;
229          return;
230 
231       case SPECIAL_EFFECT:
232          sed1376Registers[address] = value & 0xD3;
233          return;
234 
235       case MOD_RATE:
236          sed1376Registers[address] = value & 0x3F;
237          return;
238 
239       case DISP_ADDR_2:
240       case PIP_ADDR_2:
241          sed1376Registers[address] = value & 0x01;
242          return;
243 
244       case PWM_CONTROL:
245          sed1376Registers[address] = value & 0x9B;
246          return;
247 
248       case LINE_SIZE_1:
249       case PIP_LINE_SZ_1:
250       case PIP_X_START_1:
251       case PIP_X_END_1:
252       case PIP_Y_START_1:
253       case PIP_Y_END_1:
254       case HORIZ_START_1:
255       case VERT_TOTAL_1:
256       case VERT_PERIOD_1:
257       case VERT_START_1:
258       case FPLINE_START_1:
259       case FPFRAME_START_1:
260          sed1376Registers[address] = value & 0x03;
261          return;
262 
263       case LUT_WRITE_LOC:
264          sed1376BLut[value] = sed1376Registers[LUT_B_WRITE];
265          sed1376GLut[value] = sed1376Registers[LUT_G_WRITE];
266          sed1376RLut[value] = sed1376Registers[LUT_R_WRITE];
267          //whether or not LUT_X_READ are changed on a write when LUT_WRITE_LOC == LUT_READ_LOC is yet to be tested, turn this off for now
268          /*
269          if(sed1376Registers[LUT_WRITE_LOC] == sed1376Registers[LUT_READ_LOC]){
270             sed1376Registers[LUT_B_READ] = sed1376BLut[value];
271             sed1376Registers[LUT_G_READ] = sed1376GLut[value];
272             sed1376Registers[LUT_R_READ] = sed1376RLut[value];
273          }
274          */
275          sed1376OutputLut[value] = makeRgb16FromSed666(sed1376RLut[value], sed1376GLut[value], sed1376BLut[value]);
276          return;
277 
278       case LUT_READ_LOC:
279          sed1376Registers[LUT_B_READ] = sed1376BLut[value];
280          sed1376Registers[LUT_G_READ] = sed1376GLut[value];
281          sed1376Registers[LUT_R_READ] = sed1376RLut[value];
282          return;
283 
284       case GPIO_CONF_0:
285       case GPIO_CONT_0:
286          sed1376Registers[address] = value & 0x7F;
287          sed1376UpdateLcdStatus();
288          return;
289 
290       case GPIO_CONF_1:
291       case GPIO_CONT_1:
292          sed1376Registers[address] = value & 0x80;
293          return;
294 
295       case MEM_CLK:
296          sed1376Registers[address] = value & 0x30;
297          return;
298 
299       case PIXEL_CLK:
300          sed1376Registers[address] = value & 0x73;
301          return;
302 
303       case LUT_B_WRITE:
304       case LUT_G_WRITE:
305       case LUT_R_WRITE:
306          sed1376Registers[address] = value & 0xFC;
307          return;
308 
309       case HORIZ_TOTAL:
310       case HORIZ_PERIOD:
311          sed1376Registers[address] = value & 0x7F;
312          return;
313 
314       case FPFRAME_WIDTH:
315          sed1376Registers[address] = value & 0x87;
316          return;
317 
318       case DTFD_GCP_INDEX:
319          sed1376Registers[address] = value & 0x1F;
320          return;
321 
322       case SCRATCH_0:
323       case SCRATCH_1:
324       case DISP_ADDR_0:
325       case DISP_ADDR_1:
326       case PIP_ADDR_0:
327       case PIP_ADDR_1:
328       case LINE_SIZE_0:
329       case PIP_LINE_SZ_0:
330       case PIP_X_START_0:
331       case PIP_X_END_0:
332       case PIP_Y_START_0:
333       case PIP_Y_END_0:
334       case HORIZ_START_0:
335       case VERT_TOTAL_0:
336       case VERT_PERIOD_0:
337       case VERT_START_0:
338       case FPLINE_WIDTH:
339       case FPLINE_START_0:
340       case FPFRAME_START_0:
341       case DTFD_GCP_DATA:
342       case PWM_CONFIG:
343       case PWM_LENGTH:
344       case PWM_DUTY_CYCLE:
345          //simple write, no actions needed
346          sed1376Registers[address] = value;
347          return;
348 
349       default:
350          debugLog("SED1376 unknown register write, wrote 0x%02X to 0x%02X, PC 0x%08X.\n", value, address, flx68000GetPc());
351          return;
352    }
353 }
354 
sed1376Render(void)355 void sed1376Render(void){
356    //render if LCD on, PLL on, power save off and force blank off, SED1376 clock is provided by the CPU, if its off so is the SED
357    if(palmMisc.lcdOn && dbvzIsPllOn() && !sed1376PowerSaveEnabled() && !(sed1376Registers[DISP_MODE] & 0x80)){
358       bool color = !!(sed1376Registers[PANEL_TYPE] & 0x40);
359       bool pictureInPictureEnabled = !!(sed1376Registers[SPECIAL_EFFECT] & 0x10);
360       uint8_t bitDepth = 1 << (sed1376Registers[DISP_MODE] & 0x07);
361       uint16_t rotation = 90 * (sed1376Registers[SPECIAL_EFFECT] & 0x03);
362       uint32_t index;
363 
364       sed1376ScreenStartAddress = sed1376GetBufferStartAddress();
365       sed1376LineSize = (sed1376Registers[LINE_SIZE_1] << 8 | sed1376Registers[LINE_SIZE_0]) * 4;
366       selectRenderer(color, bitDepth);
367 
368       if(sed1376RenderPixel){
369          uint16_t pixelX;
370          uint16_t pixelY;
371 
372          MULTITHREAD_DOUBLE_LOOP(pixelX, pixelY) for(pixelY = 0; pixelY < sed1376FramebufferHeight; pixelY++)
373             for(pixelX = 0; pixelX < sed1376FramebufferWidth; pixelX++)
374                sed1376Framebuffer[pixelY * sed1376FramebufferWidth + pixelX] = sed1376RenderPixel(pixelX, pixelY);
375 
376          //debugLog("Screen start address:0x%08X, buffer width:%d, swivel view:%d degrees\n", sed1376ScreenStartAddress, lineSize, rotation);
377          //debugLog("Screen format, color:%s, BPP:%d\n", boolString(color), bitDepth);
378 
379          if(pictureInPictureEnabled){
380             uint16_t pipStartX = sed1376Registers[PIP_X_START_1] << 8 | sed1376Registers[PIP_X_START_0];
381             uint16_t pipStartY = sed1376Registers[PIP_Y_START_1] << 8 | sed1376Registers[PIP_Y_START_0];
382             uint16_t pipEndX = (sed1376Registers[PIP_X_END_1] << 8 | sed1376Registers[PIP_X_END_0]) + 1;
383             uint16_t pipEndY = (sed1376Registers[PIP_Y_END_1] << 8 | sed1376Registers[PIP_Y_END_0]) + 1;
384 
385             if(rotation == 0 || rotation == 180){
386                pipStartX *= 32 / bitDepth;
387                pipEndX *= 32 / bitDepth;
388             }
389             else{
390                pipStartY *= 32 / bitDepth;
391                pipEndY *= 32 / bitDepth;
392             }
393             //debugLog("PIP state, start x:%d, end x:%d, start y:%d, end y:%d\n", pipStartX, pipEndX, pipStartY, pipEndY);
394             //render PIP only if PIP window is onscreen
395             if(pipStartX < sed1376FramebufferWidth && pipStartY < sed1376FramebufferHeight){
396                pipEndX = FAST_MIN(pipEndX, sed1376FramebufferWidth);
397                pipEndY = FAST_MIN(pipEndY, sed1376FramebufferHeight);
398                sed1376ScreenStartAddress = sed1376GetPipStartAddress();
399                sed1376LineSize = (sed1376Registers[PIP_LINE_SZ_1] << 8 | sed1376Registers[PIP_LINE_SZ_0]) * 4;
400                MULTITHREAD_DOUBLE_LOOP(pixelX, pixelY) for(pixelY = pipStartY; pixelY < pipEndY; pixelY++)
401                   for(pixelX = pipStartX; pixelX < pipEndX; pixelX++)
402                      sed1376Framebuffer[pixelY * sed1376FramebufferWidth + pixelX] = sed1376RenderPixel(pixelX, pixelY);
403             }
404          }
405 
406          //rotation
407          //later, unemulated
408 
409          //display inversion
410          if((sed1376Registers[DISP_MODE] & 0x30) == 0x10)
411             MULTITHREAD_LOOP(index) for(index = 0; index < sed1376FramebufferWidth * sed1376FramebufferHeight; index++)
412                sed1376Framebuffer[index] = ~sed1376Framebuffer[index];
413       }
414       else{
415          debugLog("Invalid screen format, color:%s, BPP:%d, rotation:%d\n", color ? "true" : "false", bitDepth, rotation);
416       }
417    }
418    else{
419       //black screen
420       memset(sed1376Framebuffer, 0x00, sed1376FramebufferWidth * sed1376FramebufferHeight * sizeof(uint16_t));
421       debugLog("Cant draw screen, LCD on:%s, PLL on:%s, power save on:%s, forced blank on:%s\n", palmMisc.lcdOn ? "true" : "false", dbvzIsPllOn() ? "true" : "false", sed1376PowerSaveEnabled() ? "true" : "false", !!(sed1376Registers[DISP_MODE] & 0x80) ? "true" : "false");
422    }
423 }
424 
sed1376UpdateLcdStatus(void)425 void sed1376UpdateLcdStatus(void){
426    palmMisc.lcdOn = !!(sed1376Registers[GPIO_CONT_0] & sed1376Registers[GPIO_CONF_0] & 0x20);
427    palmMisc.backlightLevel = !!(sed1376Registers[GPIO_CONT_0] & sed1376Registers[GPIO_CONF_0] & 0x10) ? (50 + m515BacklightAmplifierState() * 50) : 0;
428 }
429