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