Compare commits

...
63 Commits
Author SHA1 Message Date
Near 01184dc6bd Update to ares v115 release.
[No official changelog available for this version. -Ed.]
2020-07-24 21:18:00 +00:00
Near 17f1f24dc2 Update to ares v114r25 release.
- PlayStation: implemented all GTE instructions (though certainly with
    many serious bugs remaining)
  - ares: improved Debug class notifications
2020-07-23 03:48:00 +00:00
Near e2a5547ae7 Update to ares v114r24 release.
Ridge Racer now boots and gets in-game, but likely GTE errors prevent it
from being very playable yet.

  - PlayStation: added DMA IRQ support
  - PlayStation: added more GTE instructions
  - ares: started on a new Debug class for notifications (unimplemented
    features, etc.)
2020-07-19 19:53:00 +00:00
Near 6447903966 Update to ares v114r23 release.
- PlayStation: improved timer support
  - PlayStation: added more GPU GP0 rendering commands
  - lucia: split the emulator list and emulator options into separate
    settings panels
  - lucia: added fast boot option for PlayStation and Neo Geo Pocket
    cores
2020-07-17 20:14:00 +00:00
Near f97a1f954e Update to ares v114r22 release.
- PlayStation: fixed masking bug in LWL and LWR instructions
  - PlayStation: improved timer support
  - PlayStation: added EXE side-loading support
  - PlayStation: added fast boot support
  - PlayStation: added libc putchar logging support
2020-07-17 00:07:00 +00:00
Near 5c6ef3eb4d Update to ares v114r21 release.
- PlayStation: improved GTE support
  - PlayStation: improved CD-ROM support
2020-07-15 19:50:00 +00:00
Near ce2abced51 Update to ares v114r20 release.
- PlayStation: implemented CD-DA support
  - PlayStation: implemented CD data seeking and reading support
  - PlayStation: implemented CD-ROM DMA support
2020-07-12 15:18:00 +00:00
Near 50b62604a8 Update to ares v114r19 release.
- PlayStation: started implementing controller support
  - PlayStation: improved texture semi-transparency support
  - PlayStation: fixed a bug in render clipping
2020-07-10 18:43:00 +00:00
Near fb855d62af Update to ares v114r18 release.
- PlayStation: implemented free-running timer mode
  - PlayStation: implemented draw rectangle commands
2020-07-09 19:43:00 +00:00
Near 5f035496bb Update to ares v114r17 release.
- PlayStation: started implementing timers
  - MSX: added more cartridge mappers (Konami SCC, ASCII 8kbit, ASCII
    16kbit, etc)
2020-07-08 11:59:00 +00:00
Near bfdb287505 Update to ares v114r16 release.
- Super Famicom: mosaic fix wasn't exactly right before; now it should
    be
  - PlayStation: started implementing CD interface
  - PlayStation: started implementing GTE coprocessor
2020-07-04 19:46:00 +00:00
Near a37e5ba378 Update to ares v114r15 release.
- PlayStation: added reverb support
2020-07-03 14:37:00 +00:00
Near b12ac07464 Update to ares v114r14 release.
- Super Famicom: fixed bugs in hires + interlaced mosaic rendering
  - PlayStation: started implementing the SPU (no DMA, CD-DA, reverb, or
    sweep support yet)
2020-07-02 11:18:00 +00:00
Near 46bba9b798 Update to ares v114r13 release.
- PlayStation: added I/O handling for the SPU (no emulation yet)
  - PlayStation: added gaussian interpolation table generation
  - Super Famicom: replaced hard-coded constants with gaussian
    interpolation table generation
2020-07-01 08:09:00 +00:00
Near b1b4452d23 Update to ares v114r12 release.
- PlayStation: added dithering, shaded texture, and partial semi-
    transparency rendering support
2020-06-29 12:31:00 +00:00
Near 60bddfc33f Update to ares v114r11 release.
- PlayStation: added gouraud shaded and textured triangle rendering
2020-06-28 06:47:00 +00:00
Near c2a9cc9fbc Update to ares v114r10 release.
- PlayStation: started on GPU renderer (solid triangles only so far)
2020-06-27 08:02:00 +00:00
Near f376ce8499 Update to ares v114r09 release.
- PlayStation: improved DMA support somewhat
  - PlayStation: implemented some GP0 and GP1 commands
2020-06-25 19:31:00 +00:00
Near 0ed46cc2b1 Update to ares v114r08 release.
- PlayStation: fixed a bug in the SLLV instruction
  - PlayStation: implemented interrupts
2020-06-24 09:25:00 +00:00
Near d9ea05c4c4 Update to ares v114r07 release.
- PlayStation: started implementing the CPU SCC (missing some
    registers still)
  - PlayStation: implemented basic DMA support
2020-06-22 21:49:00 +00:00
Near a4caa85c0b Update to ares v114r06 release.
- PlayStation: implemented all core CPU instructions (no SCC or GTE
    instructions yet)
2020-06-19 00:12:00 +00:00
Near 8b0359aa78 Update to ares v114r05 release.
I've started on a Sony PlayStation emulation core with this WIP. The
goal is to work on the MIPS CPU core while I am still well-versed with
the MIPS ISA. The Nintendo 64 core will remain my primary focus,
however.

  - PlayStation: implemented initial driver skeleton (no emulation code
    yet)
  - lucia: added option to group the load menu options into submenus by
    manufacturer
  - lucia: connected Cocoa-specific application menu options to their
    appropriate callbacks
  - hiro/Cocoa: fixed compilation issues for HexEdit (the widget is not
    implemented, however)
  - hiro/Cocoa: fixed a subpixel precision rendering issue causing
    blurry fonts [LIJI]
2020-06-18 15:02:00 +00:00
Near ba05897d75 Update to ares v114r04 release.
This WIP adds non-SIMD implementations of all RSP vector unit
functionality. This allows compiling without SSE, but was mainly added
for debugging SSE issues. It's also useful for documenting in code what
the SSE instructions are doing in C++ code.

  - Nintendo 64: added non-SSE implementation of RSP vector unit
2020-06-16 03:45:00 +00:00
Near 6cd757dae8 Update to ares v114r03 release.
- nall: added bump_allocator class
  - Nintendo 64: restructured dynamic recompilers to be cleaner
2020-06-13 20:40:00 +00:00
Near fb9859ad2f Update to ares v114r02 release.
The new recompilers increase the framerate in Namco Museum from 60fps to
160fps, and in Zelda: Ocarina of Time from 55fps to 135fps (presuming no
RDP renderer.)

  - Nintendo 64: completed basic CPU dynamic recompiler
  - Nintendo 64: completed basic RSP dynamic recompiler
2020-06-12 17:27:00 +00:00
Near f518bcb374 Update to ares v114r01 release.
- Nintendo 64: started work on CPU dynamic recompiler
2020-06-11 21:07:00 +00:00
Near 20469c66e9 Update to ares v114 release.
First public release of ares. Bumped the version number early for the
milestone.

  - SFC: added missing SA1 board definition (for boards without BWRAM)
2020-06-07 14:38:00 +00:00
Near 3559a7659b Update to ares v113r05 release.
- N64: added FPU support for ordered and unordered comparisons
  - N64: reduced red tape in the CPU and RSP instruction decoder switch
    tables
2020-06-01 13:57:00 +00:00
Near 1f45125d45 Update to ares v113r04 release.
Initial required work to attempt implementing a cached interpreter ...

  - N64: moved all CPU and RSP instruction decoding out of opcode
    handlers
2020-05-30 17:32:00 +00:00
Near 03f7fe2b25 Update to ares v113r03 release.
- N64: redesigned the bus handler
  - N64: execute eight CPU instructions per synchronization event
  - N64: range test the CPU Compare register
  - N64: decode more RDP display list commands
2020-05-28 14:24:00 +00:00
Near a05937bfa4 Update to ares v113r02 release.
- ruby: added new FreeBSD UHID driver (to support hotplugging and the
    Xbox 360 D-pad properly)
  - N64: started on Nintendo 64DD support
2020-05-27 11:01:00 +00:00
Near d9d208d615 Update to ares v113r01 release.
- N64: better input mapping for C-buttons
  - lucia: improved input mapping code
  - ruby: disabled FreeBSD SDL joypad hotplugging hack (causes input
    issues in games)
2020-05-26 02:58:00 +00:00
Near 842ea44b90 Update to ares v113 release.
- N64: improved TLB emulation (Super Mario 64 is now fully playable)
  - lucia: implement analog axis support
2020-05-24 05:01:00 +00:00
Near e67dfac166 Update to ares v112r30 release.
Many N64 games are now playable.

  - N64: VSUB should clear VCO
  - N64: corrected VRCP(L) and VRSQ(L) instructions
  - N64: many other smaller various fixes
2020-05-21 15:17:00 +00:00
Near a5d2ee5e5f Update to ares v112r29 release.
- N64: added missing BLTZAL, BGEZAL instructions
  - N64: added missing SLT, SLTI instructions (they were erroneously
    aliased to SLTU, SLTIU)
  - N64: mask low two bits of PC when writing to SP_PC_REG
  - N64: refactored RSP DMA; only mask the low two bits SP_PBUS_ADDRESS
  - N64: RSP DMA seemingly updates SP_PBUS_ADDRESS and SP_DRAM_ADDRESS
2020-05-20 05:13:00 +00:00
Near bef70d2550 Update to ares v112r28 release.
- N64: implemented missing RSP vector load/store instructions
  - N64: fixed RSP MFC2 and MTC2 instructions
  - N64: rewrote and simplified CPU TLB handling
2020-05-20 00:51:00 +00:00
Near f656e317c8 Update to ares v112r27 release.
- N64: improved CPU disassembler
  - N64: added RSP disassembler
  - N64: corrected reads from SP_SEMAPHORE
  - N64: improved PIF joypad polling functionality
  - N64: added I/O logging to trace logger
2020-05-18 23:35:00 +00:00
Near 494b1cb0ba Update to ares v112r26 release.
- N64: CPU and RSP instruction improvements
  - N64: improved CPU and RSP branch handling
2020-05-18 03:37:00 +00:00
Near be25bb110c Update to ares v112r25 release.
- N64: added skeleton I/O reading and writing for DPS registers
  - N64: added skeleton RDP renderer
2020-05-17 06:27:00 +00:00
Near 1f59ef7457 Update to ares v112r24 release.
- N64: added PIF boot ROM emulation
  - N64: detected NUS-CIC revision based on ROM bootcode checksum
  - N64: added skeleton I/O reading and writing for all VI and PI
    registers
2020-05-17 04:15:00 +00:00
Near 74e6d389a6 Update to ares v112r23 release.
This WIP separates the RSP SCC registers so that RDP rendering commands
are not processed inside of the RSP. The register interface is shared
between the SCC and the CPU bus, so there is no technically correct
location for these handlers.

  - N64: moved RSP SCC registers to outer RSP and RDP I/O handlers
2020-05-16 00:53:00 +00:00
Near 9449b78675 Update to ares v112r22 release.
- N64: added RDRAM and interrupt sources to debugger
  - N64: emulated basic RDP commands to detect sync commands for DP IRQ
    generation
2020-05-15 01:40:00 +00:00
Near 388f555304 Update to ares v112r21 release.
- N64: emulated FPU FR=0 mode; fixes intro sound in Namco Museum
2020-05-14 22:51:00 +00:00
Near 4cf677063c Update to ares v112r20 release.
- N64: improved AI FIFO emulation
  - N64: emulated VT[e] addressing in RSP vector unit
2020-05-13 14:59:00 +00:00
Near 890b56f84d Update to ares v112r19 release.
- N64: improved TLB emulation and made it much faster (but limited to
    32-bit mode without ASIDs)
2020-05-13 02:23:00 +00:00
Near ddd3a439ff Update to ares v112r18 release.
- N64: improved audio playback (AI_LENGTH &=~ 7, not (AI_LENGTH | 7) +
    1)
  - N64: improved exception handling (ERL bit isn't set on standard
    exceptions)
  - N64: added very incomplete, inital TLB emulation
  - N64: various improvements to the disasssembler (including FPU
    instruction support)
  - N64: improved SI emulation (Namco Museum input now works)
2020-05-12 10:13:00 +00:00
Near 318beb509f Update to ares v112r17 release.
This WIP boots the first official Nintendo 64 title, Namco Museum. It
remains to be playable, however.

  - N64: improved interrupt handling
  - N64: implemented basic state machine handling for PI serial data
    transfers
  - N64: resume previously executed instruction after ERET for non-
    interrupt exceptions
  - N64: various other improvements
2020-05-11 06:56:00 +00:00
Near 6f6e2ea260 Update to ares v112r16 release.
- N64: skip delay slot when executing ERET instruction
  - N64: improve IRQ handling (always update CPU SCC Cause bits when
    toggling MI IRQs)
  - N64: audio emulation improvements
2020-05-10 19:57:00 +00:00
Near 67cf63b532 Update to ares v112r15 release.
- N64: implemented SI DMA
  - N64: implemented basic AI audio emulation
  - N64: various improvements and bugfixes
2020-05-10 04:26:00 +00:00
Near a427fe2a71 Update to ares v112r14 release.
- N64: implemented CPU SCC ERET instruction
  - N64: improved interrupt support slightly
  - N64: rewrote CPU disassembler to be more sophisticated
2020-05-09 00:52:00 +00:00
Near e6c42199aa Update to ares v112r13 release.
- N64: RSP vector instruction fixes
  - N64: started on initial exception and interrupt support
2020-05-07 22:51:00 +00:00
Near b224caf4bd Update to ares v112r12 release.
- N64: implemented all official RSP vector instructions
  - N64: implemented reserved RSP vector instructions VMULQ, VMACQ,
    VRNDP, VRNDN
  - N64: implemented RSP instructions MFC2 and MFC2
2020-05-07 07:49:00 +00:00
Near bd565eb982 Update to ares v112r11 release.
- N64: core now requires SSE 4.1 support (for vector instructions)
  - N64: implemented VABS, VADD, VADDC, VAND, VCL, VCH, VMOV, VNAND,
    VNOP, VSAR, VSUB, VSUBC instructions
2020-05-05 22:51:00 +00:00
Near f149b5d2a5 Update to ares v112r10 release.
- N64: removed libco completely, using direct cycle counters now
    instead
  - N64: emulated RSP DMA between IRAM/DRAM and RDRAM
  - N64: emulated RSP core R4000 instructions
  - N64: started working on RSP COP2 vector instructions
2020-05-05 03:30:00 +00:00
Near 32d8abd372 Update to ares v112r09 release.
Emulation is now far enough along to complete the boot loading code and
begin executing game code. The "hello world" CPU rendering demo now runs
correctly in both 15bpp and 24bpp output modes.

  - N64: implemented skeleton interfaces (MI, VI, AI, PI, RI, SI)
  - N64: implemented PI DMA
  - N64: added basic frame buffer rendering (320x240 only)
2020-05-03 17:30:00 +00:00
Near 5ef188c876 Update to ares v112r08 release.
- N64: correct JAL and JALR instructions
2020-05-02 17:41:00 +00:00
Near 590773508e Update to ares v112r07 release.
- N64: redesigned CPU register casting (use a union class instead of
    #defines)
  - N64: completed CPU coprocessor 1 FPU
2020-05-02 15:11:00 +00:00
Near 2f24221be9 Update to ares v112r06 release.
- N64: much faster endian decoding
  - N64: implemented CPU coprocessor 0 skeleton (excluding MMU)
  - N64: implemented CPU coprocessor 1 skeleton (excluding FPU)
2020-05-01 21:17:00 +00:00
Near e7b120f2e5 Update to ares v112r05 release.
- N64: implemented bus skeleton
  - N64: simulated the PIF boot ROM (partially)
2020-04-30 18:58:00 +00:00
Near 4a1084179b Update to ares v112r04 release.
- N64: merged the VR4300 core into the CPU class
  - mia: added N64 deinterleaving support
2020-04-30 13:30:00 +00:00
Near e9bd3dda68 Update to ares v112r03 release.
- VR4300: implemented most of the general instructions
2020-04-29 19:19:00 +00:00
Near c1f9897a03 Update to ares v112r02 release.
- VR4300: implemented register-immediate instructions
  - VR4300: implemented half of the general instructions
  - ares: added Port::supported() -> vector<string> API
  - nall: improve checks in directory::exists() and directory::create()
2020-04-29 15:52:00 +00:00
Near 291a0ea8de Update to ares v112r01 release.
- VR4300: implemented special instructions
2020-04-28 19:20:00 +00:00
333 changed files with 21774 additions and 783 deletions

No files matched your search

+4
View File
@@ -46,6 +46,10 @@ ifneq ($(filter $(cores),md),)
include $(ares.path)/md/GNUmakefile
endif
ifneq ($(filter $(cores),ps1),)
include $(ares.path)/ps1/GNUmakefile
endif
ifneq ($(filter $(cores),pce),)
include $(ares.path)/pce/GNUmakefile
endif
Binary file not shown.
Binary file not shown.
Binary file not shown.
Whitespace-only changes.
+12 -2
View File
@@ -1,5 +1,5 @@
database
revision: 2020-01-21
revision: 2020-06-07
//Boards (Production)
@@ -587,7 +587,7 @@ board: SHVC-YJ0N-01
//Boards (Generic)
database
revision: 2020-01-01
revision: 2020-06-07
board: ARM-LOROM-RAM
memory type=ROM content=Program
@@ -887,6 +887,16 @@ board: OBC1-LOROM-RAM
map address=70-71,f0-f1:6000-7fff,e000-ffff mask=0xe000
memory type=RAM content=Save
board: SA1
processor architecture=W65C816S
map address=00-3f,80-bf:2200-23ff
mcu
map address=00-3f,80-bf:8000-ffff mask=0x408000
map address=c0-ff:0000-ffff
memory type=ROM content=Program
memory type=RAM content=Internal
map address=00-3f,80-bf:3000-37ff size=0x800
board: SA1-RAM
processor architecture=W65C816S
map address=00-3f,80-bf:2200-23ff
+1
View File
@@ -1,4 +1,5 @@
#include <ares/ares.hpp>
#include <ares/debug/debug.cpp>
#include <ares/node/node.cpp>
#include <ares/resource/resource.cpp>
+3
View File
@@ -6,6 +6,7 @@
#include <nall/adaptive-array.hpp>
#include <nall/any.hpp>
#include <nall/array.hpp>
#include <nall/bump-allocator.hpp>
#include <nall/chrono.hpp>
#include <nall/directory.hpp>
#include <nall/dl.hpp>
@@ -17,6 +18,7 @@
#include <nall/set.hpp>
#include <nall/shared-pointer.hpp>
#include <nall/string.hpp>
#include <nall/terminal.hpp>
#include <nall/traits.hpp>
#include <nall/unique-pointer.hpp>
#include <nall/variant.hpp>
@@ -58,6 +60,7 @@ namespace ares {
#include <ares/information.hpp>
#include <ares/types.hpp>
#include <ares/random.hpp>
#include <ares/debug/debug.hpp>
#include <ares/node/node.hpp>
#include <ares/platform.hpp>
#include <ares/interface.hpp>
+17
View File
@@ -0,0 +1,17 @@
namespace ares {
Debug _debug;
auto Debug::reset() -> void {
_unimplementedNotices.reset();
}
auto Debug::_unimplemented(const string& text) -> void {
//only warn about missing unimplemented functionality once per type
if(_unimplementedNotices.find(text)) return;
_unimplementedNotices.append(text);
print(terminal::color::magenta("[unimplemented] "), text, "\n");
}
}
+22
View File
@@ -0,0 +1,22 @@
namespace ares {
struct Debug {
static constexpr bool enabled = true;
auto reset() -> void;
template<typename... P> auto unimplemented(P&&... p) -> void {
return _unimplemented({forward<P>(p)...});
}
private:
auto _unimplemented(const string&) -> void;
vector<string> _unimplementedNotices;
};
extern Debug _debug;
}
#define debug(function, ...) if constexpr(ares::_debug.enabled) ares::_debug.function(__VA_ARGS__)
+8 -5
View File
@@ -1,11 +1,14 @@
#pragma once
namespace ares {
static const string Name = "ares";
static const string Version = "112";
static const string License = "GPLv3+";
static const string Website = "https://ares.dev";
static const string Name = "ares";
static const string Version = "115";
static const string Copyright = "ares team";
static const string License = "ISC";
static const string LicenseURI = "https://opensource.org/licenses/ISC";
static const string Website = "ares.dev";
static const string WebsiteURI = "https://ares.dev";
//incremented only when serialization format changes
static const string SerializerVersion = "112";
static const string SerializerVersion = "115";
}
+3
View File
@@ -5,6 +5,7 @@ struct Port : Object {
auto type() const -> string { return _type; }
auto family() const -> string { return _family; }
auto hotSwappable() const -> bool { return _hotSwappable; }
auto supported() const -> vector<string> { return _supported; }
auto setAllocate(function<Node::Peripheral (string)> allocate) -> void { _allocate = allocate; }
auto setConnect(function<void ()> connect) -> void { _connect = connect; }
@@ -12,6 +13,7 @@ struct Port : Object {
auto setType(string type) -> void { _type = type; }
auto setFamily(string family) -> void { _family = family; }
auto setHotSwappable(bool hotSwappable) -> void { _hotSwappable = hotSwappable; }
auto setSupported(vector<string> supported) -> void { _supported = supported; }
auto connected() -> Node::Peripheral {
return find<Node::Peripheral>(0);
@@ -68,4 +70,5 @@ protected:
string _type;
string _family;
bool _hotSwappable = false;
vector<string> _supported;
};
+15
View File
@@ -1,3 +1,18 @@
using i8 = int8_t;
using i16 = int16_t;
using i32 = int32_t;
using i64 = int64_t;
using i128 = int128_t;
using u8 = uint8_t;
using u16 = uint16_t;
using u32 = uint32_t;
using u64 = uint64_t;
using u128 = uint128_t;
using f32 = float32_t;
using f64 = float64_t;
using int1 = nall::Integer< 1>;
using int2 = nall::Integer< 2>;
using int3 = nall::Integer< 3>;
-2
View File
@@ -21,7 +21,6 @@ ares.objects += $(if $(findstring spc700,$(ares.components)),ares-processor-spc7
ares.objects += $(if $(findstring tlcs900h,$(ares.components)),ares-processor-tlcs900h)
ares.objects += $(if $(findstring upd96050,$(ares.components)),ares-processor-upd96050)
ares.objects += $(if $(findstring v30mz,$(ares.components)),ares-processor-v30mz)
ares.objects += $(if $(findstring vr4300,$(ares.components)),ares-processor-vr4300)
ares.objects += $(if $(findstring wdc65816,$(ares.components)),ares-processor-wdc65816)
ares.objects += $(if $(findstring z80,$(ares.components)),ares-processor-z80)
@@ -49,7 +48,6 @@ $(object.path)/ares-processor-spc700.o: $(ares.path)/component/processor/spc70
$(object.path)/ares-processor-tlcs900h.o: $(ares.path)/component/processor/tlcs900h/tlcs900h.cpp
$(object.path)/ares-processor-upd96050.o: $(ares.path)/component/processor/upd96050/upd96050.cpp
$(object.path)/ares-processor-v30mz.o: $(ares.path)/component/processor/v30mz/v30mz.cpp
$(object.path)/ares-processor-vr4300.o: $(ares.path)/component/processor/vr4300/vr4300.cpp
$(object.path)/ares-processor-wdc65816.o: $(ares.path)/component/processor/wdc65816/wdc65816.cpp
$(object.path)/ares-processor-z80.o: $(ares.path)/component/processor/z80/z80.cpp
@@ -237,7 +237,7 @@ auto SM83::instruction() -> void {
op(0xee, XOR_Direct_Data, A)
op(0xef, RST_Implied, 0x28)
op(0xf0, LDH_Direct_Address, A)
op(0xf1, POP_Direct, AF)
op(0xf1, POP_Direct_AF, AF)
op(0xf2, LDH_Direct_Indirect, A, C)
op(0xf3, DI)
op(0xf5, PUSH_Direct, AF)
@@ -271,6 +271,10 @@ auto SM83::instructionPOP_Direct(uint16& data) -> void {
data = pop();
}
auto SM83::instructionPOP_Direct_AF(uint16& data) -> void {
data = pop() & ~15; //flag bits 0-3 are forced to zero
}
auto SM83::instructionPUSH_Direct(uint16& data) -> void {
idle();
push(data);
+1 -1
View File
@@ -3,7 +3,7 @@
namespace ares {
#include "registers.cpp"
#include "registers.hpp"
#include "memory.cpp"
#include "algorithms.cpp"
#include "instruction.cpp"
+1
View File
@@ -115,6 +115,7 @@ struct SM83 {
auto instructionOR_Direct_Direct(uint8&, uint8&) -> void;
auto instructionOR_Direct_Indirect(uint8&, uint16&) -> void;
auto instructionPOP_Direct(uint16&) -> void;
auto instructionPOP_Direct_AF(uint16&) -> void;
auto instructionPUSH_Direct(uint16&) -> void;
auto instructionRES_Index_Direct(uint3, uint8&) -> void;
auto instructionRES_Index_Indirect(uint3, uint16&) -> void;
@@ -1,2 +0,0 @@
auto VR4300::serialize(serializer& s) -> void {
}
@@ -1,11 +0,0 @@
#include <ares/ares.hpp>
#include "vr4300.hpp"
namespace ares {
#include "serialization.cpp"
auto VR4300::power() -> void {
}
}
@@ -1,15 +0,0 @@
#pragma once
//NEC VR4300
namespace ares {
struct VR4300 {
//vr4300.cpp
auto power() -> void;
//serialization.cpp
auto serialize(serializer&) -> void;
};
}
+1 -1
View File
@@ -4,7 +4,7 @@
namespace ares {
#include "disassembler.cpp"
#include "registers.cpp"
#include "registers.hpp"
#include "memory.cpp"
#include "instruction.cpp"
#include "algorithms.cpp"
+1
View File
@@ -10,6 +10,7 @@ auto ControllerPort::load(Node::Object parent) -> void {
port->setType("Controller");
port->setHotSwappable(true);
port->setAllocate([&](auto name) { return allocate(name); });
port->setSupported({"Gamepad"});
}
auto ControllerPort::unload() -> void {
+1
View File
@@ -10,6 +10,7 @@ auto ControllerPort::load(Node::Object parent) -> void {
port->setType("Controller");
port->setHotSwappable(true);
port->setAllocate([&](auto name) { return allocate(name); });
port->setSupported({"Gamepad"});
}
auto ControllerPort::unload() -> void {
+1
View File
@@ -11,6 +11,7 @@ auto ControllerPort::load(Node::Object parent) -> void {
port->setType("Controller");
port->setHotSwappable(true);
port->setAllocate([&](auto name) { return allocate(name); });
port->setSupported({"Control Pad", "Fighting Pad"});
}
auto ControllerPort::unload() -> void {
+1
View File
@@ -10,6 +10,7 @@ auto ControllerPort::load(Node::Object parent) -> void {
port->setType("Controller");
port->setHotSwappable(true);
port->setAllocate([&](auto name) { return allocate(name); });
port->setSupported({"Gamepad"});
}
auto ControllerPort::unload() -> void {
+43
View File
@@ -0,0 +1,43 @@
//ASCII 16kbit
struct ASC16 : Interface {
using Interface::Interface;
Memory::Readable<uint8> rom;
auto load(Markup::Node document) -> void override {
auto board = document["game/board"];
Interface::load(rom, board["memory(type=ROM,content=Program)"]);
}
auto save(Markup::Node document) -> void override {
}
auto unload() -> void override {
}
auto read(uint16 address, uint8 data) -> uint8 override {
if(address >= 0x4000 && address <= 0x7fff) data = rom.read(bank[0] << 14 | (uint14)address);
if(address >= 0x8000 && address <= 0xbfff) data = rom.read(bank[1] << 14 | (uint14)address);
if(address >= 0xc000 && address <= 0xffff) data = rom.read(bank[0] << 14 | (uint14)address);
if(address >= 0x0000 && address <= 0x3fff) data = rom.read(bank[1] << 14 | (uint14)address);
return data;
}
auto write(uint16 address, uint8 data) -> void override {
if(address >= 0x6000 && address <= 0x6fff) bank[0] = data;
if(address >= 0x7000 && address <= 0xbfff) bank[1] = data;
}
auto power() -> void override {
bank[0] = 0x0f; //R-Type = 0x0f; others = 0x00
bank[1] = 0x00;
}
auto serialize(serializer& s) -> void override {
s.array(bank);
}
uint8 bank[2];
};
+51
View File
@@ -0,0 +1,51 @@
//ASCII 8kbit
struct ASC8 : Interface {
using Interface::Interface;
Memory::Readable<uint8> rom;
auto load(Markup::Node document) -> void override {
auto board = document["game/board"];
Interface::load(rom, board["memory(type=ROM,content=Program)"]);
}
auto save(Markup::Node document) -> void override {
}
auto unload() -> void override {
}
auto read(uint16 address, uint8 data) -> uint8 override {
if(address >= 0x4000 && address <= 0x5fff) data = rom.read(bank[0] << 13 | (uint13)address);
if(address >= 0x6000 && address <= 0x7fff) data = rom.read(bank[1] << 13 | (uint13)address);
if(address >= 0x8000 && address <= 0x9fff) data = rom.read(bank[2] << 13 | (uint13)address);
if(address >= 0xa000 && address <= 0xbfff) data = rom.read(bank[3] << 13 | (uint13)address);
if(address >= 0xc000 && address <= 0xdfff) data = rom.read(bank[0] << 13 | (uint13)address);
if(address >= 0xe000 && address <= 0xffff) data = rom.read(bank[1] << 13 | (uint13)address);
if(address >= 0x0000 && address <= 0x1fff) data = rom.read(bank[2] << 13 | (uint13)address);
if(address >= 0x2000 && address <= 0x3fff) data = rom.read(bank[3] << 13 | (uint13)address);
return data;
}
auto write(uint16 address, uint8 data) -> void override {
if(address >= 0x6000 && address <= 0x67ff) bank[0] = data;
if(address >= 0x6800 && address <= 0x6fff) bank[1] = data;
if(address >= 0x7000 && address <= 0x77ff) bank[2] = data;
if(address >= 0x7800 && address <= 0x7fff) bank[3] = data;
}
auto power() -> void override {
bank[0] = 0;
bank[1] = 0;
bank[2] = 0;
bank[3] = 0;
}
auto serialize(serializer& s) -> void override {
s.array(bank);
}
uint8 bank[4];
};
+50
View File
@@ -0,0 +1,50 @@
namespace Board {
#include "asc16.cpp"
#include "asc8.cpp"
#include "cross-blaim.cpp"
#include "konami.cpp"
#include "konami-scc.cpp"
#include "linear.cpp"
#include "super-lode-runner.cpp"
#include "super-pierrot.cpp"
auto Interface::load(Memory::Readable<uint8>& memory, Markup::Node node) -> bool {
if(!node) return false;
memory.allocate(node["size"].natural());
auto name = string{node["content"].string(), ".", node["type"].string()}.downcase();
if(auto fp = platform->open(cartridge.node, name, File::Read, File::Required)) {
memory.load(fp);
return true;
}
return false;
}
auto Interface::load(Memory::Writable<uint8>& memory, Markup::Node node) -> bool {
if(!node) return false;
memory.allocate(node["size"].natural());
if(node["volatile"]) return true;
auto name = string{node["content"].string(), ".", node["type"].string()}.downcase();
if(auto fp = platform->open(cartridge.node, name, File::Read)) {
memory.load(fp);
return true;
}
return false;
}
auto Interface::save(Memory::Writable<uint8>& memory, Markup::Node node) -> bool {
if(!node) return false;
if(node["volatile"]) return true;
auto name = string{node["content"].string(), ".", node["type"].string()}.downcase();
if(auto fp = platform->open(cartridge.node, name, File::Write)) {
memory.save(fp);
return true;
}
return false;
}
auto Interface::main() -> void {
cartridge.step(system.colorburst());
}
}
+22
View File
@@ -0,0 +1,22 @@
namespace Board {
struct Interface {
Interface(Cartridge& cartridge) : cartridge(cartridge) {}
virtual ~Interface() = default;
virtual auto load(Markup::Node) -> void {}
virtual auto save(Markup::Node) -> void {}
virtual auto unload() -> void {}
virtual auto main() -> void;
virtual auto read(uint16 address, uint8 data) -> uint8 { return data; }
virtual auto write(uint16 address, uint8 data) -> void {}
virtual auto power() -> void {}
virtual auto serialize(serializer&) -> void {}
auto load(Memory::Readable<uint8>&, Markup::Node) -> bool;
auto load(Memory::Writable<uint8>&, Markup::Node) -> bool;
auto save(Memory::Writable<uint8>&, Markup::Node) -> bool;
Cartridge& cartridge;
};
}
+56
View File
@@ -0,0 +1,56 @@
//Cross Blaim
struct CrossBlaim : Interface {
using Interface::Interface;
Memory::Readable<uint8> rom;
auto load(Markup::Node document) -> void override {
auto board = document["game/board"];
Interface::load(rom, board["memory(type=ROM,content=Program)"]);
}
auto save(Markup::Node document) -> void override {
}
auto unload() -> void override {
}
auto read(uint16 address, uint8 data) -> uint8 override {
if(mode == 0 || mode == 1) {
if(address >> 14 == 0) data = rom.read((uint14)address + 16_KiB);
if(address >> 14 == 1) data = rom.read((uint14)address);
if(address >> 14 == 2) data = rom.read((uint14)address + 16_KiB);
if(address >> 14 == 3) data = rom.read((uint14)address + 16_KiB);
}
if(mode == 2) {
if(address >> 14 == 0) data = 0xff;
if(address >> 14 == 1) data = rom.read((uint14)address);
if(address >> 14 == 2) data = rom.read((uint14)address + 32_KiB);
if(address >> 14 == 3) data = 0xff;
}
if(mode == 3) {
if(address >> 14 == 0) data = 0xff;
if(address >> 14 == 1) data = rom.read((uint14)address);
if(address >> 14 == 2) data = rom.read((uint14)address + 48_KiB);
if(address >> 14 == 3) data = 0xff;
}
return data;
}
auto write(uint16 address, uint8 data) -> void override {
mode = data;
}
auto power() -> void override {
mode = 0;
}
auto serialize(serializer& s) -> void override {
s.integer(mode);
}
uint2 mode;
};
+252
View File
@@ -0,0 +1,252 @@
//Konami (with Sound Creative Chip)
struct KonamiSCC : Interface {
using Interface::Interface;
Memory::Readable<uint8> rom;
Node::Stream stream;
auto load(Markup::Node document) -> void override {
auto board = document["game/board"];
Interface::load(rom, board["memory(type=ROM,content=Program)"]);
stream = cartridge.node->append<Node::Stream>("SCC");
stream->setChannels(1);
stream->setFrequency(system.colorburst() / 16.0);
stream->addHighPassFilter(20.0, 1);
}
auto save(Markup::Node document) -> void override {
}
auto unload() -> void override {
}
auto main() -> void override {
int sample = 256 * 5;
for(auto& voice : voices) {
if(voice.frequency <= 8) continue; //voice is halted when frequency < 9
voice.clock += 32;
while(voice.clock > voice.frequency) {
voice.clock -= voice.frequency + 1;
voice.counter++;
}
sample += voice.wave[voice.counter] * voice.volume * voice.key >> 3;
}
stream->sample(mixer[sample] / 32768.0);
cartridge.step(16);
}
auto read(uint16 address, uint8 data) -> uint8 override {
if(address >= 0x4000 && address <= 0x5fff) data = rom.read(bank[0] << 13 | (uint13)address);
if(address >= 0x6000 && address <= 0x7fff) data = rom.read(bank[1] << 13 | (uint13)address);
if(address >= 0x8000 && address <= 0x9fff) data = rom.read(bank[2] << 13 | (uint13)address);
if(address >= 0xa000 && address <= 0xbfff) data = rom.read(bank[3] << 13 | (uint13)address);
if(address >= 0xc000 && address <= 0xdfff) data = rom.read(bank[0] << 13 | (uint13)address);
if(address >= 0xe000 && address <= 0xffff) data = rom.read(bank[1] << 13 | (uint13)address);
if(address >= 0x0000 && address <= 0x1fff) data = rom.read(bank[2] << 13 | (uint13)address);
if(address >= 0x2000 && address <= 0x3fff) data = rom.read(bank[3] << 13 | (uint13)address);
address.bit(8) = 0; //SCC ignores A8
if(address >= 0x9800 && address <= 0x981f) {
data = voices[0].wave[address & 0x1f];
if(test.bit(6)) data = voices[0].wave[address + voices[0].counter & 0x1f];
}
if(address >= 0x9820 && address <= 0x983f) {
data = voices[1].wave[address & 0x1f];
if(test.bit(6)) data = voices[1].wave[address + voices[1].counter & 0x1f];
}
if(address >= 0x9840 && address <= 0x985f) {
data = voices[2].wave[address & 0x1f];
if(test.bit(6)) data = voices[2].wave[address + voices[2].counter & 0x1f];
}
if(address >= 0x9860 && address <= 0x987f) {
data = voices[3].wave[address & 0x1f];
if(test.bit(6) || test.bit(7)) data = voices[3].wave[address + voices[3 + test.bit(6)].counter & 0x1f];
}
if(address >= 0x9880 && address <= 0x98df) {
data = 0xff;
}
if(address >= 0x98e0 && address <= 0x98ff) {
//reading the test register sets it to 0xff
data = test = 0xff;
}
return data;
}
auto write(uint16 address, uint8 data) -> void override {
if(address >= 0x5000 && address <= 0x57ff) bank[0] = data;
if(address >= 0x7000 && address <= 0x77ff) bank[1] = data;
if(address >= 0x9000 && address <= 0x97ff) bank[2] = data;
if(address >= 0xb000 && address <= 0xb7ff) bank[3] = data;
address.bit(8) = 0; //SCC ignores A8
if(address >= 0x9800 && address <= 0x981f && !test.bit(6)) {
voices[0].wave[address & 0x1f] = data;
}
if(address >= 0x9820 && address <= 0x983f && !test.bit(6)) {
voices[1].wave[address & 0x1f] = data;
}
if(address >= 0x9840 && address <= 0x985f && !test.bit(6)) {
voices[2].wave[address & 0x1f] = data;
}
if(address >= 0x9860 && address <= 0x987f && !test.bit(6) && !test.bit(7)) {
voices[3].wave[address & 0x1f] = data;
voices[4].wave[address & 0x1f] = data; //shares data with wave channel 3
}
if(address == 0x9880) {
voices[0].frequency.bit(0, 7) = data.bit(0,7);
if(voices[0].frequency < 9) voices[0].clock = 0;
if(test.bit(5)) voices[0].clock = 0, voices[0].counter = 0;
}
if(address == 0x9881) {
voices[0].frequency.bit(8,11) = data.bit(0,3);
if(voices[0].frequency < 9) voices[0].clock = 0;
if(test.bit(5)) voices[0].clock = 0, voices[0].counter = 0;
}
if(address == 0x9882) {
voices[1].frequency.bit(0, 7) = data.bit(0,7);
if(voices[1].frequency < 9) voices[1].clock = 0;
if(test.bit(5)) voices[1].clock = 0, voices[1].counter = 0;
}
if(address == 0x9883) {
voices[1].frequency.bit(8,11) = data.bit(0,3);
if(voices[1].frequency < 9) voices[1].clock = 0;
if(test.bit(5)) voices[1].clock = 0, voices[1].counter = 0;
}
if(address == 0x9884) {
voices[2].frequency.bit(0, 7) = data.bit(0,7);
if(voices[2].frequency < 9) voices[2].clock = 0;
if(test.bit(5)) voices[2].clock = 0, voices[2].counter = 0;
}
if(address == 0x9885) {
voices[2].frequency.bit(8,11) = data.bit(0,3);
if(voices[2].frequency < 9) voices[2].clock = 0;
if(test.bit(5)) voices[2].clock = 0, voices[2].counter = 0;
}
if(address == 0x9886) {
voices[3].frequency.bit(0, 7) = data.bit(0,7);
if(voices[3].frequency < 9) voices[3].clock = 0;
if(test.bit(5)) voices[3].clock = 0, voices[3].counter = 0;
}
if(address == 0x9887) {
voices[3].frequency.bit(8,11) = data.bit(0,3);
if(voices[3].frequency < 9) voices[3].clock = 0;
if(test.bit(5)) voices[3].clock = 0, voices[3].counter = 0;
}
if(address == 0x9888) {
voices[4].frequency.bit(0, 7) = data.bit(0,7);
if(voices[4].frequency < 9) voices[4].clock = 0;
if(test.bit(5)) voices[4].clock = 0, voices[4].counter = 0;
}
if(address == 0x9889) {
voices[4].frequency.bit(8,11) = data.bit(0,3);
if(voices[4].frequency < 9) voices[4].clock = 0;
if(test.bit(5)) voices[4].clock = 0, voices[4].counter = 0;
}
if(address == 0x988a) {
voices[0].volume = data.bit(0,3);
}
if(address == 0x988b) {
voices[1].volume = data.bit(0,3);
}
if(address == 0x988c) {
voices[2].volume = data.bit(0,3);
}
if(address == 0x988d) {
voices[3].volume = data.bit(0,3);
}
if(address == 0x988e) {
voices[4].volume = data.bit(0,3);
}
if(address == 0x988f) {
voices[0].key = data.bit(0);
voices[1].key = data.bit(1);
voices[2].key = data.bit(2);
voices[3].key = data.bit(3);
voices[4].key = data.bit(4);
}
if(address >= 0x98e0 && address <= 0x98ff) test = data;
}
auto power() -> void override {
bank[0] = 0;
bank[1] = 1;
bank[2] = 2;
bank[3] = 3;
voices[0] = {};
voices[1] = {};
voices[2] = {};
voices[3] = {};
voices[4] = {};
test = 0;
mixer.resize(512 * 5);
auto table = mixer.data() + 256 * 5;
for(int n : range(256 * 5)) {
int16 volume = n * 8 * 16 / 5;
table[+n] = +volume;
table[-n] = -volume;
}
}
auto serialize(serializer& s) -> void override {
s.array(bank);
for(auto& voice : voices) {
s.integer(voice.clock);
s.integer(voice.frequency);
s.integer(voice.counter);
s.integer(voice.volume);
s.integer(voice.key);
s.array(voice.wave);
}
s.integer(test);
}
uint8 bank[4];
struct Voice {
uint16 clock;
uint12 frequency;
uint5 counter;
uint4 volume;
uint1 key;
int8 wave[32];
} voices[5];
uint8 test;
//unserialized:
vector<int16> mixer;
};
+45
View File
@@ -0,0 +1,45 @@
//Konami (without Sound Creative Chip)
struct Konami : Interface {
using Interface::Interface;
Memory::Readable<uint8> rom;
auto load(Markup::Node document) -> void override {
auto board = document["game/board"];
Interface::load(rom, board["memory(type=ROM,content=Program)"]);
}
auto save(Markup::Node document) -> void override {
}
auto unload() -> void override {
}
auto read(uint16 address, uint8 data) -> uint8 override {
if(address >> 13 == 2) data = rom.read(bank[0] << 13 | (uint13)address);
if(address >> 13 == 3) data = rom.read(bank[1] << 13 | (uint13)address);
if(address >> 13 == 4) data = rom.read(bank[2] << 13 | (uint13)address);
if(address >> 13 == 5) data = rom.read(bank[3] << 13 | (uint13)address);
return data;
}
auto write(uint16 address, uint8 data) -> void override {
if(address >> 13 == 2) bank[0] = 0;
if(address >> 13 == 3) bank[1] = data;
if(address >> 13 == 4) bank[2] = data;
if(address >> 13 == 5) bank[3] = data;
}
auto power() -> void override {
bank[0] = 0;
bank[1] = 1;
bank[2] = 0;
bank[3] = 0;
}
auto serialize(serializer& s) -> void override {
s.array(bank);
}
uint8 bank[4];
};
+29
View File
@@ -0,0 +1,29 @@
struct Linear : Interface {
using Interface::Interface;
Memory::Readable<uint8> rom;
auto load(Markup::Node document) -> void override {
auto board = document["game/board"];
Interface::load(rom, board["memory(type=ROM,content=Program)"]);
}
auto save(Markup::Node document) -> void override {
}
auto unload() -> void override {
}
auto read(uint16 address, uint8 data) -> uint8 override {
data = rom.read(address);
return data;
}
auto write(uint16 address, uint8 data) -> void override {
}
auto power() -> void override {
}
auto serialize(serializer& s) -> void override {
}
};
@@ -0,0 +1,37 @@
//Super Lode Runner
//(not working: requires MSX BASIC)
struct SuperLodeRunner : Interface {
using Interface::Interface;
Memory::Readable<uint8> rom;
auto load(Markup::Node document) -> void override {
auto board = document["game/board"];
Interface::load(rom, board["memory(type=ROM,content=Program)"]);
}
auto save(Markup::Node document) -> void override {
}
auto unload() -> void override {
}
auto read(uint16 address, uint8 data) -> uint8 override {
if(address >= 0x8000 && address <= 0xbfff) data = rom.read(bank << 14 | (uint14)address);
return data;
}
auto write(uint16 address, uint8 data) -> void override {
if(address >= 0x0000 && address <= 0x3fff) bank = data;
}
auto power() -> void override {
bank = 0;
}
auto serialize(serializer& s) -> void override {
s.integer(bank);
}
uint8 bank;
};
@@ -0,0 +1,47 @@
//Super Pierrot
//(not working)
struct SuperPierrot : Interface {
using Interface::Interface;
Memory::Readable<uint8> rom;
auto load(Markup::Node document) -> void override {
auto board = document["game/board"];
Interface::load(rom, board["memory(type=ROM,content=Program)"]);
}
auto save(Markup::Node document) -> void override {
}
auto unload() -> void override {
}
auto read(uint16 address, uint8 data) -> uint8 override {
if(address >= 0x4000 && address <= 0x7fff) data = rom.read(bank[0] << 14 | (uint14)address);
if(address >= 0x8000 && address <= 0xbfff) data = rom.read(bank[1] << 14 | (uint14)address);
return data;
}
auto write(uint16 address, uint8 data) -> void override {
if(address >= 0x4000 && address <= 0x4fff) bank[0] = data;
if(address >= 0x6000 && address <= 0x6fff) bank[0] = data;
if(address >= 0x8000 && address <= 0x8fff) bank[0] = data;
if(address >= 0xa000 && address <= 0xafff) bank[0] = data;
if(address >= 0x5000 && address <= 0x5fff) bank[1] = data;
if(address >= 0x7000 && address <= 0x7fff) bank[1] = data;
if(address >= 0x9000 && address <= 0x9fff) bank[1] = data;
if(address >= 0xa000 && address <= 0xafff) bank[1] = data;
}
auto power() -> void override {
bank[0] = 0;
bank[1] = 0;
}
auto serialize(serializer& s) -> void override {
s.array(bank);
}
uint8 bank[2];
};
+29 -59
View File
@@ -4,6 +4,7 @@ namespace ares::MSX {
Cartridge& cartridge = cartridgeSlot.cartridge;
Cartridge& expansion = expansionSlot.cartridge;
#include "board/board.cpp"
#include "slot.cpp"
#include "serialization.cpp"
@@ -23,87 +24,56 @@ auto Cartridge::connect() -> void {
auto document = BML::unserialize(information.manifest);
information.name = document["game/label"].string();
information.region = document["game/region"].string();
information.board = document["game/board"].string();
if(auto memory = document["game/board/memory(type=ROM,content=Program)"]) {
rom.allocate(memory["size"].natural());
if(auto fp = platform->open(node, "program.rom", File::Read, File::Required)) {
rom.load(fp);
}
}
if(auto memory = document["game/board/memory(type=RAM,content=Save)"]) {
ram.allocate(memory["size"].natural());
if(auto fp = platform->open(node, "save.ram", File::Read)) {
ram.load(fp);
}
}
if(information.board == "ASC16") board = new Board::ASC16{*this};
if(information.board == "ASC8") board = new Board::ASC8{*this};
if(information.board == "CrossBlaim") board = new Board::CrossBlaim{*this};
if(information.board == "Konami") board = new Board::Konami{*this};
if(information.board == "KonamiSCC") board = new Board::KonamiSCC{*this};
if(information.board == "Linear") board = new Board::Linear{*this};
if(information.board == "SuperLodeRunner") board = new Board::SuperLodeRunner{*this};
if(information.board == "SuperPierrot") board = new Board::SuperPierrot{*this};
if(!board) board = new Board::KonamiSCC{*this};
board->load(document);
power();
}
auto Cartridge::disconnect() -> void {
if(!node) return;
save();
rom.reset();
ram.reset();
node = {};
if(board) board->unload(), board.reset();
node.reset();
}
auto Cartridge::save() -> void {
if(!node) return;
auto document = BML::unserialize(information.manifest);
board->save(document);
}
if(auto memory = document["game/board/memory(type=RAM,content=Save)"]) {
if(auto fp = platform->open(node, "save.ram", File::Write)) {
ram.save(fp);
}
}
auto Cartridge::main() -> void {
if(board) return board->main();
step(system.colorburst());
}
auto Cartridge::step(uint clocks) -> void {
Thread::step(clocks);
Thread::synchronize(cpu);
}
auto Cartridge::power() -> void {
Thread::create(system.colorburst(), {&Cartridge::main, this});
if(board) board->power();
}
uint2 RB; //Cross Blaim
uint8 B8K[4]={0,1,0,0}; //Konami 8K (non-SCC)
auto Cartridge::read(uint16 address) -> uint8 {
if(!rom) return 0xff;
/*
if(RB==0||RB==1){
if(address>>14==0)return rom.read((uint14)address+16_KiB);
if(address>>14==1)return rom.read((uint14)address);
if(address>>14==2)return rom.read((uint14)address+16_KiB);
if(address>>14==3)return rom.read((uint14)address+16_KiB);
}
if(RB==2){
if(address>>14==0)return 0xff;
if(address>>14==1)return rom.read((uint14)address);
if(address>>14==2)return rom.read((uint14)address+32_KiB);
if(address>>14==3)return 0xff;
}
if(RB==3){
if(address>>14==0)return 0xff;
if(address>>14==1)return rom.read((uint14)address);
if(address>>14==2)return rom.read((uint14)address+48_KiB);
if(address>>14==3)return 0xff;
}
*/
if(address>>13==2)return rom.read(B8K[0]<<13|(uint13)address);
if(address>>13==3)return rom.read(B8K[1]<<13|(uint13)address);
if(address>>13==4)return rom.read(B8K[2]<<13|(uint13)address);
if(address>>13==5)return rom.read(B8K[3]<<13|(uint13)address);
return rom.read(address);
if(board) return board->read(address, 0xff);
return 0xff;
}
auto Cartridge::write(uint16 address, uint8 data) -> void {
RB = data;
if(address>>13==2)B8K[0]=0;
if(address>>13==3)B8K[1]=data;
if(address>>13==4)B8K[2]=data;
if(address>>13==5)B8K[3]=data;
if(board) return board->write(address, data);
}
}
+9 -1
View File
@@ -1,4 +1,7 @@
struct Cartridge {
struct Cartridge;
#include "board/board.hpp"
struct Cartridge : Thread {
Node::Peripheral node;
Memory::Readable<uint8> rom;
Memory::Writable<uint8> ram;
@@ -13,6 +16,8 @@ struct Cartridge {
auto disconnect() -> void;
auto save() -> void;
auto main() -> void;
auto step(uint clocks) -> void;
auto power() -> void;
auto read(uint16 address) -> uint8;
@@ -21,11 +26,14 @@ struct Cartridge {
//serialization.cpp
auto serialize(serializer&) -> void;
unique_pointer<Board::Interface> board;
private:
struct Information {
string manifest;
string name;
string region;
string board;
} information;
};
+1 -1
View File
@@ -1,3 +1,3 @@
auto Cartridge::serialize(serializer& s) -> void {
if(ram) s.array(ram.data(), ram.size());
if(board) board->serialize(s);
}
+1
View File
@@ -10,6 +10,7 @@ auto ControllerPort::load(Node::Object parent) -> void {
port->setType("Controller");
port->setHotSwappable(true);
port->setAllocate([&](auto name) { return allocate(name); });
port->setSupported({"Gamepad"});
}
auto ControllerPort::unload() -> void {
+1
View File
@@ -13,6 +13,7 @@ auto Keyboard::load(Node::Object parent) -> void {
port->setAllocate([&](auto name) { return allocate(port, name); });
port->setConnect([&] { connect(); });
port->setDisconnect([&] { disconnect(); });
port->setSupported({"Japanese"});
}
auto Keyboard::unload() -> void {
+19 -5
View File
@@ -1,13 +1,27 @@
ares.components += vr4300
ares.objects += ares-n64-interface ares-n64-system
ares.objects += ares-n64-cartridge ares-n64-controller
ares.objects += ares-n64-cpu ares-n64-rdp ares-n64-rsp
ares.objects += ares-n64-interface ares-n64-memory ares-n64-system
ares.objects += ares-n64-cartridge ares-n64-controller ares-n64-dd
ares.objects += ares-n64-mi ares-n64-vi ares-n64-ai
ares.objects += ares-n64-pi ares-n64-ri ares-n64-si
ares.objects += ares-n64-rdram ares-n64-cpu ares-n64-rdp ares-n64-rsp
$(object.path)/ares-n64-interface.o: $(ares.path)/n64/interface/interface.cpp
$(object.path)/ares-n64-memory.o: $(ares.path)/n64/memory/memory.cpp
$(object.path)/ares-n64-system.o: $(ares.path)/n64/system/system.cpp
$(object.path)/ares-n64-cartridge.o: $(ares.path)/n64/cartridge/cartridge.cpp
$(object.path)/ares-n64-controller.o: $(ares.path)/n64/controller/controller.cpp
$(object.path)/ares-n64-dd.o: $(ares.path)/n64/dd/dd.cpp
$(object.path)/ares-n64-sp.o: $(ares.path)/n64/sp/sp.cpp
$(object.path)/ares-n64-dp.o: $(ares.path)/n64/dp/dp.cpp
$(object.path)/ares-n64-mi.o: $(ares.path)/n64/mi/mi.cpp
$(object.path)/ares-n64-vi.o: $(ares.path)/n64/vi/vi.cpp
$(object.path)/ares-n64-ai.o: $(ares.path)/n64/ai/ai.cpp
$(object.path)/ares-n64-pi.o: $(ares.path)/n64/pi/pi.cpp
$(object.path)/ares-n64-ri.o: $(ares.path)/n64/ri/ri.cpp
$(object.path)/ares-n64-si.o: $(ares.path)/n64/si/si.cpp
$(object.path)/ares-n64-rdram.o: $(ares.path)/n64/rdram/rdram.cpp
$(object.path)/ares-n64-cpu.o: $(ares.path)/n64/cpu/cpu.cpp
$(object.path)/ares-n64-rdp.o: $(ares.path)/n64/rdp/rdp.cpp
$(object.path)/ares-n64-rsp.o: $(ares.path)/n64/rsp/rsp.cpp
flags += -msse4.2
options += -msse4.2
+61
View File
@@ -0,0 +1,61 @@
#include <n64/n64.hpp>
namespace ares::Nintendo64 {
AI ai;
#include "io.cpp"
#include "debugger.cpp"
#include "serialization.cpp"
auto AI::load(Node::Object parent) -> void {
node = parent->append<Node::Component>("AI");
stream = node->append<Node::Stream>("AI");
stream->setChannels(2);
stream->setFrequency(44100.0);
debugger.load(node);
}
auto AI::unload() -> void {
node = {};
stream = {};
debugger = {};
}
auto AI::main() -> void {
sample();
step(dac.period);
}
auto AI::sample() -> void {
if(io.dmaCount == 0) return stream->sample(0.0, 0.0);
auto data = rdram.ram.readWord(io.dmaAddress[0]);
auto left = i16(data >> 16);
auto right = i16(data >> 0);
stream->sample(left / 32768.0, right / 32768.0);
io.dmaAddress[0] += 4;
io.dmaLength [0] -= 4;
if(!io.dmaLength[0]) {
mi.raise(MI::IRQ::AI);
if(--io.dmaCount) {
io.dmaAddress[0] = io.dmaAddress[1];
io.dmaLength [0] = io.dmaLength [1];
}
}
}
auto AI::step(uint clocks) -> void {
clock += clocks;
}
auto AI::power() -> void {
Thread::reset();
io = {};
dac = {};
stream->setFrequency(dac.frequency);
}
}
+52
View File
@@ -0,0 +1,52 @@
//Audio Interface
struct AI : Thread, Memory::IO<AI> {
Node::Component node;
Node::Stream stream;
struct Debugger {
//debugger.cpp
auto load(Node::Object) -> void;
auto io(string_view) -> void;
struct Tracer {
Node::Notification io;
} tracer;
} debugger;
//ai.cpp
auto load(Node::Object) -> void;
auto unload() -> void;
auto main() -> void;
auto sample() -> void;
auto step(uint clocks) -> void;
auto power() -> void;
//io.cpp
auto readWord(u32 address) -> u32;
auto writeWord(u32 address, u32 data) -> void;
//serialization.cpp
auto serialize(serializer&) -> void;
struct FIFO {
uint24 address = 0;
} fifo[2];
struct IO {
uint1 dmaEnable = 0;
uint24 dmaAddress[2] = {};
uint18 dmaLength[2] = {};
uint2 dmaCount = 0;
uint14 dacRate = 0;
uint4 bitRate = 0;
} io;
struct DAC {
u32 frequency = 44100;
u32 precision = 16;
u32 period = 93'750'000 / 44'100;
} dac;
};
extern AI ai;
+9
View File
@@ -0,0 +1,9 @@
auto AI::Debugger::load(Node::Object parent) -> void {
tracer.io = parent->append<Node::Notification>("I/O", "AI");
}
auto AI::Debugger::io(string_view message) -> void {
if(tracer.io->enabled()) {
tracer.io->notify(message);
}
}
+82
View File
@@ -0,0 +1,82 @@
static const vector<string> registerNames = {
"AI_DRAM_ADDRESS",
"AI_LENGTH",
"AI_CONTROL",
"AI_STATUS",
"AI_DACRATE",
"AI_BITRATE",
};
auto AI::readWord(u32 address) -> u32 {
address = (address & 0xfffff) >> 2;
uint32 data;
if(address != 3) {
//AI_LENGTH (mirrored)
data.bit(0,17) = io.dmaLength[0];
}
if(address == 3) {
//AI_STATUS
data.bit( 0) = io.dmaCount > 1;
data.bit(20) = 1;
data.bit(24) = 1;
data.bit(30) = io.dmaCount > 0;
data.bit(31) = io.dmaCount > 1;
}
if(debugger.tracer.io->enabled()) {
debugger.io({registerNames(address, "AI_UNKNOWN"), " => ", hex(data, 8L)});
}
return data;
}
auto AI::writeWord(u32 address, u32 data_) -> void {
address = (address & 0xfffff) >> 2;
uint32 data = data_;
if(address == 0) {
//AI_DRAM_ADDRESS
if(io.dmaCount < 2) {
io.dmaAddress[io.dmaCount] = data.bit(0,23) & ~7;
}
}
if(address == 1) {
//AI_LENGTH
uint18 length = data.bit(0,17) & ~7;
if(io.dmaCount < 2 && length) {
io.dmaLength[io.dmaCount] = length;
io.dmaCount++;
}
}
if(address == 2) {
//AI_CONTROL
io.dmaEnable = data.bit(0);
}
if(address == 3) {
//AI_STATUS
mi.lower(MI::IRQ::AI);
}
if(address == 4) {
//AI_DACRATE
auto frequency = dac.frequency;
io.dacRate = data.bit(0,13);
dac.frequency = max(1, 93'750'000 / 2 / (io.dacRate + 1)) * 1.037;
dac.period = 93'750'000 / dac.frequency;
if(frequency != dac.frequency) stream->setFrequency(dac.frequency);
}
if(address == 5) {
//AI_BITRATE
io.bitRate = data.bit(0,3);
dac.precision = io.bitRate + 1;
}
if(debugger.tracer.io->enabled()) {
debugger.io({registerNames(address, "AI_UNKNOWN"), " <= ", hex(data, 8L)});
}
}
+3
View File
@@ -0,0 +1,3 @@
auto AI::serialize(serializer& s) -> void {
Thread::serialize(s);
}
+5
View File
@@ -21,12 +21,16 @@ auto Cartridge::connect() -> void {
auto document = BML::unserialize(information.manifest);
information.name = document["game/label"].string();
information.region = document["game/region"].string();
information.cic = document["game/board/cic"].string();
if(auto memory = document["game/board/memory(type=ROM,content=Program)"]) {
rom.allocate(memory["size"].natural());
if(auto fp = platform->open(node, "program.rom", File::Read, File::Required)) {
rom.load(fp);
}
} else {
rom.allocate(16);
}
power();
@@ -35,6 +39,7 @@ auto Cartridge::connect() -> void {
auto Cartridge::disconnect() -> void {
if(!node) return;
save();
rom.reset();
node = {};
}
+5 -1
View File
@@ -1,9 +1,11 @@
struct Cartridge {
Node::Peripheral node;
Memory::Readable<uint8> rom;
Memory::Readable rom;
auto manifest() const -> string { return information.manifest; }
auto name() const -> string { return information.name; }
auto region() const -> string { return information.region; }
auto cic() const -> string { return information.cic; }
//cartridge.cpp
auto allocate(Node::Port) -> Node::Peripheral;
@@ -20,6 +22,8 @@ private:
struct Information {
string manifest;
string name;
string region;
string cic;
} information;
};
+1
View File
@@ -2,6 +2,7 @@ struct Controller {
Node::Peripheral node;
virtual ~Controller() = default;
virtual auto read() -> uint32 { return 0; }
};
#include "port.hpp"
+57
View File
@@ -1,3 +1,60 @@
Gamepad::Gamepad(Node::Port parent) {
node = parent->append<Node::Peripheral>("Gamepad");
x = node->append<Node::Axis> ("X-axis");
y = node->append<Node::Axis> ("Y-axis");
up = node->append<Node::Button>("Up");
down = node->append<Node::Button>("Down");
left = node->append<Node::Button>("Left");
right = node->append<Node::Button>("Right");
b = node->append<Node::Button>("B");
a = node->append<Node::Button>("A");
cameraUp = node->append<Node::Button>("C-Up");
cameraDown = node->append<Node::Button>("C-Down");
cameraLeft = node->append<Node::Button>("C-Left");
cameraRight = node->append<Node::Button>("C-Right");
l = node->append<Node::Button>("L");
r = node->append<Node::Button>("R");
z = node->append<Node::Button>("Z");
start = node->append<Node::Button>("Start");
}
auto Gamepad::read() -> uint32 {
platform->input(x);
platform->input(y);
platform->input(up);
platform->input(down);
platform->input(left);
platform->input(right);
platform->input(b);
platform->input(a);
platform->input(cameraUp);
platform->input(cameraDown);
platform->input(cameraLeft);
platform->input(cameraRight);
platform->input(l);
platform->input(r);
platform->input(z);
platform->input(start);
uint32 data;
data.byte(0) = -y->value() >> 8;
data.byte(1) = +x->value() >> 8;
data.bit(16) = cameraRight->value();
data.bit(17) = cameraLeft->value();
data.bit(18) = cameraDown->value();
data.bit(19) = cameraUp->value();
data.bit(20) = l->value();
data.bit(21) = r->value();
data.bit(22) = 0;
data.bit(23) = 0;
data.bit(24) = right->value() & !left->value();
data.bit(25) = left->value() & !right->value();
data.bit(26) = down->value() & !up->value();
data.bit(27) = up->value() & !down->value();
data.bit(28) = start->value();
data.bit(29) = z->value();
data.bit(30) = b->value();
data.bit(31) = a->value();
return data;
}
+18
View File
@@ -1,3 +1,21 @@
struct Gamepad : Controller {
Node::Axis x;
Node::Axis y;
Node::Button up;
Node::Button down;
Node::Button left;
Node::Button right;
Node::Button b;
Node::Button a;
Node::Button cameraUp;
Node::Button cameraDown;
Node::Button cameraLeft;
Node::Button cameraRight;
Node::Button l;
Node::Button r;
Node::Button z;
Node::Button start;
Gamepad(Node::Port);
auto read() -> uint32 override;
};
+1
View File
@@ -12,6 +12,7 @@ auto ControllerPort::load(Node::Object parent) -> void {
port->setType("Controller");
port->setHotSwappable(true);
port->setAllocate([&](auto name) { return allocate(name); });
port->setSupported({"Gamepad"});
}
auto ControllerPort::unload() -> void {
+151
View File
@@ -0,0 +1,151 @@
#include "registers.hpp"
#include "scc-tlb.cpp"
#include "scc-registers.cpp"
#include "fpu-registers.cpp"
#include "memory.cpp"
#include "decoder.cpp"
#include "cpu-instructions.cpp"
#include "scc-instructions.cpp"
#include "fpu-instructions.cpp"
#include "recompiler.cpp"
#include "disassembler.cpp"
#include "serialization.cpp"
auto CPU::raiseException(uint code, uint coprocessor) -> void {
if(debugger.tracer.exception->enabled()) {
if(code != 0) debugger.exception(hex(code, 2L));
}
if(!scc.status.exceptionLevel) {
scc.epc = PC;
scc.status.exceptionLevel = 1;
scc.cause.exceptionCode = code;
scc.cause.coprocessorError = coprocessor;
if(scc.cause.branchDelay = branch.inDelaySlot()) scc.epc -= 4;
} else {
scc.cause.exceptionCode = code;
scc.cause.coprocessorError = coprocessor;
}
u64 vectorBase = !scc.status.vectorLocation ? i32(0x8000'0000) : i32(0xbfc0'0200);
u32 vectorOffset = (code == 2 || code == 3) ? 0x0000 : 0x0180;
PC = vectorBase + vectorOffset;
branch.exception();
context.setMode();
}
auto CPU::instruction() -> void {
if(auto interrupts = scc.cause.interruptPending & scc.status.interruptMask) {
if(scc.status.interruptEnable && !scc.status.exceptionLevel && !scc.status.errorLevel) {
if(debugger.tracer.interrupt->enabled()) {
debugger.interrupt(hex(scc.cause.interruptPending, 2L));
}
scc.cause.interruptPending = interrupts;
step(2);
return raiseException(0);
}
}
//devirtualize the program counter
auto address = readAddress(PC)(0);
if constexpr(Accuracy::CPU::Interpreter == 0) {
auto block = recompiler.block(address);
block->execute();
step(block->step);
}
if constexpr(Accuracy::CPU::Interpreter == 1) {
pipeline.address = PC;
pipeline.instruction = bus.readWord(address);
debugger.instruction();
//instructionDebug();
decoderEXECUTE();
instructionEpilogue();
step(2);
}
}
auto CPU::instructionEpilogue() -> bool {
GPR[0].u64 = 0;
if(--scc.random.index < scc.wired.index) {
scc.random.index = 31;
}
switch(branch.state) {
case Branch::Step: PC += 4; return 0;
case Branch::Take: PC += 4; branch.delaySlot(); return 0;
case Branch::DelaySlot: PC = branch.pc; branch.reset(); return 1;
case Branch::Exception: branch.reset(); return 1;
case Branch::Discard: PC += 8; branch.reset(); return 1;
}
unreachable;
}
auto CPU::instructionDebug() -> void {
pipeline.address = PC;
pipeline.instruction = readWord(pipeline.address)(0);
static vector<bool> mask;
if(!mask) mask.resize(0x0800'0000);
if(mask[(PC & 0x1fff'ffff) >> 2]) return;
mask[(PC & 0x1fff'ffff) >> 2] = 1;
static uint counter = 0;
//if(++counter > 100) return;
print(
disassembler.hint(hex(pipeline.address, 8L)), " ",
//disassembler.hint(hex(pipeline.instruction, 8L)), " ",
disassembler.disassemble(pipeline.address, pipeline.instruction), "\n"
);
}
auto CPU::powerR4300(bool reset) -> void {
for(uint n : range(32)) GPR[n].u64 = 0;
scc = {};
scc.status.softReset = 1; //reset;
fpu = {};
LO.u64 = 0;
HI.u64 = 0;
GPR[Core::Register::SP].u64 = u32(0xa400'1ff0);
PC = u32(0xbfc0'0000);
branch.reset();
fesetround(FE_TONEAREST);
context.setMode();
if constexpr(Accuracy::CPU::Interpreter == 0) {
recompiler.allocator.resize(512_MiB, bump_allocator::executable | bump_allocator::zero_fill);
recompiler.reset();
}
}
auto CPU::Context::setMode() -> void {
mode = min(2, self.scc.status.privilegeMode);
if(self.scc.status.exceptionLevel) mode = Mode::Kernel;
if(self.scc.status.errorLevel) mode = Mode::Kernel;
segment[0] = Segment::Mapped;
segment[1] = Segment::Mapped;
segment[2] = Segment::Mapped;
segment[3] = Segment::Mapped;
if(mode == Mode::Kernel) {
segment[4] = Segment::Cached;
segment[5] = Segment::Uncached;
segment[6] = Segment::Mapped;
segment[7] = Segment::Mapped;
}
if(mode == Mode::Supervisor) {
segment[4] = Segment::Invalid;
segment[5] = Segment::Invalid;
segment[6] = Segment::Mapped;
segment[7] = Segment::Invalid;
}
if(mode == Mode::User) {
segment[4] = Segment::Invalid;
segment[5] = Segment::Invalid;
segment[6] = Segment::Invalid;
segment[7] = Segment::Invalid;
}
}
+72
View File
@@ -0,0 +1,72 @@
//{
//core.cpp
auto raiseException(uint, uint = 0) -> void;
auto instruction() -> void;
auto instructionEpilogue() -> bool;
auto instructionDebug() -> void;
auto powerR4300(bool reset) -> void;
//memory.cpp
auto readAddress (u32 address) -> maybe<u32>;
auto writeAddress(u32 address) -> maybe<u32>;
auto readByte (u32 address) -> maybe<u32>;
auto readHalf (u32 address) -> maybe<u32>;
auto readWord (u32 address) -> maybe<u32>;
auto readDouble (u32 address) -> maybe<u64>;
auto writeByte (u32 address, u8 data) -> bool;
auto writeHalf (u32 address, u16 data) -> bool;
auto writeWord (u32 address, u32 data) -> bool;
auto writeDouble (u32 address, u64 data) -> bool;
//decoder.cpp
auto decoderEXECUTE() -> void;
auto decoderSPECIAL() -> void;
auto decoderREGIMM() -> void;
auto decoderCOP0() -> void;
auto decoderCOP1() -> void;
auto instructionCOP2() -> void;
auto instructionCOP3() -> void;
auto instructionINVALID() -> void;
enum Interrupt : uint {
Software0 = 0,
Software1 = 1,
RCP = 2,
Cartridge = 3,
Reset = 4,
ReadRDB = 5,
WriteRDB = 6,
Timer = 7,
};
//serialization.cpp
auto serializeR4300(serializer&) -> void;
struct Context {
CPU& self;
Context(CPU& self) : self(self) {}
enum Mode : uint { Kernel, Supervisor, User };
enum Segment : uint { Invalid, Mapped, Cached, Uncached };
uint mode;
uint segment[8]; //512_MiB chunks
//core.cpp
auto setMode() -> void;
} context{*this};
#include "exception.hpp"
#include "pipeline.hpp"
#include "cpu.hpp"
#include "scc.hpp"
#include "fpu.hpp"
#include "recompiler.hpp"
#include "disassembler.hpp"
static constexpr bool Endian = 1; //0 = little, 1 = big
static constexpr uint FlipLE = (Endian == 0 ? 7 : 0);
static constexpr uint FlipBE = (Endian == 1 ? 7 : 0);
//};
+584
View File
@@ -0,0 +1,584 @@
auto CPU::instructionADD(r64& rd, cr64& rs, cr64& rt) -> void {
//if(rs.u32 > ~rt.u32) return exception.arithmeticOverflow();
rd.u64 = i32(rs.u32 + rt.u32);
}
auto CPU::instructionADDI(r64& rt, cr64& rs, i16 imm) -> void {
//if(rs.i32 > ~imm) return exception.arithmeticOverflow();
rt.u64 = i32(rs.u32 + imm);
}
auto CPU::instructionADDIU(r64& rt, cr64& rs, i16 imm) -> void {
rt.u64 = i32(rs.u32 + imm);
}
auto CPU::instructionADDU(r64& rd, cr64& rs, cr64& rt) -> void {
rd.u64 = i32(rs.u32 + rt.u32);
}
auto CPU::instructionAND(r64& rd, cr64& rs, cr64& rt) -> void {
rd.u64 = rs.u64 & rt.u64;
}
auto CPU::instructionANDI(r64& rt, cr64& rs, u16 imm) -> void {
rt.u64 = rs.u64 & imm;
}
auto CPU::instructionBEQ(cr64& rs, cr64& rt, i16 imm) -> void {
if(rs.u64 == rt.u64) branch.take(PC + 4 + (imm << 2));
}
auto CPU::instructionBEQL(cr64& rs, cr64& rt, i16 imm) -> void {
if(rs.u64 == rt.u64) branch.take(PC + 4 + (imm << 2));
else branch.discard();
}
auto CPU::instructionBGEZ(cr64& rs, i16 imm) -> void {
if(rs.i64 >= 0) branch.take(PC + 4 + (imm << 2));
}
auto CPU::instructionBGEZAL(cr64& rs, i16 imm) -> void {
RA.u64 = i32(PC + 8);
if(rs.i64 >= 0) branch.take(PC + 4 + (imm << 2));
}
auto CPU::instructionBGEZALL(cr64& rs, i16 imm) -> void {
RA.u64 = i32(PC + 8);
if(rs.i64 >= 0) branch.take(PC + 4 + (imm << 2));
else branch.discard();
}
auto CPU::instructionBGEZL(cr64& rs, i16 imm) -> void {
if(rs.i64 >= 0) branch.take(PC + 4 + (imm << 2));
else branch.discard();
}
auto CPU::instructionBGTZ(cr64& rs, i16 imm) -> void {
if(rs.i64 > 0) branch.take(PC + 4 + (imm << 2));
}
auto CPU::instructionBGTZL(cr64& rs, i16 imm) -> void {
if(rs.i64 > 0) branch.take(PC + 4 + (imm << 2));
else branch.discard();
}
auto CPU::instructionBLEZ(cr64& rs, i16 imm) -> void {
if(rs.i64 <= 0) branch.take(PC + 4 + (imm << 2));
}
auto CPU::instructionBLEZL(cr64& rs, i16 imm) -> void {
if(rs.i64 <= 0) branch.take(PC + 4 + (imm << 2));
else branch.discard();
}
auto CPU::instructionBLTZ(cr64& rs, i16 imm) -> void {
if(rs.i64 < 0) branch.take(PC + 4 + (imm << 2));
}
auto CPU::instructionBLTZAL(cr64& rs, i16 imm) -> void {
RA.u64 = i32(PC + 8);
if(rs.i64 < 0) branch.take(PC + 4 + (imm << 2));
}
auto CPU::instructionBLTZALL(cr64& rs, i16 imm) -> void {
RA.u64 = i32(PC + 8);
if(rs.i64 < 0) branch.take(PC + 4 + (imm << 2));
else branch.discard();
}
auto CPU::instructionBLTZL(cr64& rs, i16 imm) -> void {
if(rs.i64 < 0) branch.take(PC + 4 + (imm << 2));
else branch.discard();
}
auto CPU::instructionBNE(cr64& rs, cr64& rt, i16 imm) -> void {
if(rs.u64 != rt.u64) branch.take(PC + 4 + (imm << 2));
}
auto CPU::instructionBNEL(cr64& rs, cr64& rt, i16 imm) -> void {
if(rs.u64 != rt.u64) branch.take(PC + 4 + (imm << 2));
else branch.discard();
}
auto CPU::instructionBREAK() -> void {
exception.breakpoint();
}
auto CPU::instructionCACHE(u8 cache, u8 operation) -> void {
//todo
if(cache == 0) {
//instruction
if(operation == 0) {
//index invalidate
}
if(operation == 1) {
//index load tag
}
if(operation == 2) {
//index store tag
}
if(operation == 4) {
//hit invalidate
}
if(operation == 5) {
//fill
}
if(operation == 6) {
//hit write back
}
}
if(cache == 1) {
//data
if(operation == 0) {
//index write back invalidate
}
if(operation == 1) {
//index load tag
}
if(operation == 2) {
//index store tag
}
if(operation == 3) {
//create dirty exclusive
}
if(operation == 4) {
//hit invalidate
}
if(operation == 5) {
//hit write back invalidate
}
if(operation == 6) {
//hit write back
}
}
}
auto CPU::instructionDADD(r64& rd, cr64& rs, cr64& rt) -> void {
//if(rs.u64 > ~rt.u64) return exception.arithmeticOverflow();
rd.u64 = rs.u64 + rt.u64;
}
auto CPU::instructionDADDI(r64& rt, cr64& rs, i16 imm) -> void {
//if(rs.i64 > ~imm) return exception.arithmeticOverflow();
rt.u64 = rs.u64 + imm;
}
auto CPU::instructionDADDIU(r64& rt, cr64& rs, i16 imm) -> void {
rt.u64 = rs.u64 + imm;
}
auto CPU::instructionDADDU(r64& rd, cr64& rs, cr64& rt) -> void {
rd.u64 = rs.u64 + rt.u64;
}
auto CPU::instructionDDIV(cr64& rs, cr64& rt) -> void {
if(rt.i64) {
//cast to i128 to prevent exception on INT64_MIN / -1
LO.u64 = i128(rs.i64) / i128(rt.i64);
HI.u64 = i128(rs.i64) % i128(rt.i64);
} else {
LO.u64 = rs.i64 < 0 ? +1 : -1;
HI.u64 = rs.i64;
}
}
auto CPU::instructionDDIVU(cr64& rs, cr64& rt) -> void {
if(rt.u64) {
LO.u64 = rs.u64 / rt.u64;
HI.u64 = rs.u64 % rt.u64;
} else {
LO.u64 = -1;
HI.u64 = rs.u64;
}
}
auto CPU::instructionDIV(cr64& rs, cr64& rt) -> void {
if(rt.i32) {
//cast to i64 to prevent exception on INT32_MIN / -1
LO.u64 = i32(i64(rs.i32) / i64(rt.i32));
HI.u64 = i32(i64(rs.i32) % i64(rt.i32));
} else {
LO.u64 = rs.i32 < 0 ? +1 : -1;
HI.u64 = rs.i32;
}
}
auto CPU::instructionDIVU(cr64& rs, cr64& rt) -> void {
if(rt.u32) {
LO.u64 = i32(rs.u32 / rt.u32);
HI.u64 = i32(rs.u32 % rt.u32);
} else {
LO.u64 = -1;
HI.u64 = rs.i32;
}
}
auto CPU::instructionDMULT(cr64& rs, cr64& rt) -> void {
u128 result = rs.i128() * rt.i128();
LO.u64 = result >> 0;
HI.u64 = result >> 64;
}
auto CPU::instructionDMULTU(cr64& rs, cr64& rt) -> void {
u128 result = rs.u128() * rt.u128();
LO.u64 = result >> 0;
HI.u64 = result >> 64;
}
auto CPU::instructionDSLL(r64& rd, cr64& rt, u8 sa) -> void {
rd.u64 = rt.u64 << sa;
}
auto CPU::instructionDSLLV(r64& rd, cr64& rt, cr64& rs) -> void {
rd.u64 = rt.u64 << (rs.u32 & 63);
}
auto CPU::instructionDSRA(r64& rd, cr64& rt, u8 sa) -> void {
rd.u64 = rt.i64 >> sa;
}
auto CPU::instructionDSRAV(r64& rd, cr64& rt, cr64& rs) -> void {
rd.u64 = rt.i64 >> (rs.u32 & 63);
}
auto CPU::instructionDSRL(r64& rd, cr64& rt, u8 sa) -> void {
rd.u64 = rt.u64 >> sa;
}
auto CPU::instructionDSRLV(r64& rd, cr64& rt, cr64& rs) -> void {
rd.u64 = rt.u64 >> (rs.u32 & 63);
}
auto CPU::instructionDSUB(r64& rd, cr64& rs, cr64& rt) -> void {
//if(rs.u64 < rt.u64) return exception.arithmeticOverflow();
rd.u64 = rs.u64 - rt.u64;
}
auto CPU::instructionDSUBU(r64& rd, cr64& rs, cr64& rt) -> void {
rd.u64 = rs.u64 - rt.u64;
}
auto CPU::instructionJ(u32 imm) -> void {
branch.take((PC + 4 & 0xf000'0000) | (imm << 2));
}
auto CPU::instructionJAL(u32 imm) -> void {
RA.u64 = i32(PC + 8);
branch.take((PC + 4 & 0xf000'0000) | (imm << 2));
}
auto CPU::instructionJALR(r64& rd, cr64& rs) -> void {
rd.u64 = i32(PC + 8);
branch.take(rs.u32);
}
auto CPU::instructionJR(cr64& rs) -> void {
branch.take(rs.u32);
}
auto CPU::instructionLB(r64& rt, cr64& rs, i16 imm) -> void {
if(auto data = readByte(rs.u32 + imm)) rt.u64 = i8(*data);
}
auto CPU::instructionLBU(r64& rt, cr64& rs, i16 imm) -> void {
if(auto data = readByte(rs.u32 + imm)) rt.u64 = u8(*data);
}
auto CPU::instructionLD(r64& rt, cr64& rs, i16 imm) -> void {
if(auto data = readDouble(rs.u32 + imm)) rt.u64 = *data;
}
auto CPU::instructionLDL(r64& rt, cr64& rs, i16 imm) -> void {
auto address = rs.u32 + imm;
auto shift = 8 * ((address ^ FlipLE) & 7);
auto mask = u64(0) - 1 << shift;
if(auto data = readDouble(address & ~7)) {
rt.u64 = rt.u64 & ~mask | *data << shift;
}
}
auto CPU::instructionLDR(r64& rt, cr64& rs, i16 imm) -> void {
auto address = rs.u32 + imm;
auto shift = 8 * ((address ^ FlipBE) & 7);
auto mask = u64(0) - 1 >> shift;
if(auto data = readDouble(address & ~7)) {
rt.u64 = rt.u64 & ~mask | *data >> shift;
}
}
auto CPU::instructionLH(r64& rt, cr64& rs, i16 imm) -> void {
if(auto data = readHalf(rs.u32 + imm)) rt.u64 = i16(*data);
}
auto CPU::instructionLHU(r64& rt, cr64& rs, i16 imm) -> void {
if(auto data = readHalf(rs.u32 + imm)) rt.u64 = u16(*data);
}
auto CPU::instructionLL(r64& rt, cr64& rs, i16 imm) -> void {
if(auto data = readWord(rs.u32 + imm)) {
rt.u64 = i32(*data);
scc.ll = tlb.physicalAddress >> 4;
scc.llbit = 1;
}
}
auto CPU::instructionLLD(r64& rt, cr64& rs, i16 imm) -> void {
if(auto data = readDouble(rs.u32 + imm)) {
rt.u64 = *data;
scc.ll = tlb.physicalAddress >> 4;
scc.llbit = 1;
}
}
auto CPU::instructionLUI(r64& rt, u16 imm) -> void {
rt.u64 = i32(imm << 16);
}
auto CPU::instructionLW(r64& rt, cr64& rs, i16 imm) -> void {
if(auto data = readWord(rs.u32 + imm)) rt.u64 = i32(*data);
}
auto CPU::instructionLWL(r64& rt, cr64& rs, i16 imm) -> void {
auto address = rs.u32 + imm;
auto shift = 8 * ((address ^ FlipLE) & 3);
auto mask = u32(0) - 1 << shift;
if(auto data = readWord(address & ~3)) {
rt.u64 = i32(rt.u32 & ~mask | *data << shift);
}
}
auto CPU::instructionLWR(r64& rt, cr64& rs, i16 imm) -> void {
auto address = rs.u32 + imm;
auto shift = 8 * ((address ^ FlipBE) & 3);
auto mask = u32(0) - 1 >> shift;
if(auto data = readWord(address & ~3)) {
rt.u64 = i32(rt.u32 & ~mask | *data >> shift);
}
}
auto CPU::instructionLWU(r64& rt, cr64& rs, i16 imm) -> void {
if(auto data = readWord(rs.u32 + imm)) rt.u64 = u32(*data);
}
auto CPU::instructionMFHI(r64& rd) -> void {
rd.u64 = HI.u64;
}
auto CPU::instructionMFLO(r64& rd) -> void {
rd.u64 = LO.u64;
}
auto CPU::instructionMTHI(cr64& rs) -> void {
HI.u64 = rs.u64;
}
auto CPU::instructionMTLO(cr64& rs) -> void {
LO.u64 = rs.u64;
}
auto CPU::instructionMULT(cr64& rs, cr64& rt) -> void {
u64 result = i64(rs.i32) * i64(rt.i32);
LO.u64 = i32(result >> 0);
HI.u64 = i32(result >> 32);
}
auto CPU::instructionMULTU(cr64& rs, cr64& rt) -> void {
u64 result = u64(rs.u32) * u64(rt.u32);
LO.u64 = i32(result >> 0);
HI.u64 = i32(result >> 32);
}
auto CPU::instructionNOR(r64& rd, cr64& rs, cr64& rt) -> void {
rd.u64 = ~(rs.u64 | rt.u64);
}
auto CPU::instructionOR(r64& rd, cr64& rs, cr64& rt) -> void {
rd.u64 = rs.u64 | rt.u64;
}
auto CPU::instructionORI(r64& rt, cr64& rs, u16 imm) -> void {
rt.u64 = rs.u64 | imm;
}
auto CPU::instructionSB(cr64& rt, cr64& rs, i16 imm) -> void {
writeByte(rs.u32 + imm, rt.u32);
}
auto CPU::instructionSC(r64& rt, cr64& rs, i16 imm) -> void {
if(scc.llbit) {
scc.llbit = 0;
rt.u64 = writeWord(rs.u32 + imm, rt.u32);
} else {
rt.u64 = 0;
}
}
auto CPU::instructionSCD(r64& rt, cr64& rs, i16 imm) -> void {
if(scc.llbit) {
scc.llbit = 0;
rt.u64 = writeDouble(rs.u32 + imm, rt.u64);
} else {
rt.u64 = 0;
}
}
auto CPU::instructionSD(cr64& rt, cr64& rs, i16 imm) -> void {
writeDouble(rs.u32 + imm, rt.u64);
}
auto CPU::instructionSDL(cr64& rt, cr64& rs, i16 imm) -> void {
auto address = rs.u32 + imm;
auto shift = 8 * ((address ^ FlipLE) & 7);
auto mask = u64(0) - 1 >> shift;
if(auto data = readDouble(address & ~7)) {
writeDouble(address & ~7, *data & ~mask | rt.u64 >> shift);
}
}
auto CPU::instructionSDR(cr64& rt, cr64& rs, i16 imm) -> void {
auto address = rs.u32 + imm;
auto shift = 8 * ((address ^ FlipBE) & 7);
auto mask = u64(0) - 1 << shift;
if(auto data = readDouble(address & ~7)) {
writeDouble(address & ~7, *data & ~mask | rt.u64 << shift);
}
}
auto CPU::instructionSH(cr64& rt, cr64& rs, i16 imm) -> void {
writeHalf(rs.u32 + imm, rt.u32);
}
auto CPU::instructionSLL(r64& rd, cr64& rt, u8 sa) -> void {
rd.u64 = i32(rt.u32 << sa);
}
auto CPU::instructionSLLV(r64& rd, cr64& rt, cr64& rs) -> void {
rd.u64 = i32(rt.u32 << (rs.u32 & 31));
}
auto CPU::instructionSLT(r64& rd, cr64& rs, cr64& rt) -> void {
rd.u64 = rs.i64 < rt.i64;
}
auto CPU::instructionSLTI(r64& rt, cr64& rs, i16 imm) -> void {
rt.u64 = rs.i64 < imm;
}
auto CPU::instructionSLTIU(r64& rt, cr64& rs, i16 imm) -> void {
rt.u64 = rs.u64 < imm;
}
auto CPU::instructionSLTU(r64& rd, cr64& rs, cr64& rt) -> void {
rd.u64 = rs.u64 < rt.u64;
}
auto CPU::instructionSRA(r64& rd, cr64& rt, u8 sa) -> void {
rd.u64 = rt.i32 >> sa;
}
auto CPU::instructionSRAV(r64& rd, cr64& rt, cr64& rs) -> void {
rd.u64 = rt.i32 >> (rs.u32 & 31);
}
auto CPU::instructionSRL(r64& rd, cr64& rt, u8 sa) -> void {
rd.u64 = i32(rt.u32 >> sa);
}
auto CPU::instructionSRLV(r64& rd, cr64& rt, cr64& rs) -> void {
rd.u64 = i32(rt.u32 >> (rs.u32 & 31));
}
auto CPU::instructionSUB(r64& rd, cr64& rs, cr64& rt) -> void {
//if(rs.u32 < rt.u32) return exception.arithmeticOverflow();
rd.u64 = i32(rs.u32 - rt.u32);
}
auto CPU::instructionSUBU(r64& rd, cr64& rs, cr64& rt) -> void {
rd.u64 = i32(rs.u32 - rt.u32);
}
auto CPU::instructionSW(cr64& rt, cr64& rs, i16 imm) -> void {
writeWord(rs.u32 + imm, rt.u32);
}
auto CPU::instructionSWL(cr64& rt, cr64& rs, i16 imm) -> void {
auto address = rs.u32 + imm;
auto shift = 8 * ((address ^ FlipLE) & 3);
auto mask = u32(0) - 1 >> shift;
if(auto data = readWord(address & ~3)) {
writeWord(address & ~3, *data & ~mask | rt.u32 >> shift);
}
}
auto CPU::instructionSWR(cr64& rt, cr64& rs, i16 imm) -> void {
auto address = rs.u32 + imm;
auto shift = 8 * ((address ^ FlipBE) & 3);
auto mask = u32(0) - 1 << shift;
if(auto data = readWord(address & ~3)) {
writeWord(address & ~3, *data & ~mask | rt.u32 << shift);
}
}
auto CPU::instructionSYNC() -> void {
//no operation; for compatibility with R4000-series code
}
auto CPU::instructionSYSCALL() -> void {
exception.systemCall();
}
auto CPU::instructionTEQ(cr64& rs, cr64& rt) -> void {
if(rs.u64 == rt.u64) exception.trap();
}
auto CPU::instructionTEQI(cr64& rs, i16 imm) -> void {
if(rs.i64 == imm) exception.trap();
}
auto CPU::instructionTGE(cr64& rs, cr64& rt) -> void {
if(rs.i64 >= rt.i64) exception.trap();
}
auto CPU::instructionTGEI(cr64& rs, i16 imm) -> void {
if(rs.i64 >= imm) exception.trap();
}
auto CPU::instructionTGEIU(cr64& rs, i16 imm) -> void {
if(rs.u64 >= imm) exception.trap();
}
auto CPU::instructionTGEU(cr64& rs, cr64& rt) -> void {
if(rs.u64 >= rt.u64) exception.trap();
}
auto CPU::instructionTLT(cr64& rs, cr64& rt) -> void {
if(rs.i64 < rt.i64) exception.trap();
}
auto CPU::instructionTLTI(cr64& rs, i16 imm) -> void {
if(rs.i64 < imm) exception.trap();
}
auto CPU::instructionTLTIU(cr64& rs, i16 imm) -> void {
if(rs.u64 < imm) exception.trap();
}
auto CPU::instructionTLTU(cr64& rs, cr64& rt) -> void {
if(rs.u64 < rt.u64) exception.trap();
}
auto CPU::instructionTNE(cr64& rs, cr64& rt) -> void {
if(rs.u64 != rt.u64) exception.trap();
}
auto CPU::instructionTNEI(cr64& rs, i16 imm) -> void {
if(rs.i64 != imm) exception.trap();
}
auto CPU::instructionXOR(r64& rd, cr64& rs, cr64& rt) -> void {
rd.u64 = rs.u64 ^ rt.u64;
}
auto CPU::instructionXORI(r64& rt, cr64& rs, u16 imm) -> void {
rt.u64 = rs.u64 ^ imm;
}
+160
View File
@@ -0,0 +1,160 @@
//{
union r64 {
struct { int32_t order_msb2(i32h, i32); };
struct { uint32_t order_msb2(u32h, u32); };
struct { float32_t order_msb2(f32h, f32); };
struct { int64_t i64; };
struct { uint64_t u64; };
struct { float64_t f64; };
auto i128() const -> int128_t { return (int128_t)i64; }
auto u128() const -> uint128_t { return (uint128_t)u64; }
};
using cr64 = const r64;
struct Core {
enum Register : uint {
R0, //zero (read-only)
AT, //assembler temporary
V0, V1, //arithmetic values
A0, A1, A2, A3, //subroutine parameters
T0, T1, T2, T3, T4, T5, T6, T7, //temporary registers
S0, S1, S2, S3, S4, S5, S6, S7, //saved registers
T8, T9, //temporary registers
K0, K1, //kernel registers
GP, //global pointer
SP, //stack pointer
S8, //saved register
RA, //return address
};
r64 r[32];
r64 lo;
r64 hi;
u64 pc; //program counter
} core;
struct Branch {
enum : u32 { Step, Take, DelaySlot, Exception, Discard };
auto inDelaySlot() const -> bool { return state == DelaySlot; }
auto reset() -> void { state = Step; }
auto take(u32 address) -> void { state = Take; pc = address; }
auto delaySlot() -> void { state = DelaySlot; }
auto exception() -> void { state = Exception; }
auto discard() -> void { state = Discard; }
u64 pc;
u32 state;
} branch;
//cpu-instructions.cpp
auto instructionADD(r64& rd, cr64& rs, cr64& rt) -> void;
auto instructionADDI(r64& rt, cr64& rs, i16 imm) -> void;
auto instructionADDIU(r64& rt, cr64& rs, i16 imm) -> void;
auto instructionADDU(r64& rd, cr64& rs, cr64& rt) -> void;
auto instructionAND(r64& rd, cr64& rs, cr64& rt) -> void;
auto instructionANDI(r64& rt, cr64& rs, u16 imm) -> void;
auto instructionBEQ(cr64& rs, cr64& rt, i16 imm) -> void;
auto instructionBEQL(cr64& rs, cr64& rt, i16 imm) -> void;
auto instructionBGEZ(cr64& rs, i16 imm) -> void;
auto instructionBGEZAL(cr64& rs, i16 imm) -> void;
auto instructionBGEZALL(cr64& rs, i16 imm) -> void;
auto instructionBGEZL(cr64& rs, i16 imm) -> void;
auto instructionBGTZ(cr64& rs, i16 imm) -> void;
auto instructionBGTZL(cr64& rs, i16 imm) -> void;
auto instructionBLEZ(cr64& rs, i16 imm) -> void;
auto instructionBLEZL(cr64& rs, i16 imm) -> void;
auto instructionBLTZ(cr64& rs, i16 imm) -> void;
auto instructionBLTZAL(cr64& rs, i16 imm) -> void;
auto instructionBLTZALL(cr64& rs, i16 imm) -> void;
auto instructionBLTZL(cr64& rs, i16 imm) -> void;
auto instructionBNE(cr64& rs, cr64& rt, i16 imm) -> void;
auto instructionBNEL(cr64& rs, cr64& rt, i16 imm) -> void;
auto instructionBAL(bool take, i16 imm) -> void;
auto instructionBALL(bool take, i16 imm) -> void;
auto instructionBREAK() -> void;
auto instructionCACHE(u8 cache, u8 operation) -> void;
auto instructionDADD(r64& rd, cr64& rs, cr64& rt) -> void;
auto instructionDADDI(r64& rt, cr64& rs, i16 imm) -> void;
auto instructionDADDIU(r64& rt, cr64& rs, i16 imm) -> void;
auto instructionDADDU(r64& rd, cr64& rs, cr64& rt) -> void;
auto instructionDDIV(cr64& rs, cr64& rt) -> void;
auto instructionDDIVU(cr64& rs, cr64& rt) -> void;
auto instructionDIV(cr64& rs, cr64& rt) -> void;
auto instructionDIVU(cr64& rs, cr64& rt) -> void;
auto instructionDMULT(cr64& rs, cr64& rt) -> void;
auto instructionDMULTU(cr64& rs, cr64& rt) -> void;
auto instructionDSLL(r64& rd, cr64& rt, u8 sa) -> void;
auto instructionDSLLV(r64& rd, cr64& rt, cr64& rs) -> void;
auto instructionDSRA(r64& rd, cr64& rt, u8 sa) -> void;
auto instructionDSRAV(r64& rd, cr64& rt, cr64& rs) -> void;
auto instructionDSRL(r64& rd, cr64& rt, u8 sa) -> void;
auto instructionDSRLV(r64& rd, cr64& rt, cr64& rs) -> void;
auto instructionDSUB(r64& rd, cr64& rs, cr64& rt) -> void;
auto instructionDSUBU(r64& rd, cr64& rs, cr64& rt) -> void;
auto instructionJ(u32 imm) -> void;
auto instructionJAL(u32 imm) -> void;
auto instructionJALR(r64& rd, cr64& rs) -> void;
auto instructionJR(cr64& rs) -> void;
auto instructionLB(r64& rt, cr64& rs, i16 imm) -> void;
auto instructionLBU(r64& rt, cr64& rs, i16 imm) -> void;
auto instructionLD(r64& rt, cr64& rs, i16 imm) -> void;
auto instructionLDL(r64& rt, cr64& rs, i16 imm) -> void;
auto instructionLDR(r64& rt, cr64& rs, i16 imm) -> void;
auto instructionLH(r64& rt, cr64& rs, i16 imm) -> void;
auto instructionLHU(r64& rt, cr64& rs, i16 imm) -> void;
auto instructionLUI(r64& rt, u16 imm) -> void;
auto instructionLL(r64& rt, cr64& rs, i16 imm) -> void;
auto instructionLLD(r64& rt, cr64& rs, i16 imm) -> void;
auto instructionLW(r64& rt, cr64& rs, i16 imm) -> void;
auto instructionLWL(r64& rt, cr64& rs, i16 imm) -> void;
auto instructionLWR(r64& rt, cr64& rs, i16 imm) -> void;
auto instructionLWU(r64& rt, cr64& rs, i16 imm) -> void;
auto instructionMFHI(r64& rd) -> void;
auto instructionMFLO(r64& rd) -> void;
auto instructionMTHI(cr64& rs) -> void;
auto instructionMTLO(cr64& rs) -> void;
auto instructionMULT(cr64& rs, cr64& rt) -> void;
auto instructionMULTU(cr64& rs, cr64& rt) -> void;
auto instructionNOR(r64& rd, cr64& rs, cr64& rt) -> void;
auto instructionOR(r64& rd, cr64& rs, cr64& rt) -> void;
auto instructionORI(r64& rt, cr64& rs, u16 imm) -> void;
auto instructionSB(cr64& rt, cr64& rs, i16 imm) -> void;
auto instructionSC(r64& rt, cr64& rs, i16 imm) -> void;
auto instructionSD(cr64& rt, cr64& rs, i16 imm) -> void;
auto instructionSCD(r64& rt, cr64& rs, i16 imm) -> void;
auto instructionSDL(cr64& rt, cr64& rs, i16 imm) -> void;
auto instructionSDR(cr64& rt, cr64& rs, i16 imm) -> void;
auto instructionSH(cr64& rt, cr64& rs, i16 imm) -> void;
auto instructionSLL(r64& rd, cr64& rt, u8 sa) -> void;
auto instructionSLLV(r64& rd, cr64& rt, cr64& rs) -> void;
auto instructionSLT(r64& rd, cr64& rs, cr64& rt) -> void;
auto instructionSLTI(r64& rt, cr64& rs, i16 imm) -> void;
auto instructionSLTIU(r64& rt, cr64& rs, i16 imm) -> void;
auto instructionSLTU(r64& rd, cr64& rs, cr64& rt) -> void;
auto instructionSRA(r64& rd, cr64& rt, u8 sa) -> void;
auto instructionSRAV(r64& rd, cr64& rt, cr64& rs) -> void;
auto instructionSRL(r64& rd, cr64& rt, u8 sa) -> void;
auto instructionSRLV(r64& rd, cr64& rt, cr64& rs) -> void;
auto instructionSUB(r64& rd, cr64& rs, cr64& rt) -> void;
auto instructionSUBU(r64& rd, cr64& rs, cr64& rt) -> void;
auto instructionSW(cr64& rt, cr64& rs, i16 imm) -> void;
auto instructionSWL(cr64& rt, cr64& rs, i16 imm) -> void;
auto instructionSWR(cr64& rt, cr64& rs, i16 imm) -> void;
auto instructionSYNC() -> void;
auto instructionSYSCALL() -> void;
auto instructionTEQ(cr64& rs, cr64& rt) -> void;
auto instructionTEQI(cr64& rs, i16 imm) -> void;
auto instructionTGE(cr64& rs, cr64& rt) -> void;
auto instructionTGEI(cr64& rs, i16 imm) -> void;
auto instructionTGEIU(cr64& rs, i16 imm) -> void;
auto instructionTGEU(cr64& rs, cr64& rt) -> void;
auto instructionTLT(cr64& rs, cr64& rt) -> void;
auto instructionTLTI(cr64& rs, i16 imm) -> void;
auto instructionTLTIU(cr64& rs, i16 imm) -> void;
auto instructionTLTU(cr64& rs, cr64& rt) -> void;
auto instructionTNE(cr64& rs, cr64& rt) -> void;
auto instructionTNEI(cr64& rs, i16 imm) -> void;
auto instructionXOR(r64& rd, cr64& rs, cr64& rt) -> void;
auto instructionXORI(r64& rt, cr64& rs, u16 imm) -> void;
//};
+66
View File
@@ -0,0 +1,66 @@
#define OP pipeline.instruction
#define RD GPR[RDn]
#define RT GPR[RTn]
#define RS GPR[RSn]
#define jp(id, name, ...) case id: return decoder##name(__VA_ARGS__)
#define op(id, name, ...) case id: return instruction##name(__VA_ARGS__)
#define br(id, name, ...) case id: return instruction##name(__VA_ARGS__)
auto CPU::decoderEXECUTE() -> void {
#define DECODER_EXECUTE
#include "decoder.hpp"
}
auto CPU::decoderSPECIAL() -> void {
#define DECODER_SPECIAL
#include "decoder.hpp"
}
auto CPU::decoderREGIMM() -> void {
#define DECODER_REGIMM
#include "decoder.hpp"
}
auto CPU::decoderCOP0() -> void {
if(!scc.status.enable.coprocessor0) {
if(context.mode != Context::Mode::Kernel) return exception.coprocessor0();
}
#define DECODER_COP0
#include "decoder.hpp"
//undefined instructions do not throw a reserved instruction exception
}
auto CPU::decoderCOP1() -> void {
if(!scc.status.enable.coprocessor1) {
return exception.coprocessor1();
}
#define DECODER_COP1
#include "decoder.hpp"
//undefined instructions do not throw a reserved instruction exception
}
auto CPU::instructionCOP2() -> void {
exception.coprocessor2();
}
auto CPU::instructionCOP3() -> void {
exception.coprocessor3();
}
auto CPU::instructionINVALID() -> void {
exception.reservedInstruction();
}
#undef jp
#undef op
#undef br
#undef OP
#undef RD
#undef RT
#undef RS
+353
View File
@@ -0,0 +1,353 @@
//shares instruction decoding between interpreter and dynamic recompiler
#define SA (OP >> 6 & 31)
#define RDn (OP >> 11 & 31)
#define RTn (OP >> 16 & 31)
#define RSn (OP >> 21 & 31)
#define FD (OP >> 6 & 31)
#define FS (OP >> 11 & 31)
#define FT (OP >> 16 & 31)
#define IMMi16 i16(OP)
#define IMMu16 u16(OP)
#define IMMu26 (OP & 0x03ff'ffff)
#ifdef DECODER_EXECUTE
{
switch(OP >> 26) {
jp(0x00, SPECIAL);
jp(0x01, REGIMM);
br(0x02, J, IMMu26);
br(0x03, JAL, IMMu26);
br(0x04, BEQ, RS, RT, IMMi16);
br(0x05, BNE, RS, RT, IMMi16);
br(0x06, BLEZ, RS, IMMi16);
br(0x07, BGTZ, RS, IMMi16);
op(0x08, ADDI, RT, RS, IMMi16);
op(0x09, ADDIU, RT, RS, IMMi16);
op(0x0a, SLTI, RT, RS, IMMi16);
op(0x0b, SLTIU, RT, RS, IMMi16);
op(0x0c, ANDI, RT, RS, IMMu16);
op(0x0d, ORI, RT, RS, IMMu16);
op(0x0e, XORI, RT, RS, IMMu16);
op(0x0f, LUI, RT, IMMu16);
jp(0x10, COP0);
jp(0x11, COP1);
op(0x12, COP2);
op(0x13, COP3);
br(0x14, BEQL, RS, RT, IMMi16);
br(0x15, BNEL, RS, RT, IMMi16);
br(0x16, BLEZL, RS, IMMi16);
br(0x17, BGTZL, RS, IMMi16);
op(0x18, DADDI, RT, RS, IMMi16);
op(0x19, DADDIU, RT, RS, IMMi16);
op(0x1a, LDL, RT, RS, IMMi16);
op(0x1b, LDR, RT, RS, IMMi16);
op(0x1c, INVALID);
op(0x1d, INVALID);
op(0x1e, INVALID);
op(0x1f, INVALID);
op(0x20, LB, RT, RS, IMMi16);
op(0x21, LH, RT, RS, IMMi16);
op(0x22, LWL, RT, RS, IMMi16);
op(0x23, LW, RT, RS, IMMi16);
op(0x24, LBU, RT, RS, IMMi16);
op(0x25, LHU, RT, RS, IMMi16);
op(0x26, LWR, RT, RS, IMMi16);
op(0x27, LWU, RT, RS, IMMi16);
op(0x28, SB, RT, RS, IMMi16);
op(0x29, SH, RT, RS, IMMi16);
op(0x2a, SWL, RT, RS, IMMi16);
op(0x2b, SW, RT, RS, IMMi16);
op(0x2c, SDL, RT, RS, IMMi16);
op(0x2d, SDR, RT, RS, IMMi16);
op(0x2e, SWR, RT, RS, IMMi16);
op(0x2f, CACHE, OP >> 16 & 3, OP >> 18 & 3);
op(0x30, LL, RT, RS, IMMi16);
op(0x31, LWC1, FT, RS, IMMi16);
op(0x32, COP2); //LWC2
op(0x33, COP3); //LWC3
op(0x34, LLD, RT, RS, IMMi16);
op(0x35, LDC1, FT, RS, IMMi16);
op(0x36, COP2); //LDC2
op(0x37, LD, RT, RS, IMMi16);
op(0x38, SC, RT, RS, IMMi16);
op(0x39, SWC1, FT, RS, IMMi16);
op(0x3a, COP2); //SWC2
op(0x3b, COP3); //SWC3
op(0x3c, SCD, RT, RS, IMMi16);
op(0x3d, SDC1, FT, RS, IMMi16);
op(0x3e, COP2); //SDC2
op(0x3f, SD, RT, RS, IMMi16);
}
}
#undef DECODER_EXECUTE
#endif
#ifdef DECODER_SPECIAL
{
switch(OP & 0x3f) {
op(0x00, SLL, RD, RT, SA);
op(0x01, INVALID);
op(0x02, SRL, RD, RT, SA);
op(0x03, SRA, RD, RT, SA);
op(0x04, SLLV, RD, RT, RS);
op(0x05, INVALID);
op(0x06, SRLV, RD, RT, RS);
op(0x07, SRAV, RD, RT, RS);
br(0x08, JR, RS);
br(0x09, JALR, RD, RS);
op(0x0a, INVALID);
op(0x0b, INVALID);
br(0x0c, SYSCALL);
br(0x0d, BREAK);
op(0x0e, INVALID);
op(0x0f, SYNC);
op(0x10, MFHI, RD);
op(0x11, MTHI, RS);
op(0x12, MFLO, RD);
op(0x13, MTLO, RS);
op(0x14, DSLLV, RD, RT, RS);
op(0x15, INVALID);
op(0x16, DSRLV, RD, RT, RS);
op(0x17, DSRAV, RD, RT, RS);
op(0x18, MULT, RS, RT);
op(0x19, MULTU, RS, RT);
op(0x1a, DIV, RS, RT);
op(0x1b, DIVU, RS, RT);
op(0x1c, DMULT, RS, RT);
op(0x1d, DMULTU, RS, RT);
op(0x1e, DDIV, RS, RT);
op(0x1f, DDIVU, RS, RT);
op(0x20, ADD, RD, RS, RT);
op(0x21, ADDU, RD, RS, RT);
op(0x22, SUB, RD, RS, RT);
op(0x23, SUBU, RD, RS, RT);
op(0x24, AND, RD, RS, RT);
op(0x25, OR, RD, RS, RT);
op(0x26, XOR, RD, RS, RT);
op(0x27, NOR, RD, RS, RT);
op(0x28, INVALID);
op(0x29, INVALID);
op(0x2a, SLT, RD, RS, RT);
op(0x2b, SLTU, RD, RS, RT);
op(0x2c, DADD, RD, RS, RT);
op(0x2d, DADDU, RD, RS, RT);
op(0x2e, DSUB, RD, RS, RT);
op(0x2f, DSUBU, RD, RS, RT);
op(0x30, TGE, RS, RT);
op(0x31, TGEU, RS, RT);
op(0x32, TLT, RS, RT);
op(0x33, TLTU, RS, RT);
op(0x34, TEQ, RS, RT);
op(0x35, INVALID);
op(0x36, TNE, RS, RT);
op(0x37, INVALID);
op(0x38, DSLL, RD, RT, SA);
op(0x39, INVALID);
op(0x3a, DSRL, RD, RT, SA);
op(0x3b, DSRA, RD, RT, SA);
op(0x3c, DSLL, RD, RT, SA + 32);
op(0x3d, INVALID);
op(0x3e, DSRL, RD, RT, SA + 32);
op(0x3f, DSRA, RD, RT, SA + 32);
}
}
#undef DECODER_SPECIAL
#endif
#ifdef DECODER_REGIMM
{
switch(OP >> 16 & 0x1f) {
br(0x00, BLTZ, RS, IMMi16);
br(0x01, BGEZ, RS, IMMi16);
br(0x02, BLTZL, RS, IMMi16);
br(0x03, BGEZL, RS, IMMi16);
op(0x04, INVALID);
op(0x05, INVALID);
op(0x06, INVALID);
op(0x07, INVALID);
op(0x08, TGEI, RS, IMMi16);
op(0x09, TGEIU, RS, IMMi16);
op(0x0a, TLTI, RS, IMMi16);
op(0x0b, TLTIU, RS, IMMi16);
op(0x0c, TEQI, RS, IMMi16);
op(0x0d, INVALID);
op(0x0e, TNEI, RS, IMMi16);
op(0x0f, INVALID);
br(0x10, BLTZAL, RS, IMMi16);
br(0x11, BGEZAL, RS, IMMi16);
br(0x12, BLTZALL, RS, IMMi16);
br(0x13, BGEZALL, RS, IMMi16);
op(0x14, INVALID);
op(0x15, INVALID);
op(0x16, INVALID);
op(0x17, INVALID);
op(0x18, INVALID);
op(0x19, INVALID);
op(0x1a, INVALID);
op(0x1b, INVALID);
op(0x1c, INVALID);
op(0x1d, INVALID);
op(0x1e, INVALID);
op(0x1f, INVALID);
}
}
#undef DECODER_REGIMM
#endif
#ifdef DECODER_COP0
{
switch(OP >> 21 & 0x1f) {
op(0x00, MFC0, RT, RDn);
op(0x01, DMFC0, RT, RDn);
op(0x02, INVALID); //CFC0
op(0x03, INVALID);
op(0x04, MTC0, RT, RDn);
op(0x05, DMTC0, RT, RDn);
op(0x06, INVALID); //CTC0
op(0x07, INVALID);
op(0x08, INVALID); //BC0
op(0x09, INVALID);
op(0x0a, INVALID);
op(0x0b, INVALID);
op(0x0c, INVALID);
op(0x0d, INVALID);
op(0x0e, INVALID);
op(0x0f, INVALID);
}
switch(OP & 0x3f) {
op(0x01, TLBR);
op(0x02, TLBWI);
op(0x06, TLBWR);
op(0x08, TLBP);
br(0x18, ERET);
}
}
#undef DECODER_COP0
#endif
#ifdef DECODER_COP1
{
switch(OP >> 21 & 0x1f) {
op(0x00, MFC1, RT, FS);
op(0x01, DMFC1, RT, FS);
op(0x02, CFC1, RT, RDn);
op(0x03, INVALID);
op(0x04, MTC1, RT, FS);
op(0x05, DMTC1, RT, FS);
op(0x06, CTC1, RT, RDn);
op(0x07, INVALID);
br(0x08, BC1, OP >> 16 & 1, OP >> 17 & 1, IMMi16);
op(0x09, INVALID);
op(0x0a, INVALID);
op(0x0b, INVALID);
op(0x0c, INVALID);
op(0x0d, INVALID);
op(0x0e, INVALID);
op(0x0f, INVALID);
}
if((OP >> 21 & 31) == 16)
switch(OP & 0x3f) {
op(0x00, FADD_S, FD, FS, FT);
op(0x01, FSUB_S, FD, FS, FT);
op(0x02, FMUL_S, FD, FS, FT);
op(0x03, FDIV_S, FD, FS, FT);
op(0x04, FSQRT_S, FD, FS);
op(0x05, FABS_S, FD, FS);
op(0x06, FMOV_S, FD, FS);
op(0x07, FNEG_S, FD, FS);
op(0x08, FROUND_L_S, FD, FS);
op(0x09, FTRUNC_L_S, FD, FS);
op(0x0a, FCEIL_L_S, FD, FS);
op(0x0b, FFLOOR_L_S, FD, FS);
op(0x0c, FROUND_W_S, FD, FS);
op(0x0d, FTRUNC_W_S, FD, FS);
op(0x0e, FCEIL_W_S, FD, FS);
op(0x0f, FFLOOR_W_S, FD, FS);
op(0x21, FCVT_D_S, FD, FS);
op(0x24, FCVT_W_S, FD, FS);
op(0x25, FCVT_L_S, FD, FS);
op(0x30, FC_F_S, FS, FT);
op(0x31, FC_UN_S, FS, FT);
op(0x32, FC_EQ_S, FS, FT);
op(0x33, FC_UEQ_S, FS, FT);
op(0x34, FC_OLT_S, FS, FT);
op(0x35, FC_ULT_S, FS, FT);
op(0x36, FC_OLE_S, FS, FT);
op(0x37, FC_ULE_S, FS, FT);
op(0x38, FC_SF_S, FS, FT);
op(0x39, FC_NGLE_S, FS, FT);
op(0x3a, FC_SEQ_S, FS, FT);
op(0x3b, FC_NGL_S, FS, FT);
op(0x3c, FC_LT_S, FS, FT);
op(0x3d, FC_NGE_S, FS, FT);
op(0x3e, FC_LE_S, FS, FT);
op(0x3f, FC_NGT_S, FS, FT);
}
if((OP >> 21 & 31) == 17)
switch(OP & 0x3f) {
op(0x00, FADD_D, FD, FS, FT);
op(0x01, FSUB_D, FD, FS, FT);
op(0x02, FMUL_D, FD, FS, FT);
op(0x03, FDIV_D, FD, FS, FT);
op(0x04, FSQRT_D, FD, FS);
op(0x05, FABS_D, FD, FS);
op(0x06, FMOV_D, FD, FS);
op(0x07, FNEG_D, FD, FS);
op(0x08, FROUND_L_D, FD, FS);
op(0x09, FTRUNC_L_D, FD, FS);
op(0x0a, FCEIL_L_D, FD, FS);
op(0x0b, FFLOOR_L_D, FD, FS);
op(0x0c, FROUND_W_D, FD, FS);
op(0x0d, FTRUNC_W_D, FD, FS);
op(0x0e, FCEIL_W_D, FD, FS);
op(0x0f, FFLOOR_W_D, FD, FS);
op(0x20, FCVT_S_D, FD, FS);
op(0x24, FCVT_W_D, FD, FS);
op(0x25, FCVT_L_D, FD, FS);
op(0x30, FC_F_D, FS, FT);
op(0x31, FC_UN_D, FS, FT);
op(0x32, FC_EQ_D, FS, FT);
op(0x33, FC_UEQ_D, FS, FT);
op(0x34, FC_OLT_D, FS, FT);
op(0x35, FC_ULT_D, FS, FT);
op(0x36, FC_OLE_D, FS, FT);
op(0x37, FC_ULE_D, FS, FT);
op(0x38, FC_SF_D, FS, FT);
op(0x39, FC_NGLE_D, FS, FT);
op(0x3a, FC_SEQ_D, FS, FT);
op(0x3b, FC_NGL_D, FS, FT);
op(0x3c, FC_LT_D, FS, FT);
op(0x3d, FC_NGE_D, FS, FT);
op(0x3e, FC_LE_D, FS, FT);
op(0x3f, FC_NGT_D, FS, FT);
}
if((OP >> 21 & 31) == 20)
switch(OP & 0x3f) {
op(0x20, FCVT_S_W, FD, FS);
op(0x21, FCVT_D_W, FD, FS);
}
if((OP >> 21 & 31) == 21)
switch(OP & 0x3f) {
op(0x20, FCVT_S_L, FD, FS);
op(0x21, FCVT_D_L, FD, FS);
}
}
#undef DECODER_COP1
#endif
#undef SA
#undef RDn
#undef RTn
#undef RSn
#undef FD
#undef FS
#undef FT
#undef IMMi16
#undef IMMu16
#undef IMMu26
+483
View File
@@ -0,0 +1,483 @@
auto CPU::Disassembler::disassemble(u32 address, u32 instruction) -> string {
this->address = address;
this->instruction = instruction;
auto v = EXECUTE();
if(!v) v.append("invalid", string{"$", hex(instruction, 8L)});
if(!instruction) v = {"nop"};
auto s = pad(v.takeFirst(), -8L);
return {s, v.merge(",")};
}
auto CPU::Disassembler::EXECUTE() -> vector<string> {
auto rtName = [&] { return cpuRegisterName (instruction >> 16 & 31); };
auto rtValue = [&] { return cpuRegisterValue(instruction >> 16 & 31); };
auto rsValue = [&] { return cpuRegisterValue(instruction >> 21 & 31); };
auto ftName = [&] { return fpuRegisterName (instruction >> 16 & 31); };
auto ftValue = [&] { return fpuRegisterValue(instruction >> 16 & 31); };
auto imm16i = [&] { return immediate(i16(instruction)); };
auto imm16u = [&] { return immediate(u16(instruction), 16L); };
auto jump = [&] { return immediate(address + 4 & 0xf000'0000 | (instruction & 0x03ff'ffff) << 2); };
auto branch = [&] { return immediate(address + 4 + (i16(instruction) << 2)); };
auto offset = [&] { return cpuRegisterIndex(instruction >> 21 & 31, i16(instruction)); };
auto ALU = [&](string_view name) -> vector<string> {
return {name, rtName(), rsValue(), immediate(u16(instruction))};
};
auto ADDI = [&](string_view add, string_view sub, string_view mov) -> vector<string> {
if(!(instruction >> 21 & 31)) return {mov, rtName(), immediate(i16(instruction), 32L)};
return {i16(instruction) >= 0 ? add : sub, rtName(), rsValue(), immediate(abs(i16(instruction)))};
};
auto BRANCH1 = [&](string_view name) -> vector<string> {
return {name, rsValue(), branch()};
};
auto BRANCH2 = [&](string_view name) -> vector<string> {
return {name, rsValue(), rtValue(), branch()};
};
auto CACHE = [&](string_view name) -> vector<string> {
auto cache = instruction >> 16 & 3;
auto op = instruction >> 18 & 7;
string type = "reserved";
if(cache == 0) switch(op) {
case 0: type = "code(IndexInvalidate)"; break;
case 1: type = "code(IndexLoadTag)"; break;
case 2: type = "code(IndexStoreTag)"; break;
case 4: type = "code(HitInvalidate)"; break;
case 5: type = "code(Fill)"; break;
case 6: type = "code(HitWriteBack)"; break;
}
if(cache == 1) switch(op) {
case 0: type = "data(IndexWriteBackInvalidate)"; break;
case 1: type = "data(IndexLoadTag)"; break;
case 2: type = "data(IndexStoreTag)"; break;
case 3: type = "data(CreateDirtyExclusive)"; break;
case 4: type = "data(HitInvalidate)"; break;
case 5: type = "data(HitWriteBackInvalidate)"; break;
case 6: type = "data(HitWriteBack)"; break;
}
return {name, type, offset()};
};
auto JUMP = [&](string_view name) -> vector<string> {
return {name, jump()};
};
auto LOAD = [&](string_view name) -> vector<string> {
return {name, rtName(), offset()};
};
auto STORE = [&](string_view name) -> vector<string> {
return {name, rtValue(), offset()};
};
switch(instruction >> 26) {
case 0x00: return SPECIAL();
case 0x01: return REGIMM();
case 0x02: return JUMP("j");
case 0x03: return JUMP("jal");
case 0x04: return BRANCH2("beq");
case 0x05: return BRANCH2("bne");
case 0x06: return BRANCH1("blez");
case 0x07: return BRANCH1("bgtz");
case 0x08: return ADDI("addi", "subi", "li");
case 0x09: return ADDI("addiu", "subiu", "liu");
case 0x0a: return ALU("slti");
case 0x0b: return ALU("sltiu");
case 0x0c: return ALU("andi");
case 0x0d: return ALU("ori");
case 0x0e: return ALU("xori");
case 0x0f: return {"lui", rtName(), imm16u()};
case 0x10: return SCC();
case 0x11: return FPU();
case 0x12: break; //COP2
case 0x13: break; //COP3
case 0x14: return BRANCH2("beql");
case 0x15: return BRANCH2("bnel");
case 0x16: return BRANCH1("blezl");
case 0x17: return BRANCH1("bgtzl");
case 0x18: return ADDI("daddi", "dsubi", "dli");
case 0x19: return ADDI("daddiu", "dsubiu", "dliu");
case 0x1a: return LOAD("ldl");
case 0x1b: return LOAD("ldr");
case 0x1c: break;
case 0x1d: break;
case 0x1e: break;
case 0x1f: break;
case 0x20: return LOAD("lb");
case 0x21: return LOAD("lh");
case 0x22: return LOAD("lwl");
case 0x23: return LOAD("lw");
case 0x24: return LOAD("lbu");
case 0x25: return LOAD("lhu");
case 0x26: return LOAD("lwr");
case 0x27: return LOAD("lwu");
case 0x28: return STORE("sb");
case 0x29: return STORE("sh");
case 0x2a: return STORE("swl");
case 0x2b: return STORE("sw");
case 0x2c: return STORE("sdl");
case 0x2d: return STORE("sdr");
case 0x2e: return STORE("swr");
case 0x2f: return CACHE("cache");
case 0x30: return LOAD("ll");
case 0x31: return {"lwc1", ftName(), offset()};
case 0x32: break; //LWC2
case 0x33: break; //LWC3
case 0x34: return LOAD("lld");
case 0x35: return {"ldc1", ftName(), offset()};
case 0x36: break; //LDC2
case 0x37: return LOAD("ld");
case 0x38: return STORE("sc");
case 0x39: return {"swc1", ftValue(), offset()};
case 0x3a: break; //SWC2
case 0x3b: break; //SWC3
case 0x3c: return STORE("scd");
case 0x3d: return {"sdc1", ftValue(), offset()};
case 0x3e: break; //SDC2
case 0x3f: return STORE("sd");
}
return {};
}
auto CPU::Disassembler::SPECIAL() -> vector<string> {
auto shift = [&] { return string{instruction >> 6 & 31}; };
auto rdName = [&] { return cpuRegisterName (instruction >> 11 & 31); };
auto rdValue = [&] { return cpuRegisterValue(instruction >> 11 & 31); };
auto rtValue = [&] { return cpuRegisterValue(instruction >> 16 & 31); };
auto rsValue = [&] { return cpuRegisterValue(instruction >> 21 & 31); };
auto ALU = [&](string_view name, string_view by) -> vector<string> {
return {name, rdName(), rtValue(), by};
};
auto JALR = [&](string_view name) -> vector<string> {
if((instruction >> 11 & 31) == 31) return {name, rsValue()};
return {name, rdName(), rsValue()};
};
auto REG = [&](string_view name) -> vector<string> {
return {name, rdName(), rsValue(), rtValue()};
};
auto ST = [&](string_view name) -> vector<string> {
return {name, rsValue(), rtValue()};
};
switch(instruction & 0x3f) {
case 0x00: return ALU("sll", shift());
case 0x01: break;
case 0x02: return ALU("srl", shift());
case 0x03: return ALU("sra", shift());
case 0x04: return ALU("sllv", rsValue());
case 0x05: break;
case 0x06: return ALU("srlv", rsValue());
case 0x07: return ALU("srav", rsValue());
case 0x08: return {"jr", rsValue()};
case 0x09: return JALR("jalr");
case 0x0a: break;
case 0x0b: break;
case 0x0c: return {"syscall"};
case 0x0d: return {"break"};
case 0x0e: break;
case 0x0f: return {"sync"};
case 0x10: return {"mfhi", rdName(), {"hi", hint("{$", hex(self.core.hi.u64, 8L), "}")}};
case 0x11: return {"mthi", rsValue(), "hi"};
case 0x12: return {"mflo", rdName(), {"lo", hint("{$", hex(self.core.lo.u64, 8L), "}")}};
case 0x13: return {"mtlo", rsValue(), "lo"};
case 0x14: return ALU("dsllv", rsValue());
case 0x15: break;
case 0x16: return ALU("dsrlv", rsValue());
case 0x17: return ALU("dsrav", rsValue());
case 0x18: return ST("mult");
case 0x19: return ST("multu");
case 0x1a: return ST("div");
case 0x1b: return ST("divu");
case 0x1c: return ST("dmult");
case 0x1d: return ST("dmultu");
case 0x1e: return ST("ddiv");
case 0x1f: return ST("ddivu");
case 0x20: return REG("add");
case 0x21: return REG("addu");
case 0x22: return REG("sub");
case 0x23: return REG("subu");
case 0x24: return REG("and");
case 0x25: return REG("or");
case 0x26: return REG("xor");
case 0x27: return REG("nor");
case 0x28: break;
case 0x29: break;
case 0x2a: return REG("slt");
case 0x2b: return REG("sltu");
case 0x2c: return REG("dadd");
case 0x2d: return REG("daddu");
case 0x2e: return REG("dsub");
case 0x2f: return REG("dsubu");
case 0x30: return ST("tge");
case 0x31: return ST("tgeu");
case 0x32: return ST("tlt");
case 0x33: return ST("tltu");
case 0x34: return ST("teq");
case 0x35: break;
case 0x36: return ST("tne");
case 0x37: break;
case 0x38: return ALU("dsll", shift());
case 0x39: break;
case 0x3a: return ALU("dsrl", shift());
case 0x3b: return ALU("dsra", shift());
case 0x3c: return ALU("dsll32", shift());
case 0x3d: break;
case 0x3e: return ALU("dsrl32", shift());
case 0x3f: return ALU("dsra32", shift());
}
return {};
}
auto CPU::Disassembler::REGIMM() -> vector<string> {
auto rsValue = [&] { return cpuRegisterValue(instruction >> 21 & 31); };
auto imm16i = [&] { return immediate(i16(instruction)); };
auto branch = [&] { return immediate(address + 4 + (i16(instruction) << 2)); };
auto BRANCH = [&](string_view name) -> vector<string> {
return {name, rsValue(), branch()};
};
auto TRAP = [&](string_view name) -> vector<string> {
return {name, rsValue(), imm16i()};
};
switch(instruction >> 16 & 0x1f) {
case 0x00: return BRANCH("bltz");
case 0x01: return BRANCH("bgez");
case 0x02: return BRANCH("bltzl");
case 0x03: return BRANCH("bgezl");
case 0x04: break;
case 0x05: break;
case 0x06: break;
case 0x07: break;
case 0x08: return TRAP("tgei");
case 0x09: return TRAP("tgeiu");
case 0x0a: return TRAP("tlti");
case 0x0b: return TRAP("tltiu");
case 0x0c: return TRAP("teqi");
case 0x0d: break;
case 0x0e: return TRAP("tnei");
case 0x0f: break;
case 0x10: return BRANCH("bltzal");
case 0x11: return BRANCH("bgezal");
case 0x12: return BRANCH("bltzall");
case 0x13: return BRANCH("bgezall");
case 0x14: break;
case 0x15: break;
case 0x16: break;
case 0x17: break;
case 0x18: break;
case 0x19: break;
case 0x1a: break;
case 0x1b: break;
case 0x1c: break;
case 0x1d: break;
case 0x1e: break;
case 0x1f: break;
}
return {};
}
auto CPU::Disassembler::SCC() -> vector<string> {
auto rtName = [&] { return cpuRegisterName (instruction >> 16 & 31); };
auto rtValue = [&] { return cpuRegisterValue(instruction >> 16 & 31); };
auto sdName = [&] { return sccRegisterName (instruction >> 11 & 31); };
auto sdValue = [&] { return sccRegisterValue(instruction >> 11 & 31); };
auto branch = [&] { return immediate(address + 4 + (i16(instruction) << 2)); };
switch(instruction >> 21 & 0x1f) {
case 0x00: return {"mfc0", rtName(), sdValue()};
case 0x01: return {"dmfc0", rtName(), sdValue()};
case 0x02: break; //CFC0
case 0x04: return {"mtc0", rtValue(), sdName()};
case 0x05: return {"dmtc0", rtValue(), sdName()};
case 0x06: break; //CTC0
case 0x08: break; //BC0
}
if(!(instruction >> 25 & 1)) return {};
switch(instruction & 0x3f) {
case 0x01: return {"tlbr"};
case 0x02: return {"tlbwi"};
case 0x06: return {"tlbwr"};
case 0x08: return {"tlbp"};
case 0x18: return {"eret"};
}
return {};
}
auto CPU::Disassembler::FPU() -> vector<string> {
auto rtName = [&] { return cpuRegisterName (instruction >> 16 & 31); };
auto rtValue = [&] { return cpuRegisterValue(instruction >> 16 & 31); };
auto rdName = [&] { return fpuRegisterName (instruction >> 11 & 31); };
auto rdValue = [&] { return fpuRegisterValue(instruction >> 11 & 31); };
auto cdName = [&] { return ccrRegisterName (instruction >> 11 & 31); };
auto cdValue = [&] { return ccrRegisterValue(instruction >> 11 & 31); }; //todo
auto branch = [&] { return immediate(address + 4 + (i16(instruction) << 2)); };
switch(instruction >> 21 & 0x1f) {
case 0x00: return {"mfc1", rtName(), rdValue()};
case 0x01: return {"dmfc1", rtName(), rdValue()};
case 0x02: return {"cfc1", rtName(), cdValue()};
case 0x04: return {"mtc1", rtValue(), rdName()};
case 0x05: return {"dmtc1", rtValue(), rdName()};
case 0x06: return {"ctc1", rtValue(), cdName()};
case 0x08: switch(instruction >> 16 & 3) {
case 0x00: return {"bc1f", branch()};
case 0x01: return {"bc1t", branch()};
case 0x02: return {"bc1fl", branch()};
case 0x03: return {"bc1tl", branch()};
}
}
if(!(instruction >> 25 & 1)) return {};
auto fdName = [&] { return fpuRegisterName (instruction >> 6 & 31); };
auto fsValue = [&] { return fpuRegisterValue(instruction >> 11 & 31); };
auto ftValue = [&] { return fpuRegisterValue(instruction >> 16 & 31); };
auto DS = [&](string_view name) -> vector<string> {
return {name, fdName(), fsValue()};
};
auto DST = [&](string_view name) -> vector<string> {
return {name, fdName(), fsValue(), ftValue()};
};
auto ST = [&](string_view name) -> vector<string> {
return {name, fsValue(), ftValue()};
};
bool s = (instruction & 1 << 21) == 0;
bool i = (instruction & 1 << 23) != 0;
switch(instruction & 0x3f) {
case 0x00: return DST(s ? "adds" : "addd" );
case 0x01: return DST(s ? "subs" : "subd" );
case 0x02: return DST(s ? "muls" : "muld" );
case 0x03: return DST(s ? "divs" : "divd" );
case 0x04: return DS (s ? "sqrts" : "sqrtd" );
case 0x05: return DS (s ? "abss" : "absd" );
case 0x06: return DS (s ? "movs" : "movd" );
case 0x07: return DS (s ? "negs" : "negd" );
case 0x08: return DS (s ? "roundls" : "roundld");
case 0x09: return DS (s ? "truncls" : "truncld");
case 0x0a: return DS (s ? "ceills" : "ceilld" );
case 0x0b: return DS (s ? "floorls" : "floorld");
case 0x0c: return DS (s ? "roundws" : "roundwd");
case 0x0d: return DS (s ? "truncws" : "truncwd");
case 0x0e: return DS (s ? "ceilws" : "ceilwd" );
case 0x0f: return DS (s ? "floorws" : "floorwd");
case 0x20: return DS (i ? (s ? "cvtsw" : "cvtsl") : "cvtsd");
case 0x21: return DS (i ? (s ? "cvtdw" : "cvtdl") : "cvtds");
case 0x24: return DS (s ? "cvtws" : "cvtwd" );
case 0x25: return DS (s ? "cvtls" : "cvtld" );
case 0x30: return ST(s ? "cfs" : "cfd" );
case 0x31: return ST(s ? "cuns" : "cund" );
case 0x32: return ST(s ? "ceqs" : "ceqd" );
case 0x33: return ST(s ? "cueqs" : "cueqd" );
case 0x34: return ST(s ? "colts" : "coltd" );
case 0x35: return ST(s ? "cults" : "cultd" );
case 0x36: return ST(s ? "coles" : "coled" );
case 0x37: return ST(s ? "cules" : "culed" );
case 0x38: return ST(s ? "csfs" : "csfd" );
case 0x39: return ST(s ? "cngles" : "cngled");
case 0x3a: return ST(s ? "cseqs" : "cseqd" );
case 0x3b: return ST(s ? "cngls" : "cngld" );
case 0x3c: return ST(s ? "clts" : "cltd" );
case 0x3d: return ST(s ? "cnges" : "cnged" );
case 0x3e: return ST(s ? "cles" : "cled" );
case 0x3f: return ST(s ? "cngts" : "cngtd" );
}
return {};
}
auto CPU::Disassembler::immediate(i64 value, uint bits) const -> string {
if(value < 0) return {"-$", hex(-value, bits >> 2)};
return {"$", hex(value, bits >> 2)};
};
auto CPU::Disassembler::cpuRegisterName(uint index) const -> string {
static const string registers[32] = {
"0", "at", "v0", "v1", "a0", "a1", "a2", "a3",
"t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7",
"s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
"t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra",
};
return registers[index];
}
auto CPU::Disassembler::cpuRegisterValue(uint index) const -> string {
if(index && showValues) return {cpuRegisterName(index), hint("{$", hex(self.core.r[index].u64, 8L), "}")};
return cpuRegisterName(index);
}
auto CPU::Disassembler::cpuRegisterIndex(uint index, i16 offset) const -> string {
string adjust;
if(offset >= 0) adjust = {"+$", hex( offset)};
if(offset < 0) adjust = {"-$", hex(-offset)};
if(index && showValues) return {cpuRegisterName(index), adjust, hint("{$", hex(self.core.r[index].u64 + offset, 8L), "}")};
return {cpuRegisterName(index), adjust};
}
auto CPU::Disassembler::sccRegisterName(uint index) const -> string {
static const string registers[32] = {
"Index", "Random", "EntryLo0", "EntryLo1",
"Context", "PageMask", "Wired", "Unused7",
"BadVAddr", "Count", "EntryHi", "Compare",
"Status", "Cause", "EPC", "PrID",
"Config", "LLAddr", "WatchLo", "WatchHi",
"XContext", "Unused21", "Unused22", "Unused23",
"Unused24", "Unused25", "ParityError", "CacheError",
"TagLo", "TagHi", "ErrorEPC", "Unused31",
};
return registers[index];
}
auto CPU::Disassembler::sccRegisterValue(uint index) const -> string {
if(showValues) return {sccRegisterName(index), hint("{$", hex(self.getControlRegister(index), 8L), "}")};
return sccRegisterName(index);
}
auto CPU::Disassembler::fpuRegisterName(uint index) const -> string {
static const string registers[32] = {
"f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7",
"f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15",
"f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23",
"f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31",
};
return registers[index];
}
auto CPU::Disassembler::fpuRegisterValue(uint index) const -> string {
bool f32 = (instruction & 1 << 21) == 0;
bool f64 = (instruction & 1 << 21) != 0;
if(f32 && showValues) return {fpuRegisterName(index), hint("{", self.fpu.r[index].f32, "}")};
if(f64 && showValues) return {fpuRegisterName(index), hint("{", self.fpu.r[index].f64, "}")};
return fpuRegisterName(index);
}
auto CPU::Disassembler::ccrRegisterName(uint index) const -> string {
return {"ccr", index};
}
auto CPU::Disassembler::ccrRegisterValue(uint index) const -> string {
if(showValues) return {ccrRegisterName(index), hint("{$", hex(self.getControlRegisterFPU(index)), "}")};
return ccrRegisterName(index);
}
template<typename... P>
auto CPU::Disassembler::hint(P&&... p) const -> string {
if(showColors) return {"\e[0m\e[37m", forward<P>(p)..., "\e[0m"};
return {forward<P>(p)...};
}
+33
View File
@@ -0,0 +1,33 @@
//{
struct Disassembler {
CPU& self;
Disassembler(CPU& self) : self(self) {}
//disassembler.cpp
auto disassemble(u32 address, u32 instruction) -> string;
template<typename... P> auto hint(P&&... p) const -> string;
bool showColors = true;
bool showValues = true;
private:
auto EXECUTE() -> vector<string>;
auto SPECIAL() -> vector<string>;
auto REGIMM() -> vector<string>;
auto SCC() -> vector<string>;
auto FPU() -> vector<string>;
auto immediate(i64 value, uint bits = 0) const -> string;
auto cpuRegisterName(uint index) const -> string;
auto cpuRegisterValue(uint index) const -> string;
auto cpuRegisterIndex(uint index, i16 offset) const -> string;
auto sccRegisterName(uint index) const -> string;
auto sccRegisterValue(uint index) const -> string;
auto fpuRegisterName(uint index) const -> string;
auto fpuRegisterValue(uint index) const -> string;
auto ccrRegisterName(uint index) const -> string;
auto ccrRegisterValue(uint index) const -> string;
u32 address;
u32 instruction;
} disassembler{*this};
//};
+23
View File
@@ -0,0 +1,23 @@
//{
struct Exception {
CPU& self;
auto tlbModification() { self.raiseException(1); }
auto tlbLoad() { self.raiseException(2); }
auto tlbStore() { self.raiseException(3); }
auto addressLoad() { self.raiseException(4); }
auto addressStore() { self.raiseException(5); }
auto busInstruction() { self.raiseException(6); }
auto busData() { self.raiseException(7); }
auto systemCall() { self.raiseException(8); }
auto breakpoint() { self.raiseException(9); }
auto reservedInstruction() { self.raiseException(10); }
auto coprocessor0() { self.raiseException(11, 0); }
auto coprocessor1() { self.raiseException(11, 1); }
auto coprocessor2() { self.raiseException(11, 2); }
auto coprocessor3() { self.raiseException(11, 3); }
auto arithmeticOverflow() { self.raiseException(12); }
auto trap() { self.raiseException(13); }
auto floatingPoint() { self.raiseException(15); }
auto watchAddress() { self.raiseException(23); }
} exception{*this};
//};
+191
View File
@@ -0,0 +1,191 @@
#define CF fpu.csr.compare
#define FD(type) fgr<type>(fd)
#define FS(type) fgr<type>(fs)
#define FT(type) fgr<type>(ft)
auto CPU::instructionBC1(bool value, bool likely, i16 imm) -> void {
if(CF == value) branch.take(PC + 4 + (imm << 2));
else if(likely) branch.discard();
}
auto CPU::instructionCFC1(r64& rt, u8 rd) -> void {
rt.u64 = i32(getControlRegisterFPU(rd));
}
auto CPU::instructionCTC1(cr64& rt, u8 rd) -> void {
setControlRegisterFPU(rd, rt.u32);
}
auto CPU::instructionDMFC1(r64& rt, u8 fs) -> void {
rt.u64 = FS(u64);
}
auto CPU::instructionDMTC1(cr64& rt, u8 fs) -> void {
FS(u64) = rt.u64;
}
auto CPU::instructionFABS_S(u8 fd, u8 fs) -> void { FD(f32) = fabs(FS(f32)); }
auto CPU::instructionFABS_D(u8 fd, u8 fs) -> void { FD(f64) = fabs(FS(f64)); }
auto CPU::instructionFADD_S(u8 fd, u8 fs, u8 ft) -> void { FD(f32) = FS(f32) + FT(f32); }
auto CPU::instructionFADD_D(u8 fd, u8 fs, u8 ft) -> void { FD(f64) = FS(f64) + FT(f64); }
auto CPU::instructionFCEIL_L_S(u8 fd, u8 fs) -> void { FD(i64) = ceil(FS(f32)); }
auto CPU::instructionFCEIL_L_D(u8 fd, u8 fs) -> void { FD(i64) = ceil(FS(f64)); }
auto CPU::instructionFCEIL_W_S(u8 fd, u8 fs) -> void { FD(i32) = ceil(FS(f32)); }
auto CPU::instructionFCEIL_W_D(u8 fd, u8 fs) -> void { FD(i32) = ceil(FS(f64)); }
#define ORDERED(type, value) if(isnan(FS(type)) || isnan(FT(type))) { CF = value; return exception.floatingPoint(); }
#define UNORDERED(type, value) if(isnan(FS(type)) || isnan(FT(type))) { CF = value; return; }
auto CPU::instructionFC_EQ_S(u8 fs, u8 ft) -> void { UNORDERED(f32, 0); CF = FS(f32) == FT(f32); }
auto CPU::instructionFC_EQ_D(u8 fs, u8 ft) -> void { UNORDERED(f64, 0); CF = FS(f64) == FT(f64); }
auto CPU::instructionFC_F_S(u8 fs, u8 ft) -> void { UNORDERED(f32, 0); CF = 0; }
auto CPU::instructionFC_F_D(u8 fs, u8 ft) -> void { UNORDERED(f64, 0); CF = 0; }
auto CPU::instructionFC_LE_S(u8 fs, u8 ft) -> void { ORDERED(f32, 0); CF = FS(f32) <= FT(f32); }
auto CPU::instructionFC_LE_D(u8 fs, u8 ft) -> void { ORDERED(f64, 0); CF = FS(f64) <= FT(f64); }
auto CPU::instructionFC_LT_S(u8 fs, u8 ft) -> void { ORDERED(f32, 0); CF = FS(f32) < FT(f32); }
auto CPU::instructionFC_LT_D(u8 fs, u8 ft) -> void { ORDERED(f64, 0); CF = FS(f64) < FT(f64); }
auto CPU::instructionFC_NGE_S(u8 fs, u8 ft) -> void { ORDERED(f32, 1); CF = FS(f32) < FT(f32); }
auto CPU::instructionFC_NGE_D(u8 fs, u8 ft) -> void { ORDERED(f64, 1); CF = FS(f64) < FT(f64); }
auto CPU::instructionFC_NGL_S(u8 fs, u8 ft) -> void { ORDERED(f32, 1); CF = FS(f32) == FT(f32); }
auto CPU::instructionFC_NGL_D(u8 fs, u8 ft) -> void { ORDERED(f64, 1); CF = FS(f64) == FT(f64); }
auto CPU::instructionFC_NGLE_S(u8 fs, u8 ft) -> void { ORDERED(f32, 1); CF = 0; }
auto CPU::instructionFC_NGLE_D(u8 fs, u8 ft) -> void { ORDERED(f64, 1); CF = 0; }
auto CPU::instructionFC_NGT_S(u8 fs, u8 ft) -> void { ORDERED(f32, 1); CF = FS(f32) <= FT(f32); }
auto CPU::instructionFC_NGT_D(u8 fs, u8 ft) -> void { ORDERED(f64, 1); CF = FS(f64) <= FT(f64); }
auto CPU::instructionFC_OLE_S(u8 fs, u8 ft) -> void { UNORDERED(f32, 0); CF = FS(f32) <= FT(f32); }
auto CPU::instructionFC_OLE_D(u8 fs, u8 ft) -> void { UNORDERED(f64, 0); CF = FS(f64) <= FT(f64); }
auto CPU::instructionFC_OLT_S(u8 fs, u8 ft) -> void { UNORDERED(f32, 0); CF = FS(f32) < FT(f32); }
auto CPU::instructionFC_OLT_D(u8 fs, u8 ft) -> void { UNORDERED(f64, 0); CF = FS(f64) < FT(f64); }
auto CPU::instructionFC_SEQ_S(u8 fs, u8 ft) -> void { ORDERED(f32, 0); CF = FS(f32) == FT(f32); }
auto CPU::instructionFC_SEQ_D(u8 fs, u8 ft) -> void { ORDERED(f64, 0); CF = FS(f64) == FT(f64); }
auto CPU::instructionFC_SF_S(u8 fs, u8 ft) -> void { ORDERED(f32, 0); CF = 0; }
auto CPU::instructionFC_SF_D(u8 fs, u8 ft) -> void { ORDERED(f64, 0); CF = 0; }
auto CPU::instructionFC_UEQ_S(u8 fs, u8 ft) -> void { UNORDERED(f32, 1); CF = FS(f32) == FT(f32); }
auto CPU::instructionFC_UEQ_D(u8 fs, u8 ft) -> void { UNORDERED(f64, 1); CF = FS(f64) == FT(f64); }
auto CPU::instructionFC_ULE_S(u8 fs, u8 ft) -> void { UNORDERED(f32, 1); CF = FS(f32) <= FT(f32); }
auto CPU::instructionFC_ULE_D(u8 fs, u8 ft) -> void { UNORDERED(f64, 1); CF = FS(f64) <= FT(f64); }
auto CPU::instructionFC_ULT_S(u8 fs, u8 ft) -> void { UNORDERED(f32, 1); CF = FS(f32) < FT(f32); }
auto CPU::instructionFC_ULT_D(u8 fs, u8 ft) -> void { UNORDERED(f64, 1); CF = FS(f64) < FT(f64); }
auto CPU::instructionFC_UN_S(u8 fs, u8 ft) -> void { UNORDERED(f32, 1); CF = 0; }
auto CPU::instructionFC_UN_D(u8 fs, u8 ft) -> void { UNORDERED(f64, 1); CF = 0; }
#undef ORDERED
#undef UNORDERED
auto CPU::instructionFCVT_S_D(u8 fd, u8 fs) -> void { FD(f32) = FS(f64); }
auto CPU::instructionFCVT_S_W(u8 fd, u8 fs) -> void { FD(f32) = FS(i32); }
auto CPU::instructionFCVT_S_L(u8 fd, u8 fs) -> void { FD(f32) = FS(i64); }
auto CPU::instructionFCVT_D_S(u8 fd, u8 fs) -> void { FD(f64) = FS(f32); }
auto CPU::instructionFCVT_D_W(u8 fd, u8 fs) -> void { FD(f64) = FS(i32); }
auto CPU::instructionFCVT_D_L(u8 fd, u8 fs) -> void { FD(f64) = FS(i64); }
auto CPU::instructionFCVT_L_S(u8 fd, u8 fs) -> void { FD(i64) = FS(f32); }
auto CPU::instructionFCVT_L_D(u8 fd, u8 fs) -> void { FD(i64) = FS(f64); }
auto CPU::instructionFCVT_W_S(u8 fd, u8 fs) -> void { FD(i32) = FS(f32); }
auto CPU::instructionFCVT_W_D(u8 fd, u8 fs) -> void { FD(i32) = FS(f64); }
auto CPU::instructionFDIV_S(u8 fd, u8 fs, u8 ft) -> void {
if(FT(f32)) {
FD(f32) = FS(f32) / FT(f32);
} else if(fpu.csr.enable.divisionByZero) {
fpu.csr.flag.divisionByZero = 1;
fpu.csr.cause.divisionByZero = 1;
exception.floatingPoint();
}
}
auto CPU::instructionFDIV_D(u8 fd, u8 fs, u8 ft) -> void {
if(FT(f64)) {
FD(f64) = FS(f64) / FT(f64);
} else if(fpu.csr.enable.divisionByZero) {
fpu.csr.flag.divisionByZero = 1;
fpu.csr.cause.divisionByZero = 1;
exception.floatingPoint();
}
}
auto CPU::instructionFFLOOR_L_S(u8 fd, u8 fs) -> void { FD(i64) = floor(FS(f32)); }
auto CPU::instructionFFLOOR_L_D(u8 fd, u8 fs) -> void { FD(i64) = floor(FS(f64)); }
auto CPU::instructionFFLOOR_W_S(u8 fd, u8 fs) -> void { FD(i32) = floor(FS(f32)); }
auto CPU::instructionFFLOOR_W_D(u8 fd, u8 fs) -> void { FD(i32) = floor(FS(f64)); }
auto CPU::instructionFMOV_S(u8 fd, u8 fs) -> void { FD(f32) = FS(f32); }
auto CPU::instructionFMOV_D(u8 fd, u8 fs) -> void { FD(f64) = FS(f64); }
auto CPU::instructionFMUL_S(u8 fd, u8 fs, u8 ft) -> void { FD(f32) = FS(f32) * FT(f32); }
auto CPU::instructionFMUL_D(u8 fd, u8 fs, u8 ft) -> void { FD(f64) = FS(f64) * FT(f64); }
auto CPU::instructionFNEG_S(u8 fd, u8 fs) -> void { FD(f32) = -FS(f32); }
auto CPU::instructionFNEG_D(u8 fd, u8 fs) -> void { FD(f64) = -FS(f64); }
auto CPU::instructionFROUND_L_S(u8 fd, u8 fs) -> void { FD(i64) = nearbyint(FS(f32)); }
auto CPU::instructionFROUND_L_D(u8 fd, u8 fs) -> void { FD(i64) = nearbyint(FS(f64)); }
auto CPU::instructionFROUND_W_S(u8 fd, u8 fs) -> void { FD(i32) = nearbyint(FS(f32)); }
auto CPU::instructionFROUND_W_D(u8 fd, u8 fs) -> void { FD(i32) = nearbyint(FS(f64)); }
auto CPU::instructionFSQRT_S(u8 fd, u8 fs) -> void { FD(f32) = sqrt(FS(f32)); }
auto CPU::instructionFSQRT_D(u8 fd, u8 fs) -> void { FD(f64) = sqrt(FS(f64)); }
auto CPU::instructionFSUB_S(u8 fd, u8 fs, u8 ft) -> void { FD(f32) = FS(f32) - FT(f32); }
auto CPU::instructionFSUB_D(u8 fd, u8 fs, u8 ft) -> void { FD(f64) = FS(f64) - FT(f64); }
auto CPU::instructionFTRUNC_L_S(u8 fd, u8 fs) -> void { FD(i64) = FS(f32) < 0 ? ceil(FS(f32)) : floor(FS(f32)); }
auto CPU::instructionFTRUNC_L_D(u8 fd, u8 fs) -> void { FD(i64) = FS(f64) < 0 ? ceil(FS(f64)) : floor(FS(f64)); }
auto CPU::instructionFTRUNC_W_S(u8 fd, u8 fs) -> void { FD(i32) = FS(f32) < 0 ? ceil(FS(f32)) : floor(FS(f32)); }
auto CPU::instructionFTRUNC_W_D(u8 fd, u8 fs) -> void { FD(i32) = FS(f64) < 0 ? ceil(FS(f64)) : floor(FS(f64)); }
auto CPU::instructionLDC1(u8 ft, cr64& rs, i16 imm) -> void {
if(!scc.status.enable.coprocessor1) return exception.coprocessor1();
if(auto data = readDouble(rs.u32 + imm)) FT(u64) = *data;
}
auto CPU::instructionLWC1(u8 ft, cr64& rs, i16 imm) -> void {
if(!scc.status.enable.coprocessor1) return exception.coprocessor1();
if(auto data = readWord(rs.u32 + imm)) FT(u32) = *data;
}
auto CPU::instructionMFC1(r64& rt, u8 fs) -> void {
rt.u64 = FS(i32);
}
auto CPU::instructionMTC1(cr64& rt, u8 fs) -> void {
FS(i32) = rt.u32;
}
auto CPU::instructionSDC1(u8 ft, cr64& rs, i16 imm) -> void {
if(!scc.status.enable.coprocessor1) return exception.coprocessor1();
writeDouble(rs.u32 + imm, FT(u64));
}
auto CPU::instructionSWC1(u8 ft, cr64& rs, i16 imm) -> void {
if(!scc.status.enable.coprocessor1) return exception.coprocessor1();
writeWord(rs.u32 + imm, FT(u32));
}
#undef CF
#undef FD
#undef FS
#undef FT
+126
View File
@@ -0,0 +1,126 @@
auto CPU::FPU::setFloatingPointMode(bool mode) -> void {
if(mode == 0) {
//32x64-bit -> 16x64-bit
} else {
//16x64-bit -> 32x64-bit
}
}
template<> auto CPU::fgr<i32>(uint index) -> i32& {
if(scc.status.floatingPointMode) {
return fpu.r[index].i32;
} else if(index & 1) {
return fpu.r[index & ~1].i32h;
} else {
return fpu.r[index & ~1].i32;
}
}
template<> auto CPU::fgr<u32>(uint index) -> u32& {
return (u32&)fgr<i32>(index);
}
template<> auto CPU::fgr<f32>(uint index) -> f32& {
if(scc.status.floatingPointMode) {
return fpu.r[index].f32;
} else if(index & 1) {
return fpu.r[index & ~1].f32h;
} else {
return fpu.r[index & ~1].f32;
}
}
template<> auto CPU::fgr<i64>(uint index) -> i64& {
if(scc.status.floatingPointMode) {
return fpu.r[index].i64;
} else {
return fpu.r[index & ~1].i64;
}
}
template<> auto CPU::fgr<u64>(uint index) -> u64& {
return (u64&)fgr<i64>(index);
}
template<> auto CPU::fgr<f64>(uint index) -> f64& {
if(scc.status.floatingPointMode) {
return fpu.r[index].f64;
} else {
return fpu.r[index & ~1].f64;
}
}
auto CPU::getControlRegisterFPU(uint5 index) -> u32 {
uint32 data;
switch(index) {
case 0: //coprocessor revision identifier
data.bit(0, 7) = fpu.coprocessor.revision;
data.bit(8,15) = fpu.coprocessor.implementation;
break;
case 31: //control / status register
data.bit( 0) = fpu.csr.roundMode.bit(0);
data.bit( 1) = fpu.csr.roundMode.bit(1);
data.bit( 2) = fpu.csr.flag.inexact;
data.bit( 3) = fpu.csr.flag.underflow;
data.bit( 4) = fpu.csr.flag.overflow;
data.bit( 5) = fpu.csr.flag.divisionByZero;
data.bit( 6) = fpu.csr.flag.invalidOperation;
data.bit( 7) = fpu.csr.enable.inexact;
data.bit( 8) = fpu.csr.enable.underflow;
data.bit( 9) = fpu.csr.enable.overflow;
data.bit(10) = fpu.csr.enable.divisionByZero;
data.bit(11) = fpu.csr.enable.invalidOperation;
data.bit(12) = fpu.csr.cause.inexact;
data.bit(13) = fpu.csr.cause.underflow;
data.bit(14) = fpu.csr.cause.overflow;
data.bit(15) = fpu.csr.cause.divisionByZero;
data.bit(16) = fpu.csr.cause.invalidOperation;
data.bit(17) = fpu.csr.cause.unimplementedOperation;
data.bit(23) = fpu.csr.compare;
data.bit(24) = fpu.csr.flushed;
break;
}
return data;
}
auto CPU::setControlRegisterFPU(uint5 index, uint32 data) -> void {
//read-only variables are defined but commented out for documentation purposes
switch(index) {
case 0: //coprocessor revision identifier
//fpu.coprocessor.revision = data.bit(0, 7);
//fpu.coprocessor.implementation = data.bit(8,15);
break;
case 31: {//control / status register
uint roundModePrevious = fpu.csr.roundMode;
fpu.csr.roundMode.bit(0) = data.bit( 0);
fpu.csr.roundMode.bit(1) = data.bit( 1);
fpu.csr.flag.inexact = data.bit( 2);
fpu.csr.flag.underflow = data.bit( 3);
fpu.csr.flag.overflow = data.bit( 4);
fpu.csr.flag.divisionByZero = data.bit( 5);
fpu.csr.flag.invalidOperation = data.bit( 6);
fpu.csr.enable.inexact = data.bit( 7);
fpu.csr.enable.underflow = data.bit( 8);
fpu.csr.enable.overflow = data.bit( 9);
fpu.csr.enable.divisionByZero = data.bit(10);
fpu.csr.enable.invalidOperation = data.bit(11);
fpu.csr.cause.inexact = data.bit(12);
fpu.csr.cause.underflow = data.bit(13);
fpu.csr.cause.overflow = data.bit(14);
fpu.csr.cause.divisionByZero = data.bit(15);
fpu.csr.cause.invalidOperation = data.bit(16);
fpu.csr.cause.unimplementedOperation = data.bit(17);
fpu.csr.compare = data.bit(23);
fpu.csr.flushed = data.bit(24);
if(fpu.csr.roundMode != roundModePrevious) {
switch(fpu.csr.roundMode) {
case 0: fesetround(FE_TONEAREST); break;
case 1: fesetround(FE_TOWARDZERO); break;
case 2: fesetround(FE_UPWARD); break;
case 3: fesetround(FE_DOWNWARD); break;
}
}
} break;
}
}
+133
View File
@@ -0,0 +1,133 @@
//{
//fpu-registers.cpp
template<typename T> auto fgr(uint) -> T&;
auto getControlRegisterFPU(uint5) -> u32;
auto setControlRegisterFPU(uint5, uint32) -> void;
struct FPU {
//fpu-registers.cpp
auto setFloatingPointMode(bool) -> void;
r64 r[32];
struct Coprocessor {
static constexpr u8 revision = 0x00;
static constexpr u8 implementation = 0x0b;
} coprocessor;
struct ControlStatus {
uint2 roundMode = 0;
struct Flag {
uint1 inexact = 0;
uint1 underflow = 0;
uint1 overflow = 0;
uint1 divisionByZero = 0;
uint1 invalidOperation = 0;
} flag;
struct Enable {
uint1 inexact = 0;
uint1 underflow = 0;
uint1 overflow = 0;
uint1 divisionByZero = 0;
uint1 invalidOperation = 0;
} enable;
struct Cause {
uint1 inexact = 0;
uint1 underflow = 0;
uint1 overflow = 0;
uint1 divisionByZero = 0;
uint1 invalidOperation = 0;
uint1 unimplementedOperation = 0;
} cause;
uint1 compare = 0;
uint1 flushed = 0;
} csr;
} fpu;
//fpu-instructions.cpp
auto instructionBC1(bool value, bool likely, i16 imm) -> void;
auto instructionCFC1(r64& rt, u8 rd) -> void;
auto instructionCTC1(cr64& rt, u8 rd) -> void;
auto instructionDMFC1(r64& rt, u8 fs) -> void;
auto instructionDMTC1(cr64& rt, u8 fs) -> void;
auto instructionFABS_S(u8 fd, u8 fs) -> void;
auto instructionFABS_D(u8 fd, u8 fs) -> void;
auto instructionFADD_S(u8 fd, u8 fs, u8 ft) -> void;
auto instructionFADD_D(u8 fd, u8 fs, u8 ft) -> void;
auto instructionFCEIL_L_S(u8 fd, u8 fs) -> void;
auto instructionFCEIL_L_D(u8 fd, u8 fs) -> void;
auto instructionFCEIL_W_S(u8 fd, u8 fs) -> void;
auto instructionFCEIL_W_D(u8 fd, u8 fs) -> void;
auto instructionFC_EQ_S(u8 fs, u8 ft) -> void;
auto instructionFC_EQ_D(u8 fs, u8 ft) -> void;
auto instructionFC_F_S(u8 fs, u8 ft) -> void;
auto instructionFC_F_D(u8 fs, u8 ft) -> void;
auto instructionFC_LE_S(u8 fs, u8 ft) -> void;
auto instructionFC_LE_D(u8 fs, u8 ft) -> void;
auto instructionFC_LT_S(u8 fs, u8 ft) -> void;
auto instructionFC_LT_D(u8 fs, u8 ft) -> void;
auto instructionFC_NGE_S(u8 fs, u8 ft) -> void;
auto instructionFC_NGE_D(u8 fs, u8 ft) -> void;
auto instructionFC_NGL_S(u8 fs, u8 ft) -> void;
auto instructionFC_NGL_D(u8 fs, u8 ft) -> void;
auto instructionFC_NGLE_S(u8 fs, u8 ft) -> void;
auto instructionFC_NGLE_D(u8 fs, u8 ft) -> void;
auto instructionFC_NGT_S(u8 fs, u8 ft) -> void;
auto instructionFC_NGT_D(u8 fs, u8 ft) -> void;
auto instructionFC_OLE_S(u8 fs, u8 ft) -> void;
auto instructionFC_OLE_D(u8 fs, u8 ft) -> void;
auto instructionFC_OLT_S(u8 fs, u8 ft) -> void;
auto instructionFC_OLT_D(u8 fs, u8 ft) -> void;
auto instructionFC_SEQ_S(u8 fs, u8 ft) -> void;
auto instructionFC_SEQ_D(u8 fs, u8 ft) -> void;
auto instructionFC_SF_S(u8 fs, u8 ft) -> void;
auto instructionFC_SF_D(u8 fs, u8 ft) -> void;
auto instructionFC_UEQ_S(u8 fs, u8 ft) -> void;
auto instructionFC_UEQ_D(u8 fs, u8 ft) -> void;
auto instructionFC_ULE_S(u8 fs, u8 ft) -> void;
auto instructionFC_ULE_D(u8 fs, u8 ft) -> void;
auto instructionFC_ULT_S(u8 fs, u8 ft) -> void;
auto instructionFC_ULT_D(u8 fs, u8 ft) -> void;
auto instructionFC_UN_S(u8 fs, u8 ft) -> void;
auto instructionFC_UN_D(u8 fs, u8 ft) -> void;
auto instructionFCVT_S_D(u8 fd, u8 fs) -> void;
auto instructionFCVT_S_W(u8 fd, u8 fs) -> void;
auto instructionFCVT_S_L(u8 fd, u8 fs) -> void;
auto instructionFCVT_D_S(u8 fd, u8 fs) -> void;
auto instructionFCVT_D_W(u8 fd, u8 fs) -> void;
auto instructionFCVT_D_L(u8 fd, u8 fs) -> void;
auto instructionFCVT_L_S(u8 fd, u8 fs) -> void;
auto instructionFCVT_L_D(u8 fd, u8 fs) -> void;
auto instructionFCVT_W_S(u8 fd, u8 fs) -> void;
auto instructionFCVT_W_D(u8 fd, u8 fs) -> void;
auto instructionFDIV_S(u8 fd, u8 fs, u8 ft) -> void;
auto instructionFDIV_D(u8 fd, u8 fs, u8 ft) -> void;
auto instructionFFLOOR_L_S(u8 fd, u8 fs) -> void;
auto instructionFFLOOR_L_D(u8 fd, u8 fs) -> void;
auto instructionFFLOOR_W_S(u8 fd, u8 fs) -> void;
auto instructionFFLOOR_W_D(u8 fd, u8 fs) -> void;
auto instructionFMOV_S(u8 fd, u8 fs) -> void;
auto instructionFMOV_D(u8 fd, u8 fs) -> void;
auto instructionFMUL_S(u8 fd, u8 fs, u8 ft) -> void;
auto instructionFMUL_D(u8 fd, u8 fs, u8 ft) -> void;
auto instructionFNEG_S(u8 fd, u8 fs) -> void;
auto instructionFNEG_D(u8 fd, u8 fs) -> void;
auto instructionFROUND_L_S(u8 fd, u8 fs) -> void;
auto instructionFROUND_L_D(u8 fd, u8 fs) -> void;
auto instructionFROUND_W_S(u8 fd, u8 fs) -> void;
auto instructionFROUND_W_D(u8 fd, u8 fs) -> void;
auto instructionFSQRT_S(u8 fd, u8 fs) -> void;
auto instructionFSQRT_D(u8 fd, u8 fs) -> void;
auto instructionFSUB_S(u8 fd, u8 fs, u8 ft) -> void;
auto instructionFSUB_D(u8 fd, u8 fs, u8 ft) -> void;
auto instructionFTRUNC_L_S(u8 fd, u8 fs) -> void;
auto instructionFTRUNC_L_D(u8 fd, u8 fs) -> void;
auto instructionFTRUNC_W_S(u8 fd, u8 fs) -> void;
auto instructionFTRUNC_W_D(u8 fd, u8 fs) -> void;
auto instructionLDC1(u8 ft, cr64& rs, i16 imm) -> void;
auto instructionLWC1(u8 ft, cr64& rs, i16 imm) -> void;
auto instructionMFC1(r64& rt, u8 fs) -> void;
auto instructionMTC1(cr64& rt, u8 fs) -> void;
auto instructionSDC1(u8 ft, cr64& rs, i16 imm) -> void;
auto instructionSWC1(u8 ft, cr64& rs, i16 imm) -> void;
//};
+89
View File
@@ -0,0 +1,89 @@
auto CPU::readAddress(u32 address) -> maybe<u32> {
switch(context.segment[address >> 29]) {
case Context::Segment::Invalid:
exception.addressLoad();
return nothing;
case Context::Segment::Mapped:
if(auto lookup = tlb.load(address)) return lookup();
tlb.exception(address);
return nothing;
case Context::Segment::Cached:
case Context::Segment::Uncached:
default:
return address;
}
}
auto CPU::writeAddress(u32 address) -> maybe<u32> {
switch(context.segment[address >> 29]) {
case Context::Segment::Invalid:
exception.addressStore();
return nothing;
case Context::Segment::Mapped:
if(auto lookup = tlb.store(address)) return lookup();
tlb.exception(address);
return nothing;
case Context::Segment::Cached:
case Context::Segment::Uncached:
default:
return address;
}
}
auto CPU::readByte(u32 address) -> maybe<u32> {
if(auto physical = readAddress(address)) return bus.readByte(physical());
return nothing;
}
auto CPU::readHalf(u32 address) -> maybe<u32> {
if constexpr(Accuracy::CPU::AddressErrors) {
if(unlikely(address & 1)) return exception.addressLoad(), nothing;
}
if(auto physical = readAddress(address)) return bus.readHalf(physical());
return nothing;
}
auto CPU::readWord(u32 address) -> maybe<u32> {
if constexpr(Accuracy::CPU::AddressErrors) {
if(unlikely(address & 3)) return exception.addressLoad(), nothing;
}
if(auto physical = readAddress(address)) return bus.readWord(physical());
return nothing;
}
auto CPU::readDouble(u32 address) -> maybe<u64> {
if constexpr(Accuracy::CPU::AddressErrors) {
if(unlikely(address & 7)) return exception.addressLoad(), nothing;
}
if(auto physical = readAddress(address)) return bus.readDouble(physical());
return nothing;
}
auto CPU::writeByte(u32 address, u8 data) -> bool {
if(auto physical = writeAddress(address)) return bus.writeByte(physical(), data), true;
return false;
}
auto CPU::writeHalf(u32 address, u16 data) -> bool {
if constexpr(Accuracy::CPU::AddressErrors) {
if(unlikely(address & 1)) return exception.addressStore(), false;
}
if(auto physical = writeAddress(address)) return bus.writeHalf(physical(), data), true;
return false;
}
auto CPU::writeWord(u32 address, u32 data) -> bool {
if constexpr(Accuracy::CPU::AddressErrors) {
if(unlikely(address & 3)) return exception.addressStore(), false;
}
if(auto physical = writeAddress(address)) return bus.writeWord(physical(), data), true;
return false;
}
auto CPU::writeDouble(u32 address, u64 data) -> bool {
if constexpr(Accuracy::CPU::AddressErrors) {
if(unlikely(address & 7)) return exception.addressStore(), false;
}
if(auto physical = writeAddress(address)) return bus.writeDouble(physical(), data), true;
return false;
}
+21
View File
@@ -0,0 +1,21 @@
//{
struct Pipeline {
u32 address;
u32 instruction;
struct InstructionCache {
} ic;
struct RegisterFile {
} rf;
struct Execution {
} ex;
struct DataCache {
} dc;
struct WriteBack {
} wb;
} pipeline;
//};
+114
View File
@@ -0,0 +1,114 @@
auto CPU::Recompiler::pool(u32 address) -> Pool* {
auto& pool = pools[address >> 8 & 0x1fffff];
if(!pool) pool = (Pool*)allocator.acquire(sizeof(Pool));
return pool;
}
auto CPU::Recompiler::block(u32 address) -> Block* {
auto& block = pool(address)->blocks[address >> 2 & 0x3f];
if(!block) block = emit(address);
return block;
}
auto CPU::Recompiler::emit(u32 address) -> Block* {
if(unlikely(allocator.available() < 1_MiB)) {
print("CPU allocator flush\n");
allocator.release(bump_allocator::zero_fill);
reset();
}
auto block = (Block*)allocator.acquire(sizeof(Block));
block->code = allocator.acquire();
bind({block->code, allocator.available()});
bool branched = 0;
uint instructions = 0;
uint idle = 0;
do {
u32 instruction = bus.readWord(address);
if(instruction == 0x1000'ffff) idle += 64; //accelerate idle loops
branched = emitEXECUTE(instruction);
call(&CPU::instructionEpilogue, &self);
test(rax, rax);
jz(imm8{+1});
ret();
address += 4;
if((address & 0xfc) == 0) break; //block boundary
} while(++instructions < 64 && !branched);
ret();
block->step = 2 * instructions + idle;
allocator.reserve(size());
//print(hex(PC, 8L), " ", instructions, " ", size(), "\n");
return block;
}
#define OP instruction
#define RD &self.GPR[RDn]
#define RT &self.GPR[RTn]
#define RS &self.GPR[RSn]
#define jp(id, name) \
case id: \
return emit##name(instruction); \
#define op(id, name, ...) \
case id: \
call(&CPU::instruction##name, &self, ##__VA_ARGS__); \
return 0; \
#define br(id, name, ...) \
case id: \
call(&CPU::instruction##name, &self, ##__VA_ARGS__); \
return 1; \
auto CPU::Recompiler::emitEXECUTE(u32 instruction) -> bool {
#define DECODER_EXECUTE
#include "decoder.hpp"
return 0;
}
auto CPU::Recompiler::emitSPECIAL(u32 instruction) -> bool {
#define DECODER_SPECIAL
#include "decoder.hpp"
return 0;
}
auto CPU::Recompiler::emitREGIMM(u32 instruction) -> bool {
#define DECODER_REGIMM
#include "decoder.hpp"
return 0;
}
auto CPU::Recompiler::emitCOP0(u32 instruction) -> bool {
#define DECODER_COP0
#include "decoder.hpp"
return 0;
}
auto CPU::Recompiler::emitCOP1(u32 instruction) -> bool {
call(&CPU::Recompiler::emitCheckCOP1, this);
test(rax, rax);
jz(imm8{+1});
ret();
#define DECODER_COP1
#include "decoder.hpp"
return 0;
}
auto CPU::Recompiler::emitCheckCOP1() -> bool {
if(!self.scc.status.enable.coprocessor1) {
return self.exception.coprocessor1(), true;
}
return false;
}
#undef jp
#undef op
#undef br
#undef OP
#undef RD
#undef RT
#undef RS
+41
View File
@@ -0,0 +1,41 @@
//{
struct Recompiler : recompiler::amd64 {
CPU& self;
Recompiler(CPU& self) : self(self) {}
struct Block {
auto execute() -> void {
((void (*)())code)();
}
u32 step;
u8* code;
};
struct Pool {
Block* blocks[1 << 6];
};
auto reset() -> void {
for(u32 index : range(1 << 21)) pools[index] = nullptr;
}
auto invalidate(u32 address) -> void {
pools[address >> 8 & 0x1fffff] = nullptr;
}
auto pool(u32 address) -> Pool*;
auto block(u32 address) -> Block*;
auto emit(u32 address) -> Block*;
auto emitEXECUTE(u32 instruction) -> bool;
auto emitSPECIAL(u32 instruction) -> bool;
auto emitREGIMM(u32 instruction) -> bool;
auto emitCOP0(u32 instruction) -> bool;
auto emitCOP1(u32 instruction) -> bool;
auto emitCheckCOP1() -> bool;
bump_allocator allocator;
Pool* pools[1 << 21]; //2_MiB * sizeof(void*) == 16_MiB
} recompiler{*this};
//};
+20
View File
@@ -0,0 +1,20 @@
#if !defined(CPU_REGISTERS_HPP)
#define CPU_REGISTERS_HPP
//CPU
#define GPR core.r
#define LO core.lo
#define HI core.hi
#define PC core.pc
#define RA GPR[Core::Register::RA]
#else
#undef CPU_REGISTERS_HPP
//CPU
#undef GPR
#undef LO
#undef HI
#undef PC
#undef RA
#endif
+59
View File
@@ -0,0 +1,59 @@
auto CPU::instructionDMFC0(r64& rt, u8 rd) -> void {
rt.u64 = getControlRegister(rd);
}
auto CPU::instructionDMTC0(cr64& rt, u8 rd) -> void {
setControlRegister(rd, rt.u64);
}
auto CPU::instructionERET() -> void {
branch.exception();
if(scc.status.errorLevel) {
PC = scc.epcError;
scc.status.errorLevel = 0;
} else {
PC = scc.epc;
scc.status.exceptionLevel = 0;
}
scc.llbit = 0;
context.setMode();
}
auto CPU::instructionMFC0(r64& rt, u8 rd) -> void {
rt.u64 = i32(getControlRegister(rd));
}
auto CPU::instructionMTC0(cr64& rt, u8 rd) -> void {
setControlRegister(rd, i32(rt.u32));
}
auto CPU::instructionTLBP() -> void {
scc.index.tlbEntry = 0; //technically undefined
scc.index.probeFailure = 1;
for(uint index : range(TLB::Entries)) {
auto& entry = tlb.entry[index];
auto mask = ~entry.pageMask & ~0x1fff;
if((entry.virtualAddress & mask) != (scc.tlb.virtualAddress & mask)) continue;
if(!entry.global[0] || !entry.global[1]) {
if(entry.addressSpaceID != scc.tlb.addressSpaceID) continue;
}
scc.index.tlbEntry = index;
scc.index.probeFailure = 0;
break;
}
}
auto CPU::instructionTLBR() -> void {
if(scc.index.tlbEntry >= TLB::Entries) return;
scc.tlb = tlb.entry[scc.index.tlbEntry];
}
auto CPU::instructionTLBWI() -> void {
if(scc.index.tlbEntry >= TLB::Entries) return;
tlb.entry[scc.index.tlbEntry] = scc.tlb;
}
auto CPU::instructionTLBWR() -> void {
if(scc.random.index >= TLB::Entries) return;
tlb.entry[scc.index.tlbEntry] = scc.tlb;
}
+257
View File
@@ -0,0 +1,257 @@
auto CPU::getControlRegister(uint5 index) -> u64 {
uint64 data;
switch(index) {
case 0: //index
data.bit( 0, 5) = scc.index.tlbEntry;
data.bit(31) = scc.index.probeFailure;
break;
case 1: //random
data.bit(0,4) = scc.random.index;
data.bit(5) = scc.random.unused;
break;
case 2: //entrylo0
data.bit(0) = scc.tlb.global[0];
data.bit(1) = scc.tlb.valid[0];
data.bit(2) = scc.tlb.dirty[0];
data.bit(3, 5) = scc.tlb.cacheAlgorithm[0];
data.bit(6,29) = scc.tlb.physicalAddress[0].bit(12,35);
break;
case 3: //entrylo1
data.bit(0) = scc.tlb.global[1];
data.bit(1) = scc.tlb.valid[1];
data.bit(2) = scc.tlb.dirty[1];
data.bit(3, 5) = scc.tlb.cacheAlgorithm[1];
data.bit(6,29) = scc.tlb.physicalAddress[1].bit(12,35);
break;
case 4: //context
data.bit( 4,22) = scc.context.badVirtualAddress;
data.bit(23,63) = scc.context.pageTableEntryBase;
break;
case 5: //pagemask
data.bit(13,24) = scc.tlb.pageMask.bit(13,24);
break;
case 6: //wired
data.bit(0,4) = scc.wired.index;
data.bit(5) = scc.wired.unused;
break;
case 8: //badvaddr
data = scc.badVirtualAddress;
break;
case 9: //count
data.bit(0,31) = scc.count;
break;
case 10: //entryhi
data.bit( 0, 7) = scc.tlb.addressSpaceID;
data.bit(13,39) = scc.tlb.virtualAddress.bit(13,31);
data.bit(40,61) = scc.tlb.unused;
data.bit(62,63) = scc.tlb.region;
break;
case 11: //compare
data.bit(0,31) = scc.compare;
break;
case 12: //status
data.bit( 0) = scc.status.interruptEnable;
data.bit( 1) = scc.status.exceptionLevel;
data.bit( 2) = scc.status.errorLevel;
data.bit( 3, 4) = scc.status.privilegeMode;
data.bit( 5) = scc.status.userExtendedAddressing;
data.bit( 6) = scc.status.supervisorExtendedAddressing;
data.bit( 7) = scc.status.kernelExtendedAddressing;
data.bit( 8,15) = scc.status.interruptMask;
data.bit(16) = scc.status.de;
data.bit(17) = scc.status.ce;
data.bit(18) = scc.status.condition;
data.bit(20) = scc.status.softReset;
data.bit(21) = scc.status.tlbShutdown;
data.bit(22) = scc.status.vectorLocation;
data.bit(24) = scc.status.instructionTracing;
data.bit(25) = scc.status.reverseEndian;
data.bit(26) = scc.status.floatingPointMode;
data.bit(27) = scc.status.lowPowerMode;
data.bit(28) = scc.status.enable.coprocessor0;
data.bit(29) = scc.status.enable.coprocessor1;
data.bit(30) = scc.status.enable.coprocessor2;
data.bit(31) = scc.status.enable.coprocessor3;
context.setMode();
break;
case 13: //cause
data.bit( 2, 6) = scc.cause.exceptionCode;
data.bit( 8,15) = scc.cause.interruptPending;
data.bit(28,29) = scc.cause.coprocessorError;
data.bit(31) = scc.cause.branchDelay;
break;
case 14: //exception program counter
data = scc.epc;
break;
case 15: //coprocessor revision identifier
data.bit(0, 7) = scc.coprocessor.revision;
data.bit(8,15) = scc.coprocessor.implementation;
break;
case 16: //configuration
data.bit( 0, 1) = scc.configuration.coherencyAlgorithmKSEG0;
data.bit( 2, 3) = scc.configuration.cu;
data.bit(15) = scc.configuration.bigEndian;
data.bit(24,27) = scc.configuration.sysadWritebackPattern;
data.bit(28,30) = scc.configuration.systemClockRatio;
break;
case 17: //load linked address
data = scc.ll;
break;
case 18: //watchlo
data.bit(0) = scc.watchLo.trapOnWrite;
data.bit(1) = scc.watchLo.trapOnRead;
data.bit(3,31) = scc.watchLo.physicalAddress.bit(3,31);
break;
case 19: //watchhi
data.bit(0,3) = scc.watchHi.physicalAddressExtended;
break;
case 20: //xcontext
data.bit( 4,30) = scc.xcontext.badVirtualAddress;
data.bit(31,32) = scc.xcontext.region;
data.bit(33,63) = scc.xcontext.pageTableEntryBase;
break;
case 26: //parity error
data.bit(0,7) = scc.parityError.diagnostic;
break;
case 28: //taglo
data.bit(6, 7) = scc.tagLo.primaryCacheState;
data.bit(8,27) = scc.tagLo.physicalAddress.bit(12,31);
break;
case 31: //error exception program counter
data = scc.epcError;
break;
}
return data;
}
auto CPU::setControlRegister(uint5 index, uint64 data) -> void {
//read-only variables are defined but commented out for documentation purposes
switch(index) {
case 0: //index
scc.index.tlbEntry = data.bit( 0,5);
scc.index.probeFailure = data.bit(31);
break;
case 1: //random
//scc.random.index = data.bit(0,4);
scc.random.unused = data.bit(5);
break;
case 2: //entrylo0
scc.tlb.global[0] = data.bit(0);
scc.tlb.valid[0] = data.bit(1);
scc.tlb.dirty[0] = data.bit(2);
scc.tlb.cacheAlgorithm[0] = data.bit(3, 5);
scc.tlb.physicalAddress[0].bit(12,35) = data.bit(6,29);
scc.tlb.synchronize();
break;
case 3: //entrylo1
scc.tlb.global[1] = data.bit(0);
scc.tlb.valid[1] = data.bit(1);
scc.tlb.dirty[1] = data.bit(2);
scc.tlb.cacheAlgorithm[1] = data.bit(3, 5);
scc.tlb.physicalAddress[1].bit(12,35) = data.bit(6,29);
scc.tlb.synchronize();
break;
case 4: //context
scc.context.badVirtualAddress = data.bit( 4,22);
scc.context.pageTableEntryBase = data.bit(23,63);
break;
case 5: //pagemask
scc.tlb.pageMask.bit(13,24) = data.bit(13,24);
scc.tlb.synchronize();
break;
case 6: //wired
scc.wired.index = data.bit(0,4);
scc.wired.unused = data.bit(5);
scc.random.index = 31;
break;
case 8: //badvaddr
//scc.badVirtualAddress = data; //read-only
break;
case 9: //count
scc.count = data.bit(0,31);
break;
case 10: //entryhi
scc.tlb.addressSpaceID = data.bit( 0, 7);
scc.tlb.virtualAddress.bit(13,31) = data.bit(13,39);
scc.tlb.unused = data.bit(40,61);
scc.tlb.region = data.bit(62,63);
scc.tlb.synchronize();
break;
case 11: //compare
scc.compare = data.bit(0,31);
scc.cause.interruptPending.bit(Interrupt::Timer) = 0;
break;
case 12: {//status
bool floatingPointMode = scc.status.floatingPointMode;
scc.status.interruptEnable = data.bit( 0);
scc.status.exceptionLevel = data.bit( 1);
scc.status.errorLevel = data.bit( 2);
scc.status.privilegeMode = data.bit( 3, 4);
scc.status.userExtendedAddressing = data.bit( 5);
scc.status.supervisorExtendedAddressing = data.bit( 6);
scc.status.kernelExtendedAddressing = data.bit( 7);
scc.status.interruptMask = data.bit( 8,15);
scc.status.de = data.bit(16);
scc.status.ce = data.bit(17);
scc.status.condition = data.bit(18);
scc.status.softReset = data.bit(20);
//scc.status.tlbShutdown = data.bit(21); //read-only
scc.status.vectorLocation = data.bit(22);
scc.status.instructionTracing = data.bit(24);
scc.status.reverseEndian = data.bit(25);
scc.status.floatingPointMode = data.bit(26);
scc.status.lowPowerMode = data.bit(27);
scc.status.enable.coprocessor0 = data.bit(28);
scc.status.enable.coprocessor1 = data.bit(29);
scc.status.enable.coprocessor2 = data.bit(30);
scc.status.enable.coprocessor3 = data.bit(31);
if(floatingPointMode != scc.status.floatingPointMode) {
fpu.setFloatingPointMode(scc.status.floatingPointMode);
}
} break;
case 13: //cause
scc.cause.interruptPending.bit(0) = data.bit(8);
scc.cause.interruptPending.bit(1) = data.bit(9);
break;
case 14: //exception program counter
scc.epc = data;
break;
case 15: //coprocessor revision identifier
//scc.coprocessor.revision = data.bit(0, 7); //read-only
//scc.coprocessor.implementation = data.bit(8,15); //read-only
break;
case 16: //configuration
scc.configuration.coherencyAlgorithmKSEG0 = data.bit( 0, 1);
scc.configuration.cu = data.bit( 2, 3);
scc.configuration.bigEndian = data.bit(15);
scc.configuration.sysadWritebackPattern = data.bit(24,27);
//scc.configuration.systemClockRatio = data.bit(28,30); //read-only
break;
case 17: //load linked address
scc.ll = data;
break;
case 18: //watchlo
scc.watchLo.trapOnWrite = data.bit(0);
scc.watchLo.trapOnRead = data.bit(1);
scc.watchLo.physicalAddress.bit(3,31) = data.bit(3,31);
break;
case 19: //watchhi
scc.watchHi.physicalAddressExtended = data.bit(0,3);
break;
case 20: //xcontext
scc.xcontext.badVirtualAddress = data.bit( 4,30);
scc.xcontext.region = data.bit(31,32);
scc.xcontext.pageTableEntryBase = data.bit(33,63);
break;
case 26: //parity error
scc.parityError.diagnostic = data.bit(0,7);
break;
case 28: //taglo
scc.tagLo.primaryCacheState = data.bit(6, 7);
scc.tagLo.physicalAddress.bit(12,31) = data.bit(8,27);
break;
case 31: //error exception program counter
scc.epcError = data;
break;
}
}
+38
View File
@@ -0,0 +1,38 @@
auto CPU::TLB::load(u32 address) -> maybe<u32> {
for(auto& entry : this->entry) {
if(!entry.globals) continue;
if((address & entry.addressMaskHi) != entry.addressCompare) continue;
bool lo = address & entry.addressSelect;
if(!entry.valid[lo]) return self.exception.tlbLoad(), nothing;
return physicalAddress = entry.physicalAddress[lo] + (address & entry.addressMaskLo);
}
return self.exception.tlbLoad(), nothing;
}
auto CPU::TLB::store(u32 address) -> maybe<u32> {
for(auto& entry : this->entry) {
if(!entry.globals) continue;
if((address & entry.addressMaskHi) != entry.addressCompare) continue;
bool lo = address & entry.addressSelect;
if(!entry.valid[lo]) return self.exception.tlbStore(), nothing;
if(!entry.dirty[lo]) return self.exception.tlbModification(), nothing;
return physicalAddress = entry.physicalAddress[lo] + (address & entry.addressMaskLo);
}
return self.exception.tlbStore(), nothing;
}
auto CPU::TLB::exception(u32 address) -> void {
self.scc.badVirtualAddress = address;
self.scc.tlb.addressSpaceID = 0; //address >> 32; //note: needs u64 address
self.scc.tlb.virtualAddress = address >> 13;
self.scc.context.badVirtualAddress = address >> 13;
self.scc.xcontext.badVirtualAddress = address >> 13;
}
auto CPU::TLB::Entry::synchronize() -> void {
globals = global[0] && global[1];
addressMaskHi = ~(pageMask | 0x1fff);
addressMaskLo = (pageMask | 0x1fff) >> 1;
addressSelect = addressMaskLo + 1;
addressCompare = virtualAddress & addressMaskHi;
}
+186
View File
@@ -0,0 +1,186 @@
//{
//scc-registers.cpp
auto getControlRegister(uint5) -> u64;
auto setControlRegister(uint5, uint64) -> void;
//Translation Lookaside Buffer
struct TLB {
CPU& self;
TLB(CPU& self) : self(self) {}
static constexpr uint Entries = 32;
//tlb.cpp
auto load(u32 address) -> maybe<u32>;
auto store(u32 address) -> maybe<u32>;
auto exception(u32 address) -> void;
struct Entry {
//scc-tlb.cpp
auto synchronize() -> void;
uint1 global[2] = {};
uint1 valid[2] = {};
uint1 dirty[2] = {};
uint3 cacheAlgorithm[2] = {2, 2};
uint32 physicalAddress[2] = {};
uint32 pageMask = 0;
uint32 virtualAddress = 0;
uint8 addressSpaceID = 0;
//unimplemented:
uint22 unused = 0;
uint2 region = 0;
//internal:
uint1 globals = 0;
uint32 addressMaskHi = 0;
uint32 addressMaskLo = 0;
uint32 addressSelect = 0;
uint32 addressCompare = 0;
} entry[TLB::Entries];
u32 physicalAddress = 0;
} tlb{*this};
//System Control Coprocessor
struct SCC {
//0
struct Index {
uint6 tlbEntry = 0;
uint1 probeFailure = 0;
} index;
//1
struct Random {
uint5 index = 31;
uint1 unused = 0;
} random;
//2: EntryLo0
//3: EntryLo1
//5: PageMask
//10: EntryHi
TLB::Entry tlb;
//4
struct Context {
uint19 badVirtualAddress = 0;
uint41 pageTableEntryBase = 0;
} context;
//6
struct Wired {
uint5 index = 0;
uint1 unused = 0;
} wired;
//8
u64 badVirtualAddress = 0;
//9
u32 count = 0;
//11
u32 compare = 0;
//12
struct Status {
uint1 interruptEnable = 0;
uint1 exceptionLevel = 0;
uint1 errorLevel = 1;
uint2 privilegeMode = 0;
uint1 userExtendedAddressing = 0;
uint1 supervisorExtendedAddressing = 0;
uint1 kernelExtendedAddressing = 0;
uint8 interruptMask = 0xff;
uint1 de = 0; //unused
uint1 ce = 0; //unused
uint1 condition = 0;
uint1 softReset = 0;
uint1 tlbShutdown = 0;
uint1 vectorLocation = 1;
uint1 instructionTracing = 0;
uint1 reverseEndian = 0;
uint1 floatingPointMode = 1;
uint1 lowPowerMode = 0;
struct Enable {
uint1 coprocessor0 = 1;
uint1 coprocessor1 = 1;
uint1 coprocessor2 = 0;
uint1 coprocessor3 = 0;
} enable;
} status;
//13
struct Cause {
uint5 exceptionCode = 0;
uint8 interruptPending = 0x00;
uint2 coprocessorError = 0;
uint1 branchDelay = 0;
} cause;
//14: Exception Program Counter
u64 epc;
//15: Coprocessor Revision Identifier
struct Coprocessor {
static constexpr u8 revision = 0x00;
static constexpr u8 implementation = 0x0b;
} coprocessor;
//16
struct Configuration {
uint2 coherencyAlgorithmKSEG0 = 0;
uint2 cu = 0; //reserved
uint1 bigEndian = 1;
uint2 sysadWritebackPattern = 0;
uint2 systemClockRatio = 6;
} configuration;
//17: Load Linked Address
u64 ll;
uint1 llbit;
//18
struct WatchLo {
uint1 trapOnWrite = 0;
uint1 trapOnRead = 0;
uint32 physicalAddress = 0;
} watchLo;
//19
struct WatchHi {
uint4 physicalAddressExtended = 0; //unused; for R4000 compatibility only
} watchHi;
//20
struct XContext {
uint27 badVirtualAddress = 0;
uint2 region = 0;
uint31 pageTableEntryBase = 0;
} xcontext;
//26
struct ParityError {
uint8 diagnostic = 0; //unused; for R4000 compatibility only
} parityError;
//28
struct TagLo {
uint2 primaryCacheState = 0;
uint32 physicalAddress = 0;
} tagLo;
//31: Error Exception Program Counter
u64 epcError;
} scc;
//scc-instructions.cpp
auto instructionDMFC0(r64& rt, u8 rd) -> void;
auto instructionDMTC0(cr64& rt, u8 rd) -> void;
auto instructionERET() -> void;
auto instructionMFC0(r64& rt, u8 rd) -> void;
auto instructionMTC0(cr64& rt, u8 rd) -> void;
auto instructionTLBP() -> void;
auto instructionTLBR() -> void;
auto instructionTLBWI() -> void;
auto instructionTLBWR() -> void;
//};
+2
View File
@@ -0,0 +1,2 @@
auto CPU::serializeR4300(serializer& s) -> void {
}
+21 -6
View File
@@ -3,28 +3,43 @@
namespace ares::Nintendo64 {
CPU cpu;
#include "core/core.cpp"
#include "debugger.cpp"
#include "serialization.cpp"
auto CPU::load(Node::Object parent) -> void {
node = parent->append<Node::Component>("CPU");
debugger.load(node);
}
auto CPU::unload() -> void {
node = {};
debugger = {};
}
auto CPU::main() -> void {
step(20);
instruction();
}
auto CPU::step(uint clocks) -> void {
Thread::step(clocks);
Thread::synchronize();
vi.clock -= clocks;
ai.clock -= clocks;
rsp.clock -= clocks;
rdp.clock -= clocks;
while( vi.clock < 0) vi.main();
while( ai.clock < 0) ai.main();
while(rsp.clock < 0) rsp.main();
while(rdp.clock < 0) rdp.main();
if(scc.count < scc.compare && scc.count + clocks >= scc.compare) {
scc.cause.interruptPending.bit(Interrupt::Timer) = 1;
}
scc.count += clocks;
}
auto CPU::power() -> void {
VR4300::power();
Thread::create(93'750'000, {&CPU::main, this});
auto CPU::power(bool reset) -> void {
Thread::reset();
powerR4300(reset);
}
}
+18 -2
View File
@@ -1,8 +1,22 @@
//NEC VR4300
struct CPU : VR4300, Thread {
struct CPU : Thread {
Node::Component node;
struct Debugger {
//debugger.cpp
auto load(Node::Object) -> void;
auto instruction() -> void;
auto exception(string_view) -> void;
auto interrupt(string_view) -> void;
struct Tracer {
Node::Instruction instruction;
Node::Notification exception;
Node::Notification interrupt;
} tracer;
} debugger;
//cpu.cpp
auto load(Node::Object) -> void;
auto unload() -> void;
@@ -10,10 +24,12 @@ struct CPU : VR4300, Thread {
auto main() -> void;
auto step(uint clocks) -> void;
auto power() -> void;
auto power(bool reset) -> void;
//serialization.cpp
auto serialize(serializer&) -> void;
#include "core/core.hpp"
};
extern CPU cpu;
+31
View File
@@ -0,0 +1,31 @@
auto CPU::Debugger::load(Node::Object parent) -> void {
tracer.instruction = parent->append<Node::Instruction>("Instruction", "CPU");
tracer.instruction->setAddressBits(32);
tracer.exception = parent->append<Node::Notification>("Exception", "CPU");
tracer.interrupt = parent->append<Node::Notification>("Interrupt", "CPU");
}
auto CPU::Debugger::instruction() -> void {
if(tracer.instruction->enabled()) {
u32 address = cpu.pipeline.address;
u32 instruction = cpu.pipeline.instruction;
if(tracer.instruction->address(address)) {
cpu.disassembler.showColors = 0;
tracer.instruction->notify(cpu.disassembler.disassemble(address, instruction), {});
cpu.disassembler.showColors = 1;
}
}
}
auto CPU::Debugger::exception(string_view type) -> void {
if(tracer.exception->enabled()) {
tracer.exception->notify(type);
}
}
auto CPU::Debugger::interrupt(string_view type) -> void {
if(tracer.interrupt->enabled()) {
tracer.interrupt->notify(type);
}
}
+1 -1
View File
@@ -1,4 +1,4 @@
auto CPU::serialize(serializer& s) -> void {
VR4300::serialize(s);
Thread::serialize(s);
serializeR4300(s);
}
+31
View File
@@ -0,0 +1,31 @@
#include <n64/n64.hpp>
namespace ares::Nintendo64 {
DD dd;
#include "io.cpp"
#include "serialization.cpp"
auto DD::load(Node::Object parent) -> void {
node = parent->append<Node::Component>("Disk Drive");
c2s.allocate(0x400);
ds.allocate(0x100);
ms.allocate(0x40);
}
auto DD::unload() -> void {
c2s.reset();
ds.reset();
ms.reset();
node.reset();
iplrom.reset();
}
auto DD::power(bool reset) -> void {
iplrom.allocate(4_MiB);
if(auto fp = platform->open(node, "64dd.ipl.rom", File::Read)) {
iplrom.load(fp);
}
}
}
+24
View File
@@ -0,0 +1,24 @@
//Disk Drive
struct DD : Memory::IO<DD> {
Node::Component node;
Memory::Readable iplrom;
Memory::Writable c2s;
Memory::Writable ds;
Memory::Writable ms;
//dd.cpp
auto load(Node::Object) -> void;
auto unload() -> void;
auto power(bool reset) -> void;
//io.cpp
auto readWord(u32 address) -> u32;
auto writeWord(u32 address, u32 data) -> void;
//serialization.cpp
auto serialize(serializer&) -> void;
};
extern DD dd;
+8
View File
@@ -0,0 +1,8 @@
auto DD::readWord(u32 address) -> u32 {
uint32 data;
return data;
}
auto DD::writeWord(u32 address, u32 data) -> void {
}
+2
View File
@@ -0,0 +1,2 @@
auto DD::serialize(serializer& s) -> void {
}
+85
View File
@@ -0,0 +1,85 @@
#define decode(write, access, ...) \
if(address <= 0x007f'ffff) return rdram.ram.access(__VA_ARGS__); \
if(address <= 0x03ef'ffff) return unmapped; \
if(address <= 0x03ff'ffff) return rdram.access(__VA_ARGS__); \
if(address <= 0x0400'0fff) return rsp.dmem.access(__VA_ARGS__); \
if(address <= 0x0400'1fff) { \
if constexpr(write) { rsp.recompiler.invalidate(); } \
return rsp.imem.access(__VA_ARGS__); \
} \
if(address <= 0x0403'ffff) return unmapped; \
if(address <= 0x0407'ffff) return rsp.access(__VA_ARGS__); \
if(address <= 0x040f'ffff) return rsp.status.access(__VA_ARGS__); \
if(address <= 0x041f'ffff) return rdp.access(__VA_ARGS__); \
if(address <= 0x042f'ffff) return rdp.io.access(__VA_ARGS__); \
if(address <= 0x043f'ffff) return mi.access(__VA_ARGS__); \
if(address <= 0x044f'ffff) return vi.access(__VA_ARGS__); \
if(address <= 0x045f'ffff) return ai.access(__VA_ARGS__); \
if(address <= 0x046f'ffff) return pi.access(__VA_ARGS__); \
if(address <= 0x047f'ffff) return ri.access(__VA_ARGS__); \
if(address <= 0x048f'ffff) return si.access(__VA_ARGS__); \
if(address <= 0x04ff'ffff) return unmapped; \
if(address <= 0x0500'03ff) return dd.c2s.access(__VA_ARGS__); \
if(address <= 0x0500'04ff) return dd.ds.access(__VA_ARGS__); \
if(address <= 0x0500'057f) return dd.access(__VA_ARGS__); \
if(address <= 0x0500'05bf) return dd.ms.access(__VA_ARGS__); \
if(address <= 0x05ff'ffff) return unmapped; \
if(address <= 0x063f'ffff) return dd.iplrom.access(__VA_ARGS__); \
if(address <= 0x0fff'ffff) return unmapped; \
if(address <= 0x1fbf'ffff) return cartridge.rom.access(__VA_ARGS__); \
if(address <= 0x1fc0'07bf) return pi.rom.access(__VA_ARGS__); \
if(address <= 0x1fc0'07ff) return pi.ram.access(__VA_ARGS__); \
return unmapped; \
#define unmapped 0
inline auto Bus::readByte(u32 address) -> u8 {
address &= 0x1fff'ffff;
decode(0, readByte, address);
}
inline auto Bus::readHalf(u32 address) -> u16 {
address &= 0x1fff'fffe;
decode(0, readHalf, address);
}
inline auto Bus::readWord(u32 address) -> u32 {
address &= 0x1fff'fffc;
decode(0, readWord, address);
}
inline auto Bus::readDouble(u32 address) -> u64 {
address &= 0x1fff'fff8;
decode(0, readDouble, address);
}
#undef unmapped
#define unmapped
inline auto Bus::writeByte(u32 address, u8 data) -> void {
address &= 0x1fff'ffff;
cpu.recompiler.invalidate(address);
decode(1, writeByte, address, data);
}
inline auto Bus::writeHalf(u32 address, u16 data) -> void {
address &= 0x1fff'fffe;
cpu.recompiler.invalidate(address);
decode(1, writeHalf, address, data);
}
inline auto Bus::writeWord(u32 address, u32 data) -> void {
address &= 0x1fff'fffc;
cpu.recompiler.invalidate(address);
decode(1, writeWord, address, data);
}
inline auto Bus::writeDouble(u32 address, u64 data) -> void {
address &= 0x1fff'fff8;
cpu.recompiler.invalidate(address + 0);
cpu.recompiler.invalidate(address + 4);
decode(2, writeDouble, address, data);
}
#undef unmapped
#undef decode
+7
View File
@@ -0,0 +1,7 @@
#include <n64/n64.hpp>
namespace ares::Nintendo64 {
Bus bus;
}
+203
View File
@@ -0,0 +1,203 @@
namespace Memory {
struct Readable {
auto reset() -> void {
delete[] data;
data = nullptr;
size = 0;
maskByte = 0;
maskHalf = 0;
maskWord = 0;
maskDouble = 0;
maskQuad = 0;
}
auto allocate(u32 capacity) -> void {
size = capacity & ~7;
u32 mask = bit::round(size) - 1;
maskByte = mask & ~0;
maskHalf = mask & ~1;
maskWord = mask & ~3;
maskDouble = mask & ~7;
maskQuad = mask & ~15;
data = new u8[mask + 1];
}
auto fill(u32 value = 0) -> void {
for(u32 address = 0; address < size; address += 4) {
*(u32*)&data[address & maskWord] = bswap32(value);
}
}
auto load(Shared::File fp) -> void {
for(u32 address = 0; address < min(size, fp->size()); address += 4) {
*(u32*)&data[address & maskWord] = bswap32(fp->readm(4));
}
}
//N64 CPU requires aligned memory accesses
auto readByte(u32 address) -> u8 { return *(u8*)&data[address & maskByte]; }
auto readHalf(u32 address) -> u16 { return bswap16(*(u16*)&data[address & maskHalf]); }
auto readWord(u32 address) -> u32 { return bswap32(*(u32*)&data[address & maskWord]); }
auto readDouble(u32 address) -> u64 { return bswap64(*(u64*)&data[address & maskDouble]); }
auto readQuad(u32 address) -> u128 { return bswap128(*(u128*)&data[address & maskQuad]); }
auto writeByte(u32 address, u8 value) -> void {}
auto writeHalf(u32 address, u16 value) -> void {}
auto writeWord(u32 address, u32 value) -> void {}
auto writeDouble(u32 address, u64 value) -> void {}
auto writeQuad(u32 address, u128 value) -> void {}
//N64 RSP allows unaligned memory accesses in certain cases
auto readHalfUnaligned(u32 address) -> u16 { return bswap16(*(u16*)&data[address & maskByte]); }
auto readWordUnaligned(u32 address) -> u32 { return bswap32(*(u32*)&data[address & maskByte]); }
auto readDoubleUnaligned(u32 address) -> u64 { return bswap64(*(u64*)&data[address & maskByte]); }
auto readQuadUnaligned(u32 address) -> u128 { return bswap128(*(u128*)&data[address & maskByte]); }
auto writeHalfUnaligned(u32 address, u16 value) -> void {}
auto writeWordUnaligned(u32 address, u32 value) -> void {}
auto writeDoubleUnaligned(u32 address, u64 value) -> void {}
auto writeQuadUnaligned(u32 address, u128 value) -> void {}
//private:
u8* data = nullptr;
u32 size = 0;
u32 maskByte = 0;
u32 maskHalf = 0;
u32 maskWord = 0;
u32 maskDouble = 0;
u32 maskQuad = 0;
};
struct Writable {
auto reset() -> void {
delete[] data;
data = nullptr;
size = 0;
maskByte = 0;
maskHalf = 0;
maskWord = 0;
maskDouble = 0;
maskQuad = 0;
}
auto allocate(u32 capacity) -> void {
size = capacity & ~7;
u32 mask = bit::round(size) - 1;
maskByte = mask & ~0;
maskHalf = mask & ~1;
maskWord = mask & ~3;
maskDouble = mask & ~7;
maskQuad = mask & ~15;
data = new u8[mask + 1];
}
auto fill(u32 value = 0) -> void {
for(u32 address = 0; address < size; address += 4) {
*(u32*)&data[address & maskWord] = bswap32(value);
writeWord(address, value);
}
}
auto load(Shared::File fp) -> void {
for(u32 address = 0; address < min(size, fp->size()); address += 4) {
*(u32*)&data[address & maskWord] = bswap32(fp->readm(4));
}
}
//N64 CPU requires aligned memory accesses
auto readByte(u32 address) -> u8 { return *(u8*)&data[address & maskByte]; }
auto readHalf(u32 address) -> u16 { return bswap16(*(u16*)&data[address & maskHalf]); }
auto readWord(u32 address) -> u32 { return bswap32(*(u32*)&data[address & maskWord]); }
auto readDouble(u32 address) -> u64 { return bswap64(*(u64*)&data[address & maskDouble]); }
auto readQuad(u32 address) -> u128 { return bswap128(*(u128*)&data[address & maskQuad]); }
auto writeByte(u32 address, u8 value) -> void { *(u8*)&data[address & maskByte] = value; }
auto writeHalf(u32 address, u16 value) -> void { *(u16*)&data[address & maskHalf] = bswap16(value); }
auto writeWord(u32 address, u32 value) -> void { *(u32*)&data[address & maskWord] = bswap32(value); }
auto writeDouble(u32 address, u64 value) -> void { *(u64*)&data[address & maskDouble] = bswap64(value); }
auto writeQuad(u32 address, u128 value) -> void { *(u128*)&data[address & maskQuad] = bswap128(value); }
//N64 RSP allows unaligned memory accesses in certain cases
auto readHalfUnaligned(u32 address) -> u16 { return bswap16(*(u16*)&data[address & maskByte]); }
auto readWordUnaligned(u32 address) -> u32 { return bswap32(*(u32*)&data[address & maskByte]); }
auto readDoubleUnaligned(u32 address) -> u64 { return bswap64(*(u64*)&data[address & maskByte]); }
auto readQuadUnaligned(u32 address) -> u128 { return bswap128(*(u128*)&data[address & maskByte]); }
auto writeHalfUnaligned(u32 address, u16 value) -> void { *(u16*)&data[address & maskByte] = bswap16(value); }
auto writeWordUnaligned(u32 address, u32 value) -> void { *(u32*)&data[address & maskByte] = bswap32(value); }
auto writeDoubleUnaligned(u32 address, u64 value) -> void { *(u64*)&data[address & maskByte] = bswap64(value); }
auto writeQuadUnaligned(u32 address, u128 value) -> void { *(u128*)&data[address & maskByte] = bswap128(value); }
//private:
u8* data = nullptr;
u32 size = 0;
u32 maskByte = 0;
u32 maskHalf = 0;
u32 maskWord = 0;
u32 maskDouble = 0;
u32 maskQuad = 0;
};
template<typename T>
struct IO {
auto readByte(u32 address) -> u8 {
auto data = ((T*)this)->readWord(address);
switch(address & 3) {
case 0: return data >> 24;
case 1: return data >> 16;
case 2: return data >> 8;
case 3: return data >> 0;
} unreachable;
}
auto readHalf(u32 address) -> u16 {
auto data = ((T*)this)->readWord(address);
switch(address & 2) {
case 0: return data >> 16;
case 2: return data >> 0;
} unreachable;
}
auto readDouble(u32 address) -> u64 {
u64 data = ((T*)this)->readWord(address);
return data << 32 | ((T*)this)->readWord(address + 4);
}
auto writeByte(u32 address, u8 data) -> void {
switch(address & 3) {
case 0: return ((T*)this)->writeWord(address, data << 24);
case 1: return ((T*)this)->writeWord(address, data << 16);
case 2: return ((T*)this)->writeWord(address, data << 8);
case 3: return ((T*)this)->writeWord(address, data << 0);
} unreachable;
}
auto writeHalf(u32 address, u16 data) -> void {
switch(address & 2) {
case 0: return ((T*)this)->writeWord(address, data << 16);
case 2: return ((T*)this)->writeWord(address, data << 0);
} unreachable;
}
auto writeDouble(u32 address, u64 data) -> void {
((T*)this)->writeWord(address + 0, data >> 32);
((T*)this)->writeWord(address + 4, data >> 0);
}
};
}
struct Bus {
//bus.hpp
auto readByte(u32 address) -> u8;
auto readHalf(u32 address) -> u16;
auto readWord(u32 address) -> u32;
auto readDouble(u32 address) -> u64;
auto writeByte(u32 address, u8 data) -> void;
auto writeHalf(u32 address, u16 data) -> void;
auto writeWord(u32 address, u32 data) -> void;
auto writeDouble(u32 address, u64 data) -> void;
};
extern Bus bus;
+16
View File
@@ -0,0 +1,16 @@
auto MI::Debugger::load(Node::Object parent) -> void {
tracer.interrupt = parent->append<Node::Notification>("Interrupt", "RCP");
tracer.io = parent->append<Node::Notification>("I/O", "MI");
}
auto MI::Debugger::interrupt(string_view type) -> void {
if(tracer.interrupt->enabled()) {
tracer.interrupt->notify(type);
}
}
auto MI::Debugger::io(string_view message) -> void {
if(tracer.io->enabled()) {
tracer.io->notify(message);
}
}
+87
View File
@@ -0,0 +1,87 @@
static const vector<string> registerNames = {
"MI_INIT_MODE",
"MI_VERSION",
"MI_INTR",
"MI_INTR_MASK",
};
auto MI::readWord(u32 address) -> u32 {
address = (address & 0xfffff) >> 2;
uint32 data;
if(address == 0) {
//MI_INIT_MODE
}
if(address == 1) {
//MI_VERSION
data.byte(0) = revision.io;
data.byte(1) = revision.rac;
data.byte(2) = revision.rdp;
data.byte(3) = revision.rsp;
}
if(address == 2) {
//MI_INTR
data.bit(0) = irq.sp.line;
data.bit(1) = irq.si.line;
data.bit(2) = irq.ai.line;
data.bit(3) = irq.vi.line;
data.bit(4) = irq.pi.line;
data.bit(5) = irq.dp.line;
}
if(address == 3) {
//MI_INTR_MASK
data.bit(0) = irq.sp.mask;
data.bit(1) = irq.si.mask;
data.bit(2) = irq.ai.mask;
data.bit(3) = irq.vi.mask;
data.bit(4) = irq.pi.mask;
data.bit(5) = irq.dp.mask;
}
if(debugger.tracer.io->enabled()) {
debugger.io({registerNames(address, "MI_UNKNOWN"), " => ", hex(data, 8L)});
}
return data;
}
auto MI::writeWord(u32 address, u32 data_) -> void {
address = (address & 0xfffff) >> 2;
uint32 data = data_;
if(address == 0) {
//MI_INIT_MODE
if(data.bit(11)) mi.lower(MI::IRQ::DP);
}
if(address == 1) {
//MI_VERSION (read-only)
}
if(address == 2) {
//MI_INTR (read-only)
}
if(address == 3) {
//MI_INTR_MASK
if(data.bit( 0)) irq.sp.mask = 0;
if(data.bit( 1)) irq.sp.mask = 1;
if(data.bit( 2)) irq.si.mask = 0;
if(data.bit( 3)) irq.si.mask = 1;
if(data.bit( 4)) irq.ai.mask = 0;
if(data.bit( 5)) irq.ai.mask = 1;
if(data.bit( 6)) irq.vi.mask = 0;
if(data.bit( 7)) irq.vi.mask = 1;
if(data.bit( 8)) irq.pi.mask = 0;
if(data.bit( 9)) irq.pi.mask = 1;
if(data.bit(10)) irq.dp.mask = 0;
if(data.bit(11)) irq.dp.mask = 1;
poll();
}
if(debugger.tracer.io->enabled()) {
debugger.io({registerNames(address, "MI_UNKNOWN"), " <= ", hex(data, 8L)});
}
}
+74
View File
@@ -0,0 +1,74 @@
#include <n64/n64.hpp>
namespace ares::Nintendo64 {
MI mi;
#include "io.cpp"
#include "debugger.cpp"
#include "serialization.cpp"
auto MI::load(Node::Object parent) -> void {
node = parent->append<Node::Component>("MI");
debugger.load(node);
}
auto MI::unload() -> void {
node.reset();
debugger = {};
}
auto MI::raise(IRQ source) -> void {
if(debugger.tracer.interrupt->enabled()) {
if(!irq.sp.line && source == IRQ::SP) debugger.interrupt("SP");
if(!irq.si.line && source == IRQ::SI) debugger.interrupt("SI");
if(!irq.ai.line && source == IRQ::AI) debugger.interrupt("AI");
if(!irq.vi.line && source == IRQ::VI) debugger.interrupt("VI");
if(!irq.pi.line && source == IRQ::PI) debugger.interrupt("PI");
if(!irq.dp.line && source == IRQ::DP) debugger.interrupt("DP");
}
switch(source) {
case IRQ::SP: irq.sp.line = 1; break;
case IRQ::SI: irq.si.line = 1; break;
case IRQ::AI: irq.ai.line = 1; break;
case IRQ::VI: irq.vi.line = 1; break;
case IRQ::PI: irq.pi.line = 1; break;
case IRQ::DP: irq.dp.line = 1; break;
}
poll();
}
auto MI::lower(IRQ source) -> void {
switch(source) {
case IRQ::SP: irq.sp.line = 0; break;
case IRQ::SI: irq.si.line = 0; break;
case IRQ::AI: irq.ai.line = 0; break;
case IRQ::VI: irq.vi.line = 0; break;
case IRQ::PI: irq.pi.line = 0; break;
case IRQ::DP: irq.dp.line = 0; break;
}
poll();
}
auto MI::poll() -> void {
bool line = 0;
line |= irq.sp.line & irq.sp.mask;
line |= irq.si.line & irq.si.mask;
line |= irq.ai.line & irq.ai.mask;
line |= irq.vi.line & irq.vi.mask;
line |= irq.pi.line & irq.pi.mask;
line |= irq.dp.line & irq.dp.mask;
cpu.scc.cause.interruptPending.bit(2) = line;
}
auto MI::power() -> void {
irq.sp.line = 0; irq.sp.mask = 1;
irq.si.line = 0; irq.si.mask = 1;
irq.ai.line = 0; irq.ai.mask = 1;
irq.vi.line = 0; irq.vi.mask = 1;
irq.pi.line = 0; irq.pi.mask = 1;
irq.dp.line = 0; irq.dp.mask = 1;
}
}
+62
View File
@@ -0,0 +1,62 @@
//MIPS Interface
struct MI : Memory::IO<MI> {
Node::Component node;
struct Debugger {
//debugger.cpp
auto load(Node::Object) -> void;
auto interrupt(string_view) -> void;
auto io(string_view) -> void;
struct Tracer {
Node::Notification interrupt;
Node::Notification io;
} tracer;
} debugger;
//mi.cpp
auto load(Node::Object) -> void;
auto unload() -> void;
enum class IRQ : uint { SP, SI, AI, VI, PI, DP };
auto raise(IRQ) -> void;
auto lower(IRQ) -> void;
auto poll() -> void;
auto power() -> void;
//io.cpp
auto readWord(u32 address) -> u32;
auto writeWord(u32 address, u32 data) -> void;
//serialization.cpp
auto serialize(serializer&) -> void;
private:
struct Interrupt {
bool line;
bool mask;
};
struct IRQs {
Interrupt sp;
Interrupt si;
Interrupt ai;
Interrupt vi;
Interrupt pi;
Interrupt dp;
} irq;
struct IO {
} io;
struct Revision {
static constexpr u8 io = 0x02; //I/O interface
static constexpr u8 rac = 0x01; //RAMBUS ASIC cell
static constexpr u8 rdp = 0x02; //Reality Display Processor
static constexpr u8 rsp = 0x02; //Reality Signal Processor
} revision;
};
extern MI mi;
+2
View File
@@ -0,0 +1,2 @@
auto MI::serialize(serializer& s) -> void {
}
+52 -4
View File
@@ -2,19 +2,67 @@
//started: 2020-04-28
#include <ares/ares.hpp>
#include <component/processor/vr4300/vr4300.hpp>
#include <nall/hashset.hpp>
#include <nall/recompiler/amd64/amd64.hpp>
#include <nmmintrin.h>
using v128 = __m128i;
namespace ares::Nintendo64 {
#include <ares/inline.hpp>
struct Accuracy {
//enable all accuracy flags
static constexpr bool Reference = 0;
struct CPU {
//0 = dynamic recompiler; 1 = interpreter
static constexpr bool Interpreter = 0 | Reference;
//exceptions when the CPU accesses unaligned memory addresses
static constexpr bool AddressErrors = 0 | Reference;
};
struct RSP {
//0 = dynamic recompiler; 1 = interpreter
static constexpr bool Interpreter = 0 | Reference;
//VU instructions
static constexpr bool SISD = 0 | Reference;
static constexpr bool SIMD = SISD == 0;
};
};
struct Region {
static inline auto NTSC() -> bool;
static inline auto PAL() -> bool;
};
struct Thread {
auto reset() -> void {
clock = 0;
}
auto serialize(serializer& s) -> void {
s.integer(clock);
}
i64 clock;
};
#include <n64/memory/memory.hpp>
#include <n64/system/system.hpp>
#include <n64/cartridge/cartridge.hpp>
#include <n64/controller/controller.hpp>
#include <n64/dd/dd.hpp>
#include <n64/mi/mi.hpp>
#include <n64/vi/vi.hpp>
#include <n64/ai/ai.hpp>
#include <n64/pi/pi.hpp>
#include <n64/ri/ri.hpp>
#include <n64/si/si.hpp>
#include <n64/rdram/rdram.hpp>
#include <n64/cpu/cpu.hpp>
#include <n64/rdp/rdp.hpp>
#include <n64/rsp/rsp.hpp>
#include <n64/memory/bus.hpp>
}
#include <n64/interface/interface.hpp>
+18
View File
@@ -0,0 +1,18 @@
auto PI::Debugger::load(Node::Object parent) -> void {
tracer.io = parent->append<Node::Notification>("I/O", "PI");
memory.ram = parent->append<Node::Memory>("PI RAM");
memory.ram->setSize(64);
memory.ram->setRead([&](uint32 address) -> uint8 {
return pi.ram.readByte(address);
});
memory.ram->setWrite([&](uint32 address, uint8 data) -> void {
return pi.ram.writeByte(address, data);
});
}
auto PI::Debugger::io(string_view message) -> void {
if(tracer.io->enabled()) {
tracer.io->notify(message);
}
}
+186
View File
@@ -0,0 +1,186 @@
static const vector<string> registerNames = {
"PI_DRAM_ADDRESS",
"PI_PBUS_ADDRESS",
"PI_READ_LENGTH",
"PI_WRITE_LENGTH",
"PI_STATUS",
"PI_BSD_DOM1_LAT",
"PI_BSD_DOM1_PWD",
"PI_BSD_DOM1_PGS",
"PI_BSD_DOM1_RLS",
"PI_BSD_DOM2_LAT",
"PI_BSD_DOM2_PWD",
"PI_BSD_DOM2_PGS",
"PI_BSD_DOM2_RLS",
};
auto PI::readWord(u32 address) -> u32 {
address = (address & 0xfffff) >> 2;
uint32 data;
if(address == 0) {
//PI_DRAM_ADDRESS
data = io.dramAddress;
}
if(address == 1) {
//PI_CART_ADDRESS
data = io.pbusAddress;
}
if(address == 2) {
//PI_READ_LENGTH
data = io.readLength;
}
if(address == 3) {
//PI_WRITE_LENGTH
data = io.writeLength;
}
if(address == 4) {
//PI_STATUS
data.bit(0) = io.dmaBusy;
data.bit(1) = io.ioBusy;
data.bit(2) = io.error;
data.bit(3) = io.interrupt;
}
if(address == 5) {
//PI_BSD_DOM1_LAT
data.bit(0,7) = bsd1.latency;
}
if(address == 6) {
//PI_BSD_DOM1_PWD
data.bit(0,7) = bsd1.pulseWidth;
}
if(address == 7) {
//PI_BSD_DOM1_PGS
data.bit(0,7) = bsd1.pageSize;
}
if(address == 8) {
//PI_BSD_DOM1_RLS
data.bit(0,7) = bsd1.releaseDuration;
}
if(address == 9) {
//PI_BSD_DOM2_LAT
data.bit(0,7) = bsd2.latency;
}
if(address == 10) {
//PI_BSD_DOM2_PWD
data.bit(0,7) = bsd2.pulseWidth;
}
if(address == 11) {
//PI_BSD_DOM2_PGS
data.bit(0,7) = bsd2.pageSize;
}
if(address == 12) {
//PI_BSD_DOM2_RLS
data.bit(0,7) = bsd2.releaseDuration;
}
if(debugger.tracer.io->enabled()) {
debugger.io({registerNames(address, "PI_UNKNOWN"), " => ", hex(data, 8L)});
}
return data;
}
auto PI::writeWord(u32 address, u32 data_) -> void {
address = (address & 0xfffff) >> 2;
uint32 data = data_;
//only PI_STATUS can be written while PI is busy
if(address != 4 && (io.dmaBusy || io.ioBusy)) {
io.error = 1;
return;
}
if(address == 0) {
//PI_DRAM_ADDRESS
io.dramAddress = uint24(data) & ~7;
}
if(address == 1) {
//PI_PBUS_ADDRESS
io.pbusAddress = uint29(data) & ~1;
}
if(address == 2) {
//PI_READ_LENGTH
io.readLength = (uint24(data) | 7) + 1;
for(u32 address = 0; address < io.readLength; address += 2) {
u16 data = bus.readHalf(io.dramAddress + address);
bus.writeHalf(io.pbusAddress + address, data);
}
io.interrupt = 1;
mi.raise(MI::IRQ::PI);
}
if(address == 3) {
//PI_WRITE_LENGTH
io.writeLength = (uint24(data) | 7) + 1;
for(u32 address = 0; address < io.writeLength; address += 2) {
u16 data = bus.readHalf(io.pbusAddress + address);
bus.writeHalf(io.dramAddress + address, data);
}
io.interrupt = 1;
mi.raise(MI::IRQ::PI);
}
if(address == 4) {
//PI_STATUS
if(data.bit(0)) io.error = 0;
if(data.bit(1)) io.interrupt = 0, mi.lower(MI::IRQ::PI);
}
if(address == 5) {
//PI_BSD_DOM1_LAT
bsd1.latency = data.bit(0,7);
}
if(address == 6) {
//PI_BSD_DOM1_PWD
bsd1.pulseWidth = data.bit(0,7);
}
if(address == 7) {
//PI_BSD_DOM1_PGS
bsd1.pageSize = data.bit(0,7);
}
if(address == 8) {
//PI_BSD_DOM1_RLS
bsd1.releaseDuration = data.bit(0,7);
}
if(address == 9) {
//PI_BSD_DOM2_LAT
bsd2.latency = data.bit(0,7);
}
if(address == 10) {
//PI_BSD_DOM2_PWD
bsd2.pulseWidth = data.bit(0,7);
}
if(address == 11) {
//PI_BSD_DOM2_PGS
bsd2.pageSize = data.bit(0,7);
}
if(address == 12) {
//PI_BSD_DOM2_RLS
bsd2.releaseDuration = data.bit(0,7);
}
if(debugger.tracer.io->enabled()) {
debugger.io({registerNames(address, "PI_UNKNOWN"), " <= ", hex(data, 8L)});
}
}
+57
View File
@@ -0,0 +1,57 @@
#include <n64/n64.hpp>
namespace ares::Nintendo64 {
PI pi;
#include "io.cpp"
#include "debugger.cpp"
#include "serialization.cpp"
auto PI::load(Node::Object parent) -> void {
node = parent->append<Node::Component>("PI");
rom.allocate(0x7c0);
ram.allocate(0x040);
debugger.load(node);
}
auto PI::unload() -> void {
rom.reset();
ram.reset();
node = {};
debugger = {};
}
auto PI::power() -> void {
rom.fill();
ram.fill();
io = {};
bsd1 = {};
bsd2 = {};
string iplrom = cartridge.region() == "NTSC" ? "pif.ntsc.rom" : "pif.pal.rom";
iplrom = "pif.rom";
if(auto fp = platform->open(node, iplrom, File::Read, File::Required)) {
rom.load(fp);
}
//write CIC seeds into PIF RAM so that cartridge checksum function passes
string cic = cartridge.cic();
if(1) { //power
if(cic == "CIC-NUS-6101" || cic == "CIC-NUS-7102") ram.writeWord(0x24, 0x00043f3f);
if(cic == "CIC-NUS-6102" || cic == "CIC-NUS-7101") ram.writeWord(0x24, 0x00003f3f);
if(cic == "CIC-NUS-6103" || cic == "CIC-NUS-7103") ram.writeWord(0x24, 0x0000783f);
if(cic == "CIC-NUS-6105" || cic == "CIC-NUS-7105") ram.writeWord(0x24, 0x0000913f);
if(cic == "CIC-NUS-6106" || cic == "CIC-NUS-7106") ram.writeWord(0x24, 0x0000853f);
}
if(0) { //reset
if(cic == "CIC-NUS-6101" || cic == "CIC-NUS-7102") ram.writeWord(0x24, 0x00063f3f);
if(cic == "CIC-NUS-6102" || cic == "CIC-NUS-7101") ram.writeWord(0x24, 0x00023f3f);
if(cic == "CIC-NUS-6103" || cic == "CIC-NUS-7103") ram.writeWord(0x24, 0x0002783f);
if(cic == "CIC-NUS-6105" || cic == "CIC-NUS-7105") ram.writeWord(0x24, 0x0002913f);
if(cic == "CIC-NUS-6106" || cic == "CIC-NUS-7106") ram.writeWord(0x24, 0x0002853f);
}
}
}
+53
View File
@@ -0,0 +1,53 @@
//Peripheral Interface
struct PI : Memory::IO<PI> {
Node::Component node;
Memory::Readable rom;
Memory::Writable ram;
struct Debugger {
//debugger.cpp
auto load(Node::Object) -> void;
auto io(string_view) -> void;
struct Tracer {
Node::Notification io;
} tracer;
struct Memory {
Node::Memory ram;
} memory;
} debugger;
//pi.cpp
auto load(Node::Object) -> void;
auto unload() -> void;
auto power() -> void;
//io.cpp
auto readWord(u32 address) -> u32;
auto writeWord(u32 address, u32 data) -> void;
//serialization.cpp
auto serialize(serializer&) -> void;
struct IO {
uint1 dmaBusy = 0;
uint1 ioBusy = 0;
uint1 error = 0;
uint1 interrupt = 0;
uint32 dramAddress = 0;
uint32 pbusAddress = 0;
uint32 readLength = 0;
uint32 writeLength = 0;
} io;
struct BSD {
uint8 latency = 0;
uint8 pulseWidth = 0;
uint8 pageSize = 0;
uint8 releaseDuration = 0;
} bsd1, bsd2;
};
extern PI pi;
+2
View File
@@ -0,0 +1,2 @@
auto PI::serialize(serializer& s) -> void {
}
+16
View File
@@ -0,0 +1,16 @@
auto RDP::Debugger::load(Node::Object parent) -> void {
tracer.command = parent->append<Node::Notification>("Command", "RDP");
tracer.io = parent->append<Node::Notification>("I/O", "RDP");
}
auto RDP::Debugger::command(string_view message) -> void {
if(tracer.command->enabled()) {
tracer.command->notify(message);
}
}
auto RDP::Debugger::io(string_view message) -> void {
if(tracer.io->enabled()) {
tracer.io->notify(message);
}
}
Loaded 100 of 333 files, more files were not shown because too many files have changed in this diff. Show more