Allow inline comments, print more ram on debug, remove mul, muli, div and divi instructions and add ls, lsi, rs and rsi instructions
This commit is contained in:
parent
0ee423d9d6
commit
00720d1602
24
emulator.py
24
emulator.py
|
@ -1,13 +1,15 @@
|
|||
#!/usr/bin/env python3
|
||||
asm = open("instructions", "r").read().split("\n")
|
||||
asm = [[int(part, 16) if part[:2] == "0x" else part for part in instruction.split()] for instruction in asm if len(instruction) > 0 and instruction[0] != "#"]
|
||||
asm = [[int(part, 16) if part[:2] == "0x" else part for part in instruction.split("#")[0].strip().split()] for instruction in asm if len(instruction) > 0]
|
||||
rs = dict([[item, 0] for item in ["ax", "bx", "cx", "dx", "ip", "sp", "sb", "fl"]])
|
||||
ram = [0 for i in range(1024)]
|
||||
|
||||
def debug():
|
||||
# prints the registers and the first 10 values in ram
|
||||
print(rs, end=" ");
|
||||
print(str(ram[:10])[:-1] + ", ...]")
|
||||
print("Registers: " + str(rs));
|
||||
i = len(ram) - 1
|
||||
while ram[i] == 0: i -= 1
|
||||
print("Ram: " + str(ram[:i+1])[:-1] + ", ...]")
|
||||
def handle_three(i):
|
||||
instruction = i[0]
|
||||
first = i[1]
|
||||
|
@ -16,14 +18,10 @@ def handle_three(i):
|
|||
if instruction == "addi": rs[second] += first
|
||||
if instruction == "sub": rs[second] -= rs[first]
|
||||
if instruction == "subi": rs[second] -= first
|
||||
if instruction == "mul": rs[second] *= rs[first]
|
||||
if instruction == "muli": rs[second] *= first
|
||||
if instruction == "div":
|
||||
rs[second] /= rs[first]
|
||||
rs[second] = int(rs[second])
|
||||
if instruction == "divi":
|
||||
rs[second] /= first
|
||||
rs[second] = int(rs[second])
|
||||
if instruction == "ls": rs[second] <<= rs[first]
|
||||
if instruction == "lsi": rs[second] <<= first
|
||||
if instruction == "rs": rs[second] >>= rs[first]
|
||||
if instruction == "rsi": rs[second] >>= first
|
||||
if instruction == "xor": rs[second] ^= rs[first]
|
||||
if instruction == "xori": rs[second] ^= first
|
||||
if instruction == "and": rs[second] &= rs[first]
|
||||
|
@ -49,9 +47,9 @@ def handle_two(i):
|
|||
handle_two(["pushi", rs["ip"]])
|
||||
instruction = "jmp"
|
||||
if instruction == "je":
|
||||
if rs["fl"] & 1 > 0: instruction = "jmp"
|
||||
if rs["fl"] & 0b001 > 0: instruction = "jmp"
|
||||
if instruction == "jne":
|
||||
if rs["fl"] & 1 == 0: instruction = "jmp"
|
||||
if rs["fl"] & 0b001 == 0: instruction = "jmp"
|
||||
if instruction == "jl":
|
||||
if rs["fl"] & 0b100 > 0: instruction = "jmp"
|
||||
if instruction == "jle":
|
||||
|
|
29
instructions
29
instructions
|
@ -75,12 +75,24 @@ andi 0x1 cx
|
|||
testi 0x0 cx
|
||||
je hailstone_even
|
||||
# was odd
|
||||
muli 0x3 bx
|
||||
# preserve ax, cx
|
||||
push ax
|
||||
push cx
|
||||
# put result pointer in ax
|
||||
mov sp ax
|
||||
pushi 0x0
|
||||
movi 0x3 cx
|
||||
call multiply
|
||||
# pop result
|
||||
pop bx
|
||||
# finish preserve ax, cx
|
||||
pop cx
|
||||
pop ax
|
||||
addi 0x1 bx
|
||||
jmp hailstone_handled
|
||||
hailstone_even:
|
||||
# was even
|
||||
divi 0x2 bx
|
||||
rsi 0x1 bx # right shift by one bit, really divide by two
|
||||
hailstone_handled:
|
||||
# add 1 to the step counter
|
||||
lea ax cx
|
||||
|
@ -100,7 +112,18 @@ jne factorial_tcr_cont
|
|||
sea ax cx
|
||||
ret
|
||||
factorial_tcr_cont:
|
||||
mul bx cx
|
||||
# multiply bx and cx, store result in cx
|
||||
# first preserve ax, bx
|
||||
push ax
|
||||
push bx
|
||||
mov sp ax
|
||||
pushi 0x0
|
||||
call multiply
|
||||
# pop result
|
||||
pop cx
|
||||
# finish preserve
|
||||
pop bx
|
||||
pop ax
|
||||
subi 0x1 bx
|
||||
# tail call recursion
|
||||
jmp factorial_tcr
|
||||
|
|
16
readme.md
16
readme.md
|
@ -4,20 +4,24 @@ Made for fun, put assembly code in the file called "instructions" and it will ru
|
|||
|
||||
The example "instructions" file I wrote contains three different ways of calculating the factorial of a number (iteratively, recursively and tail call recursively) and a tail call recursive hailstone implementation. It calculates the number of steps to hailstone 73 (0x49), 6!, 5! and 4! and puts them on ax, bx, cx and dx, then exits. The correct answers should be 115, 720, 120 and 24
|
||||
|
||||
The registers are ax, bx, cx and dx. The stack pointers are sp and sb (sb is unused), instruction pointer is ip and flags register is fl
|
||||
The registers are `ax`, `bx`, `cx` and `dx`. The stack pointers are `sp` and `sb` (`sb` is unused), instruction pointer is `ip` and flags register is `fl`
|
||||
|
||||
The stack starts at zero and grows up, the maximum address in ram is 1023. sp points to the item after the top item on the stack, but if you just use push and pop you won't have to worry about it.
|
||||
The stack starts at zero and grows up, the maximum address in ram is 1023. `sp` points to the item after the top item on the stack, but if you just use `push` and `pop` you won't have to worry about it.
|
||||
|
||||
The lea instruction takes a register that holds an address and a register to put the contents of that address into, leai takes a literal address and loads it into the register specified by the second argument. sea and seai both take their arguments in that order as well
|
||||
The `lea` instruction takes a register that holds an address and a register to put the contents of that address into, `leai` takes a literal address and loads it into the register specified by the second argument. `sea` and `seai` both take their arguments in that order as well
|
||||
|
||||
The call function is just the jmp instruction but first it pushes the instruction pointer to the stack. The ret instruction just pops an instruction pointer and jumps to it.
|
||||
The `call` function is just the `jmp` instruction but first it pushes the instruction pointer to the stack. The `ret` instruction just pops an instruction pointer and jumps to it.
|
||||
|
||||
test and testi set three bits on the flags register, 001 if they are equal, 010 for greater than and 100 for less than. The conditional jumps include je, jne, jl, jg, jle and jge
|
||||
`test` and `testi` set three bits on the flags register, 001 if they are equal, 010 for greater than and 100 for less than. The conditional jumps include `je`, `jne`, `jl`, `jg`, `jle` and `jge`
|
||||
|
||||
At any point you can use the debug instruction to print out the contents of the registers and the first ten values in ram, or the abort instruction to debug then exit.
|
||||
At any point you can use the `debug` instruction to print out the contents of the registers and ram, or the `abort` instruction to `debug` then exit.
|
||||
|
||||
Registers are callee-preserved.
|
||||
|
||||
All integer literals are hexadecimal and start with 0x
|
||||
|
||||
Comments start with `#` but any unrecognized instruction is treated as a comment
|
||||
|
||||
The math operations include `add`, `sub` (subtract), `ls` (left shift), `rs` (right shift), bitwise `xor` and `and` and their immediate counterparts (suffixed with `i`)
|
||||
|
||||
There is no multiply or divide instruction, but they are both easily implemented, I wrote a multiply subroutine used in all three of the factorial implementations and the hailstone subroutine in the `instructions` file
|
||||
|
|
Loading…
Reference in New Issue