commit 75123a629ad89254ef164fd79c5d1dcda2b8ef00 Author: CrazyEttin <> Date: Sat Jul 23 23:05:32 2022 +0300 Initial commit diff --git a/disassembler.pas b/disassembler.pas new file mode 100644 index 0000000..7e04a70 --- /dev/null +++ b/disassembler.pas @@ -0,0 +1,105 @@ +program Disassembler; + +uses Crt, Sysutils; + +var + Op, Regs: 0 .. $f; //Opcode + X, Y: 0 .. 3; //Register arguments + Addr, IP, EP: word; //Address argument and instruction and end pointers + Opcodes: array [0 .. $f] of string; //Opcodes in human readable form + Bin: array [0 .. $ffef] of byte; //Program in binary form + Prog: file of byte; //Program file + +begin + + //Populate the opcode array + Opcodes [0] := 'HALT '; + Opcodes [1] := 'RET '; + Opcodes [2] := 'SHL '; + Opcodes [3] := 'SHR '; + Opcodes [4] := 'ROL '; + Opcodes [5] := 'ROR '; + Opcodes [6] := 'NAND '; + Opcodes [7] := 'AND '; + Opcodes [8] := 'OR '; + Opcodes [9] := 'XOR '; + Opcodes [$a] := 'LOAD '; + Opcodes [$b] := 'STORE '; + Opcodes [$c] := 'BREQ '; + Opcodes [$d] := 'BRNEQ '; + Opcodes [$e] := 'CLEQ '; + Opcodes [$f] := 'CLNEQ '; + + //Initialise the pointer + IP := 0; + + //Read a program file and check for errors + if ParamCount <> 1 then begin + writeln ('Usage: disassembler program'); + exit; + end; + {$i-} + assign (Prog, ParamStr (1)); + reset (Prog); + {$i+} + if IOResult <> 0 then begin + writeln ('Usage: disassembler program'); + exit; + end; + repeat + read (Prog, Bin [IP]); + IP := IP + 1; + until (eof (Prog)) or (IP = $fff0); + + //Save the end point and reinitialise the instruction pointer + EP := IP; + IP := 0; + + //Begin the main loop + repeat + + //Print the memory location + if IP < $1000 then write (' '); + if IP < $100 then write (' '); + if IP < $10 then write (' '); + write (IntToHex (IP, 1), ' '); + + //Fetch the instruction and increment the instruction pointer + //Opcode + Op := Bin [IP] and $f0 shr 4; + //Register arguments + Regs := Bin [IP] and $f; + X := Bin [IP] and $c shr 2; + Y := Bin [IP] and 3; + IP := IP + 1; + //Address argument + if Op >= $a then begin + Addr := Bin [IP]; + Addr := Addr shl 8; + IP := IP + 1; + Addr := Addr + Bin [IP]; + IP := IP - 1; + end; + + //Print the data + write (IntToHex (Op, 1), IntToHex (Regs, 1)); + if Op >= $a then write (IntToHex (Addr, 4)) else write (' '); + write (' '); + + //Print the instruction + write (Opcodes [Op]); + if Op = $b then writeln (IntToHex (Addr, 1), ', R', X) + else begin + if Op >= 2 then if Op <> $b then write ('R', X); + if Op >= 6 then if Op <= 9 then write (', R', Y); + if OP >= $c then write (', R', Y); + if Op >= $a then write (', ', IntToHex (Addr, 1)); + writeln (); + end; + + + + + until (IP >= EP); + +end. diff --git a/emulator.pas b/emulator.pas new file mode 100644 index 0000000..c4f4fd8 --- /dev/null +++ b/emulator.pas @@ -0,0 +1,146 @@ +program Emulator; + +uses Crt; + +const + IO = $ffff; + +var + Halt: boolean; //Halt flag + Op: 0 .. $f; //Opcode + X, Y: 0 .. 3; //Register arguments + Addr, IP, RP: word; //Address argument and instruction and return pointers + R: array [0 .. 3] of byte; //General-purpose registers + Mem: array [0 .. $ffef] of byte; //Memory + Prog: file of byte; //Program file + Ch: ansichar; //Character for input and output + +begin + + //Initialise the halt flag and the pointers + Halt := false; + IP := 0; + RP := $fff0; + + //Read a program file and check for errors + if ParamCount <> 1 then begin + writeln ('Usage: emulator program'); + exit; + end; + {$i-} + assign (Prog, ParamStr (1)); + reset (Prog); + {$i+} + if IOResult <> 0 then begin + writeln ('Usage: emulator program'); + exit; + end; + repeat + read (Prog, Mem [IP]); + IP := IP + 1; + until (eof (Prog)) or (IP = $fff0); + + //Reinitialise the instruction pointer + IP := 0; + + //Begin the main loop + while (Halt = false) do begin + + //Fetch the instruction and increment the instruction pointer + //Opcode + Op := Mem [IP] and $f0 shr 4; + //Register arguments + X := Mem [IP] and $c shr 2; + Y := Mem [IP] and 3; + IP := IP + 1; + if IP > $ffef then break; + //Address argument + if Op >= $a then begin + Addr := Mem [IP]; + Addr := Addr shl 8; + IP := IP + 1; + if IP > $ffef then break; + Addr := Addr + Mem [IP]; + IP := IP + 1; + if IP > $ffef then break; + end; + + //Decode and execute the instruction + //Halt + if Op = 0 then Halt := true + //Ret + else if Op = 1 then begin + IP := Mem [RP]; + RP := RP + 1; + if RP > $fff0 then break; + end + //Shl + else if Op = 2 then R [X] := R [X] shl 1 + //Shr + else if Op = 3 then R [X] := R [X] shr 1 + //Rol + else if Op = 4 then R [X] := RolByte (R [X]) + //Ror + else if Op = 5 then R [X] := RorByte (R [X]) + //Nand + else if Op = 6 then R [X] := not (R [X] and R [Y]) + //And + else if Op = 7 then R [X] := R [X] and R [Y] + //Or + else if Op = 8 then R [X] := R [X] or R [Y] + //Xor + else if Op = 9 then R [X] := R [X] xor R [Y] + //Load + else if Op = $a then begin + //Input + if Addr = IO then begin + Ch := ReadKey; + write (Ch); + R [X] := byte (Ch); + end + //Regular load + else R [X] := Mem [Addr]; + end + //Store + else if Op = $b then begin + //Output + if Addr = IO then begin + Ch := ansichar (R [X]); + write (Ch); + end + //Regular store + else Mem [Addr] := R [X]; + end + //Breq + else if Op = $c then begin + if R [X] = R [Y] then IP := Addr; + end + //Brneq + else if Op = $d then begin + if R [X] <> R [Y] then IP := Addr; + end + //Cleq + else if Op = $e then begin + if R [X] = R [Y] then begin + RP := RP - 1; + if RP > $fff0 then break; + Mem [RP] := IP; + IP := Addr; + if IP > $ffef then break; + end; + end + //Clneq + else if Op = $f then begin + if R [X] <> R [Y] then begin + RP := RP - 1; + if RP > $fff0 then break; + Mem [RP] := IP; + IP := Addr; + if IP > $ffef then break; + end; + end; + + //End the main loop + end + +end. diff --git a/license.md b/license.md new file mode 100644 index 0000000..fabda6a --- /dev/null +++ b/license.md @@ -0,0 +1,23 @@ +MIT License +=========== + +Copyright (c) 2022 + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..943b9b2 --- /dev/null +++ b/readme.md @@ -0,0 +1,58 @@ +Thingamajig +=========== + +Thingamajig is a RISC-y and MISC-y hobbyist instruction set +architecture. Its git repository can be found at +https://ahti.space/git/crazyettin/Thingamajig. + +Registers and Memory +-------------------- + + * 24-bit instruction register IR + * 16-bit instruction and return pointers IP and RP + * 8-bit general-purpose registers R0-R3 + * 8-bit memory addresses 0-FFFF + +Multi-byte values are big-endian. Memory addresses FFF0-FFFF are +reserved for memory mapped devices. The instruction and return pointers +should not have values higher than FFEF and FFF0 respectively to avoid +the reserved addresses. The instruction and return pointers are +initialised as 0 and FFF0 respectively; other registers and memory are +unitialised. + +Memory-Mapped Devices +--------------------- + +Input (when read from) and output (when written to) are mapped to +address FFFF. Arbitrary devices can be mapped to the other reserved +addresses. + +Instructions +------------ + +Instructions without an address argument are 8-bit and those with one +24-bit. The instruction pointer is incremented before being accessed or +modified. + +0 HALT +1 RET IP = *RP; RP += 1 + +2 SHL RX RX <<= 1 Logical shifts +3 SHR RX RX >>= 1 +4 ROL RX RX <<= 1 Rotating shifts +5 ROR RX RX >>= 1 + +6 NAND RX, RY RX = ~(RX & RY) +7 AND RX, RY RX &= RY +8 OR RX, RY RX |= RY +9 XOR RX, RY RX ^= RY + +A LOAD RX, ADDR RX = *ADDR +B STORE RX, ADDR *ADDR = RX Written as "STORE ADDR, RX" in + assembly for the sake of + consistency. + +C BREQ RX, RY, ADDR if (RX == RY) IP = ADDR +D BRNEQ RX, RY, ADDR if (RX != RY) IP = ADDR +E CLEQ RX, RY, ADDR if (RX == RY) {RP -= 1; *RP = IP; IP = ADDR} +F CLNEQ RX, RY, ADDR if (RX != RY) {RP -= 1; *RP = IP; IP = ADDR}