Add initial working version
All checks were successful
Build / build (push) Successful in 4s

This commit is contained in:
shylie 2025-07-07 09:24:19 -04:00
parent 464ea876d1
commit 9e2604855e
9 changed files with 693 additions and 49 deletions

View File

@ -0,0 +1,14 @@
name: Build
on: push
jobs:
build:
runs-on: shy-server
steps:
- name: Checkout current
uses: actions/checkout@v4
- name: Configure
run: cmake -S emulator -B emulator/build
- name: Build
run: cmake --build emulator/build

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
emulator/.cache/
emulator/build/
emulator/customasm/*.bin

View File

@ -1,52 +1,52 @@
╔══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╗
║ Foot ISA ║
╠═════════════════════════════════════════════════════════════════════════════════════════════════════════════════╦══╦═════════════════════════════════╣
║ Instruction Format ║ ║ Addressing Modes ║
╠══════════╦═════════════════════════╦════════════════════╦═══════════════════════════════════════════════════════╣ ╠═══════╦═════════════════════════╣
║ ║ ║ ║ Encoding ║ ║ 000 ║ Immediate ║
║ Mnemonic ║ Description ║ Semantics ╠═════════════╦═════════════╦═════════════╦═════════════╣ ╠═══════╬═════════════════════════╣
║ ║ ║ ║ 24-31 ║ 16-23 ║ 8-15 ║ 0-7 ║ ║ 001 ║ Direct ║
╠══════════╬═════════════════════════╬════════════════════╬══════╦══════╬══════╦══════╬══════╦══════╬══════╦══════╣ ╠═══════╬═════════════════════════╣
║ CNST ║ Immediate ║ D = I ║ CCCR ║ 0000 ║ DDDd ║ dddd ║ iiii ║ iiii ║ iiii ║ iiii ║ ║ 010 ║ Indirect Auto-increment ║
╠══════════╬═════════════════════════╬════════════════════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╣ ╠═══════╬═════════════════════════╣
║ CMPR ║ Compare ║ C = A <=> B ║ CCCR ║ 0001 ║ BBBb ║ bbbb ║ 0000 ║ 0000 ║ AAAa ║ aaaa ║ ║ 011 ║ Indirect Auto-decrement ║
╠══════════╬═════════════════════════╬════════════════════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╣ ╠═══════╬═════════════════════════╣
║ BWNG ║ Bitwise negate ║ D = ~A ║ CCCR ║ 0001 ║ DDDd ║ dddd ║ 0000 ║ 0001 ║ AAAa ║ aaaa ║ ║ 100 ║ Indirect ║
╠══════════╬═════════════════════════╬════════════════════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╣ ╠═══════╬═════════════════════════╣
║ ARNG ║ Arithmetic negate ║ D = -A ║ CCCR ║ 0001 ║ DDDd ║ dddd ║ 0000 ║ 0010 ║ AAAa ║ aaaa ║ ║ 101 ║ Indirect 1-word offset ║
╠══════════╬═════════════════════════╬════════════════════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╣ ╠═══════╬═════════════════════════╣
║ LONG ║ Logical negate ║ D = !A ║ CCCR ║ 0001 ║ DDDd ║ dddd ║ 0000 ║ 0011 ║ AAAa ║ aaaa ║ ║ 110 ║ Indirect 2-word offset ║
╠══════════╬═════════════════════════╬════════════════════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╣ ╠═══════╬═════════════════════════╣
║ CONF ║ Configure processor ║ ║ CCCR ║ 0001 ║ DDDd ║ dddd ║ 0000 ║ 0100 ║ AAAa ║ aaaa ║ ║ 111 ║ Indirect 3-word offset ║
╠══════════╬═════════════════════════╬════════════════════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╣ ╠═══════╩═════════════════════════╣
║ BWOR ║ Bitwise or ║ D = A | B ║ CCCR ║ 0010 ║ DDDd ║ dddd ║ BBBb ║ bbbb ║ AAAa ║ aaaa ║ ║ Conditional Execution Flags ║
╠══════════╬═════════════════════════╬════════════════════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╣ ╠═══════╦═════════════════════════╣
║ BAND ║ Bitwise and ║ D = A & B ║ CCCR ║ 0011 ║ DDDd ║ dddd ║ BBBb ║ bbbb ║ AAAa ║ aaaa ║ ║ 000 ║ Always ║
╠══════════╬═════════════════════════╬════════════════════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╣ ╠═══════╬═════════════════════════╣
║ BXOR ║ Bitwise xor ║ D = A ^ B ║ CCCR ║ 0100 ║ DDDd ║ dddd ║ BBBb ║ bbbb ║ AAAa ║ aaaa ║ ║ 001 ║ A < B ║
╠══════════╬═════════════════════════╬════════════════════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╣ ╠═══════╬═════════════════════════╣
║ URSH ║ Unsigned right shift ║ D = A >> B ║ CCCR ║ 0101 ║ DDDd ║ dddd ║ BBBb ║ bbbb ║ AAAa ║ aaaa ║ ║ 010 ║ A <= B ║
╠══════════╬═════════════════════════╬════════════════════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╣ ╠═══════╬═════════════════════════╣
║ SRSH ║ Signed right shift ║ D = A >> B ║ CCCR ║ 0110 ║ DDDd ║ dddd ║ BBBb ║ bbbb ║ AAAa ║ aaaa ║ ║ 011 ║ A = B ║
╠══════════╬═════════════════════════╬════════════════════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╣ ╠═══════╬═════════════════════════╣
║ ZLSH ║ Pad zero left shift ║ D = A << B ║ CCCR ║ 0111 ║ DDDd ║ dddd ║ BBBb ║ bbbb ║ AAAa ║ aaaa ║ ║ 100 ║ A >= B ║
╠══════════╬═════════════════════════╬════════════════════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╣ ╠═══════╬═════════════════════════╣
║ CLSH ║ Circular left shift ║ D = A << B ║ CCCR ║ 1000 ║ DDDd ║ dddd ║ BBBb ║ bbbb ║ AAAa ║ aaaa ║ ║ 101 ║ A > B ║
╠══════════╬═════════════════════════╬════════════════════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╣ ╠═══════╬═════════════════════════╣
║ ADDI ║ Addition ║ D = A + B ║ CCCR ║ 1001 ║ DDDd ║ dddd ║ BBBb ║ bbbb ║ AAAa ║ aaaa ║ ║ 110 ║ A != B ║
╠══════════╬═════════════════════════╬════════════════════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╣ ╠═══════╬═════════════════════════╣
║ SUBT ║ Subtraction ║ D = A - B ║ CCCR ║ 1010 ║ DDDd ║ dddd ║ BBBb ║ bbbb ║ AAAa ║ aaaa ║ ║ 111 ║ Reserved ║
╠══════════╬═════════════════════════╬════════════════════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╣ ╠═══════╩═════════════════════════╣
║ MULS ║ Signed multiplication ║ D = A * B >> shift ║ CCCR ║ 1011 ║ DDDd ║ dddd ║ BBBb ║ bbbb ║ AAAa ║ aaaa ║ ║ Processor configuration bits ║
╠══════════╬═════════════════════════╬════════════════════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╣ ╠═══════╦═════════════════════════╣
MULU ║ Unsigned multiplication ║ D = A * B >> shift ║ CCCR ║ 1100 ║ DDDd ║ dddd ║ BBBb ║ bbbb ║ AAAa ║ aaaa ║ ║ 3-0 ║ Multiplication shift ║
╠══════════╬═════════════════════════╬════════════════════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╣ ╠═══════╬═════════════════════════╣
MULR ║ Integer multiplication ║ D = A * B ║ CCCR ║ 1101 ║ DDDd ║ dddd ║ BBBb ║ bbbb ║ AAAa ║ aaaa ║ ║ 7-4 ║ Reserved ║
╠══════════╬═════════════════════════╬════════════════════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╣ ╠═══════╬═════════════════════════╣
║ DIVI ║ Division ║ D = A / B ║ CCCR ║ 1110 ║ DDDd ║ dddd ║ BBBb ║ bbbb ║ AAAa ║ aaaa ║ ║ 11-8 ║ Reserved ║
╠══════════╬═════════════════════════╬════════════════════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╣ ╠═══════╬═════════════════════════╣
║ MODU ║ Modulus ║ D = A % B ║ CCCR ║ 1111 ║ DDDd ║ dddd ║ BBBb ║ bbbb ║ AAAa ║ aaaa ║ ║ 15-12 ║ Reserved ║
╚══════════╩═════════════════════════╩════════════════════╩══════╩══════╩══════╩══════╩══════╩══════╩══════╩══════╩══╩═══════╩═════════════════════════╝
╔════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
Foot ISA
╠═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════╦══╦═════════════════════════════════╣
Instruction Format ║ ║ Addressing Modes ║
╠══════════╦═════════════════════════╦══════════════════════╦═══════════════════════════════════════════════════════╣ ╠═══════╦═════════════════════════╣
║ ║ ║ ║ Encoding ║ ║ 000 ║ Immediate ║
║ Mnemonic ║ Description ║ Semantics ╠═════════════╦═════════════╦═════════════╦═════════════╣ ╠═══════╬═════════════════════════╣
║ ║ ║ ║ 24-31 ║ 16-23 ║ 8-15 ║ 0-7 ║ ║ 001 ║ Direct ║
╠══════════╬═════════════════════════╬══════════════════════╬══════╦══════╬══════╦══════╬══════╦══════╬══════╦══════╣ ╠═══════╬═════════════════════════╣
║ CNST ║ Immediate ║ D = I ║ CCCR ║ 0000 ║ DDDd ║ dddd ║ iiii ║ iiii ║ iiii ║ iiii ║ ║ 010 ║ Indirect Auto-increment ║
╠══════════╬═════════════════════════╬══════════════════════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╣ ╠═══════╬═════════════════════════╣
║ CMPR ║ Compare ║ C = A <=> B ║ CCCR ║ 0001 ║ BBBb ║ bbbb ║ 0000 ║ 0000 ║ AAAa ║ aaaa ║ ║ 011 ║ Indirect Auto-decrement ║
╠══════════╬═════════════════════════╬══════════════════════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╣ ╠═══════╬═════════════════════════╣
║ BWNG ║ Bitwise negate ║ D = ~A ║ CCCR ║ 0001 ║ DDDd ║ dddd ║ 0000 ║ 0001 ║ AAAa ║ aaaa ║ ║ 100 ║ Indirect ║
╠══════════╬═════════════════════════╬══════════════════════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╣ ╠═══════╬═════════════════════════╣
║ ARNG ║ Arithmetic negate ║ D = -A ║ CCCR ║ 0001 ║ DDDd ║ dddd ║ 0000 ║ 0010 ║ AAAa ║ aaaa ║ ║ 101 ║ Indirect 1-word offset ║
╠══════════╬═════════════════════════╬══════════════════════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╣ ╠═══════╬═════════════════════════╣
║ LONG ║ Logical negate ║ D = !A ║ CCCR ║ 0001 ║ DDDd ║ dddd ║ 0000 ║ 0011 ║ AAAa ║ aaaa ║ ║ 110 ║ Indirect 2-word offset ║
╠══════════╬═════════════════════════╬══════════════════════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╣ ╠═══════╬═════════════════════════╣
║ CONF ║ Configure processor ║ ║ CCCR ║ 0001 ║ DDDd ║ dddd ║ 0000 ║ 0100 ║ AAAa ║ aaaa ║ ║ 111 ║ Indirect 3-word offset ║
╠══════════╬═════════════════════════╬══════════════════════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╣ ╠═══════╩═════════════════════════╣
║ BWOR ║ Bitwise or ║ D = A | B ║ CCCR ║ 0010 ║ DDDd ║ dddd ║ BBBb ║ bbbb ║ AAAa ║ aaaa ║ ║ Conditional Execution Flags ║
╠══════════╬═════════════════════════╬══════════════════════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╣ ╠═══════╦═════════════════════════╣
║ BAND ║ Bitwise and ║ D = A & B ║ CCCR ║ 0011 ║ DDDd ║ dddd ║ BBBb ║ bbbb ║ AAAa ║ aaaa ║ ║ 000 ║ Always ║
╠══════════╬═════════════════════════╬══════════════════════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╣ ╠═══════╬═════════════════════════╣
║ BXOR ║ Bitwise xor ║ D = A ^ B ║ CCCR ║ 0100 ║ DDDd ║ dddd ║ BBBb ║ bbbb ║ AAAa ║ aaaa ║ ║ 001 ║ A < B ║
╠══════════╬═════════════════════════╬══════════════════════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╣ ╠═══════╬═════════════════════════╣
║ URSH ║ Unsigned right shift ║ D = A >> B ║ CCCR ║ 0101 ║ DDDd ║ dddd ║ BBBb ║ bbbb ║ AAAa ║ aaaa ║ ║ 010 ║ A <= B ║
╠══════════╬═════════════════════════╬══════════════════════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╣ ╠═══════╬═════════════════════════╣
║ SRSH ║ Signed right shift ║ D = A >> B ║ CCCR ║ 0110 ║ DDDd ║ dddd ║ BBBb ║ bbbb ║ AAAa ║ aaaa ║ ║ 011 ║ A = B ║
╠══════════╬═════════════════════════╬══════════════════════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╣ ╠═══════╬═════════════════════════╣
║ ZLSH ║ Pad zero left shift ║ D = A << B ║ CCCR ║ 0111 ║ DDDd ║ dddd ║ BBBb ║ bbbb ║ AAAa ║ aaaa ║ ║ 100 ║ A >= B ║
╠══════════╬═════════════════════════╬══════════════════════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╣ ╠═══════╬═════════════════════════╣
║ CLSH ║ Circular left shift ║ D = A << B ║ CCCR ║ 1000 ║ DDDd ║ dddd ║ BBBb ║ bbbb ║ AAAa ║ aaaa ║ ║ 101 ║ A > B ║
╠══════════╬═════════════════════════╬══════════════════════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╣ ╠═══════╬═════════════════════════╣
║ ADDI ║ Addition ║ D = A + B ║ CCCR ║ 1001 ║ DDDd ║ dddd ║ BBBb ║ bbbb ║ AAAa ║ aaaa ║ ║ 110 ║ A != B ║
╠══════════╬═════════════════════════╬══════════════════════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╣ ╠═══════╬═════════════════════════╣
║ SUBT ║ Subtraction ║ D = A - B ║ CCCR ║ 1010 ║ DDDd ║ dddd ║ BBBb ║ bbbb ║ AAAa ║ aaaa ║ ║ 111 ║ Reserved ║
╠══════════╬═════════════════════════╬══════════════════════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╣ ╠═══════╩═════════════════════════╣
║ MULT ║ Multiplication ║ D = A * B >> shift ║ CCCR ║ 1011 ║ DDDd ║ dddd ║ BBBb ║ bbbb ║ AAAa ║ aaaa ║ ║ Processor configuration bits ║
╠══════════╬═════════════════════════╬══════════════════════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╣ ╠═══════╦═════════════════════════╣
RESERVED ║ ║ ║ CCCR ║ 1100 ║ DDDd ║ dddd ║ BBBb ║ bbbb ║ AAAa ║ aaaa ║ ║ 3-0 ║ Multiplication shift ║
╠══════════╬═════════════════════════╬══════════════════════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╣ ╠═══════╬═════════════════════════╣
RESERVED ║ ║ ║ CCCR ║ 1101 ║ DDDd ║ dddd ║ BBBb ║ bbbb ║ AAAa ║ aaaa ║ ║ 7-4 ║ Reserved ║
╠══════════╬═════════════════════════╬══════════════════════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╣ ╠═══════╬═════════════════════════╣
║ DIVI ║ Division ║ D = (A << shift) / B ║ CCCR ║ 1110 ║ DDDd ║ dddd ║ BBBb ║ bbbb ║ AAAa ║ aaaa ║ ║ 11-8 ║ Reserved ║
╠══════════╬═════════════════════════╬══════════════════════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╬══════╣ ╠═══════╬═════════════════════════╣
║ MODU ║ Modulus ║ D = (A << shift) % B ║ CCCR ║ 1111 ║ DDDd ║ dddd ║ BBBb ║ bbbb ║ AAAa ║ aaaa ║ ║ 15-12 ║ Reserved ║
╚══════════╩═════════════════════════╩══════════════════════╩══════╩══════╩══════╩══════╩══════╩══════╩══════╩══════╩══╩═══════╩═════════════════════════╝
╔═════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╗
║ Foot ISA Table Key ║

5
emulator/CMakeLists.txt Normal file
View File

@ -0,0 +1,5 @@
cmake_minimum_required(VERSION 3.20)
project(foot-emu)
add_executable(foot-emu main.cpp foot-emulator.cpp foot-emulator.h)

View File

@ -0,0 +1,8 @@
#include "foot.asm"
CNST. r0, #0xFFFF
loop:
CNST. [r0], #65
CNST. [r0], #66
CNST. r31, #loop

View File

@ -0,0 +1,60 @@
#once
#subruledef operand
{
#{immediate: i5} => 0b000 @ immediate
r{register: u5} => 0b001 @ register
[r{register: u5}++] => 0b010 @ register
[r{register: u5}--] => 0b011 @ register
[r{register: u5}] => 0b100 @ register
[r{register: u5} + {offset: u2}] => 0b1 @ offset @ register
}
#subruledef crflags
{
. => 0b0000
.l => 0b0010
.le => 0b0100
.e => 0b0110
.ge => 0b1000
.g => 0b1010
.ne => 0b1100
.r => 0b0001
.rl => 0b0011
.rle => 0b0101
.re => 0b0111
.rge => 0b1001
.re => 0b1011
.rne => 0b1101
}
#ruledef
{
CNST{flags: crflags} {dst: operand}, #{imm: i16} => imm @ flags @ 0b0000 @ dst
CMPR{flags: crflags} {dst: operand}, {a: operand} => 0x00 @ a @ flags @ 0b0001 @ dst
BWNG{flags: crflags} {dst: operand}, {a: operand} => 0x01 @ a @ flags @ 0b0001 @ dst
ARNG{flags: crflags} {dst: operand}, {a: operand} => 0x02 @ a @ flags @ 0b0001 @ dst
LONG{flags: crflags} {dst: operand}, {a: operand} => 0x03 @ a @ flags @ 0b0001 @ dst
BWOR{flags: crflags} {dst: operand}, {a: operand}, {b: operand} => b @ a @ flags @ 0b0010 @ dst
BAND{flags: crflags} {dst: operand}, {a: operand}, {b: operand} => b @ a @ flags @ 0b0011 @ dst
BXOR{flags: crflags} {dst: operand}, {a: operand}, {b: operand} => b @ a @ flags @ 0b0100 @ dst
URSH{flags: crflags} {dst: operand}, {a: operand}, {b: operand} => b @ a @ flags @ 0b0101 @ dst
SRSH{flags: crflags} {dst: operand}, {a: operand}, {b: operand} => b @ a @ flags @ 0b0110 @ dst
ZLSH{flags: crflags} {dst: operand}, {a: operand}, {b: operand} => b @ a @ flags @ 0b0111 @ dst
CLSH{flags: crflags} {dst: operand}, {a: operand}, {b: operand} => b @ a @ flags @ 0b1000 @ dst
ADDI{flags: crflags} {dst: operand}, {a: operand}, {b: operand} => b @ a @ flags @ 0b1001 @ dst
SUBT{flags: crflags} {dst: operand}, {a: operand}, {b: operand} => b @ a @ flags @ 0b1010 @ dst
MULT{flags: crflags} {dst: operand}, {a: operand}, {b: operand} => b @ a @ flags @ 0b1011 @ dst
DIVI{flags: crflags} {dst: operand}, {a: operand}, {b: operand} => b @ a @ flags @ 0b1110 @ dst
MODU{flags: crflags} {dst: operand}, {a: operand}, {b: operand} => b @ a @ flags @ 0b1111 @ dst
}
#bankdef mem
{
#bits 16
#addr 0
#size 0x10000
#outp 0
}
#bank mem

375
emulator/foot-emulator.cpp Normal file
View File

@ -0,0 +1,375 @@
#include "foot-emulator.h"
#include <stdexcept>
namespace foot
{
Operand::Operand(uint8_t bits) :
addressing_mode(static_cast<AddressingMode>((bits & 0b11100000) >> 5)),
register_index(bits & 0b00011111)
{}
Device::Device(uint16_t size) :
assigned_memory_base(nullptr),
assigned_memory_size(size)
{}
uint16_t& Device::memory(uint16_t index)
{
if (index >= assigned_memory_size)
{
throw std::range_error("Index out of range");
}
return assigned_memory_base[index];
}
Emulator::Emulator() :
memory{},
mapped_base(0)
{}
void Emulator::run(const std::vector<uint16_t>& program)
{
std::copy(program.begin(), program.end(), memory.begin());
while (true)
{
run_instruction();
for (auto& device : devices)
{
device->update();
}
}
}
void Emulator::map_device(std::unique_ptr<Device>&& device)
{
mapped_base -= device->mapped_memory_size();
device->assigned_memory_base = &memory[mapped_base];
devices.push_back(std::move(device));
}
uint16_t& Emulator::memory_at(uint16_t address)
{
return memory[address];
}
uint16_t& Emulator::decode_operand(Operand operand)
{
switch (operand.addressing_mode)
{
case Operand::AddressingMode::Immediate:
dummy_value = operand.register_index;
return dummy_value;
case Operand::AddressingMode::Direct:
return registers[operand.register_index];
case Operand::AddressingMode::IndirectAutoIncrement:
return memory[++registers[operand.register_index]];
case Operand::AddressingMode::IndirectAutoDecrement:
return memory[--registers[operand.register_index]];
case Operand::AddressingMode::Indirect:
return memory[registers[operand.register_index]];
case Operand::AddressingMode::IndirectOffset1Word:
return memory[registers[operand.register_index] + 1];
case Operand::AddressingMode::IndirectOffset2Word:
return memory[registers[operand.register_index] + 2];
case Operand::AddressingMode::IndirectOffset3Word:
return memory[registers[operand.register_index] + 3];
default:
return dummy_value;
}
}
void Emulator::read_instruction()
{
uint16_t low = memory[registers[PC]++];
uint16_t high = memory[registers[PC]++];
instruction = (uint32_t(high) << 16) | uint32_t(low);
}
void Emulator::run_instruction()
{
read_instruction();
const bool rep = repeats();
const uint16_t loop_count = rep ? registers[LC] : 1;
for (int iteration = 0; iteration < loop_count; iteration++)
{
if (rep) { registers[LC] = iteration; }
switch ((instruction & 0x0F000000) >> 24)
{
case 0:
CNST();
break;
case 1:
switch ((instruction & 0x0000FF00) >> 8)
{
case 0:
CMPR();
break;
case 1:
BWNG();
break;
case 2:
ARNG();
break;
case 3:
LONG();
break;
case 4:
CONF();
break;
}
break;
case 2:
BWOR();
break;
case 3:
BAND();
break;
case 4:
BXOR();
break;
case 5:
URSH();
break;
case 6:
SRSH();
break;
case 7:
ZLSH();
break;
case 8:
CLSH();
break;
case 9:
ADDI();
break;
case 10:
SUBT();
break;
case 11:
MULT();
break;
case 14:
DIVI();
break;
case 15:
MODU();
break;
default:
break;
}
}
if (rep) { registers[LC] = loop_count; }
}
void Emulator::CNST()
{
decode_operand((instruction & 0x00FF0000) >> 16) = instruction & 0x0000FFFF;
}
void Emulator::CMPR()
{
uint16_t a = decode_operand(instruction & 0x000000FF);
uint16_t b = decode_operand((instruction & 0x00FF0000) >> 16);
if (a < b)
{
status_register = Status::Less;
}
else if (a > b)
{
status_register = Status::Greater;
}
else
{
status_register = Status::Equal;
}
}
void Emulator::BWNG()
{
uint16_t a = decode_operand(instruction & 0x000000FF);
uint16_t& d = decode_operand((instruction & 0x00FF0000) >> 16);
d = ~a;
}
void Emulator::ARNG()
{
uint16_t a = decode_operand(instruction & 0x000000FF);
uint16_t& d = decode_operand((instruction & 0x00FF0000) >> 16);
d = -a;
}
void Emulator::LONG()
{
uint16_t a = decode_operand(instruction & 0x000000FF);
uint16_t& d = decode_operand((instruction & 0x00FF0000) >> 16);
d = !a;
}
void Emulator::CONF()
{
uint16_t a = decode_operand(instruction & 0x000000FF);
uint16_t& d = decode_operand((instruction & 0x00FF0000) >> 16);
d = configuration;
configuration = a;
}
void Emulator::BWOR()
{
uint16_t a = decode_operand(instruction & 0x000000FF);
uint16_t b = decode_operand((instruction & 0x0000FF00) >> 8);
uint16_t& d = decode_operand((instruction & 0x00FF0000) >> 16);
d = a | b;
}
void Emulator::BAND()
{
uint16_t a = decode_operand(instruction & 0x000000FF);
uint16_t b = decode_operand((instruction & 0x0000FF00) >> 8);
uint16_t& d = decode_operand((instruction & 0x00FF0000) >> 16);
d = a & b;
}
void Emulator::BXOR()
{
uint16_t a = decode_operand(instruction & 0x000000FF);
uint16_t b = decode_operand((instruction & 0x0000FF00) >> 8);
uint16_t& d = decode_operand((instruction & 0x00FF0000) >> 16);
d = a ^ b;
}
void Emulator::URSH()
{
uint16_t a = decode_operand(instruction & 0x000000FF);
uint16_t b = decode_operand((instruction & 0x0000FF00) >> 8);
uint16_t& d = decode_operand((instruction & 0x00FF0000) >> 16);
d = a >> b;
}
void Emulator::SRSH()
{
uint16_t a = decode_operand(instruction & 0x000000FF);
uint16_t b = decode_operand((instruction & 0x0000FF00) >> 8);
uint16_t& d = decode_operand((instruction & 0x00FF0000) >> 16);
d = int16_t(a) >> int16_t(b);
}
void Emulator::ZLSH()
{
uint16_t a = decode_operand(instruction & 0x000000FF);
uint16_t b = decode_operand((instruction & 0x0000FF00) >> 8);
uint16_t& d = decode_operand((instruction & 0x00FF0000) >> 16);
d = a << b;
}
void Emulator::CLSH()
{
uint16_t a = decode_operand(instruction & 0x000000FF);
uint16_t b = decode_operand((instruction & 0x0000FF00) >> 8);
uint16_t& d = decode_operand((instruction & 0x00FF0000) >> 16);
d = (a << b) | (a >> (16 - b));
}
void Emulator::ADDI()
{
uint16_t a = decode_operand(instruction & 0x000000FF);
uint16_t b = decode_operand((instruction & 0x0000FF00) >> 8);
uint16_t& d = decode_operand((instruction & 0x00FF0000) >> 16);
d = a + b;
}
void Emulator::SUBT()
{
uint16_t a = decode_operand(instruction & 0x000000FF);
uint16_t b = decode_operand((instruction & 0x0000FF00) >> 8);
uint16_t& d = decode_operand((instruction & 0x00FF0000) >> 16);
d = a - b;
}
void Emulator::MULT()
{
uint16_t a = decode_operand(instruction & 0x000000FF);
uint16_t b = decode_operand((instruction & 0x0000FF00) >> 8);
uint16_t& d = decode_operand((instruction & 0x00FF0000) >> 16);
d = uint32_t(int32_t(a) * int32_t(b)) >> shift();
}
void Emulator::DIVI()
{
uint16_t a = decode_operand(instruction & 0x000000FF);
uint16_t b = decode_operand((instruction & 0x0000FF00) >> 8);
uint16_t& d = decode_operand((instruction & 0x00FF0000) >> 16);
d = uint32_t((int32_t(a) >> shift()) / int32_t(b));
}
void Emulator::MODU()
{
uint16_t a = decode_operand(instruction & 0x000000FF);
uint16_t b = decode_operand((instruction & 0x0000FF00) >> 8);
uint16_t& d = decode_operand((instruction & 0x00FF0000) >> 16);
d = uint32_t((int32_t(a) >> shift()) / int32_t(b));
}
bool Emulator::repeats()
{
return instruction & 0x10000000;
}
uint8_t Emulator::shift()
{
return configuration & 0x000F;
}
}

106
emulator/foot-emulator.h Normal file
View File

@ -0,0 +1,106 @@
#ifndef FOOT_EMULATOR
#define FOOT_EMULATOR
#include <array>
#include <cstdint>
#include <memory>
#include <vector>
namespace foot
{
struct Operand
{
Operand(uint8_t bits);
enum class AddressingMode
{
Immediate = 0,
Direct,
IndirectAutoIncrement,
IndirectAutoDecrement,
Indirect,
IndirectOffset1Word,
IndirectOffset2Word,
IndirectOffset3Word
} addressing_mode;
uint8_t register_index;
};
class Device
{
friend class Emulator;
public:
virtual ~Device() = default;
virtual void update() = 0;
virtual uint16_t mapped_memory_size() const { return assigned_memory_size; }
protected:
Device(uint16_t size);
uint16_t& memory(uint16_t index);
private:
uint16_t* assigned_memory_base;
uint16_t assigned_memory_size;
};
class Emulator
{
public:
static constexpr uint8_t PC = 31;
static constexpr uint8_t LC = 30;
Emulator();
void run(const std::vector<uint16_t>& program);
void map_device(std::unique_ptr<Device>&& device);
uint16_t& memory_at(uint16_t address);
uint16_t& decode_operand(Operand operand);
private:
std::array<uint16_t, 1 << 16> memory;
std::array<uint16_t, 1 << 5> registers;
enum class Status
{
Less,
Equal,
Greater
} status_register;
uint32_t instruction;
uint16_t configuration;
uint16_t dummy_value;
std::vector<std::unique_ptr<Device>> devices;
uint16_t mapped_base;
void read_instruction();
void run_instruction();
void CNST();
void CMPR();
void BWNG();
void ARNG();
void LONG();
void CONF();
void BWOR();
void BAND();
void BXOR();
void URSH();
void SRSH();
void ZLSH();
void CLSH();
void ADDI();
void SUBT();
void MULT();
void DIVI();
void MODU();
bool repeats();
uint8_t shift();
};
}
#endif//FOOT_EMULATOR

73
emulator/main.cpp Normal file
View File

@ -0,0 +1,73 @@
#include "foot-emulator.h"
#include <fstream>
#include <iostream>
namespace
{
std::vector<uint16_t> read_file(std::istream& stream)
{
std::vector<uint16_t> buf;
std::streampos pos = stream.tellg();
stream.seekg(0, std::ios::end);
pos = stream.tellg() - pos;
buf.resize(pos / 2);
stream.seekg(0, std::ios::beg);
for (int i = 0; i < buf.size(); i++)
{
char value[2] = {0, 0};
stream.read(value, 2);
buf[i] = (uint16_t(value[0]) << 8) | (uint16_t(value[1]) & 0xFF);
}
return buf;
}
class PrintDevice : public foot::Device
{
public:
PrintDevice() :
Device(1)
{
}
void update() override
{
uint16_t& value = memory(0);
if (value != 0)
{
std::cout << char(value);
value = 0;
}
}
};
}
int main(int argc, char** argv)
{
foot::Emulator emu;
emu.map_device(std::make_unique<PrintDevice>());
if (argc > 1)
{
std::ifstream file(argv[1], std::ios::binary);
if (!file.is_open())
{
std::cerr << "Failed to open file: " << argv[1] << std::endl;
return 1;
}
emu.run(read_file(file));
}
else
{
std::cout << "Usage: " << argv[0] << " <filename>" << std::endl;
}
return 1;
}