Files
2015-09-03 01:20:11 -07:00

401 lines
9.5 KiB
C

/*
PokeMini - Pokémon-Mini Emulator
Copyright (C) 2009-2012 JustBurn
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "PokeMini.h"
TMinxIO MinxIO;
uint8_t *EEPROM = NULL;
int PokeMini_Rumbling = 0;
int PokeMini_RumblingLatch = 0;
int PokeMini_EEPROMWritten = 0;
int PokeMini_BatteryStatus = 0; // 0 = Full, 1 = Low
int PokeMini_ShockKey = 0;
uint8_t MinxIO_IODataRead(void);
void MinxIO_IODataWrite(void);
void MinxIO_EEPROM_WEvent(uint8_t bits);
int MinxIO_EEPROM_REvent(void);
void MinxIO_EEPROM_Write(uint8_t data);
//
// Functions
//
int MinxIO_Create(void)
{
// Create EEPROM memory & format it
EEPROM = (uint8_t *)malloc(8192);
if (!EEPROM) return 0;
MinxIO_FormatEEPROM();
// Reset
MinxIO_Reset(1);
return 1;
}
void MinxIO_Destroy(void)
{
if (EEPROM) {
free(EEPROM);
EEPROM = NULL;
}
}
void MinxIO_Reset(int hardreset)
{
// Initialize State
memset(&MinxIO, 0, sizeof(TMinxIO));
PokeMini_Rumbling = 0;
PokeMini_RumblingLatch = 0;
PokeMini_ShockKey = 0;
// Init variables
PMR_KEY_PAD = 0xFF;
MinxIO.ListenState = MINX_EEPROM_IDLE;
MinxIO.OperState = MINX_EEPROM_DEVICE;
// Restore battery status
MinxIO_BatteryLow(PokeMini_BatteryStatus);
}
int MinxIO_LoadState(FILE *fi, uint32_t bsize)
{
POKELOADSS_START(32+8192);
PokeMini_Rumbling = 0;
PokeMini_RumblingLatch = 0;
PokeMini_EEPROMWritten = 1;
POKELOADSS_8(MinxIO.EEPLastPins);
POKELOADSS_8(MinxIO.ListenState);
POKELOADSS_8(MinxIO.OperState);
POKELOADSS_8(MinxIO.EEPData);
POKELOADSS_32(MinxIO.EEPBit);
POKELOADSS_16(MinxIO.EEPAddress);
POKELOADSS_X(22);
POKELOADSS_A(EEPROM, 8192);
POKELOADSS_END(32+8192);
}
int MinxIO_SaveState(FILE *fi)
{
POKESAVESS_START(32+8192);
POKESAVESS_8(MinxIO.EEPLastPins);
POKESAVESS_8(MinxIO.ListenState);
POKESAVESS_8(MinxIO.OperState);
POKESAVESS_8(MinxIO.EEPData);
POKESAVESS_32(MinxIO.EEPBit);
POKESAVESS_16(MinxIO.EEPAddress);
POKESAVESS_X(22);
POKESAVESS_A(EEPROM, 8192);
POKESAVESS_END(32+8192);
}
int MinxIO_FormatEEPROM(void)
{
if (!EEPROM) return 0;
memset(EEPROM, 0xFF, 8192);
return 1;
}
void MinxIO_Keypad(uint8_t key, int pressed)
{
if (!EEPROM) return;
switch (key) {
case MINX_KEY_A: // Key A
if (pressed) {
if (!(PMR_KEY_PAD & 0x01)) return;
MinxCPU_OnIRQAct(MINX_INTR_1C);
PMR_KEY_PAD &= ~0x01;
} else PMR_KEY_PAD |= 0x01;
break;
case MINX_KEY_B: // Key B
if (pressed) {
if (!(PMR_KEY_PAD & 0x02)) return;
MinxCPU_OnIRQAct(MINX_INTR_1B);
PMR_KEY_PAD &= ~0x02;
} else PMR_KEY_PAD |= 0x02;
break;
case MINX_KEY_C: // Key C
if (pressed) {
if (!(PMR_KEY_PAD & 0x04)) return;
MinxCPU_OnIRQAct(MINX_INTR_1A);
PMR_KEY_PAD &= ~0x04;
} else PMR_KEY_PAD |= 0x04;
break;
case MINX_KEY_UP: // Dpad Up
if (pressed) {
if (!(PMR_KEY_PAD & 0x08)) return;
MinxCPU_OnIRQAct(MINX_INTR_19);
PMR_KEY_PAD &= ~0x08;
} else PMR_KEY_PAD |= 0x08;
break;
case MINX_KEY_DOWN: // Dpad Down
if (pressed) {
if (!(PMR_KEY_PAD & 0x10)) return;
MinxCPU_OnIRQAct(MINX_INTR_18);
PMR_KEY_PAD &= ~0x10;
} else PMR_KEY_PAD |= 0x10;
break;
case MINX_KEY_LEFT: // Dpad Left
if (pressed) {
if (!(PMR_KEY_PAD & 0x20)) return;
MinxCPU_OnIRQAct(MINX_INTR_17);
PMR_KEY_PAD &= ~0x20;
} else PMR_KEY_PAD |= 0x20;
break;
case MINX_KEY_RIGHT: // Dpad Right
if (pressed) {
if (!(PMR_KEY_PAD & 0x40)) return;
MinxCPU_OnIRQAct(MINX_INTR_16);
PMR_KEY_PAD &= ~0x40;
} else PMR_KEY_PAD |= 0x40;
break;
case MINX_KEY_POWER: // Power key
if (pressed) {
if (!(PMR_KEY_PAD & 0x80)) return;
MinxCPU_OnIRQAct(MINX_INTR_15);
PMR_KEY_PAD &= ~0x80;
} else PMR_KEY_PAD |= 0x80;
break;
case MINX_KEY_SHOCK: // Shock key
if (!PokeMini_ShockKey && pressed) {
MinxCPU_OnIRQAct(MINX_INTR_10);
}
PokeMini_ShockKey = pressed;
break;
}
}
void MinxIO_BatteryLow(int low)
{
PokeMini_BatteryStatus = low;
if (low) PMR_SYS_BATT |= 0x20;
else PMR_SYS_BATT &= 0x1F;
}
void MinxIO_SetTimeStamp(uint8_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t min, uint8_t sec)
{
uint8_t checksum;
if (!EEPROM) return;
checksum = year + month + day + hour + min + sec;
PMR_SYS_CTRL3 |= 0x02;
EEPROM[0x1FF6] = 0x00;
EEPROM[0x1FF7] = 0x00;
EEPROM[0x1FF8] = 0x00;
EEPROM[0x1FF9] = year;
EEPROM[0x1FFA] = month;
EEPROM[0x1FFB] = day;
EEPROM[0x1FFC] = hour;
EEPROM[0x1FFD] = min;
EEPROM[0x1FFE] = sec;
EEPROM[0x1FFF] = checksum;
}
uint8_t MinxIO_ReadReg(int cpu, uint8_t reg)
{
// 0x10, 0x52, 0x60 to 0x62
switch(reg) {
case 0x10: // Low Battery
return PMR_SYS_BATT;
case 0x44: // Unknown
return PMR_REG_44;
case 0x45: // Unknown
return PMR_REG_45;
case 0x46: // Unknown
return PMR_REG_46;
case 0x47: // Unknown
return PMR_REG_47;
case 0x50: // Unknown (related to power management?)
return PMR_REG_50;
case 0x51: // Unknown (related to power management?)
return PMR_REG_51;
case 0x52: // Keypad
return PMR_KEY_PAD;
case 0x53: // Unknown
return PMR_REG_53;
case 0x54: // Unknown
return PMR_REG_54;
case 0x55: // Unknown
return PMR_REG_55;
case 0x60: // I/O Direction Select ( 0 = Input, 1 = Output )
return PMR_IO_DIR;
case 0x61: // I/O Data Register
return MinxIO_IODataRead();
case 0x62: // Unknown
return PMR_REG_62;
default: // Unused
return 0;
}
}
void MinxIO_WriteReg(int cpu, uint8_t reg, uint8_t val)
{
// 0x10, 0x52, 0x60 to 0x62
switch(reg) {
case 0x10: // Low Battery
PMR_SYS_BATT = (PMR_SYS_BATT & 0x20) | (val & 0x1F);
return;
case 0x44: // Unknown
PMR_REG_44 = val & 0xF7;
return;
case 0x45: // Unknown
PMR_REG_45 = val & 0x0F;
return;
case 0x46: // Unknown
PMR_REG_46 = val;
return;
case 0x47: // Unknown
PMR_REG_47 = val & 0x0F;
return;
case 0x50: // Unknown (related to power management?)
PMR_REG_50 = val;
return;
case 0x51: // Unknown (related to power management?)
PMR_REG_51 = val & 0x03;
return;
case 0x52: // Keypad
return;
case 0x53: // Unknown
PMR_REG_53 = 0x00;
case 0x54: // Unknown
PMR_REG_54 = val & 0x77;
case 0x55: // Unknown
PMR_REG_55 = val & 0x07;
case 0x60: // I/O Direction Select ( 0 = Input, 1 = Output )
PMR_IO_DIR = val;
MinxIO_IODataWrite();
return;
case 0x61: // I/O Data Register
PMR_IO_DATA = val;
MinxIO_IODataWrite();
return;
case 0x62: // Unknown
PMR_REG_62 = val & 0xF0;
return;
}
}
//
// Internal
//
uint8_t MinxIO_IODataRead(void)
{
uint8_t Input = MINX_IO_PULL_UPS;
// Update all I/Os
Input = MinxIO_EEPROM_REvent() ? MINX_EEPROM_DAT : 0;
return (PMR_IO_DATA & PMR_IO_DIR) | (Input & ~PMR_IO_DIR);
}
void MinxIO_IODataWrite(void)
{
uint8_t Output;
Output = (MINX_IO_PULL_UPS & ~PMR_IO_DIR) | (PMR_IO_DATA & PMR_IO_DIR);
// Update all I/Os
PokeMini_Rumbling = Output & 0x10;
if (PokeMini_Rumbling) PokeMini_RumblingLatch = 1;
MinxIO_EEPROM_WEvent(Output);
}
void MinxIO_EEPROM_WEvent(uint8_t bits)
{
uint8_t rise = bits & ~MinxIO.EEPLastPins;
uint8_t fall = ~bits & MinxIO.EEPLastPins;
MinxIO.EEPLastPins = bits;
// "Start" Command
if ((fall & MINX_EEPROM_DAT) && (bits & MINX_EEPROM_CLK)) {
MinxIO.ListenState = MINX_EEPROM_LISTEN;
MinxIO.OperState = MINX_EEPROM_DEVICE;
MinxIO.EEPBit = 8;
MinxIO.EEPData = 0x00;
return;
}
// "Stop" Command
if ((rise & MINX_EEPROM_DAT) && (bits & MINX_EEPROM_CLK)) {
MinxIO.ListenState = MINX_EEPROM_IDLE;
return;
}
// If it's Idle, there's nothing to do...
if (MinxIO.ListenState == MINX_EEPROM_IDLE) return;
// Process each bit on clock rise
if (rise & MINX_EEPROM_CLK) {
int dat = (bits & MINX_EEPROM_DAT) ? 1 : 0;
if (MinxIO.EEPBit < 0) {
MinxIO_EEPROM_Write(MinxIO.EEPData);
MinxIO.EEPBit = 8;
MinxIO.EEPData = 0x00;
} else MinxIO.EEPData = (MinxIO.EEPData << 1) | dat;
} else if (fall & MINX_EEPROM_CLK) {
MinxIO.EEPBit = MinxIO.EEPBit - 1;
}
}
int MinxIO_EEPROM_REvent(void)
{
int valid = (MinxIO.EEPBit >= 0) && (MinxIO.EEPBit < 8);
// If it's Idle, return high...
if (MinxIO.ListenState == MINX_EEPROM_IDLE) return 1;
// Process read
if (MinxIO.OperState == MINX_EEPROM_RBYTE) {
if (valid) return (EEPROM[MinxIO.EEPAddress & 0x1FFF] >> MinxIO.EEPBit) & 1;
else return 0;
}
return valid;
}
void MinxIO_EEPROM_Write(uint8_t data)
{
switch (MinxIO.OperState) {
case MINX_EEPROM_DEVICE:
if ((data & 0xF0) == 0xA0) {
// EEPROM Device
MinxIO.OperState = data & 0x01 ? MINX_EEPROM_RBYTE : MINX_EEPROM_ADDRHI;
} else {
// Unknown Devide
PokeDPrint(POKEMSG_ERR, "Error: Accessing unknown I2C device: 0x%02X\n", (int)data);
}
break;
case MINX_EEPROM_ADDRHI:
MinxIO.EEPAddress = (MinxIO.EEPAddress & 0x00FF) | (data << 8);
MinxIO.OperState = MINX_EEPROM_ADDRLO;
break;
case MINX_EEPROM_ADDRLO:
MinxIO.EEPAddress = (MinxIO.EEPAddress & 0xFF00) | data;
MinxIO.OperState = MINX_EEPROM_WBYTE;
break;
case MINX_EEPROM_WBYTE:
PokeMini_EEPROMWritten = 1;
EEPROM[MinxIO.EEPAddress & 0x1FFF] = data;
MinxIO.EEPAddress++;
break;
case MINX_EEPROM_RBYTE:
MinxIO.EEPAddress++;
break;
}
}