From d2b2594d33f7c0a052093e62fbae357c614518cc Mon Sep 17 00:00:00 2001 From: George Wright Date: Wed, 24 Jun 2026 18:43:35 -0700 Subject: [PATCH] series7: add stub sa-1100 CPU core --- CMakeLists.txt | 2 + WindCore/series7/sa1100.cpp | 93 +++++ WindCore/series7/sa1100.h | 97 +++++ WindCore/series7/sa1100_memory.cpp | 650 +++++++++++++++++++++++++++++ WindCore/series7/series7_defs.h | 15 + 5 files changed, 857 insertions(+) create mode 100644 WindCore/series7/sa1100.cpp create mode 100644 WindCore/series7/sa1100.h create mode 100644 WindCore/series7/sa1100_memory.cpp create mode 100644 WindCore/series7/series7_defs.h diff --git a/CMakeLists.txt b/CMakeLists.txt index b2170c1..be00226 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,6 +22,8 @@ set(WIND_CORE_SOURCES WindCore/osaris/clps7600.cpp WindCore/series5mx/etna.cpp WindCore/series5mx/windermere.cpp + WindCore/series7/sa1100.cpp + WindCore/series7/sa1100_memory.cpp ) add_library(WindCore STATIC ${WIND_CORE_SOURCES}) diff --git a/WindCore/series7/sa1100.cpp b/WindCore/series7/sa1100.cpp new file mode 100644 index 0000000..ce3135b --- /dev/null +++ b/WindCore/series7/sa1100.cpp @@ -0,0 +1,93 @@ +#include "sa1100.h" + +#include "common.h" + +#include + +namespace SA1100 { + +Emulator::Emulator() : EmuBase(false) {} + +uint32_t Emulator::getRTC() { return 0; } + +void Emulator::configure() { + if (configured) + return; + + configured = true; + srand(1000); + nextTickAt = Series7::TICK_INTERVAL; + reset(); +} + +uint8_t* Emulator::getROMBuffer() { return ROM; } +size_t Emulator::getROMSize() { return sizeof(ROM); } +void Emulator::loadROM(uint8_t* buffer, size_t size) { + memcpy(ROM, buffer, size); +} + +void Emulator::executeUntil(int64_t cycles) { + if (!configured) + configure(); + + while (!asleep && passedCycles < cycles) { + if (passedCycles >= nextTickAt) { + // increment RTCDIV + if ((pwrsr & 0x3F) == 0x3F) { + pwrsr &= ~0x3F; + } else { + pwrsr++; + } + + nextTickAt += Series7::TICK_INTERVAL; + } + + // what's running? + if (halted) { + // keep the clock moving + // when does the next earliest thing happen? + // this stops us from spinning needlessly + int64_t nextEvent = nextTickAt; + if (cycles < nextEvent) + nextEvent = cycles; + passedCycles = nextEvent; + } else { + if (auto v = virtToPhys(getGPR(15) - 0xC); + v.has_value() && instructionReady()) + debugPC(v.value()); + passedCycles += tick(); + } + } +} + +const char* Emulator::identifyObjectCon(uint32_t ptr) { return NULL; } + +void Emulator::fetchStr(uint32_t str, char* buf) {} + +void Emulator::fetchName(uint32_t obj, char* buf) {} + +void Emulator::fetchProcessFilename(uint32_t obj, char* buf) {} + +void Emulator::debugPC(uint32_t pc) {} + +const char* Emulator::getDeviceName() const { return "Series 7"; } +int Emulator::getDigitiserWidth() const { return 783; } +int Emulator::getDigitiserHeight() const { return 480; } +int Emulator::getLCDOffsetX() const { return 71; } +int Emulator::getLCDOffsetY() const { return 0; } +int Emulator::getLCDWidth() const { return 640; } +int Emulator::getLCDHeight() const { return 480; } + +void Emulator::readLCDIntoBuffer(uint8_t** lines, bool is32BitOutput) const {} + +void Emulator::diffPorts(uint32_t oldval, uint32_t newval) {} + +void Emulator::diffInterrupts(uint16_t oldval, uint16_t newval) {} + +uint32_t Emulator::readKeyboard() {} + +void Emulator::setKeyboardKey(EpocKey key, bool value) {} + +void Emulator::updateTouchInput(int32_t x, int32_t y, bool down) {} + +} // namespace SA1100 diff --git a/WindCore/series7/sa1100.h b/WindCore/series7/sa1100.h new file mode 100644 index 0000000..23d8e10 --- /dev/null +++ b/WindCore/series7/sa1100.h @@ -0,0 +1,97 @@ +#ifndef SA1100_H +#define SA1100_H + +#include "emubase.h" +#include "series7_defs.h" + +namespace SA1100 { + +// Relatively simple implementation of a StrongARM SA-1100 CPU +// +// The following resources are invaluable to determining how the SA-1100 +// operates: +// +// SA-1100 Technical documentation: +// https://courses.cs.washington.edu/courses/cse466/00au/sa1100dev.pdf +// Address space layout: +// https://people.kth.se/~maguire/badge3/memorymap.html +// ARMware emulator: +// https://github.com/Halajohn/ARMware +// bookboot: +// https://web.archive.org/web/20060716045807/http://linux-7110.sourceforge.net/files/People/Klaasjan/netbook/ + +class Emulator : public EmuBase { +public: + // 16MB ROM + uint8_t ROM[0x1000000]; + // 16MB RAM/bank0, mapped at base addr 0xC0000000 + uint8_t MemoryBlockC0[0x1000000]; + // 16MB RAM/bank1, mapped at base addr 0xC8000000 + // On the netBook, the OS.img minus the 256-byte wrapper is copied to this RAM + // See the bookboot README file (linked above) + uint8_t MemoryBlockC8[0x1000000]; + enum { MemoryBlockMask = 0x0FFFFFF }; + +private: + uint32_t pwrsr = 0x00002000; // cold start flag + bool halted = false, asleep = false; + uint32_t getRTC(); + +public: + MaybeU32 readPhysical(uint32_t physAddr, ValueSize valueSize) override; + bool writePhysical(uint32_t value, uint32_t physAddr, + ValueSize valueSize) override; + +private: + uint8_t readPhysical8(uint32_t physAddr); + uint32_t readPhysical32(uint32_t physAddr); + + bool writePhysical8(uint8_t value, uint32_t physAddr); + bool writePhysical32(uint32_t value, uint32_t physAddr); + + uint8_t readPCM8(uint32_t physAddr); + uint32_t readPCM32(uint32_t physAddr); + + uint8_t readSCM8(uint32_t physAddr); + uint32_t readSCM32(uint32_t physAddr); + + bool writePCM8(uint8_t value, uint32_t physAddr); + bool writePCM32(uint32_t value, uint32_t physAddr); + + bool writeSCM8(uint8_t value, uint32_t physAddr); + bool writeSCM32(uint32_t value, uint32_t physAddr); + + bool configured = false; + void configure(); + + const char* identifyObjectCon(uint32_t ptr); + void fetchStr(uint32_t str, char* buf); + void fetchName(uint32_t obj, char* buf); + void fetchProcessFilename(uint32_t obj, char* buf); + void debugPC(uint32_t pc); + void diffPorts(uint32_t oldval, uint32_t newval); + void diffInterrupts(uint16_t oldval, uint16_t newval); + uint32_t readKeyboard(); + +public: + Emulator(); + uint8_t* getROMBuffer() override; + size_t getROMSize() override; + void loadROM(uint8_t* buffer, size_t size) override; + void executeUntil(int64_t cycles) override; + int32_t getClockSpeed() const override { return Series7::CLOCK_SPEED; } + const char* getDeviceName() const override; + int getDigitiserWidth() const override; + int getDigitiserHeight() const override; + int getLCDOffsetX() const override; + int getLCDOffsetY() const override; + int getLCDWidth() const override; + int getLCDHeight() const override; + void readLCDIntoBuffer(uint8_t** lines, bool is32BitOutput) const override; + void setKeyboardKey(EpocKey key, bool value) override; + void updateTouchInput(int32_t x, int32_t y, bool down) override; +}; + +} // namespace SA1100 + +#endif // SA1100_H diff --git a/WindCore/series7/sa1100_memory.cpp b/WindCore/series7/sa1100_memory.cpp new file mode 100644 index 0000000..fbb79da --- /dev/null +++ b/WindCore/series7/sa1100_memory.cpp @@ -0,0 +1,650 @@ +#include "sa1100.h" + +#include "common.h" + +namespace SA1100 { + +MaybeU32 Emulator::readPhysical(uint32_t physAddr, ValueSize valueSize) { + if (valueSize == V8) { + return readPhysical8(physAddr); + } else { + return readPhysical32(physAddr); + } +} + +bool Emulator::writePhysical(uint32_t value, uint32_t physAddr, + ValueSize valueSize) { + uint8_t region = (physAddr >> 24) & 0xFF; + if (valueSize == V8) { + return writePhysical8((uint8_t)value, physAddr); + } else { + return writePhysical32(value, physAddr); + } +} + +uint8_t Emulator::readPCM8(uint32_t physAddr) { + // Peripheral Control Modules + return 0x0; +} + +bool Emulator::writePCM8(uint8_t value, uint32_t physAddr) { return true; } + +uint32_t Emulator::readPCM32(uint32_t physAddr) { + // Peripheral Control Modules + return 0x0; +} + +bool Emulator::writePCM32(uint32_t value, uint32_t physAddr) { return true; } + +uint8_t Emulator::readSCM8(uint32_t physAddr) { + // System Control Modules + return 0x0; +} + +bool Emulator::writeSCM8(uint8_t value, uint32_t physAddr) { return true; } + +uint32_t Emulator::readSCM32(uint32_t physAddr) { + // System Control Modules + return 0x0; +} + +bool Emulator::writeSCM32(uint32_t value, uint32_t physAddr) { return true; } + +uint8_t Emulator::readPhysical8(uint32_t physAddr) { + uint8_t region = (physAddr >> 24) & 0xFF; + + switch (region) { + case 0x00: + return ROM[physAddr & 0xFFFFFF]; + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + case 0x29: + case 0x2A: + case 0x2B: + case 0x2C: + case 0x2D: + case 0x2E: + case 0x2F: + // PCMCIA slot 0 + return 0x0; + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + case 0x38: + case 0x39: + case 0x3A: + case 0x3B: + case 0x3C: + case 0x3D: + case 0x3E: + case 0x3F: + // PCMCIA slot 1 + return 0x0; + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4A: + case 0x4B: + case 0x4C: + case 0x4D: + case 0x4E: + case 0x4F: + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: + case 0x58: + case 0x59: + case 0x5A: + case 0x5B: + case 0x5C: + case 0x5D: + case 0x5E: + case 0x5F: + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6A: + case 0x6B: + case 0x6C: + case 0x6D: + case 0x6E: + case 0x6F: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + case 0x78: + case 0x79: + case 0x7A: + case 0x7B: + case 0x7C: + case 0x7D: + case 0x7E: + case 0x7F: + // Reserved, causes a data abort exception + return 0x0; + case 0x80: + // Peripheral Control Modules + return readPCM8(physAddr); + case 0x90: + // System Control Modules + return readSCM8(physAddr); + case 0xA0: + // Memory Control Registers + // Not valid in byte access + return 0x0; + case 0xB0: + // LCD/DMA Control Registers + return 0x0; + case 0xC0: + case 0xC1: + case 0xC2: + case 0xC3: + case 0xC4: + case 0xC5: + case 0xC6: + case 0xC7: + return MemoryBlockC0[physAddr & MemoryBlockMask]; + case 0xC8: + case 0xC9: + case 0xCA: + case 0xCB: + case 0xCC: + case 0xCD: + case 0xCE: + case 0xCF: + return MemoryBlockC8[physAddr & MemoryBlockMask]; + default: + return 0x0; // just throw accesses to unmapped RAM away + } +} + +bool Emulator::writePhysical8(uint8_t value, uint32_t physAddr) { + uint8_t region = (physAddr >> 24) & 0xFF; + + switch (region) { + case 0x00: + // Read-only + return false; + break; + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + case 0x29: + case 0x2A: + case 0x2B: + case 0x2C: + case 0x2D: + case 0x2E: + case 0x2F: + // PCMCIA slot 0 + return true; + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + case 0x38: + case 0x39: + case 0x3A: + case 0x3B: + case 0x3C: + case 0x3D: + case 0x3E: + case 0x3F: + // PCMCIA slot 1 + return true; + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4A: + case 0x4B: + case 0x4C: + case 0x4D: + case 0x4E: + case 0x4F: + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: + case 0x58: + case 0x59: + case 0x5A: + case 0x5B: + case 0x5C: + case 0x5D: + case 0x5E: + case 0x5F: + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6A: + case 0x6B: + case 0x6C: + case 0x6D: + case 0x6E: + case 0x6F: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + case 0x78: + case 0x79: + case 0x7A: + case 0x7B: + case 0x7C: + case 0x7D: + case 0x7E: + case 0x7F: + // Reserved, causes a data abort exception + return false; + case 0x80: + // Peripheral Control Modules + return writePCM8(value, physAddr); + case 0x90: + // System Control Modules + return writeSCM8(value, physAddr); + case 0xA0: + // Memory Control Registers + // Invalid to write to with bytes + return false; + case 0xB0: + // LCD/DMA Control Registers + return true; + case 0xC0: + case 0xC1: + case 0xC2: + case 0xC3: + case 0xC4: + case 0xC5: + case 0xC6: + case 0xC7: + MemoryBlockC0[physAddr & MemoryBlockMask] = value; + return true; + case 0xC8: + case 0xC9: + case 0xCA: + case 0xCB: + case 0xCC: + case 0xCD: + case 0xCE: + case 0xCF: + MemoryBlockC8[physAddr & MemoryBlockMask] = value; + return true; + default: + // just throw accesses to unmapped RAM away + return true; + } +} + +uint32_t Emulator::readPhysical32(uint32_t physAddr) { + uint8_t region = (physAddr >> 24) & 0xFF; + uint32_t result; + + switch (region) { + case 0x00: + LOAD_32LE(result, physAddr & 0xFFFFFF, ROM); + break; + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + case 0x29: + case 0x2A: + case 0x2B: + case 0x2C: + case 0x2D: + case 0x2E: + case 0x2F: + // PCMCIA slot 0 + result = 0x0; + break; + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + case 0x38: + case 0x39: + case 0x3A: + case 0x3B: + case 0x3C: + case 0x3D: + case 0x3E: + case 0x3F: + // PCMCIA slot 1 + result = 0x0; + break; + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4A: + case 0x4B: + case 0x4C: + case 0x4D: + case 0x4E: + case 0x4F: + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: + case 0x58: + case 0x59: + case 0x5A: + case 0x5B: + case 0x5C: + case 0x5D: + case 0x5E: + case 0x5F: + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6A: + case 0x6B: + case 0x6C: + case 0x6D: + case 0x6E: + case 0x6F: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + case 0x78: + case 0x79: + case 0x7A: + case 0x7B: + case 0x7C: + case 0x7D: + case 0x7E: + case 0x7F: + // Reserved, causes a data abort exception + result = 0x0; + break; + case 0x80: + // Peripheral Control Modules + result = readPCM32(physAddr); + break; + case 0x90: + // System Control Modules + result = readSCM32(physAddr); + break; + case 0xA0: + // Memory Control Registers + result = 0x0; + break; + case 0xB0: + // LCD/DMA Control Registers + result = 0x0; + break; + case 0xC0: + case 0xC1: + case 0xC2: + case 0xC3: + case 0xC4: + case 0xC5: + case 0xC6: + case 0xC7: + LOAD_32LE(result, physAddr & MemoryBlockMask, MemoryBlockC0); + break; + case 0xC8: + case 0xC9: + case 0xCA: + case 0xCB: + case 0xCC: + case 0xCD: + case 0xCE: + case 0xCF: + LOAD_32LE(result, physAddr & MemoryBlockMask, MemoryBlockC8); + break; + default: + return 0x0; // just throw accesses to unmapped RAM away + } + + return result; +} + +bool Emulator::writePhysical32(uint32_t value, uint32_t physAddr) { + uint8_t region = (physAddr >> 24) & 0xFF; + + switch (region) { + case 0x00: + // Read-only + return false; + break; + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + case 0x29: + case 0x2A: + case 0x2B: + case 0x2C: + case 0x2D: + case 0x2E: + case 0x2F: + // PCMCIA slot 0 + return true; + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + case 0x38: + case 0x39: + case 0x3A: + case 0x3B: + case 0x3C: + case 0x3D: + case 0x3E: + case 0x3F: + // PCMCIA slot 1 + return true; + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4A: + case 0x4B: + case 0x4C: + case 0x4D: + case 0x4E: + case 0x4F: + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: + case 0x58: + case 0x59: + case 0x5A: + case 0x5B: + case 0x5C: + case 0x5D: + case 0x5E: + case 0x5F: + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6A: + case 0x6B: + case 0x6C: + case 0x6D: + case 0x6E: + case 0x6F: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + case 0x78: + case 0x79: + case 0x7A: + case 0x7B: + case 0x7C: + case 0x7D: + case 0x7E: + case 0x7F: + // Reserved, causes a data abort exception + return false; + case 0x80: + // Peripheral Control Modules + return writePCM32(value, physAddr); + case 0x90: + // System Control Modules + return writeSCM32(value, physAddr); + case 0xA0: + // Memory Control Registers + return false; + case 0xB0: + // LCD/DMA Control Registers + return true; + case 0xC0: + case 0xC1: + case 0xC2: + case 0xC3: + case 0xC4: + case 0xC5: + case 0xC6: + case 0xC7: + STORE_32LE(value, physAddr & MemoryBlockMask, MemoryBlockC0); + return true; + case 0xC8: + case 0xC9: + case 0xCA: + case 0xCB: + case 0xCC: + case 0xCD: + case 0xCE: + case 0xCF: + STORE_32LE(value, physAddr & MemoryBlockMask, MemoryBlockC8); + return true; + default: + // just throw accesses to unmapped RAM away + return true; + } +} + +} // namespace SA1100 diff --git a/WindCore/series7/series7_defs.h b/WindCore/series7/series7_defs.h new file mode 100644 index 0000000..a32233c --- /dev/null +++ b/WindCore/series7/series7_defs.h @@ -0,0 +1,15 @@ +#ifndef PSION7_DEFS_H +#define PSION7_DEFS_H + +namespace Series7 { + +enum { + CLOCK_SPEED = 132710000, // 132.71 MHz + TICKS_3_6864_MHZ = CLOCK_SPEED / 3686400, + TICKS_1_HZ = CLOCK_SPEED / 1, + TICK_INTERVAL = CLOCK_SPEED / 64 +}; + +} // namespace Series7 + +#endif // PSION7_DEFS_H -- 2.45.2