2019-12-20 23:46:22 +00:00
|
|
|
#include "etna.h"
|
2019-12-24 23:34:02 +00:00
|
|
|
#include "arm710.h"
|
2019-12-20 23:46:22 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
enum EtnaReg {
|
|
|
|
regUnk0 = 0,
|
|
|
|
regUnk1 = 1,
|
|
|
|
regUartIntStatus = 2,
|
|
|
|
regUartIntMask = 3,
|
|
|
|
regUartBaudRateLo8 = 4,
|
|
|
|
regUartBaudRateHi4 = 5,
|
|
|
|
regPcCdIntStatus = 6,
|
|
|
|
regPcCdIntMask = 7,
|
|
|
|
regIntClear = 8,
|
|
|
|
regSktVarA0 = 9,
|
|
|
|
regSktVarA1 = 0xA,
|
|
|
|
regSktCtrl = 0xB,
|
|
|
|
regWake1 = 0xC,
|
|
|
|
regSktVarB0 = 0xD,
|
|
|
|
regSktVarB1 = 0xE,
|
|
|
|
regWake2 = 0xF
|
|
|
|
};
|
|
|
|
|
|
|
|
static const char *nameReg(uint32_t reg) {
|
|
|
|
switch (reg) {
|
|
|
|
case regUnk0: return "unk0";
|
|
|
|
case regUnk1: return "unk1";
|
|
|
|
case regUartIntStatus: return "UartIntStatus";
|
|
|
|
case regUartIntMask: return "UartIntMask";
|
|
|
|
case regUartBaudRateLo8: return "UartBaudRateLo8";
|
|
|
|
case regUartBaudRateHi4: return "UartBaudRateHi4";
|
|
|
|
case regPcCdIntStatus: return "PcCdIntStatus";
|
|
|
|
case regPcCdIntMask: return "PcCdIntMask";
|
|
|
|
case regIntClear: return "IntClear";
|
|
|
|
case regSktVarA0: return "SktVarA0";
|
|
|
|
case regSktVarA1: return "SktVarA1";
|
|
|
|
case regSktCtrl: return "SktCtrl";
|
|
|
|
case regWake1: return "wake1";
|
|
|
|
case regSktVarB0: return "SktVarB0";
|
|
|
|
case regSktVarB1: return "SktVarB1";
|
|
|
|
case regWake2: return "wake2";
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-12-24 23:34:02 +00:00
|
|
|
Etna::Etna(ARM710 *owner) {
|
2019-12-21 11:09:58 +00:00
|
|
|
this->owner = owner;
|
|
|
|
|
2019-12-20 23:46:22 +00:00
|
|
|
for (int i = 0; i < 0x80; i++)
|
|
|
|
prom[i] = 0;
|
|
|
|
|
2019-12-25 17:02:46 +00:00
|
|
|
// defaults expected by the touchscreen code
|
|
|
|
prom[0xA] = 20;
|
|
|
|
prom[0xB] = 20;
|
|
|
|
prom[0xC] = 20;
|
|
|
|
prom[0xD] = 30;
|
|
|
|
|
2019-12-20 23:46:22 +00:00
|
|
|
// some basic stuff to begin with
|
|
|
|
// set up the Psion's unique ID
|
|
|
|
prom[0x1B] = 0xDE;
|
|
|
|
prom[0x1A] = 0xAD;
|
|
|
|
prom[0x19] = 0xBE;
|
|
|
|
prom[0x18] = 0xEF;
|
|
|
|
|
|
|
|
// give ourselves a neat custom device name
|
|
|
|
const char *key = "PSIONPSIONPSION";
|
|
|
|
const char *name = "WindEmu!";
|
|
|
|
prom[0x28] = strlen(name);
|
|
|
|
if (prom[0x28] > 15)
|
|
|
|
prom[0x28] = 15;
|
|
|
|
for (int i = 0; i < prom[0x28]; i++)
|
|
|
|
prom[0x29 + i] = name[i] ^ key[i];
|
|
|
|
|
|
|
|
// calculate the checksum
|
|
|
|
uint8_t chk = 0;
|
|
|
|
for (int i = 0; i < 0x7F; i++)
|
|
|
|
chk ^= prom[i];
|
|
|
|
|
|
|
|
// EPOC is expecting 66
|
2019-12-25 19:25:34 +00:00
|
|
|
prom[0x7F] = chk ^ 66;
|
2019-12-20 23:46:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
uint32_t Etna::readReg8(uint32_t reg)
|
|
|
|
{
|
2019-12-25 19:25:34 +00:00
|
|
|
// if (!promReadActive)
|
|
|
|
// owner->log("ETNA readReg8: reg=%s @ pc=%08x,lr=%08x", nameReg(reg), owner->getGPR(15) - 4, owner->getGPR(14));
|
2019-12-20 23:46:22 +00:00
|
|
|
switch (reg) {
|
2019-12-21 11:09:58 +00:00
|
|
|
case regIntClear: return 0;
|
|
|
|
case regSktVarA0: return 1; // will store some status flags
|
2019-12-20 23:46:22 +00:00
|
|
|
case regSktVarA1: return 0; // will store some more status flags
|
|
|
|
case regWake1: return wake1;
|
|
|
|
case regWake2: return wake2;
|
|
|
|
}
|
|
|
|
return 0xFF;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t Etna::readReg32(uint32_t reg)
|
|
|
|
{
|
|
|
|
// may be able to remove this, p. sure Etna is byte addressing only
|
2019-12-25 19:25:34 +00:00
|
|
|
owner->log("ETNA readReg32: reg=%x", reg);
|
2019-12-20 23:46:22 +00:00
|
|
|
return 0xFFFFFFFF;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Etna::writeReg8(uint32_t reg, uint8_t value)
|
|
|
|
{
|
|
|
|
if (!promReadActive)
|
2019-12-25 19:25:34 +00:00
|
|
|
owner->log("ETNA writeReg8: reg=%s value=%02x @ pc=%08x,lr=%08x", nameReg(reg), value, owner->getGPR(15) - 4, owner->getGPR(14));
|
2019-12-20 23:46:22 +00:00
|
|
|
switch (reg) {
|
2019-12-21 11:09:58 +00:00
|
|
|
case regIntClear: pendingInterrupts &= ~value; break;
|
2019-12-20 23:46:22 +00:00
|
|
|
case regWake1: wake1 = value; break;
|
|
|
|
case regWake2: wake2 = value; break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Etna::writeReg32(uint32_t reg, uint32_t value)
|
|
|
|
{
|
|
|
|
// may be able to remove this, p. sure Etna is byte addressing only
|
2019-12-25 19:25:34 +00:00
|
|
|
owner->log("ETNA writeReg32: reg=%x value=%08x", reg, value);
|
2019-12-20 23:46:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Etna::setPromBit0High()
|
|
|
|
{
|
|
|
|
// begin reading a word
|
|
|
|
promReadAddress = 0;
|
|
|
|
promReadValue = 0;
|
|
|
|
promAddressBitsReceived = 0;
|
|
|
|
promReadActive = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Etna::setPromBit0Low()
|
|
|
|
{
|
|
|
|
promReadActive = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Etna::setPromBit1High()
|
|
|
|
{
|
|
|
|
if (promAddressBitsReceived < 10) {
|
|
|
|
// we're still receiving the address
|
|
|
|
promReadAddress <<= 1;
|
|
|
|
promReadAddress |= ((wake1 & 4) >> 2);
|
|
|
|
if (++promAddressBitsReceived == 10) {
|
|
|
|
// we can fetch the value now
|
|
|
|
int addressInBytes = promReadAddress * 2;
|
|
|
|
addressInBytes %= sizeof(prom);
|
|
|
|
promReadValue = prom[addressInBytes] | (prom[addressInBytes + 1] << 8);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
wake1 &= ~8;
|
|
|
|
if (promReadValue & 0x8000)
|
|
|
|
wake1 |= 8;
|
|
|
|
promReadValue <<= 1;
|
|
|
|
}
|
|
|
|
}
|