mirror of https://github.com/Treeki/WindEmu.git
add skeleton support for the Etna chip, including PROM support
This commit is contained in:
parent
3851109a0a
commit
0ed437c6d3
|
@ -12,7 +12,7 @@ Hardware features:
|
|||
- ❌ Touch panel: not implemented
|
||||
- ❌ Audio: not implemented
|
||||
- ❌ Serial/UART support: stubbed out
|
||||
- ❌ ETNA (CompactFlash): not implemented
|
||||
- ❌ ETNA (PCMCIA/CompactFlash): mostly stubbed out
|
||||
- ✅ RTC: implemented
|
||||
- ❌ RTC alarm: not implemented
|
||||
- ❌ Standby mode: not implemented
|
||||
|
|
|
@ -22,6 +22,7 @@ DEFINES += QT_DEPRECATED_WARNINGS
|
|||
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
|
||||
|
||||
SOURCES += \
|
||||
etna.cpp \
|
||||
wind.cpp \
|
||||
isa-arm.c \
|
||||
decoder.c \
|
||||
|
@ -30,6 +31,7 @@ SOURCES += \
|
|||
emu.cpp
|
||||
|
||||
HEADERS += \
|
||||
etna.h \
|
||||
wind_hw.h \
|
||||
wind.h \
|
||||
macros.h \
|
||||
|
|
|
@ -108,6 +108,12 @@ void Emu::writeReg8(uint32_t reg, uint8_t value) {
|
|||
uint32_t oldPorts = portValues;
|
||||
portValues &= 0xFF00FFFF;
|
||||
portValues |= (uint32_t)value << 16;
|
||||
if ((portValues & 0x10000) && !(oldPorts & 0x10000))
|
||||
etna.setPromBit0High();
|
||||
else if (!(portValues & 0x10000) && (oldPorts & 0x10000))
|
||||
etna.setPromBit0Low();
|
||||
if ((portValues & 0x20000) && !(oldPorts & 0x20000))
|
||||
etna.setPromBit1High();
|
||||
diffPorts(oldPorts, portValues);
|
||||
} else if (reg == PCDR) {
|
||||
uint32_t oldPorts = portValues;
|
||||
|
@ -200,6 +206,8 @@ uint32_t Emu::readPhys8(uint32_t physAddress) {
|
|||
uint8_t region = (physAddress >> 24) & 0xF1;
|
||||
if (region == 0)
|
||||
result = ROM[physAddress & 0xFFFFFF];
|
||||
else if (region == 0x20 && physAddress <= 0x20000FFF)
|
||||
result = etna.readReg8(physAddress & 0xFFF);
|
||||
else if (region == 0x80 && physAddress <= 0x80000FFF)
|
||||
result = readReg8(physAddress & 0xFFF);
|
||||
else if (region == 0xC0)
|
||||
|
@ -244,6 +252,8 @@ uint32_t Emu::readPhys32(uint32_t physAddress) {
|
|||
uint8_t region = (physAddress >> 24) & 0xF1;
|
||||
if (region == 0)
|
||||
LOAD_32LE(result, physAddress & 0xFFFFFF, ROM);
|
||||
else if (region == 0x20 && physAddress <= 0x20000FFF)
|
||||
result = etna.readReg32(physAddress & 0xFFF);
|
||||
else if (region == 0x80 && physAddress <= 0x80000FFF)
|
||||
result = readReg32(physAddress & 0xFFF);
|
||||
else if (region == 0xC0)
|
||||
|
@ -277,6 +287,8 @@ void Emu::writePhys8(uint32_t physAddress, uint8_t value) {
|
|||
else if (region == 0xD1)
|
||||
MemoryBlockD1[physAddress & MemoryBlockMask] = (uint8_t)value;
|
||||
#endif
|
||||
else if (region == 0x20 && physAddress <= 0x20000FFF)
|
||||
etna.writeReg8(physAddress & 0xFFF, value);
|
||||
else if (region == 0x80 && physAddress <= 0x80000FFF)
|
||||
writeReg8(physAddress & 0xFFF, value);
|
||||
// else
|
||||
|
@ -313,6 +325,8 @@ void Emu::writePhys32(uint32_t physAddress, uint32_t value) {
|
|||
else if (region == 0xD1)
|
||||
STORE_32LE(value, physAddress & MemoryBlockMask, MemoryBlockD1);
|
||||
#endif
|
||||
else if (region == 0x20 && physAddress <= 0x20000FFF)
|
||||
etna.writeReg32(physAddress & 0xFFF, value);
|
||||
else if (region == 0x80 && physAddress <= 0x80000FFF)
|
||||
writeReg32(physAddress & 0xFFF, value);
|
||||
// else
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
#include "arm.h"
|
||||
#include "wind_hw.h"
|
||||
#include "etna.h"
|
||||
#include <unordered_set>
|
||||
|
||||
class Emu {
|
||||
|
@ -27,6 +28,7 @@ class Emu {
|
|||
int64_t nextTickAt = 0;
|
||||
Timer tc1, tc2;
|
||||
UART uart1, uart2;
|
||||
Etna etna;
|
||||
bool asleep = false;
|
||||
|
||||
std::unordered_set<uint32_t> _breakpoints;
|
||||
|
|
|
@ -0,0 +1,145 @@
|
|||
#include "etna.h"
|
||||
#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;
|
||||
}
|
||||
|
||||
|
||||
Etna::Etna() {
|
||||
for (int i = 0; i < 0x80; i++)
|
||||
prom[i] = 0;
|
||||
|
||||
// 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
|
||||
prom[0x7F] = chk ^ 66;
|
||||
}
|
||||
|
||||
|
||||
uint32_t Etna::readReg8(uint32_t reg)
|
||||
{
|
||||
if (!promReadActive)
|
||||
printf("ETNA readReg8: reg=%s\n", nameReg(reg));
|
||||
switch (reg) {
|
||||
case regSktVarA0: return 0; // will store some status flags
|
||||
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
|
||||
printf("ETNA readReg32: reg=%x\n", reg);
|
||||
return 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
void Etna::writeReg8(uint32_t reg, uint8_t value)
|
||||
{
|
||||
if (!promReadActive)
|
||||
printf("ETNA writeReg8: reg=%s value=%02x\n", nameReg(reg), value);
|
||||
switch (reg) {
|
||||
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
|
||||
printf("ETNA writeReg32: reg=%x value=%08x\n", reg, value);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
class Etna {
|
||||
uint8_t prom[0x80] = {};
|
||||
uint16_t promReadAddress = 0, promReadValue = 0;
|
||||
bool promReadActive = false;
|
||||
int promAddressBitsReceived = 0;
|
||||
|
||||
uint8_t wake1 = 0, wake2 = 0;
|
||||
|
||||
public:
|
||||
Etna();
|
||||
|
||||
uint32_t readReg8(uint32_t reg);
|
||||
uint32_t readReg32(uint32_t reg);
|
||||
void writeReg8(uint32_t reg, uint8_t value);
|
||||
void writeReg32(uint32_t reg, uint32_t value);
|
||||
|
||||
// PROM
|
||||
void setPromBit0High(); // port B, bit 0
|
||||
void setPromBit0Low(); // port B, bit 0
|
||||
void setPromBit1High(); // port B, bit 1
|
||||
};
|
|
@ -18,8 +18,9 @@ void diffPorts(uint32_t oldval, uint32_t newval) {
|
|||
if (changes & 0x1000) printf("PRT lcd backlight: %d\n", newval&0x1000);
|
||||
if (changes & 0x2000) printf("PRT enable uart0: %d\n", newval&0x2000);
|
||||
if (changes & 0x4000) printf("PRT dictaphone: %d\n", newval&0x4000);
|
||||
if (changes & 0x10000) printf("PRT EECS: %d\n", newval&0x10000);
|
||||
if (changes & 0x20000) printf("PRT EECLK: %d\n", newval&0x20000);
|
||||
// PROM read process makes this super spammy in stdout
|
||||
// if (changes & 0x10000) printf("PRT EECS: %d\n", newval&0x10000);
|
||||
// if (changes & 0x20000) printf("PRT EECLK: %d\n", newval&0x20000);
|
||||
if (changes & 0x40000) printf("PRT contrast0: %d\n", newval&0x40000);
|
||||
if (changes & 0x80000) printf("PRT contrast1: %d\n", newval&0x80000);
|
||||
if (changes & 0x100000) printf("PRT contrast2: %d\n", newval&0x100000);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#include <stdint.h>
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
const int CLOCK_SPEED = 0x9000*1000;
|
||||
const int TICK_INTERVAL = CLOCK_SPEED / 64;
|
||||
|
|
Loading…
Reference in New Issue