add skeleton support for the Etna chip, including PROM support

This commit is contained in:
Ash Wolf 2019-12-20 23:46:22 +00:00
parent 3851109a0a
commit 0ed437c6d3
8 changed files with 194 additions and 6 deletions

View File

@ -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

View File

@ -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 \

View File

@ -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

View File

@ -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;

145
WindCore/etna.cpp Normal file
View File

@ -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;
}
}

24
WindCore/etna.h Normal file
View File

@ -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
};

View File

@ -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);

View File

@ -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;