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:
Niles Rogoff 2017-07-02 14:16:30 -07:00
parent 0ee423d9d6
commit 00720d1602
No known key found for this signature in database
GPG Key ID: B78B908F23430F80
3 changed files with 47 additions and 22 deletions

View File

@ -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":

View File

@ -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

View File

@ -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