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
|
- ❌ Touch panel: not implemented
|
||||||
- ❌ Audio: not implemented
|
- ❌ Audio: not implemented
|
||||||
- ❌ Serial/UART support: stubbed out
|
- ❌ Serial/UART support: stubbed out
|
||||||
- ❌ ETNA (CompactFlash): not implemented
|
- ❌ ETNA (PCMCIA/CompactFlash): mostly stubbed out
|
||||||
- ✅ RTC: implemented
|
- ✅ RTC: implemented
|
||||||
- ❌ RTC alarm: not implemented
|
- ❌ RTC alarm: not implemented
|
||||||
- ❌ Standby mode: 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
|
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
|
||||||
|
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
|
etna.cpp \
|
||||||
wind.cpp \
|
wind.cpp \
|
||||||
isa-arm.c \
|
isa-arm.c \
|
||||||
decoder.c \
|
decoder.c \
|
||||||
|
@ -30,6 +31,7 @@ SOURCES += \
|
||||||
emu.cpp
|
emu.cpp
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
|
etna.h \
|
||||||
wind_hw.h \
|
wind_hw.h \
|
||||||
wind.h \
|
wind.h \
|
||||||
macros.h \
|
macros.h \
|
||||||
|
|
|
@ -108,6 +108,12 @@ void Emu::writeReg8(uint32_t reg, uint8_t value) {
|
||||||
uint32_t oldPorts = portValues;
|
uint32_t oldPorts = portValues;
|
||||||
portValues &= 0xFF00FFFF;
|
portValues &= 0xFF00FFFF;
|
||||||
portValues |= (uint32_t)value << 16;
|
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);
|
diffPorts(oldPorts, portValues);
|
||||||
} else if (reg == PCDR) {
|
} else if (reg == PCDR) {
|
||||||
uint32_t oldPorts = portValues;
|
uint32_t oldPorts = portValues;
|
||||||
|
@ -200,6 +206,8 @@ uint32_t Emu::readPhys8(uint32_t physAddress) {
|
||||||
uint8_t region = (physAddress >> 24) & 0xF1;
|
uint8_t region = (physAddress >> 24) & 0xF1;
|
||||||
if (region == 0)
|
if (region == 0)
|
||||||
result = ROM[physAddress & 0xFFFFFF];
|
result = ROM[physAddress & 0xFFFFFF];
|
||||||
|
else if (region == 0x20 && physAddress <= 0x20000FFF)
|
||||||
|
result = etna.readReg8(physAddress & 0xFFF);
|
||||||
else if (region == 0x80 && physAddress <= 0x80000FFF)
|
else if (region == 0x80 && physAddress <= 0x80000FFF)
|
||||||
result = readReg8(physAddress & 0xFFF);
|
result = readReg8(physAddress & 0xFFF);
|
||||||
else if (region == 0xC0)
|
else if (region == 0xC0)
|
||||||
|
@ -244,6 +252,8 @@ uint32_t Emu::readPhys32(uint32_t physAddress) {
|
||||||
uint8_t region = (physAddress >> 24) & 0xF1;
|
uint8_t region = (physAddress >> 24) & 0xF1;
|
||||||
if (region == 0)
|
if (region == 0)
|
||||||
LOAD_32LE(result, physAddress & 0xFFFFFF, ROM);
|
LOAD_32LE(result, physAddress & 0xFFFFFF, ROM);
|
||||||
|
else if (region == 0x20 && physAddress <= 0x20000FFF)
|
||||||
|
result = etna.readReg32(physAddress & 0xFFF);
|
||||||
else if (region == 0x80 && physAddress <= 0x80000FFF)
|
else if (region == 0x80 && physAddress <= 0x80000FFF)
|
||||||
result = readReg32(physAddress & 0xFFF);
|
result = readReg32(physAddress & 0xFFF);
|
||||||
else if (region == 0xC0)
|
else if (region == 0xC0)
|
||||||
|
@ -277,6 +287,8 @@ void Emu::writePhys8(uint32_t physAddress, uint8_t value) {
|
||||||
else if (region == 0xD1)
|
else if (region == 0xD1)
|
||||||
MemoryBlockD1[physAddress & MemoryBlockMask] = (uint8_t)value;
|
MemoryBlockD1[physAddress & MemoryBlockMask] = (uint8_t)value;
|
||||||
#endif
|
#endif
|
||||||
|
else if (region == 0x20 && physAddress <= 0x20000FFF)
|
||||||
|
etna.writeReg8(physAddress & 0xFFF, value);
|
||||||
else if (region == 0x80 && physAddress <= 0x80000FFF)
|
else if (region == 0x80 && physAddress <= 0x80000FFF)
|
||||||
writeReg8(physAddress & 0xFFF, value);
|
writeReg8(physAddress & 0xFFF, value);
|
||||||
// else
|
// else
|
||||||
|
@ -313,6 +325,8 @@ void Emu::writePhys32(uint32_t physAddress, uint32_t value) {
|
||||||
else if (region == 0xD1)
|
else if (region == 0xD1)
|
||||||
STORE_32LE(value, physAddress & MemoryBlockMask, MemoryBlockD1);
|
STORE_32LE(value, physAddress & MemoryBlockMask, MemoryBlockD1);
|
||||||
#endif
|
#endif
|
||||||
|
else if (region == 0x20 && physAddress <= 0x20000FFF)
|
||||||
|
etna.writeReg32(physAddress & 0xFFF, value);
|
||||||
else if (region == 0x80 && physAddress <= 0x80000FFF)
|
else if (region == 0x80 && physAddress <= 0x80000FFF)
|
||||||
writeReg32(physAddress & 0xFFF, value);
|
writeReg32(physAddress & 0xFFF, value);
|
||||||
// else
|
// else
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "arm.h"
|
#include "arm.h"
|
||||||
#include "wind_hw.h"
|
#include "wind_hw.h"
|
||||||
|
#include "etna.h"
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
|
|
||||||
class Emu {
|
class Emu {
|
||||||
|
@ -27,6 +28,7 @@ class Emu {
|
||||||
int64_t nextTickAt = 0;
|
int64_t nextTickAt = 0;
|
||||||
Timer tc1, tc2;
|
Timer tc1, tc2;
|
||||||
UART uart1, uart2;
|
UART uart1, uart2;
|
||||||
|
Etna etna;
|
||||||
bool asleep = false;
|
bool asleep = false;
|
||||||
|
|
||||||
std::unordered_set<uint32_t> _breakpoints;
|
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 & 0x1000) printf("PRT lcd backlight: %d\n", newval&0x1000);
|
||||||
if (changes & 0x2000) printf("PRT enable uart0: %d\n", newval&0x2000);
|
if (changes & 0x2000) printf("PRT enable uart0: %d\n", newval&0x2000);
|
||||||
if (changes & 0x4000) printf("PRT dictaphone: %d\n", newval&0x4000);
|
if (changes & 0x4000) printf("PRT dictaphone: %d\n", newval&0x4000);
|
||||||
if (changes & 0x10000) printf("PRT EECS: %d\n", newval&0x10000);
|
// PROM read process makes this super spammy in stdout
|
||||||
if (changes & 0x20000) printf("PRT EECLK: %d\n", newval&0x20000);
|
// 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 & 0x40000) printf("PRT contrast0: %d\n", newval&0x40000);
|
||||||
if (changes & 0x80000) printf("PRT contrast1: %d\n", newval&0x80000);
|
if (changes & 0x80000) printf("PRT contrast1: %d\n", newval&0x80000);
|
||||||
if (changes & 0x100000) printf("PRT contrast2: %d\n", newval&0x100000);
|
if (changes & 0x100000) printf("PRT contrast2: %d\n", newval&0x100000);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
#include <stdint.h>
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
const int CLOCK_SPEED = 0x9000*1000;
|
const int CLOCK_SPEED = 0x9000*1000;
|
||||||
const int TICK_INTERVAL = CLOCK_SPEED / 64;
|
const int TICK_INTERVAL = CLOCK_SPEED / 64;
|
||||||
|
|
Loading…
Reference in New Issue