diff --git a/src/dbvz.c b/src/dbvz.c index cf2dbe8..6b4a31d 100644 --- a/src/dbvz.c +++ b/src/dbvz.c @@ -10,6 +10,7 @@ #include "ads7846.h" #include "sdCard.h" #include "audio/blip_buf.h" +#include "m68k/m68k.h" #include "debug/sandbox.h" @@ -18,6 +19,9 @@ dbvz_chip_t dbvzChipSelects[DBVZ_CHIP_END]; uint8_t dbvzReg[DBVZ_REG_SIZE]; +uint16_t* dbvzFramebuffer; +uint16_t dbvzFramebufferWidth; +uint16_t dbvzFramebufferHeight; static bool dbvzInterruptChanged;//reduces time wasted on checking interrupts that where updated to a new value identical to the old one, does not need to be in states static double dbvzSysclksPerClk32;//how many SYSCLK cycles before toggling the 32.768 kHz crystal @@ -54,6 +58,81 @@ static int32_t audioGetFramePercentage(void); #include "dbvzRegisterAccessors.c.h" #include "dbvzTiming.c.h" +void dbvzLcdRender(void){ + static const uint16_t masterColorLut[16] = {0xFFFF, 0xEEEE, 0xDDDD, 0xCCCC, 0xBBBB, 0xAAAA, 0x9999, 0x8888, 0x7777, 0x6666, 0x5555, 0x4444, 0x3333, 0x2222, 0x1111, 0x0000};//TODO: get a real LUT + static const uint8_t bppLut[4] = {1, 4, 16, 4/*invaild, assume most common value*/}; + uint16_t colorLut2Bpp[4];//stores indexes to masterColorLut + uint32_t startAddress = registerArrayRead32(LSSA); + uint8_t bitsPerPixel = bppLut[registerArrayRead8(LPICF) & 0x03]; + uint16_t pageWidth = registerArrayRead8(LVPW) * 2;//in bytes + bool invertColors = registerArrayRead8(LPOLCF) & 0x01; + uint8_t pixelShift = registerArrayRead8(LPOSR); + uint16_t width = FAST_MIN(registerArrayRead16(LXMAX), dbvzFramebufferWidth); + uint16_t height = FAST_MIN(registerArrayRead16(LYMAX), dbvzFramebufferHeight); + uint16_t y; + uint16_t x; + + //TODO: cursor not implemented, not that anything will use a hardware terminal cursor on Palm OS + + switch(bitsPerPixel){ + case 1: + for(y = 0; y < height; y++){ + for(x = 0; x < width / 16; x++){ + uint16_t dataUnit = m68k_read_memory_16(startAddress + y * pageWidth + x * 2); + uint8_t index; + + for(index = 0; index < 16; index++) + if(x * 16 + index >= pixelShift) + dbvzFramebuffer[y * dbvzFramebufferWidth + x * 16 + index - pixelShift] = !!(dataUnit & 1 << 16 - index) == invertColors ? masterColorLut[0] : masterColorLut[15]; + } + } + break; + + case 4: + colorLut2Bpp[0] = 0; + colorLut2Bpp[1] = registerArrayRead8(LGPMR) & 0x0F; + colorLut2Bpp[2] = registerArrayRead8(LGPMR) >> 4; + colorLut2Bpp[3] = 15; + + for(y = 0; y < height; y++){ + for(x = 0; x < width / 8; x++){ + uint16_t dataUnit = m68k_read_memory_16(startAddress + y * pageWidth + x * 2); + uint8_t index; + + for(index = 0; index < 8; index++) + if(x * 8 + index >= pixelShift) + dbvzFramebuffer[y * dbvzFramebufferWidth + x * 8 + index - pixelShift] = invertColors ? masterColorLut[15 - colorLut2Bpp[dataUnit >> 14 - index * 2 & 0x03]] : masterColorLut[colorLut2Bpp[dataUnit >> 14 - index * 2 & 0x03]]; + } + } + break; + + case 16: + for(y = 0; y < height; y++){ + for(x = 0; x < width / 4; x++){ + uint16_t dataUnit = m68k_read_memory_16(startAddress + y * pageWidth + x * 2); + uint8_t index; + + for(index = 0; index < 4; index++) + if(x * 4 + index >= pixelShift) + dbvzFramebuffer[y * dbvzFramebufferWidth + x * 4 + index - pixelShift] = invertColors ? masterColorLut[15 - (dataUnit >> 12 - index * 4 & 0x0F)] : masterColorLut[dataUnit >> 12 - index * 4 & 0x0F]; + } + } + break; + + default: + debugLog("Invalid DBVZ LCD controller pixel depth!\n"); + break; + } +} + +bool dbvzLcdEnabled(void){ + //if LSSA is set to something other than the Palm OS constant render from it instead of the SED1376 + //the enable bit is perpously ignored because it is managed by the OS and apps may not set it + //return palmAllowInvalidBehavior && registerArrayRead32(LSSA) != 0xBADC0DE0; + //return palmAllowInvalidBehavior && registerArrayRead8(LCKCON) & 0x80; + return false; +} + bool dbvzIsPllOn(void){ return !(dbvzSysclksPerClk32 < 1.0); } @@ -540,6 +619,7 @@ uint32_t dbvzGetRegister32(uint32_t address){ case IMR: case RTCTIME: case IDR: + case LSSA: //simple read, no actions needed return registerArrayRead32(address); diff --git a/src/dbvz.h b/src/dbvz.h index 0d7f9f6..74f7fd6 100644 --- a/src/dbvz.h +++ b/src/dbvz.h @@ -66,8 +66,13 @@ typedef struct{ //variables extern dbvz_chip_t dbvzChipSelects[]; extern uint8_t dbvzReg[];//needed for direct execution of the DBVZ regs without a RAM access function +extern uint16_t* dbvzFramebuffer; +extern uint16_t dbvzFramebufferWidth; +extern uint16_t dbvzFramebufferHeight; //CPU +void dbvzLcdRender(void); +bool dbvzLcdEnabled(void); bool dbvzIsPllOn(void); bool m515BacklightAmplifierState(void); bool dbvzAreRegistersXXFFMapped(void); diff --git a/src/dbvzRegisterNames.c.h b/src/dbvzRegisterNames.c.h index f30eeab..cc3d3ae 100644 --- a/src/dbvzRegisterNames.c.h +++ b/src/dbvzRegisterNames.c.h @@ -150,10 +150,12 @@ #define LCWCH 0xA1C/*Cursor Width & Height Register*/ #define LBLKC 0xA1F/*Blink Control Register*/ #define LPICF 0xA20/*Panel Interface Config Register*/ +#define LPOLCF 0xA21/*LCD Polarity Configuration Register*/ #define LPXCD 0xA25/*Pixel Clock Divider Register*/ #define LCKCON 0xA27/*Clocking Control Register*/ #define LRRA 0xA28/*LCD Refresh Rate Adjustment Register*/ /*Reserved 0xA2B Octet Terminal Count Register On Original Dragonball*/ +#define LPOSR 0xA2D/*LCD Panning Offset Register*/ #define LFRCM 0xA31/*Frame Rate Control Modulation Register*/ #define LGPMR 0xA33/*Gray Palette Mapping Register*/ #define DMACR 0xA39/*DMA Control Register*/ diff --git a/src/emulator.c b/src/emulator.c index 08b4e30..769d4a9 100644 --- a/src/emulator.c +++ b/src/emulator.c @@ -182,6 +182,11 @@ uint32_t emulatorInit(uint8_t* palmRomData, uint32_t palmRomSize, uint8_t* palmB palmCycleCounter = 0.0; palmClockMultiplier = 1.00 - DBVZ_CPU_PERCENT_WAITING; sed1376Framebuffer = palmFramebuffer; + sed1376FramebufferWidth = 160; + sed1376FramebufferHeight = 160; + dbvzFramebuffer = palmFramebuffer; + dbvzFramebufferWidth = 160; + dbvzFramebufferHeight = 160; //initialize components blip_set_rates(palmAudioResampler, DBVZ_AUDIO_MAX_CLOCK_RATE, AUDIO_SAMPLE_RATE); @@ -745,7 +750,10 @@ void emulatorRunFrame(void){ dbvzExecute(); //LCD controller - sed1376Render(); + if(dbvzLcdEnabled()) + dbvzLcdRender(); + else + sed1376Render(); //backlight level, 0% = 1/4 color intensity, 50% = 1/2 color intensity, 100% = full color intensity switch(palmMisc.backlightLevel){ @@ -792,7 +800,12 @@ void emulatorSkipFrame(void){ dbvzExecute(); //LCD controller, skip this - //sed1376Render(); + /* + if(dbvzLcdEnabled()) + dbvzLcdRender(); + else + sed1376Render(); + */ #if defined(EMU_SUPPORT_PALM_OS5) } #endif diff --git a/src/sed1376.c b/src/sed1376.c index dabc9bb..1e7d581 100644 --- a/src/sed1376.c +++ b/src/sed1376.c @@ -25,6 +25,8 @@ uint16_t* sed1376Framebuffer; +uint16_t sed1376FramebufferWidth; +uint16_t sed1376FramebufferHeight; uint8_t sed1376Ram[0x20000]; static uint8_t sed1376Registers[0xB4]; @@ -366,9 +368,9 @@ void sed1376Render(void){ uint16_t pixelX; uint16_t pixelY; - MULTITHREAD_DOUBLE_LOOP(pixelX, pixelY) for(pixelY = 0; pixelY < 160; pixelY++) - for(pixelX = 0; pixelX < 160; pixelX++) - sed1376Framebuffer[pixelY * 160 + pixelX] = sed1376RenderPixel(pixelX, pixelY); + MULTITHREAD_DOUBLE_LOOP(pixelX, pixelY) for(pixelY = 0; pixelY < sed1376FramebufferHeight; pixelY++) + for(pixelX = 0; pixelX < sed1376FramebufferWidth; pixelX++) + sed1376Framebuffer[pixelY * sed1376FramebufferWidth + pixelX] = sed1376RenderPixel(pixelX, pixelY); //debugLog("Screen start address:0x%08X, buffer width:%d, swivel view:%d degrees\n", sed1376ScreenStartAddress, lineSize, rotation); //debugLog("Screen format, color:%s, BPP:%d\n", boolString(color), bitDepth); @@ -389,14 +391,14 @@ void sed1376Render(void){ } //debugLog("PIP state, start x:%d, end x:%d, start y:%d, end y:%d\n", pipStartX, pipEndX, pipStartY, pipEndY); //render PIP only if PIP window is onscreen - if(pipStartX < 160 && pipStartY < 160){ - pipEndX = FAST_MIN(pipEndX, 160); - pipEndY = FAST_MIN(pipEndY, 160); + if(pipStartX < sed1376FramebufferWidth && pipStartY < sed1376FramebufferHeight){ + pipEndX = FAST_MIN(pipEndX, sed1376FramebufferWidth); + pipEndY = FAST_MIN(pipEndY, sed1376FramebufferHeight); sed1376ScreenStartAddress = sed1376GetPipStartAddress(); sed1376LineSize = (sed1376Registers[PIP_LINE_SZ_1] << 8 | sed1376Registers[PIP_LINE_SZ_0]) * 4; MULTITHREAD_DOUBLE_LOOP(pixelX, pixelY) for(pixelY = pipStartY; pixelY < pipEndY; pixelY++) for(pixelX = pipStartX; pixelX < pipEndX; pixelX++) - sed1376Framebuffer[pixelY * 160 + pixelX] = sed1376RenderPixel(pixelX, pixelY); + sed1376Framebuffer[pixelY * sed1376FramebufferWidth + pixelX] = sed1376RenderPixel(pixelX, pixelY); } } @@ -405,7 +407,7 @@ void sed1376Render(void){ //display inversion if((sed1376Registers[DISP_MODE] & 0x30) == 0x10) - MULTITHREAD_LOOP(index) for(index = 0; index < 160 * 160; index++) + MULTITHREAD_LOOP(index) for(index = 0; index < sed1376FramebufferWidth * sed1376FramebufferHeight; index++) sed1376Framebuffer[index] = ~sed1376Framebuffer[index]; } else{ @@ -414,7 +416,7 @@ void sed1376Render(void){ } else{ //black screen - memset(sed1376Framebuffer, 0x00, 160 * 160 * sizeof(uint16_t)); + memset(sed1376Framebuffer, 0x00, sed1376FramebufferWidth * sed1376FramebufferHeight * sizeof(uint16_t)); 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"); } } diff --git a/src/sed1376.h b/src/sed1376.h index 0288419..4fa0a8f 100644 --- a/src/sed1376.h +++ b/src/sed1376.h @@ -5,6 +5,8 @@ #include extern uint16_t* sed1376Framebuffer; +extern uint16_t sed1376FramebufferWidth; +extern uint16_t sed1376FramebufferHeight; extern uint8_t sed1376Ram[]; void sed1376Reset(void);