initial cpu structure :thonk:
Signed-off-by: Amneesh Singh <natto@weirdnatto.in>
This commit is contained in:
@@ -1,2 +1,7 @@
|
||||
BasedOnStyle: LLVM
|
||||
IndentWidth: 4
|
||||
BasedOnStyle: Mozilla
|
||||
IndentWidth: 4
|
||||
BreakBeforeBraces: Attach
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
AlignConsecutiveAssignments: Consecutive
|
||||
BreakAfterAttributes: Always
|
||||
AllowShortEnumsOnASingleLine: False
|
@@ -1,10 +1,17 @@
|
||||
#include "matar.hh"
|
||||
#include "emulator.hh"
|
||||
#include <iostream>
|
||||
|
||||
int main() {
|
||||
std::cout << "Hello" << std::endl;
|
||||
if (run() > 0) {
|
||||
std::cerr << "Crashed" << std::endl;
|
||||
int
|
||||
main(int argc, const char* argv[]) {
|
||||
if (argc != 2) {
|
||||
std::cerr << "Usage: " << argv[0] << " <file>" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
try {
|
||||
emulator::run(argv[1]);
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "Exception: " << e.what() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@@ -10,6 +10,6 @@ executable(
|
||||
meson.project_name(),
|
||||
target_sources,
|
||||
link_with: target_deps,
|
||||
include_directories: includes,
|
||||
install : true
|
||||
include_directories: inc,
|
||||
install : true,
|
||||
)
|
12
flake.nix
12
flake.nix
@@ -16,12 +16,12 @@
|
||||
eachSystem (system:
|
||||
let
|
||||
pkgs = import nixpkgs { inherit system; };
|
||||
llvm = pkgs.llvmPackages;
|
||||
llvm = pkgs.llvmPackages_16;
|
||||
stdenv = llvm.libcxxStdenv;
|
||||
|
||||
nativeBuildInputs = with pkgs; [ meson ninja ];
|
||||
in
|
||||
{
|
||||
rec {
|
||||
packages = rec {
|
||||
matar = stdenv.mkDerivation rec {
|
||||
name = "matar";
|
||||
@@ -44,7 +44,13 @@
|
||||
matar = pkgs.mkShell.override { inherit stdenv; } {
|
||||
name = "matar";
|
||||
packages = nativeBuildInputs ++ (with pkgs; [
|
||||
clang-tools
|
||||
# dev tools
|
||||
clang-tools_16
|
||||
|
||||
# other tools
|
||||
valgrind
|
||||
|
||||
llvm.lldb
|
||||
]);
|
||||
};
|
||||
default = matar;
|
||||
|
16
include/emulator.hh
Normal file
16
include/emulator.hh
Normal file
@@ -0,0 +1,16 @@
|
||||
#ifndef EMULATOR_HH
|
||||
#define EMULATOR_HH
|
||||
|
||||
// Why do I have a public API? We will know that in the future
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace emulator {
|
||||
void
|
||||
run(std::string filepath);
|
||||
|
||||
void
|
||||
run(std::ifstream& ifile);
|
||||
}
|
||||
|
||||
#endif /* EMULATOR_HH */
|
@@ -1,6 +0,0 @@
|
||||
#ifndef MATAR_HH
|
||||
#define MATAR_HH
|
||||
|
||||
int run();
|
||||
|
||||
#endif /* MATAR_HH */
|
5
include/meson.build
Normal file
5
include/meson.build
Normal file
@@ -0,0 +1,5 @@
|
||||
headers = files(
|
||||
'emulator.hh'
|
||||
)
|
||||
|
||||
install_headers(headers, subdir: meson.project_name(), preserve_path: true)
|
@@ -6,10 +6,7 @@ project('matar', 'cpp',
|
||||
'optimization=3',
|
||||
'cpp_std=c++20'])
|
||||
|
||||
includes = include_directories('include')
|
||||
|
||||
inc = include_directories('include')
|
||||
subdir('include')
|
||||
subdir('src')
|
||||
subdir('apps')
|
||||
|
||||
import('pkgconfig').generate(lib)
|
39
src/bits.hh
Normal file
39
src/bits.hh
Normal file
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include <concepts>
|
||||
#include <limits>
|
||||
|
||||
using std::size_t;
|
||||
|
||||
template<std::integral Int>
|
||||
inline bool
|
||||
get_nth_bit(Int num, size_t n) {
|
||||
return (1 && (num >> n));
|
||||
}
|
||||
|
||||
template<std::integral Int>
|
||||
inline void
|
||||
set_nth_bit(Int& num, size_t n) {
|
||||
num |= (1 << n);
|
||||
}
|
||||
|
||||
template<std::integral Int>
|
||||
inline void
|
||||
rst_nth_bit(Int& num, size_t n) {
|
||||
num &= ~(1 << n);
|
||||
}
|
||||
|
||||
template<std::integral Int>
|
||||
inline void
|
||||
chg_nth_bit(Int& num, size_t n, bool x) {
|
||||
num ^= (num ^ -x) & 1 << n;
|
||||
}
|
||||
|
||||
/// read range of bits from start to end inclusive
|
||||
template<std::unsigned_integral Int>
|
||||
inline Int
|
||||
get_bit_range(Int& num, size_t start, size_t end) {
|
||||
// NOTE: we do not require -1 if it is a signed integral (which it is not)
|
||||
Int left = std::numeric_limits<Int>::digits - 1 - end;
|
||||
return num << left >> (left + start);
|
||||
}
|
14
src/bus.hh
Normal file
14
src/bus.hh
Normal file
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
|
||||
class Bus {
|
||||
std::vector<uint8_t> data;
|
||||
|
||||
public:
|
||||
Bus() = default;
|
||||
Bus(std::istream& ifile)
|
||||
: data(std::istreambuf_iterator<char>(ifile),
|
||||
std::istreambuf_iterator<char>()) {}
|
||||
};
|
61
src/cpu/arm/insruction.hh
Normal file
61
src/cpu/arm/insruction.hh
Normal file
@@ -0,0 +1,61 @@
|
||||
enum class ArmInstructionFormat {
|
||||
DataProcessingAndFsrTransfer,
|
||||
Multiply,
|
||||
MultiplyLong,
|
||||
SingleDataSwap,
|
||||
BranchAndExchange,
|
||||
HalfwordDataTransferRegisterOffset,
|
||||
HalfwordDataTransferImmediateOffset,
|
||||
SingleDataTransfer,
|
||||
Undefined,
|
||||
BlockDataTransfer,
|
||||
Branch,
|
||||
CoprocessorDataTransfer,
|
||||
CoprocessorDataOperation,
|
||||
CoprocessorRegisterTransfer,
|
||||
SoftwareInterrupt
|
||||
};
|
||||
|
||||
enum class Condition {
|
||||
EQ = 0b0000,
|
||||
NE = 0b0001,
|
||||
CS = 0b0010,
|
||||
CC = 0b0011,
|
||||
MI = 0b0100,
|
||||
PL = 0b0101,
|
||||
VS = 0b0110,
|
||||
VC = 0b0111,
|
||||
HI = 0b1000,
|
||||
LS = 0b1001,
|
||||
GE = 0b1010,
|
||||
LT = 0b1011,
|
||||
GT = 0b1100,
|
||||
LE = 0b1101,
|
||||
AL = 0b1110
|
||||
};
|
||||
|
||||
enum class OpCode {
|
||||
AND = 0b0000,
|
||||
EOR = 0b0001,
|
||||
SUB = 0b0010,
|
||||
RSB = 0b0011,
|
||||
ADD = 0b0100,
|
||||
ADC = 0b0101,
|
||||
SBC = 0b0110,
|
||||
RSC = 0b0111,
|
||||
TST = 0b1000,
|
||||
TEQ = 0b1001,
|
||||
CMP = 0b1010,
|
||||
CMN = 0b1011,
|
||||
ORR = 0b1100,
|
||||
MOV = 0b1101,
|
||||
BIC = 0b1110,
|
||||
MVN = 0b1111
|
||||
};
|
||||
|
||||
enum class Shift {
|
||||
LSL = 0b00,
|
||||
LSR = 0b01,
|
||||
ASR = 0b10,
|
||||
ROR = 0b11
|
||||
};
|
2
src/cpu/arm/meson.build
Normal file
2
src/cpu/arm/meson.build
Normal file
@@ -0,0 +1,2 @@
|
||||
lib_sources += files(
|
||||
)
|
106
src/cpu/cpu.cc
106
src/cpu/cpu.cc
@@ -1,6 +1,104 @@
|
||||
#include "cpu.hh"
|
||||
#include <iostream>
|
||||
#include "cpu/utility.hh"
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
|
||||
namespace cpu {
|
||||
void run() { std::cout << "Hello from inside the CPU" << std::endl; }
|
||||
} // namespace cpu
|
||||
/* change modes */
|
||||
void
|
||||
Cpu::chg_mode(Mode from, Mode to) {
|
||||
if (from == to)
|
||||
return;
|
||||
|
||||
/* TODO: replace visible registers with view once I understand how to
|
||||
* concatenate views */
|
||||
#define STORE_BANKED(mode, MODE) \
|
||||
std::copy(gpr + GPR_##MODE##_BANKED_FIRST, \
|
||||
gpr + GPR_##MODE##_BANKED_FIRST + GPR_##MODE##_BANKED_COUNT, \
|
||||
gpr_banked.mode)
|
||||
|
||||
switch (from) {
|
||||
case Mode::Fiq:
|
||||
STORE_BANKED(fiq, FIQ);
|
||||
spsr_banked.fiq = spsr;
|
||||
break;
|
||||
|
||||
case Mode::Supervisor:
|
||||
STORE_BANKED(svc, SVC);
|
||||
spsr_banked.svc = spsr;
|
||||
break;
|
||||
|
||||
case Mode::Abort:
|
||||
STORE_BANKED(abt, ABT);
|
||||
spsr_banked.abt = spsr;
|
||||
break;
|
||||
|
||||
case Mode::Irq:
|
||||
STORE_BANKED(irq, IRQ);
|
||||
spsr_banked.irq = spsr;
|
||||
break;
|
||||
|
||||
case Mode::Undefined:
|
||||
STORE_BANKED(und, UND);
|
||||
spsr_banked.und = spsr;
|
||||
break;
|
||||
|
||||
case Mode::User:
|
||||
case Mode::System:
|
||||
STORE_BANKED(old, SYS_USR);
|
||||
break;
|
||||
}
|
||||
|
||||
#define RESTORE_BANKED(mode, MODE) \
|
||||
std::copy(gpr_banked.mode, \
|
||||
gpr_banked.mode + GPR_##MODE##_BANKED_COUNT, \
|
||||
gpr + GPR_##MODE##_BANKED_FIRST)
|
||||
|
||||
switch (to) {
|
||||
case Mode::Fiq:
|
||||
RESTORE_BANKED(fiq, FIQ);
|
||||
spsr = spsr_banked.fiq;
|
||||
break;
|
||||
|
||||
case Mode::Supervisor:
|
||||
RESTORE_BANKED(svc, SVC);
|
||||
spsr = spsr_banked.svc;
|
||||
break;
|
||||
|
||||
case Mode::Abort:
|
||||
RESTORE_BANKED(abt, ABT);
|
||||
spsr = spsr_banked.abt;
|
||||
break;
|
||||
|
||||
case Mode::Irq:
|
||||
RESTORE_BANKED(irq, IRQ);
|
||||
spsr = spsr_banked.irq;
|
||||
break;
|
||||
|
||||
case Mode::Undefined:
|
||||
RESTORE_BANKED(und, UND);
|
||||
spsr = spsr_banked.und;
|
||||
break;
|
||||
|
||||
case Mode::User:
|
||||
case Mode::System:
|
||||
STORE_BANKED(old, SYS_USR);
|
||||
break;
|
||||
}
|
||||
|
||||
#undef RESTORE_BANKED
|
||||
|
||||
cpsr.set_mode(to);
|
||||
}
|
||||
|
||||
// set register
|
||||
inline uint32_t&
|
||||
Cpu::operator[](size_t idx) {
|
||||
// avoid unneeded complexity like index checks
|
||||
return gpr[idx];
|
||||
}
|
||||
|
||||
// get register
|
||||
inline const uint32_t&
|
||||
Cpu::operator[](size_t idx) const {
|
||||
return gpr[idx];
|
||||
}
|
||||
|
@@ -1,7 +1,77 @@
|
||||
#pragma once
|
||||
|
||||
#define ABSOLUTE "MADarchod"
|
||||
#include "bus.hh"
|
||||
#include "psr.hh"
|
||||
|
||||
namespace cpu {
|
||||
void run();
|
||||
}
|
||||
#include <cstdint>
|
||||
|
||||
using std::size_t;
|
||||
|
||||
static constexpr size_t GPR_VISIBLE_COUNT = 16;
|
||||
|
||||
static constexpr size_t GPR_FIQ_BANKED_FIRST = 8;
|
||||
static constexpr size_t GPR_FIQ_BANKED_COUNT = 7;
|
||||
|
||||
static constexpr size_t GPR_SVC_BANKED_FIRST = 13;
|
||||
static constexpr size_t GPR_SVC_BANKED_COUNT = 2;
|
||||
|
||||
static constexpr size_t GPR_ABT_BANKED_FIRST = 13;
|
||||
static constexpr size_t GPR_ABT_BANKED_COUNT = 2;
|
||||
|
||||
static constexpr size_t GPR_IRQ_BANKED_FIRST = 13;
|
||||
static constexpr size_t GPR_IRQ_BANKED_COUNT = 2;
|
||||
|
||||
static constexpr size_t GPR_UND_BANKED_FIRST = 13;
|
||||
static constexpr size_t GPR_UND_BANKED_COUNT = 2;
|
||||
|
||||
static constexpr size_t GPR_SYS_USR_BANKED_FIRST = 8;
|
||||
static constexpr size_t GPR_SYS_USR_BANKED_COUNT = 7;
|
||||
|
||||
struct _GprBanked {
|
||||
uint32_t fiq[GPR_FIQ_BANKED_COUNT];
|
||||
uint32_t svc[GPR_SVC_BANKED_COUNT];
|
||||
uint32_t abt[GPR_ABT_BANKED_COUNT];
|
||||
uint32_t irq[GPR_IRQ_BANKED_COUNT];
|
||||
uint32_t und[GPR_UND_BANKED_COUNT];
|
||||
|
||||
/* visible registers before the mode switch */
|
||||
uint32_t old[GPR_SYS_USR_BANKED_COUNT];
|
||||
};
|
||||
typedef struct _GprBanked GprBanked;
|
||||
|
||||
struct _SpsrBanked {
|
||||
Psr fiq;
|
||||
Psr svc;
|
||||
Psr abt;
|
||||
Psr irq;
|
||||
Psr und;
|
||||
};
|
||||
typedef struct _SpsrBanked SpsrBanked;
|
||||
|
||||
class Cpu {
|
||||
uint32_t gpr[GPR_VISIBLE_COUNT]; // general purpose registers
|
||||
GprBanked gpr_banked; // banked general purpose registers
|
||||
SpsrBanked spsr_banked; // banked saved program status registers
|
||||
Psr cpsr; // current program status register
|
||||
Psr spsr; // status program status register
|
||||
Bus bus;
|
||||
|
||||
void chg_mode(Mode from, Mode to);
|
||||
|
||||
uint32_t& operator[](size_t idx);
|
||||
const uint32_t& operator[](size_t idx) const;
|
||||
|
||||
public:
|
||||
Cpu(Bus bus)
|
||||
: gpr(0)
|
||||
, gpr_banked({ { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 } })
|
||||
, spsr_banked({ 0, 0, 0, 0, 0 })
|
||||
, cpsr(0)
|
||||
, spsr(0)
|
||||
, bus(bus) {
|
||||
cpsr.set_mode(Mode::System);
|
||||
cpsr.set_irq_disabled(true);
|
||||
cpsr.set_fiq_disabled(true);
|
||||
cpsr.set_state(State::Arm);
|
||||
}
|
||||
};
|
||||
|
@@ -1,3 +1,5 @@
|
||||
lib_sources += files(
|
||||
'cpu.cc'
|
||||
)
|
||||
)
|
||||
|
||||
subdir('arm')
|
55
src/cpu/psr.hh
Normal file
55
src/cpu/psr.hh
Normal file
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
|
||||
#include "bits.hh"
|
||||
#include "utility.hh"
|
||||
#include <cstdint>
|
||||
|
||||
static constexpr uint32_t PSR_CLEAR_RESERVED = 0xf00000ff;
|
||||
static constexpr uint32_t PSR_CLEAR_MODE = 0x0b00000;
|
||||
|
||||
class Psr {
|
||||
uint32_t psr;
|
||||
|
||||
public:
|
||||
// clear the reserved bits i.e, [8:27]
|
||||
Psr(uint32_t raw) { psr = raw & PSR_CLEAR_RESERVED; }
|
||||
|
||||
// Mode : [4:0]
|
||||
Mode mode() const { return static_cast<Mode>(psr & ~PSR_CLEAR_MODE); }
|
||||
void set_mode(Mode mode) {
|
||||
psr &= PSR_CLEAR_MODE;
|
||||
psr |= static_cast<uint32_t>(mode);
|
||||
}
|
||||
|
||||
// State : [5]
|
||||
bool state() const { return get_nth_bit(psr, 5); }
|
||||
void set_state(State state) {
|
||||
chg_nth_bit(psr, 5, static_cast<bool>(state));
|
||||
}
|
||||
|
||||
#define GET_SET_NTH_BIT_FUNCTIONS(name, n) \
|
||||
bool name() const { return get_nth_bit(psr, n); } \
|
||||
void set_##name(bool val) { chg_nth_bit(psr, n, val); }
|
||||
|
||||
// FIQ disable : [6]
|
||||
GET_SET_NTH_BIT_FUNCTIONS(fiq_disabled, 6)
|
||||
|
||||
// IRQ disable : [7]
|
||||
GET_SET_NTH_BIT_FUNCTIONS(irq_disabled, 7)
|
||||
|
||||
// Reserved bits : [27:8]
|
||||
|
||||
// Overflow flag : [28]
|
||||
GET_SET_NTH_BIT_FUNCTIONS(v, 28);
|
||||
|
||||
// Carry flag : [29]
|
||||
GET_SET_NTH_BIT_FUNCTIONS(c, 29);
|
||||
|
||||
// Zero flag : [30]
|
||||
GET_SET_NTH_BIT_FUNCTIONS(z, 30);
|
||||
|
||||
// Negative flag : [30]
|
||||
GET_SET_NTH_BIT_FUNCTIONS(n, 31);
|
||||
|
||||
#undef GET_SET_NTH_BIT_FUNCTIONS
|
||||
};
|
17
src/cpu/utility.hh
Normal file
17
src/cpu/utility.hh
Normal file
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
enum class Mode {
|
||||
/* M[4:0] in PSR */
|
||||
User = 0b10000,
|
||||
Fiq = 0b10001,
|
||||
Irq = 0b10010,
|
||||
Supervisor = 0b10011,
|
||||
Abort = 0b10111,
|
||||
Undefined = 0b11011,
|
||||
System = 0b11111,
|
||||
};
|
||||
|
||||
enum class State {
|
||||
Arm = 0,
|
||||
Thumb = 1
|
||||
};
|
24
src/emulator.cc
Normal file
24
src/emulator.cc
Normal file
@@ -0,0 +1,24 @@
|
||||
#include "emulator.hh"
|
||||
#include "bus.hh"
|
||||
#include "cpu/cpu.hh"
|
||||
#include <fstream>
|
||||
|
||||
namespace emulator {
|
||||
void
|
||||
run(std::ifstream& ifile) {
|
||||
Bus bus(ifile);
|
||||
Cpu cpu(bus);
|
||||
}
|
||||
|
||||
void
|
||||
run(std::string filepath) {
|
||||
std::ifstream ifile(filepath, std::ios::in | std::ios::binary);
|
||||
|
||||
if (!ifile.is_open()) {
|
||||
throw std::ios::failure("No such file exists", std::error_code());
|
||||
}
|
||||
|
||||
run(ifile);
|
||||
ifile.close();
|
||||
}
|
||||
}
|
@@ -1,7 +0,0 @@
|
||||
#include "matar.hh"
|
||||
#include "cpu/cpu.hh"
|
||||
|
||||
int run() {
|
||||
cpu::run();
|
||||
return 0;
|
||||
}
|
@@ -1,5 +1,5 @@
|
||||
lib_sources = files(
|
||||
'matar.cc'
|
||||
'emulator.cc'
|
||||
)
|
||||
|
||||
subdir('cpu')
|
||||
@@ -7,6 +7,8 @@ subdir('cpu')
|
||||
lib = library(
|
||||
meson.project_name(),
|
||||
lib_sources,
|
||||
include_directories: includes,
|
||||
install : true
|
||||
)
|
||||
include_directories: inc,
|
||||
install: true
|
||||
)
|
||||
|
||||
import('pkgconfig').generate(lib)
|
Reference in New Issue
Block a user