mirror of
https://github.com/libretro/Mu.git
synced 2026-05-06 20:34:20 +00:00
1407 lines
42 KiB
C
1407 lines
42 KiB
C
#include <string.h>
|
|
|
|
#include <boolean.h>
|
|
|
|
#include "emulator.h"
|
|
#include "hardwareRegisterNames.h"
|
|
#include "hardwareRegisters.h"
|
|
#include "memoryAccess.h"
|
|
#include "cpu32Opcodes.h"
|
|
#include "portability.h"
|
|
#include "m68k/m68k.h"
|
|
|
|
|
|
chip_t chips[4];
|
|
int32_t pllWakeWait;
|
|
uint32_t clk32Counter;
|
|
double timer1CycleCounter;
|
|
double timer2CycleCounter;
|
|
|
|
|
|
static inline uint8_t registerArrayRead8(uint32_t address){return BUFFER_READ_8(palmReg, address, 0);}
|
|
static inline uint16_t registerArrayRead16(uint32_t address){return BUFFER_READ_16(palmReg, address, 0);}
|
|
static inline uint32_t registerArrayRead32(uint32_t address){return BUFFER_READ_32(palmReg, address, 0);}
|
|
static inline void registerArrayWrite8(uint32_t address, uint8_t value){BUFFER_WRITE_8(palmReg, address, 0, value);}
|
|
static inline void registerArrayWrite16(uint32_t address, uint16_t value){BUFFER_WRITE_16(palmReg, address, 0, value);}
|
|
static inline void registerArrayWrite32(uint32_t address, uint32_t value){BUFFER_WRITE_32(palmReg, address, 0, value);}
|
|
|
|
static inline void setIprIsrBit(uint32_t interruptBit){
|
|
//allows for setting an interrupt with masking by IMR and logging in IPR
|
|
registerArrayWrite32(IPR, registerArrayRead32(IPR) | interruptBit);
|
|
registerArrayWrite32(ISR, registerArrayRead32(ISR) | (interruptBit & ~registerArrayRead32(IMR)));
|
|
}
|
|
|
|
static inline void clearIprIsrBit(uint32_t interruptBit){
|
|
registerArrayWrite32(IPR, registerArrayRead32(IPR) & ~interruptBit);
|
|
registerArrayWrite32(ISR, registerArrayRead32(ISR) & ~interruptBit);
|
|
}
|
|
|
|
static inline void pllWakeCpuIfOff(){
|
|
uint16_t pllcr = registerArrayRead16(PLLCR);
|
|
if(pllcr & 0x0008 && pllWakeWait == -1){
|
|
//CPU is off and not already in the process of waking up
|
|
switch(pllcr & 0x0003){
|
|
|
|
case 0x0000:
|
|
pllWakeWait = 32;
|
|
break;
|
|
|
|
case 0x0001:
|
|
pllWakeWait = 48;
|
|
break;
|
|
|
|
case 0x0002:
|
|
pllWakeWait = 64;
|
|
break;
|
|
|
|
case 0x0003:
|
|
pllWakeWait = 96;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static inline bool pllOn(){
|
|
return (registerArrayRead16(PLLCR) & 0x0008) == 0;
|
|
}
|
|
|
|
static inline void setCsa(uint16_t value){
|
|
chips[CHIP_A_ROM].enable = CAST_TO_BOOL(value & 0x0001);
|
|
chips[CHIP_A_ROM].readOnly = CAST_TO_BOOL(value & 0x8000);
|
|
chips[CHIP_A_ROM].size = 0x20000/*128kb*/ << ((value >> 1) & 0x0007);
|
|
|
|
registerArrayWrite16(CSA, value & 0x81FF);
|
|
}
|
|
|
|
static inline void setCsb(uint16_t value){
|
|
uint16_t csControl1 = registerArrayRead16(CSCTRL1);
|
|
|
|
chips[CHIP_B_SED].enable = CAST_TO_BOOL(value & 0x0001);
|
|
chips[CHIP_B_SED].readOnly = CAST_TO_BOOL(value & 0x8000);
|
|
chips[CHIP_B_SED].size = 0x20000/*128kb*/ << ((value >> 1) & 0x0007);
|
|
|
|
//attributes
|
|
chips[CHIP_B_SED].supervisorOnlyProtectedMemory = CAST_TO_BOOL(value & 0x4000);
|
|
chips[CHIP_B_SED].readOnlyForProtectedMemory = CAST_TO_BOOL(value & 0x2000);
|
|
if(csControl1 & 0x4000 && csControl1 & 0x0001)
|
|
chips[CHIP_B_SED].unprotectedSize = 0x8000/*32kb*/ << (((value >> 11) & 0x0003) | 0x0004);
|
|
else
|
|
chips[CHIP_B_SED].unprotectedSize = 0x8000/*32kb*/ << ((value >> 11) & 0x0003);
|
|
|
|
registerArrayWrite16(CSB, value & 0xF9FF);
|
|
}
|
|
|
|
static inline void setCsc(uint16_t value){
|
|
uint16_t csControl1 = registerArrayRead16(CSCTRL1);
|
|
|
|
chips[CHIP_C_USB].enable = CAST_TO_BOOL(value & 0x0001);
|
|
chips[CHIP_C_USB].readOnly = CAST_TO_BOOL(value & 0x8000);
|
|
chips[CHIP_C_USB].size = 0x8000/*32kb*/ << ((value >> 1) & 0x0007);
|
|
|
|
//attributes
|
|
chips[CHIP_C_USB].supervisorOnlyProtectedMemory = CAST_TO_BOOL(value & 0x4000);
|
|
chips[CHIP_C_USB].readOnlyForProtectedMemory = CAST_TO_BOOL(value & 0x2000);
|
|
if(csControl1 & 0x4000 && csControl1 & 0x0004)
|
|
chips[CHIP_C_USB].unprotectedSize = 0x8000/*32kb*/ << (((value >> 11) & 0x0003) | 0x0004);
|
|
else
|
|
chips[CHIP_C_USB].unprotectedSize = 0x8000/*32kb*/ << ((value >> 11) & 0x0003);
|
|
|
|
registerArrayWrite16(CSC, value & 0xF9FF);
|
|
}
|
|
|
|
static inline void setCsd(uint16_t value){
|
|
uint16_t csControl1 = registerArrayRead16(CSCTRL1);
|
|
|
|
chips[CHIP_D_RAM].enable = CAST_TO_BOOL(value & 0x0001);
|
|
chips[CHIP_D_RAM].readOnly = CAST_TO_BOOL(value & 0x8000);
|
|
if(csControl1 & 0x0040 && value & 0x0200)
|
|
chips[CHIP_D_RAM].size = 0x800000/*8mb*/ << ((value >> 1) & 0x0001);
|
|
else
|
|
chips[CHIP_D_RAM].size = 0x8000/*32kb*/ << ((value >> 1) & 0x0007);
|
|
|
|
//attributes
|
|
chips[CHIP_D_RAM].supervisorOnlyProtectedMemory = CAST_TO_BOOL(value & 0x4000);
|
|
chips[CHIP_D_RAM].readOnlyForProtectedMemory = CAST_TO_BOOL(value & 0x2000);
|
|
if(csControl1 & 0x4000 && csControl1 & 0x0010)
|
|
chips[CHIP_D_RAM].unprotectedSize = 0x8000/*32kb*/ << (((value >> 11) & 0x0003) | 0x0004);
|
|
else
|
|
chips[CHIP_D_RAM].unprotectedSize = 0x8000/*32kb*/ << ((value >> 11) & 0x0003);
|
|
|
|
registerArrayWrite16(CSD, value);
|
|
}
|
|
|
|
static inline void setCsgba(uint16_t value){
|
|
uint16_t csugba = registerArrayRead16(CSUGBA);
|
|
|
|
//add extra address bits if enabled
|
|
if(csugba & 0x8000)
|
|
chips[CHIP_A_ROM].start = ((csugba >> 12) & 0x0007) << 29 | (value >> 1) << 13;
|
|
else
|
|
chips[CHIP_A_ROM].start = (value >> 1) << 13;
|
|
|
|
registerArrayWrite16(CSGBA, value & 0xFFFE);
|
|
}
|
|
|
|
static inline void setCsgbb(uint16_t value){
|
|
uint16_t csugba = registerArrayRead16(CSUGBA);
|
|
|
|
//add extra address bits if enabled
|
|
if(csugba & 0x8000)
|
|
chips[CHIP_B_SED].start = ((csugba >> 8) & 0x0007) << 29 | (value >> 1) << 13;
|
|
else
|
|
chips[CHIP_B_SED].start = (value >> 1) << 13;
|
|
|
|
registerArrayWrite16(CSGBB, value & 0xFFFE);
|
|
}
|
|
|
|
static inline void setCsgbc(uint16_t value){
|
|
uint16_t csugba = registerArrayRead16(CSUGBA);
|
|
|
|
//add extra address bits if enabled
|
|
if(csugba & 0x8000)
|
|
chips[CHIP_C_USB].start = ((csugba >> 4) & 0x0007) << 29 | (value >> 1) << 13;
|
|
else
|
|
chips[CHIP_C_USB].start = (value >> 1) << 13;
|
|
|
|
registerArrayWrite16(CSGBC, value & 0xFFFE);
|
|
}
|
|
|
|
static inline void setCsgbd(uint16_t value){
|
|
uint16_t csugba = registerArrayRead16(CSUGBA);
|
|
|
|
//add extra address bits if enabled
|
|
if(csugba & 0x8000)
|
|
chips[CHIP_D_RAM].start = (csugba & 0x0007) << 29 | (value >> 1) << 13;
|
|
else
|
|
chips[CHIP_D_RAM].start = (value >> 1) << 13;
|
|
|
|
registerArrayWrite16(CSGBD, value & 0xFFFE);
|
|
}
|
|
|
|
static inline void setCsctrl1(uint16_t value){
|
|
uint16_t oldCsctrl1 = registerArrayRead16(CSCTRL1);
|
|
|
|
registerArrayWrite16(CSCTRL1, value & 0x7F55);
|
|
if((oldCsctrl1 & 0x4055) != (value & 0x4055)){
|
|
//something important changed, update all chipselects
|
|
//CSA is not dependant on CSCTRL1
|
|
setCsb(registerArrayRead16(CSB));
|
|
setCsc(registerArrayRead16(CSC));
|
|
setCsd(registerArrayRead16(CSD));
|
|
}
|
|
}
|
|
//csctrl 2 and 3 only deal with timing and bus transfer size
|
|
|
|
|
|
void printUnknownHwAccess(unsigned int address, unsigned int value, unsigned int size, bool isWrite){
|
|
if(isWrite){
|
|
debugLog("CPU Wrote %d bits of 0x%08X to register 0x%04X, PC 0x%08X.\n", size, value, address, m68k_get_reg(NULL, M68K_REG_PC));
|
|
}
|
|
else{
|
|
debugLog("CPU Read %d bits from register 0x%04X, PC 0x%08X.\n", size, address, m68k_get_reg(NULL, M68K_REG_PC));
|
|
}
|
|
}
|
|
|
|
|
|
void checkInterrupts(){
|
|
uint32_t activeInterrupts = registerArrayRead32(ISR);
|
|
uint16_t interruptLevelControlRegister = registerArrayRead16(ILCR);
|
|
uint16_t portDEdgeSelect = registerArrayRead16(PDIRQEG);
|
|
uint32_t intLevel = 0;
|
|
bool reenablePllIfOff = false;
|
|
|
|
if(activeInterrupts & INT_EMIQ){
|
|
//EMIQ - Emulator Irq, has nothing to do with emulation, used for debugging on a dev board
|
|
intLevel = 7;
|
|
reenablePllIfOff = true;
|
|
}
|
|
|
|
if(activeInterrupts & INT_SPI1){
|
|
uint32_t spi1IrqLevel = interruptLevelControlRegister >> 12;
|
|
if(intLevel < spi1IrqLevel)
|
|
intLevel = spi1IrqLevel;
|
|
reenablePllIfOff = true;
|
|
}
|
|
|
|
if(activeInterrupts & INT_IRQ5){
|
|
if(intLevel < 5)
|
|
intLevel = 5;
|
|
reenablePllIfOff = true;
|
|
}
|
|
|
|
if(activeInterrupts & INT_IRQ3){
|
|
if(intLevel < 3)
|
|
intLevel = 3;
|
|
reenablePllIfOff = true;
|
|
}
|
|
|
|
if(activeInterrupts & INT_IRQ2){
|
|
if(intLevel < 2)
|
|
intLevel = 2;
|
|
reenablePllIfOff = true;
|
|
}
|
|
|
|
if(activeInterrupts & INT_IRQ1){
|
|
if(intLevel < 1)
|
|
intLevel = 1;
|
|
reenablePllIfOff = true;
|
|
}
|
|
|
|
if(activeInterrupts & INT_PWM2){
|
|
uint32_t pwm2IrqLevel = (interruptLevelControlRegister >> 4) & 0x0007;
|
|
if(intLevel < pwm2IrqLevel)
|
|
intLevel = pwm2IrqLevel;
|
|
reenablePllIfOff = true;
|
|
}
|
|
|
|
if(activeInterrupts & INT_UART2){
|
|
uint32_t uart2IrqLevel = (interruptLevelControlRegister >> 8) & 0x0007;
|
|
if(intLevel < uart2IrqLevel)
|
|
intLevel = uart2IrqLevel;
|
|
reenablePllIfOff = true;
|
|
}
|
|
|
|
if(activeInterrupts & INT_TMR2){
|
|
//TMR2 - Timer 2
|
|
uint32_t timer2IrqLevel = interruptLevelControlRegister & 0x0007;
|
|
if(intLevel < timer2IrqLevel)
|
|
intLevel = timer2IrqLevel;
|
|
reenablePllIfOff = true;
|
|
}
|
|
|
|
if(activeInterrupts & (INT_TMR1 | INT_PWM1 | INT_IRQ6)){
|
|
//All Fixed Level 6 Interrupts
|
|
if(intLevel < 6)
|
|
intLevel = 6;
|
|
reenablePllIfOff = true;
|
|
}
|
|
|
|
if(activeInterrupts & (INT_SPI2 | INT_UART1 | INT_WDT | INT_RTC | INT_KB | INT_RTI)){
|
|
//All Fixed Level 4 Interrupts
|
|
if(intLevel < 4)
|
|
intLevel = 4;
|
|
reenablePllIfOff = true;
|
|
}
|
|
|
|
if(activeInterrupts & INT_INT0){
|
|
//INTx, only reenable the PLL if interrupt is set to level sensitive
|
|
if(intLevel < 4)
|
|
intLevel = 4;
|
|
if(!(portDEdgeSelect & 0x01))
|
|
reenablePllIfOff = true;
|
|
}
|
|
|
|
if(activeInterrupts & INT_INT1){
|
|
//INTx, only reenable the PLL if interrupt is set to level sensitive
|
|
if(intLevel < 4)
|
|
intLevel = 4;
|
|
if(!(portDEdgeSelect & 0x02))
|
|
reenablePllIfOff = true;
|
|
}
|
|
|
|
if(activeInterrupts & INT_INT2){
|
|
//INTx, only reenable the PLL if interrupt is set to level sensitive
|
|
if(intLevel < 4)
|
|
intLevel = 4;
|
|
if(!(portDEdgeSelect & 0x04))
|
|
reenablePllIfOff = true;
|
|
}
|
|
|
|
if(activeInterrupts & INT_INT3){
|
|
//INTx, only reenable the PLL if interrupt is set to level sensitive
|
|
if(intLevel < 4)
|
|
intLevel = 4;
|
|
if(!(portDEdgeSelect & 0x08))
|
|
reenablePllIfOff = true;
|
|
}
|
|
|
|
if(reenablePllIfOff)
|
|
pllWakeCpuIfOff();
|
|
|
|
m68k_set_irq(intLevel);//should be called even if intLevel is 0, that is how the interrupt state gets cleared
|
|
}
|
|
|
|
static inline uint8_t getPortDValue(){
|
|
uint8_t requestedRow = registerArrayRead8(PKDIR) & registerArrayRead8(PKDATA);//keys are requested on port k and read on port d
|
|
uint8_t portDValue = 0x00;//ports always read the chip pins even if they are set to output
|
|
uint8_t portDData = registerArrayRead8(PDDATA);
|
|
uint8_t portDDir = registerArrayRead8(PDDIR);
|
|
uint8_t portDPolarity = registerArrayRead8(PDPOL);
|
|
|
|
portDValue |= 0x80/*battery not dead bit*/;
|
|
|
|
if(!palmSdCard.inserted){
|
|
portDValue |= 0x20;
|
|
}
|
|
|
|
if((requestedRow & 0x20) == 0){
|
|
//kbd row 0, pins are 0 when button pressed and 1 when released, Palm OS then uses PDPOL to swap back to pressed == 1
|
|
portDValue |= !palmInput.buttonCalender | !palmInput.buttonAddress << 1 | !palmInput.buttonTodo << 2 | !palmInput.buttonNotes << 3;
|
|
}
|
|
|
|
if((requestedRow & 0x40) == 0){
|
|
//kbd row 1, pins are 0 when button pressed and 1 when released, Palm OS then uses PDPOL to swap back to pressed == 1
|
|
portDValue |= !palmInput.buttonUp | !palmInput.buttonDown << 1;
|
|
}
|
|
|
|
if((requestedRow & 0x80) == 0){
|
|
//kbd row 2, pins are 0 when button pressed and 1 when released, Palm OS then uses PDPOL to swap back to pressed == 1
|
|
portDValue |= !palmInput.buttonPower | !palmInput.buttonContrast << 1 | !palmInput.buttonAddress << 3;
|
|
}
|
|
|
|
portDValue ^= portDPolarity;//only input polarity is affected by PDPOL
|
|
portDValue &= ~portDDir;//only use above pin values for inputs
|
|
portDValue |= portDData & portDDir;//if a pin is an output and has its data bit set return that too
|
|
|
|
return portDValue;
|
|
}
|
|
|
|
static inline uint8_t getPortKValue(){
|
|
uint8_t portKValue = 0x00;//ports always read the chip pins even if they are set to output
|
|
uint8_t portKData = registerArrayRead8(PKDATA);
|
|
uint8_t portKDir = registerArrayRead8(PKDIR);
|
|
uint8_t portKSel = registerArrayRead8(PKSEL);
|
|
|
|
portKValue |= !palmMisc.inDock << 2 & ~portKDir & portKSel;
|
|
portKValue |= portKData & portKDir & portKSel;
|
|
|
|
return portKValue;
|
|
}
|
|
|
|
static inline void checkPortDInts(){
|
|
uint8_t portDValue = getPortDValue();
|
|
uint8_t portDDir = registerArrayRead8(PDDIR);
|
|
uint8_t portDIntEnable = registerArrayRead8(PDIRQEN);
|
|
uint8_t portDKeyboardEnable = registerArrayRead8(PDKBEN);
|
|
uint8_t portDIrqPins = ~registerArrayRead8(PDSEL);
|
|
uint16_t interruptControlRegister = registerArrayRead16(ICR);
|
|
|
|
if(portDIntEnable & portDValue & ~portDDir & 0x01){
|
|
//int 0, polarity set with PDPOL
|
|
setIprIsrBit(INT_INT0);
|
|
}
|
|
|
|
if(portDIntEnable & portDValue & ~portDDir & 0x02){
|
|
//int 1, polarity set with PDPOL
|
|
setIprIsrBit(INT_INT1);
|
|
}
|
|
|
|
if(portDIntEnable & portDValue & ~portDDir & 0x04){
|
|
//int 2, polarity set with PDPOL
|
|
setIprIsrBit(INT_INT2);
|
|
}
|
|
|
|
if(portDIntEnable & portDValue & ~portDDir & 0x08){
|
|
//int 3, polarity set with PDPOL
|
|
setIprIsrBit(INT_INT3);
|
|
}
|
|
|
|
if(portDIrqPins & ~portDDir & 0x10 && ((portDValue & 0x10) != 0) == ((interruptControlRegister & 0x8000) != 0)){
|
|
//irq 1, polarity set in ICR
|
|
setIprIsrBit(INT_IRQ1);
|
|
}
|
|
|
|
if(portDIrqPins & ~portDDir & 0x20 && ((portDValue & 0x20) != 0) == ((interruptControlRegister & 0x4000) != 0)){
|
|
//irq 2, polarity set in ICR
|
|
setIprIsrBit(INT_IRQ2);
|
|
}
|
|
|
|
if(portDIrqPins & ~portDDir & 0x40 && ((portDValue & 0x40) != 0) == ((interruptControlRegister & 0x2000) != 0)){
|
|
//irq 3, polarity set in ICR
|
|
setIprIsrBit(INT_IRQ3);
|
|
}
|
|
|
|
if(portDIrqPins & ~portDDir & 0x80 && ((portDValue & 0x80) != 0) == ((interruptControlRegister & 0x1000) != 0)){
|
|
//irq 6, polarity set in ICR
|
|
setIprIsrBit(INT_IRQ6);
|
|
}
|
|
|
|
if(portDKeyboardEnable & ~portDValue & ~portDDir){
|
|
//active low/off level triggered interrupt
|
|
setIprIsrBit(INT_KB);
|
|
}
|
|
|
|
checkInterrupts();
|
|
}
|
|
|
|
static inline void updateAlarmLedStatus(){
|
|
if(registerArrayRead8(PBDATA) & registerArrayRead8(PBSEL) & registerArrayRead8(PBDIR) & 0x40)
|
|
palmMisc.alarmLed = true;
|
|
else
|
|
palmMisc.alarmLed = false;
|
|
}
|
|
|
|
static inline void updateLcdStatus(){
|
|
if(registerArrayRead8(PKDATA) & registerArrayRead8(PKSEL) & registerArrayRead8(PKDIR) & 0x02)
|
|
palmMisc.lcdOn = true;
|
|
else
|
|
palmMisc.lcdOn = false;
|
|
}
|
|
|
|
static inline void updateBacklightStatus(){
|
|
if(registerArrayRead8(PGDATA) & registerArrayRead8(PGSEL) & registerArrayRead8(PGDIR) & 0x02)
|
|
palmMisc.backlightOn = true;
|
|
else
|
|
palmMisc.backlightOn = false;
|
|
}
|
|
|
|
static inline void updateVibratorStatus(){
|
|
if(registerArrayRead8(PKDATA) & registerArrayRead8(PKSEL) & registerArrayRead8(PKDIR) & 0x10)
|
|
palmMisc.vibratorOn = true;
|
|
else
|
|
palmMisc.vibratorOn = false;
|
|
}
|
|
|
|
static inline void setPllfsr16(uint16_t value){
|
|
uint16_t oldPllfsr = registerArrayRead16(PLLFSR);
|
|
if(!(oldPllfsr & 0x4000)){
|
|
//frequency protect bit not set
|
|
registerArrayWrite16(PLLFSR, (value & 0x4FFF) | (oldPllfsr & 0x8000));//preserve CLK32 bit
|
|
double prescaler1 = (registerArrayRead16(PLLCR) & 0x0080) ? 2.0 : 1.0;
|
|
double p = value & 0x00FF;
|
|
double q = (value & 0x0F00) >> 8;
|
|
palmCrystalCycles = 2.0 * (14.0 * (p + 1.0) + q + 1.0) / prescaler1;
|
|
debugLog("New CPU frequency of:%f cycles per second.\n", CPU_FREQUENCY);
|
|
debugLog("New CLK32 cycle count of:%f.\n", palmCrystalCycles);
|
|
}
|
|
}
|
|
|
|
static inline void setPllcr(uint16_t value){
|
|
//values that matter are disable PLL, prescaler 1 and possibly wakeselect
|
|
registerArrayWrite16(PLLCR, value & 0x3FBB);
|
|
uint16_t pllfsr = registerArrayRead16(PLLFSR);
|
|
double prescaler1 = (value & 0x0080) ? 2.0 : 1.0;
|
|
double p = pllfsr & 0x00FF;
|
|
double q = (pllfsr & 0x0F00) >> 8;
|
|
palmCrystalCycles = 2.0 * (14.0 * (p + 1.0) + q + 1.0) / prescaler1;
|
|
debugLog("New CPU frequency of:%f cycles per second.\n", CPU_FREQUENCY);
|
|
debugLog("New CLK32 cycle count of:%f.\n", palmCrystalCycles);
|
|
|
|
if(value & 0x0008){
|
|
//The PLL shuts down 30 clock cycles of SYSCLK after the DISPLL bit is set in the PLLCR
|
|
m68k_modify_timeslice(-m68k_cycles_remaining() + 30);
|
|
debugLog("Disable PLL set, CPU off in 30 cycles!\n");
|
|
}
|
|
}
|
|
|
|
static inline void setScr(uint8_t value){
|
|
uint8_t oldScr = registerArrayRead8(SCR);
|
|
uint8_t newScr = value;
|
|
|
|
//preserve privilege violation, write protect violation and bus error timeout
|
|
newScr |= oldScr & 0xE0;
|
|
|
|
//clear violations on writing 1 to them
|
|
newScr &= ~(oldScr & value & 0xE0);
|
|
|
|
registerArrayWrite8(SCR, newScr);//must be written before calling setRegisterFFFFAccessMode
|
|
if((newScr & 0x04) != (oldScr & 0x04)){
|
|
if(newScr & 0x04)
|
|
setRegisterXXFFAccessMode();
|
|
else
|
|
setRegisterFFFFAccessMode();
|
|
}
|
|
}
|
|
|
|
static inline double dmaclksPerClk32(){
|
|
uint16_t pllcr = registerArrayRead16(PLLCR);
|
|
double dmaclks = palmCrystalCycles;
|
|
|
|
if(pllcr & 0x0080){
|
|
//prescaler 1 enabled, divide by 2
|
|
dmaclks /= 2.0;
|
|
}
|
|
|
|
if(pllcr & 0x0020){
|
|
//prescaler 2 enabled, divides value from prescaler 1 by 2
|
|
dmaclks /= 2.0;
|
|
}
|
|
|
|
return dmaclks;
|
|
}
|
|
|
|
static inline double sysclksPerClk32(){
|
|
uint16_t pllcr = registerArrayRead16(PLLCR);
|
|
double sysclks = dmaclksPerClk32();
|
|
uint16_t sysclkSelect = (pllcr >> 8) & 0x0003;
|
|
|
|
switch(sysclkSelect){
|
|
|
|
case 0x0000:
|
|
sysclks /= 2.0;
|
|
break;
|
|
|
|
case 0x0001:
|
|
sysclks /= 4.0;
|
|
break;
|
|
|
|
case 0x0002:
|
|
sysclks /= 8.0;
|
|
break;
|
|
|
|
case 0x0003:
|
|
sysclks /= 16.0;
|
|
break;
|
|
|
|
default:
|
|
//no divide for 0x0004, 0x0005, 0x0006 or 0x0007
|
|
break;
|
|
}
|
|
|
|
return sysclks;
|
|
}
|
|
|
|
static inline void rtiInterruptClk32(){
|
|
//this function is part of clk32();
|
|
uint16_t triggeredRtiInterrupts = 0;
|
|
|
|
if(clk32Counter % ((uint32_t)CRYSTAL_FREQUENCY / 512) == 0){
|
|
//RIS7 - 512HZ
|
|
triggeredRtiInterrupts |= 0x8000;
|
|
}
|
|
if(clk32Counter % ((uint32_t)CRYSTAL_FREQUENCY / 256) == 0){
|
|
//RIS6 - 256HZ
|
|
triggeredRtiInterrupts |= 0x4000;
|
|
}
|
|
if(clk32Counter % ((uint32_t)CRYSTAL_FREQUENCY / 128) == 0){
|
|
//RIS5 - 128HZ
|
|
triggeredRtiInterrupts |= 0x2000;
|
|
}
|
|
if(clk32Counter % ((uint32_t)CRYSTAL_FREQUENCY / 64) == 0){
|
|
//RIS4 - 64HZ
|
|
triggeredRtiInterrupts |= 0x1000;
|
|
}
|
|
if(clk32Counter % ((uint32_t)CRYSTAL_FREQUENCY / 32) == 0){
|
|
//RIS3 - 32HZ
|
|
triggeredRtiInterrupts |= 0x0800;
|
|
}
|
|
if(clk32Counter % ((uint32_t)CRYSTAL_FREQUENCY / 16) == 0){
|
|
//RIS2 - 16HZ
|
|
triggeredRtiInterrupts |= 0x0400;
|
|
}
|
|
if(clk32Counter % ((uint32_t)CRYSTAL_FREQUENCY / 8) == 0){
|
|
//RIS1 - 8HZ
|
|
triggeredRtiInterrupts |= 0x0200;
|
|
}
|
|
if(clk32Counter % ((uint32_t)CRYSTAL_FREQUENCY / 4) == 0){
|
|
//RIS0 - 4HZ
|
|
triggeredRtiInterrupts |= 0x0100;
|
|
}
|
|
|
|
triggeredRtiInterrupts &= registerArrayRead16(RTCIENR);
|
|
if(triggeredRtiInterrupts){
|
|
registerArrayWrite16(RTCISR, registerArrayRead16(RTCISR) | triggeredRtiInterrupts);
|
|
setIprIsrBit(INT_RTI);
|
|
}
|
|
}
|
|
|
|
static inline void timer12Clk32(){
|
|
//this function is part of clk32();
|
|
uint16_t timer1Control = registerArrayRead16(TCTL1);
|
|
uint16_t timer1Prescaler = registerArrayRead16(TPRER1) & 0x00FF;
|
|
uint16_t timer1Compare = registerArrayRead16(TCMP1);
|
|
uint16_t timer1OldCount = registerArrayRead16(TCN1);
|
|
uint16_t timer1Count = timer1OldCount;
|
|
|
|
uint16_t timer2Control = registerArrayRead16(TCTL2);
|
|
uint16_t timer2Prescaler = registerArrayRead16(TPRER2) & 0x00FF;
|
|
uint16_t timer2Compare = registerArrayRead16(TCMP2);
|
|
uint16_t timer2OldCount = registerArrayRead16(TCN2);
|
|
uint16_t timer2Count = timer2OldCount;
|
|
|
|
//timer 1
|
|
if(timer1Control & 0x0001){
|
|
//enabled
|
|
switch((timer1Control & 0x000E) >> 1){
|
|
|
|
case 0x0000://stop counter
|
|
case 0x0003://TIN pin / timer prescaler, nothing is attached to TIN
|
|
//do nothing
|
|
break;
|
|
|
|
case 0x0001://SYSCLK / timer prescaler
|
|
if(pllOn())
|
|
timer1CycleCounter += sysclksPerClk32() / (double)timer1Prescaler;
|
|
break;
|
|
|
|
case 0x0002://SYSCLK / 16 / timer prescaler
|
|
if(pllOn())
|
|
timer1CycleCounter += sysclksPerClk32() / 16.0 / (double)timer1Prescaler;
|
|
break;
|
|
|
|
default://CLK32 / timer prescaler
|
|
timer1CycleCounter += 1.0 / (double)timer1Prescaler;
|
|
break;
|
|
}
|
|
|
|
if(timer1CycleCounter >= 1.0){
|
|
timer1Count += (uint16_t)timer1CycleCounter;
|
|
timer1CycleCounter -= (uint16_t)timer1CycleCounter;
|
|
}
|
|
|
|
if(timer1OldCount < timer1Compare && timer1Count >= timer1Compare){
|
|
//the timer is not cycle accurate and may not hit the value in the compare register perfectly so check if it would have during in the emulated time
|
|
if(timer1Control & 0x0010){
|
|
//interrupt enabled
|
|
setIprIsrBit(INT_TMR1);
|
|
}
|
|
|
|
if(!(timer1Control & 0x0100)){
|
|
//not free running, reset to 0, to prevent loss of ticks after compare event just subtract timerXCompare
|
|
timer1Count -= timer1Compare;
|
|
}
|
|
}
|
|
|
|
registerArrayWrite16(TCN1, timer1Count);
|
|
}
|
|
|
|
//timer 2
|
|
if(timer2Control & 0x0001){
|
|
//enabled
|
|
switch((timer2Control & 0x000E) >> 1){
|
|
|
|
case 0x0000://stop counter
|
|
case 0x0003://TIN pin / timer prescaler, nothing is attached to TIN
|
|
//do nothing
|
|
break;
|
|
|
|
case 0x0001://SYSCLK / timer prescaler
|
|
if(pllOn())
|
|
timer2CycleCounter += sysclksPerClk32() / (double)timer2Prescaler;
|
|
break;
|
|
|
|
case 0x0002://SYSCLK / 16 / timer prescaler
|
|
if(pllOn())
|
|
timer2CycleCounter += sysclksPerClk32() / 16.0 / (double)timer2Prescaler;
|
|
break;
|
|
|
|
default://CLK32 / timer prescaler
|
|
timer2CycleCounter += 1.0 / (double)timer2Prescaler;
|
|
break;
|
|
}
|
|
|
|
if(timer2CycleCounter >= 1.0){
|
|
timer2Count += (uint16_t)timer2CycleCounter;
|
|
timer2CycleCounter -= (uint16_t)timer2CycleCounter;
|
|
}
|
|
|
|
if(timer2OldCount < timer2Compare && timer2Count >= timer2Compare){
|
|
//the timer is not cycle accurate and may not hit the value in the compare register perfectly so check if it would have during in the emulated time
|
|
if(timer2Control & 0x0010){
|
|
//interrupt enabled
|
|
setIprIsrBit(INT_TMR2);
|
|
}
|
|
|
|
if(!(timer2Control & 0x0100)){
|
|
//not free running, reset to 0, to prevent loss of ticks after compare event just subtract timerXCompare
|
|
timer2Count -= timer2Compare;
|
|
}
|
|
}
|
|
|
|
registerArrayWrite16(TCN2, timer2Count);
|
|
}
|
|
}
|
|
|
|
static inline void rtcAddSecondClk32(){
|
|
//this function is part of clk32();
|
|
|
|
//rtc
|
|
if(registerArrayRead16(RTCCTL) & 0x0080){
|
|
//rtc enable bit set
|
|
uint16_t rtcInterruptEvents;
|
|
uint32_t newRtcTime;
|
|
uint32_t oldRtcTime = registerArrayRead32(RTCTIME);
|
|
uint32_t hours = oldRtcTime >> 24;
|
|
uint32_t minutes = (oldRtcTime >> 16) & 0x0000003F;
|
|
uint32_t seconds = oldRtcTime & 0x0000003F;
|
|
|
|
seconds++;
|
|
rtcInterruptEvents = 0x0010;//1 second interrupt
|
|
if(seconds >= 60){
|
|
minutes++;
|
|
seconds = 0;
|
|
rtcInterruptEvents |= 0x0002;//1 minute interrupt
|
|
if(minutes >= 60){
|
|
hours++;
|
|
minutes = 0;
|
|
rtcInterruptEvents |= 0x0020;//1 hour interrupt
|
|
if(hours >= 24){
|
|
hours = 0;
|
|
uint16_t days = registerArrayRead16(DAYR);
|
|
days++;
|
|
registerArrayWrite16(DAYR, days & 0x01FF);
|
|
rtcInterruptEvents |= 0x0008;//1 day interrupt
|
|
}
|
|
}
|
|
}
|
|
|
|
rtcInterruptEvents &= registerArrayRead16(RTCIENR);
|
|
if(rtcInterruptEvents){
|
|
registerArrayWrite16(RTCISR, registerArrayRead16(RTCISR) | rtcInterruptEvents);
|
|
setIprIsrBit(INT_RTC);
|
|
}
|
|
|
|
newRtcTime = seconds & 0x0000003F;
|
|
newRtcTime |= minutes << 16;
|
|
newRtcTime |= hours << 24;
|
|
registerArrayWrite32(RTCTIME, newRtcTime);
|
|
}
|
|
|
|
//watchdog
|
|
uint16_t watchdogState = registerArrayRead16(WATCHDOG);
|
|
if(watchdogState & 0x0001){
|
|
//watchdog enabled
|
|
watchdogState += 0x0100;//add second to watchdog timer
|
|
watchdogState &= 0x0383;//cap overflow
|
|
if((watchdogState & 0x0200) == 0x0200){
|
|
//time expired
|
|
if(watchdogState & 0x0002){
|
|
//interrupt
|
|
setIprIsrBit(INT_WDT);
|
|
}
|
|
else{
|
|
//reset
|
|
emulatorReset();
|
|
return;
|
|
}
|
|
}
|
|
registerArrayWrite16(WATCHDOG, watchdogState);
|
|
}
|
|
}
|
|
|
|
void clk32(){
|
|
registerArrayWrite16(PLLFSR, registerArrayRead16(PLLFSR) ^ 0x8000);
|
|
|
|
//second position counter
|
|
if(clk32Counter >= CRYSTAL_FREQUENCY - 1){
|
|
clk32Counter = 0;
|
|
rtcAddSecondClk32();
|
|
}
|
|
else{
|
|
clk32Counter++;
|
|
}
|
|
|
|
//PLLCR wake select wait
|
|
if(pllWakeWait != -1){
|
|
if(pllWakeWait == 0){
|
|
//reenable PLL and CPU
|
|
registerArrayWrite16(PLLCR, registerArrayRead16(PLLCR) & 0xFFF7);
|
|
debugLog("PLL reenabled, CPU is on!\n");
|
|
}
|
|
pllWakeWait--;
|
|
}
|
|
|
|
rtiInterruptClk32();
|
|
timer12Clk32();
|
|
|
|
checkInterrupts();
|
|
}
|
|
|
|
bool cpuIsOn(){
|
|
return pllOn() && !lowPowerStopActive;
|
|
}
|
|
|
|
bool registersAreXXFFMapped(){
|
|
return CAST_TO_BOOL(registerArrayRead8(SCR) & 0x04);
|
|
}
|
|
|
|
bool sed1376ClockConnected(){
|
|
//this is the clock output pin for the SED1376, if its disabled so is the LCD controller
|
|
return !CAST_TO_BOOL(registerArrayRead8(PFSEL) & 0x04);
|
|
}
|
|
|
|
void refreshButtonState(){
|
|
checkPortDInts();
|
|
}
|
|
|
|
int interruptAcknowledge(int intLevel){
|
|
int vectorOffset = registerArrayRead8(IVR);
|
|
int vector;
|
|
|
|
//If an interrupt occurs before the IVR has been programmed, the interrupt vector number 0x0F is returned to the CPU as an uninitialized interrupt.
|
|
if(!vectorOffset)
|
|
vector = 15/*EXCEPTION_UNINITIALIZED_INTERRUPT*/;
|
|
else
|
|
vector = vectorOffset | intLevel;
|
|
|
|
lowPowerStopActive = false;
|
|
|
|
//the interrupt should only be cleared after its been handled
|
|
|
|
return vector;
|
|
}
|
|
|
|
|
|
unsigned int getHwRegister8(unsigned int address){
|
|
if((address & 0x0000F000) != 0x0000F000){
|
|
//not emu or hardware register, invalid access
|
|
return 0x00;
|
|
}
|
|
|
|
address &= 0x00000FFF;
|
|
printUnknownHwAccess(address, 0, 8, false);
|
|
switch(address){
|
|
|
|
case PDDATA:
|
|
return getPortDValue();
|
|
|
|
case PKDATA:
|
|
return getPortKValue();
|
|
|
|
//i/o direction
|
|
case PDDIR:
|
|
case PKDIR:
|
|
|
|
//select between GPIO or special function
|
|
case PBSEL:
|
|
case PCSEL:
|
|
case PDSEL:
|
|
case PESEL:
|
|
case PFSEL:
|
|
case PGSEL:
|
|
case PJSEL:
|
|
case PKSEL:
|
|
case PMSEL:
|
|
|
|
//pull up/down enable
|
|
case PAPUEN:
|
|
case PBPUEN:
|
|
case PCPDEN:
|
|
case PDPUEN:
|
|
case PEPUEN:
|
|
case PFPUEN:
|
|
case PGPUEN:
|
|
case PJPUEN:
|
|
case PKPUEN:
|
|
case PMPUEN:
|
|
//simple read, no actions needed
|
|
//PGPUEN, PGSEL PMSEL and PMPUEN lack the top 2 bits but that is handled on write
|
|
//PDSEL lacks the bottom 4 bits but that is handled on write
|
|
return registerArrayRead8(address);
|
|
|
|
default:
|
|
//printUnknownHwAccess(address, 0, 8, false);
|
|
return 0x00;
|
|
}
|
|
|
|
return 0x00;//silence warnings
|
|
}
|
|
|
|
unsigned int getHwRegister16(unsigned int address){
|
|
if((address & 0x0000F000) != 0x0000F000){
|
|
//not emu or hardware register, invalid access
|
|
return 0x0000;
|
|
}
|
|
|
|
address &= 0x00000FFF;
|
|
//printUnknownHwAccess(address, 0, 16, false);
|
|
switch(address){
|
|
|
|
//32 bit registers accessed as 16 bit
|
|
case IMR:
|
|
case IMR + 2:
|
|
case IPR:
|
|
case IPR + 2:
|
|
|
|
case PLLCR:
|
|
case PLLFSR:
|
|
case DRAMC:
|
|
case SDCTRL:
|
|
case RTCISR:
|
|
case RTCCTL:
|
|
case RTCIENR:
|
|
//simple read, no actions needed
|
|
return registerArrayRead16(address);
|
|
|
|
default:
|
|
printUnknownHwAccess(address, 0, 16, false);
|
|
return 0x0000;
|
|
}
|
|
|
|
return 0x0000;//silence warnings
|
|
}
|
|
|
|
unsigned int getHwRegister32(unsigned int address){
|
|
if((address & 0x0000F000) == 0x0000E000){
|
|
//32 bit emu register read, valid
|
|
return 0x00000000;
|
|
}
|
|
else if((address & 0x0000F000) != 0x0000F000){
|
|
//not emu or hardware register, invalid access
|
|
return 0x00000000;
|
|
}
|
|
|
|
address &= 0x00000FFF;
|
|
//printUnknownHwAccess(address, 0, 32, false);
|
|
switch(address){
|
|
|
|
//16 bit registers being read as 32 bit
|
|
case PLLFSR:
|
|
|
|
case ISR:
|
|
case IPR:
|
|
case IMR:
|
|
case RTCTIME:
|
|
case IDR:
|
|
//simple read, no actions needed
|
|
return registerArrayRead32(address);
|
|
|
|
default:
|
|
printUnknownHwAccess(address, 0, 32, false);
|
|
return 0x00000000;
|
|
}
|
|
|
|
return 0x00000000;//silence warnings
|
|
}
|
|
|
|
|
|
void setHwRegister8(unsigned int address, unsigned int value){
|
|
if((address & 0x0000F000) != 0x0000F000){
|
|
//not emu or hardware register, invalid access
|
|
return;
|
|
}
|
|
|
|
address &= 0x00000FFF;
|
|
//printUnknownHwAccess(address, value, 8, true);
|
|
switch(address){
|
|
|
|
case SCR:
|
|
setScr(value);
|
|
break;
|
|
|
|
case IVR:
|
|
//write without the bottom 3 bits
|
|
registerArrayWrite8(address, value & 0xF8);
|
|
break;
|
|
|
|
case PBSEL:
|
|
case PBDIR:
|
|
case PBDATA:
|
|
registerArrayWrite8(address, value);
|
|
updateAlarmLedStatus();
|
|
break;
|
|
|
|
case PDSEL:
|
|
//write without the bottom 4 bits
|
|
registerArrayWrite8(address, value & 0xF0);
|
|
break;
|
|
|
|
case PDPOL:
|
|
case PDIRQEN:
|
|
case PDIRQEG:
|
|
//write without the top 4 bits
|
|
registerArrayWrite8(address, value & 0x0F);
|
|
checkPortDInts();
|
|
break;
|
|
|
|
case PFSEL:
|
|
//this is the clock output pin for the SED1376, if its disabled so is the LCD controller
|
|
setSed1376Attached(!CAST_TO_BOOL(value & 0x04));
|
|
registerArrayWrite8(PFSEL, value);
|
|
break;
|
|
|
|
case PGSEL:
|
|
case PGDIR:
|
|
case PGDATA:
|
|
//port g also does spi stuff, unemulated so far
|
|
//write without the top 2 bits
|
|
registerArrayWrite8(address, value & 0x3F);
|
|
updateBacklightStatus();
|
|
break;
|
|
|
|
case PKSEL:
|
|
case PKDIR:
|
|
case PKDATA:
|
|
registerArrayWrite8(address, value);
|
|
checkPortDInts();
|
|
updateLcdStatus();
|
|
updateVibratorStatus();
|
|
break;
|
|
|
|
|
|
case PMSEL:
|
|
case PMDIR:
|
|
case PMDATA:
|
|
//unemulated
|
|
//infrared shutdown
|
|
registerArrayWrite8(address, value & 0x3F);
|
|
break;
|
|
|
|
case PMPUEN:
|
|
case PGPUEN:
|
|
//write without the top 2 bits
|
|
registerArrayWrite8(address, value & 0x3F);
|
|
break;
|
|
|
|
//select between GPIO or special function
|
|
case PCSEL:
|
|
case PESEL:
|
|
case PJSEL:
|
|
|
|
//direction select
|
|
case PADIR:
|
|
case PCDIR:
|
|
case PDDIR:
|
|
case PEDIR:
|
|
case PFDIR:
|
|
case PJDIR:
|
|
|
|
//pull up/down enable
|
|
case PAPUEN:
|
|
case PBPUEN:
|
|
case PCPDEN:
|
|
case PDPUEN:
|
|
case PEPUEN:
|
|
case PFPUEN:
|
|
case PJPUEN:
|
|
case PKPUEN:
|
|
|
|
//port data value, nothing attached to port
|
|
case PCDATA:
|
|
case PDDATA:
|
|
case PEDATA:
|
|
case PFDATA:
|
|
case PJDATA:
|
|
|
|
//misc port config
|
|
case PDKBEN:
|
|
|
|
//dragonball LCD controller, not attached to anything in Palm m515
|
|
case LCKCON:
|
|
|
|
//simple write, no actions needed
|
|
registerArrayWrite8(address, value);
|
|
break;
|
|
|
|
default:
|
|
printUnknownHwAccess(address, value, 8, true);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void setHwRegister16(unsigned int address, unsigned int value){
|
|
if((address & 0x0000F000) != 0x0000F000){
|
|
//not emu or hardware register, invalid access
|
|
return;
|
|
}
|
|
|
|
address &= 0x00000FFF;
|
|
//printUnknownHwAccess(address, value, 16, true);
|
|
switch(address){
|
|
|
|
case RTCIENR:
|
|
//missing bits 6 and 7
|
|
registerArrayWrite16(address, value & 0xFF3F);
|
|
break;
|
|
|
|
case IMR:
|
|
//this is a 32 bit register but Palm OS writes it as 16 bit chunks
|
|
registerArrayWrite16(address, value & 0x00FF);
|
|
break;
|
|
case IMR + 2:
|
|
//this is a 32 bit register but Palm OS writes it as 16 bit chunks
|
|
registerArrayWrite16(address, value & 0x03FF);
|
|
break;
|
|
|
|
case ISR + 2:
|
|
//this is a 32 bit register but Palm OS writes it as 16 bit chunks
|
|
registerArrayWrite16(ISR + 2, registerArrayRead16(ISR + 2) & ~(value & 0x0F00));
|
|
break;
|
|
|
|
case TCTL1:
|
|
case TCTL2:
|
|
registerArrayWrite16(address, value & 0x01FF);
|
|
break;
|
|
|
|
case WATCHDOG:
|
|
//writing to the watchdog resets the counter bits(8 and 9) to 0
|
|
registerArrayWrite16(address, value & 0x0083);
|
|
break;
|
|
|
|
case RTCISR:
|
|
registerArrayWrite16(RTCISR, registerArrayRead16(RTCISR) & ~value);
|
|
if(!(registerArrayRead16(RTCISR) & 0xFF00))
|
|
clearIprIsrBit(INT_RTI);
|
|
if(!(registerArrayRead16(RTCISR) & 0x003F))
|
|
clearIprIsrBit(INT_RTC);
|
|
break;
|
|
|
|
case PLLFSR:
|
|
setPllfsr16(value);
|
|
break;
|
|
|
|
case PLLCR:
|
|
setPllcr(value);
|
|
break;
|
|
|
|
case ICR:
|
|
//missing bottom 7 bits
|
|
registerArrayWrite16(address, value & 0xFF80);
|
|
break;
|
|
|
|
case DRAMC:
|
|
//unemulated
|
|
//missing bit 7 and 6
|
|
registerArrayWrite16(address, value & 0xFF3F);
|
|
break;
|
|
|
|
case DRAMMC:
|
|
//unemulated
|
|
registerArrayWrite16(address, value);
|
|
break;
|
|
|
|
case SDCTRL:
|
|
//unemulated
|
|
//missing bits 13, 9, 8 and 7
|
|
registerArrayWrite16(address, value & 0xDC7F);
|
|
break;
|
|
|
|
case CSA:
|
|
setCsa(value);
|
|
refreshBankHandlers();
|
|
break;
|
|
|
|
case CSB:
|
|
setCsb(value);
|
|
refreshBankHandlers();
|
|
break;
|
|
|
|
case CSC:
|
|
setCsc(value);
|
|
refreshBankHandlers();
|
|
break;
|
|
|
|
case CSD:
|
|
setCsd(value);
|
|
refreshBankHandlers();
|
|
break;
|
|
|
|
case CSGBA:
|
|
//sets the starting location of ROM(0x10000000)
|
|
setCsgba(value);
|
|
refreshBankHandlers();
|
|
break;
|
|
|
|
case CSGBB:
|
|
//sets the starting location of the SED1376(0x1FF80000)
|
|
setCsgbb(value);
|
|
refreshBankHandlers();
|
|
break;
|
|
|
|
case CSGBC:
|
|
//sets the starting location of USBPhilipsPDIUSBD12(address 0x10400000)
|
|
//since I dont plan on adding hotsync should be fine to leave unemulated, its unemulated in pose
|
|
setCsgbc(value);
|
|
refreshBankHandlers();
|
|
break;
|
|
|
|
case CSGBD:
|
|
//sets the starting location of RAM(0x00000000)
|
|
setCsgbd(value);
|
|
refreshBankHandlers();
|
|
break;
|
|
|
|
case CSUGBA:
|
|
registerArrayWrite16(CSUGBA, value);
|
|
//refresh all chipselect address lines
|
|
setCsgba(registerArrayRead16(CSGBA));
|
|
setCsgbb(registerArrayRead16(CSGBB));
|
|
setCsgbc(registerArrayRead16(CSGBC));
|
|
setCsgbd(registerArrayRead16(CSGBD));
|
|
refreshBankHandlers();
|
|
break;
|
|
|
|
case CSCTRL1:
|
|
setCsctrl1(value);
|
|
refreshBankHandlers();
|
|
break;
|
|
|
|
default:
|
|
printUnknownHwAccess(address, value, 16, true);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void setHwRegister32(unsigned int address, unsigned int value){
|
|
if((address & 0x0000F000) == 0x0000E000){
|
|
//32 bit emu register write, valid
|
|
return;
|
|
}
|
|
else if((address & 0x0000F000) != 0x0000F000){
|
|
//not emu or hardware register, invalid access
|
|
return;
|
|
}
|
|
|
|
address &= 0x00000FFF;
|
|
//printUnknownHwAccess(address, value, 32, true);
|
|
switch(address){
|
|
|
|
case RTCTIME:
|
|
registerArrayWrite32(address, value & 0x1F3F003F);
|
|
break;
|
|
|
|
case IDR:
|
|
case IPR:
|
|
//write to read only register, do nothing
|
|
break;
|
|
|
|
case ISR:
|
|
//clear ISR and IPR for external hardware whereever there is a 1 bit in value
|
|
registerArrayWrite32(IPR, registerArrayRead32(IPR) & ~(value & 0x000F0F00/*external hardware int mask*/));
|
|
registerArrayWrite32(ISR, registerArrayRead32(ISR) & ~(value & 0x000F0F00/*external hardware int mask*/));
|
|
break;
|
|
|
|
case IMR:
|
|
registerArrayWrite32(address, value & 0x00FF3FFF);
|
|
break;
|
|
|
|
case LSSA:
|
|
//simple write, no actions needed
|
|
registerArrayWrite32(address, value);
|
|
break;
|
|
|
|
default:
|
|
printUnknownHwAccess(address, value, 32, true);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void resetHwRegisters(){
|
|
memset(palmReg, 0x00, REG_SIZE);
|
|
clk32Counter = 0;
|
|
pllWakeWait = -1;
|
|
timer1CycleCounter = 0.0;
|
|
timer2CycleCounter = 0.0;
|
|
for(uint32_t chip = CHIP_BEGIN; chip < CHIP_END; chip++){
|
|
chips[chip].enable = false;
|
|
chips[chip].start = 0x00000000;
|
|
chips[chip].size = 0x00000000;
|
|
chips[chip].mask = 0x00000000;
|
|
|
|
chips[chip].readOnly = false;
|
|
chips[chip].readOnlyForProtectedMemory = false;
|
|
chips[chip].supervisorOnlyProtectedMemory = false;
|
|
chips[chip].unprotectedSize = 0x00000000;
|
|
}
|
|
//all chipselects are disabled at boot and CSA is mapped to 0x00000000 and covers the entire address range until CSGBA set otherwise
|
|
chips[CHIP_A_ROM].enable = true;
|
|
chips[CHIP_A_ROM].start = 0x00000000;
|
|
chips[CHIP_A_ROM].size = 0xFFFFFFFF;
|
|
chips[CHIP_A_ROM].mask = 0xFFFFFFFF;
|
|
|
|
//system control
|
|
registerArrayWrite8(SCR, 0x1C);
|
|
|
|
//CPU id
|
|
registerArrayWrite32(IDR, 0x56000000);
|
|
|
|
//i/o drive control //probably unused
|
|
registerArrayWrite16(IODCR, 0x1FFF);
|
|
|
|
//chip selects
|
|
registerArrayWrite16(CSA, 0x00B0);
|
|
registerArrayWrite16(CSD, 0x0200);
|
|
registerArrayWrite16(EMUCS, 0x0060);
|
|
registerArrayWrite16(CSCTRL2, 0x1000);
|
|
registerArrayWrite16(CSCTRL3, 0x9C00);
|
|
|
|
//phase lock loop
|
|
registerArrayWrite16(PLLCR, 0x24B3);
|
|
registerArrayWrite16(PLLFSR, 0x0347);
|
|
|
|
//power control
|
|
registerArrayWrite8(PCTLR, 0x1F);
|
|
|
|
//interrupts
|
|
registerArrayWrite32(IMR, 0x00FFFFFF);
|
|
registerArrayWrite16(ILCR, 0x6533);
|
|
|
|
//GPIO ports
|
|
registerArrayWrite8(PADATA, 0xFF);
|
|
registerArrayWrite8(PAPUEN, 0xFF);
|
|
|
|
registerArrayWrite8(PBDATA, 0xFF);
|
|
registerArrayWrite8(PBPUEN, 0xFF);
|
|
registerArrayWrite8(PBSEL, 0xFF);
|
|
|
|
registerArrayWrite8(PCPDEN, 0xFF);
|
|
registerArrayWrite8(PCSEL, 0xFF);
|
|
|
|
registerArrayWrite8(PDDATA, 0xFF);
|
|
registerArrayWrite8(PDPUEN, 0xFF);
|
|
registerArrayWrite8(PDSEL, 0xF0);
|
|
|
|
registerArrayWrite8(PEDATA, 0xFF);
|
|
registerArrayWrite8(PEPUEN, 0xFF);
|
|
registerArrayWrite8(PESEL, 0xFF);
|
|
|
|
registerArrayWrite8(PFDATA, 0xFF);
|
|
registerArrayWrite8(PFPUEN, 0xFF);
|
|
registerArrayWrite8(PFSEL, 0x87);
|
|
|
|
registerArrayWrite8(PGDATA, 0x3F);
|
|
registerArrayWrite8(PGPUEN, 0x3D);
|
|
registerArrayWrite8(PGSEL, 0x08);
|
|
|
|
registerArrayWrite8(PJDATA, 0xFF);
|
|
registerArrayWrite8(PJPUEN, 0xFF);
|
|
registerArrayWrite8(PJSEL, 0xEF);
|
|
|
|
registerArrayWrite8(PKDATA, 0x0F);
|
|
registerArrayWrite8(PKPUEN, 0xFF);
|
|
registerArrayWrite8(PKSEL, 0xFF);
|
|
|
|
registerArrayWrite8(PMDATA, 0x20);
|
|
registerArrayWrite8(PMPUEN, 0x3F);
|
|
registerArrayWrite8(PMSEL, 0x3F);
|
|
|
|
//pulse width modulation control
|
|
registerArrayWrite16(PWMC1, 0x0020);
|
|
registerArrayWrite8(PWMP1, 0xFE);
|
|
|
|
//timers
|
|
registerArrayWrite16(TCMP1, 0xFFFF);
|
|
registerArrayWrite16(TCMP2, 0xFFFF);
|
|
|
|
//serial i/o
|
|
registerArrayWrite16(UBAUD1, 0x0002);
|
|
registerArrayWrite16(UBAUD2, 0x0002);
|
|
registerArrayWrite16(HMARK, 0x0102);
|
|
|
|
//LCD control registers, unused since the SED1376 is present
|
|
registerArrayWrite8(LVPW, 0xFF);
|
|
registerArrayWrite16(LXMAX, 0x03F0);
|
|
registerArrayWrite16(LYMAX, 0x01FF);
|
|
registerArrayWrite16(LCWCH, 0x0101);
|
|
registerArrayWrite8(LBLKC, 0x7F);
|
|
registerArrayWrite16(LRRA, 0x00FF);
|
|
registerArrayWrite8(LGPMR, 0x84);
|
|
registerArrayWrite8(DMACR, 0x62);
|
|
|
|
//realtime clock
|
|
//RTCTIME is not changed on reset
|
|
registerArrayWrite16(WATCHDOG, 0x0001);
|
|
registerArrayWrite16(RTCCTL, 0x0080);//conflicting size in datasheet, it says its 8 bit but provides 16 bit values
|
|
registerArrayWrite16(STPWCH, 0x003F);//conflicting size in datasheet, it says its 8 bit but provides 16 bit values
|
|
//DAYR is not changed on reset
|
|
|
|
//SDRAM control, unused since RAM refresh is unemulated
|
|
registerArrayWrite16(SDCTRL, 0x003C);
|
|
|
|
//add register settings to misc i/o
|
|
updateAlarmLedStatus();
|
|
updateLcdStatus();
|
|
updateBacklightStatus();
|
|
updateVibratorStatus();
|
|
}
|
|
|
|
void setRtc(uint32_t days, uint32_t hours, uint32_t minutes, uint32_t seconds){
|
|
uint32_t rtcTime;
|
|
rtcTime = seconds & 0x0000003F;
|
|
rtcTime |= (minutes << 16) & 0x003F0000;
|
|
rtcTime |= (hours << 24) & 0x1F000000;
|
|
registerArrayWrite32(RTCTIME, rtcTime);
|
|
registerArrayWrite16(DAYR, days & 0x01FF);
|
|
}
|