aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--atk16_emu/emu.py176
-rw-r--r--atk16_emu/opcodes.py53
-rw-r--r--atk16_emu/test/test_opcode_alr.py86
-rw-r--r--atk16_utils/pad_bin.py3
-rw-r--r--resources/asm/sum.atk167
5 files changed, 284 insertions, 41 deletions
diff --git a/atk16_emu/emu.py b/atk16_emu/emu.py
index 680cb71..2f5b79a 100644
--- a/atk16_emu/emu.py
+++ b/atk16_emu/emu.py
@@ -1,6 +1,7 @@
import random
from typing import Literal
from dataclasses import dataclass
+import sys
from .opcodes import *
class Register:
@@ -75,9 +76,6 @@ class ALU:
pass
def process(self, S: int, L: int, R: int):
- if S < 0 or S >= 8:
- raise ValueError(f"Invalid ALU S: {S}")
-
if L < 0 or L >= 2 ** 16:
raise ValueError(f"Invalid ALU L: {L}")
@@ -94,14 +92,34 @@ class ALU:
zero = result == 0,
sign = (result & 0x8000) != 0
))
+ case 1: # L - R
+ py_sum = L - R
+ result = py_sum & 0xFFFF
+ return ALUResult(result, ALUFlags(
+ carry = py_sum < 0,
+ overflow = (L & 0x8000) != (R & 0x8000) and (L & 0x8000) != (result & 0x8000),
+ zero = result == 0,
+ sign = (result & 0x8000) != 0
+ ))
+ case 2: # L and R
+ raise NotImplementedError()
+ case 3: # L or R
+ raise NotImplementedError()
+ case 4: # L xor R
+ raise NotImplementedError()
+ case 5: # L >> R logical
+ raise NotImplementedError()
+ case 6: # L >>> R arithmetic
+ raise NotImplementedError()
+ case 7: # L << R
+ raise NotImplementedError()
- raise NotImplementedError(f"{S}")
-
+ raise ValueError(f"Invalid ALU S: {S}")
class Machine:
def __init__(self):
- self.rom = ROM(16, 16)
- self.ram = RAM(16, 16)
+ self.rom = ROM(15, 16)
+ self.ram = RAM(15, 16)
self.alu = ALU()
self.ra = Register(16)
@@ -125,10 +143,17 @@ class Machine:
def mem_read(self, addr: int):
if addr < 2 ** 15:
- return self.rom.read(addr)
+ return self.rom.read(addr & 0x7FFF)
else:
# TODO: Implement memory-mapped I/O
- return self.ram.read(addr)
+ return self.ram.read(addr & 0x7FFF)
+
+ def mem_write(self, addr: int, value: int):
+ if addr < 2 ** 15:
+ raise ValueError(f"Cannot write to ROM, addr: {addr:>04x}")
+ else:
+ # TODO: Implement memory-mapped I/O
+ self.ram.write(addr & 0x7FFF, value)
def get_nth_register(self, n: int) -> Register:
if n < 0 or n >= 8:
@@ -164,6 +189,15 @@ class Machine:
while self.running:
self.step()
+ def check_nth_flag(self, n: int) -> bool:
+ match n:
+ case 0: return self.fr.carry
+ case 1: return self.fr.overflow
+ case 2: return self.fr.zero
+ case 3: return self.fr.sign
+
+ raise ValueError(f"Invalid flag number: {n}")
+
def step(self):
if not self.running:
raise RuntimeError("Machine is not running")
@@ -174,31 +208,88 @@ class Machine:
instr = self.mem_read(pc_addr)
instruction = self.decode(instr)
- match instruction:
- case ALR(target, left, right, alu_code):
- alu_result = self.alu.process(
- S = alu_code,
- L = self.get_nth_register(left).value,
- R = self.get_nth_register(right).value,
- )
- self.fr = alu_result.flags
+ try:
+ match instruction:
+ case ALR(target, left, right, alu_code):
+ alu_result = self.alu.process(
+ S = alu_code,
+ L = self.get_nth_register(left).value,
+ R = self.get_nth_register(right).value,
+ )
+ self.fr = alu_result.flags
+
+ target_reg = self.get_nth_register(target)
+ target_reg.value = alu_result.value
+
+ case ALI(target, left, imm, alu_code):
+ alu_result = self.alu.process(
+ S = alu_code,
+ L = self.get_nth_register(left).value,
+ R = imm,
+ )
+ self.fr = alu_result.flags
- target_reg = self.get_nth_register(target)
- target_reg.value = alu_result.value
+ target_reg = self.get_nth_register(target)
+ target_reg.value = alu_result.value
- case ALI(target, left, imm, alu_code):
- alu_result = self.alu.process(
- S = alu_code,
- L = self.get_nth_register(left).value,
- R = imm,
- )
- self.fr = alu_result.flags
+ case LDR(to_reg, addr_reg):
+ addr = self.get_nth_register(addr_reg).value
+ value = self.mem_read(addr)
- target_reg = self.get_nth_register(target)
- target_reg.value = alu_result.value
+ target_reg = self.get_nth_register(to_reg)
+ target_reg.value = value
- case HLT():
- self.running = False
+ case STR(addr_reg, from_reg):
+ addr = self.get_nth_register(addr_reg).value
+ value = self.get_nth_register(from_reg).value
+
+ self.mem_write(addr, value)
+
+ case LDI(to_reg, imm):
+ target_reg = self.get_nth_register(to_reg)
+ target_reg.value = imm
+
+ case JPR(addr_reg):
+ addr = self.get_nth_register(addr_reg).value
+ self.pc.value = addr
+
+ case JPI(imm):
+ self.pc.value = (self.pc.value + imm) & 0xFFFF
+
+ case BRR(flag, addr_reg):
+ if self.check_nth_flag(flag):
+ addr = self.get_nth_register(addr_reg).value
+ self.pc.value = addr
+
+ case BRI(flag, addr_imm):
+ if self.check_nth_flag(flag):
+ self.pc.value = (self.pc.value + addr_imm) & 0xFFFF
+
+ case LPC(target_reg):
+ target_reg = self.get_nth_register(target_reg)
+ target_reg.value = self.pc.value
+
+ case NOP():
+ pass
+
+ case ISRP0():
+ # interrupt service routine, read store PC in IPC register, set PC to ISRA value
+ raise NotImplementedError()
+
+ case ISRP1():
+ # interrupt service routine, read store PC in IPC register, set PC to ISRA value
+ raise NotImplementedError()
+
+ case RTI():
+ # return from interrupt routine, read PC from IPC register
+ raise NotImplementedError()
+
+ case HLT():
+ self.running = False
+
+ except:
+ print(f"Error while executing instruction {instr:>016b} at address {pc_addr:>04x}", file=sys.stderr)
+ raise
def decode(self, instr: int):
opcode = (instr & 0xF000) >> 12
@@ -208,7 +299,28 @@ class Machine:
left=(opdata & 0b000111000000) >> 6,
right=(opdata & 0b000000111000) >> 3,
alu_code=opdata & 0b000000000111)
+ case 0b0001: return ALI(target=(opdata & 0b111000000000) >> 9,
+ left=(opdata & 0b000111000000) >> 6,
+ imm=(opdata & 0b000000111000) >> 3,
+ alu_code=opdata & 0b000000000111)
+ case 0b0010: return LDR(to_reg=(opdata & 0b111000000000) >> 9,
+ addr_reg=(opdata & 0b000111000000) >> 6)
+ case 0b0011: return STR(addr_reg=(opdata & 0b000111000000) >> 6,
+ from_reg=(opdata & 0b000000111000) >> 3)
+ case 0b0100: return LDI(to_reg=(opdata & 0b111000000000) >> 9,
+ imm=opdata & 0b000111111111)
+ case 0b0101: return JPR(addr_reg=(opdata & 0b000111000000) >> 6)
+ case 0b0110: return JPI(imm=opdata & 0b000111111111)
+ case 0b0111: return BRR(flag=(opdata & 0b011000000000) >> 9,
+ addr_reg=(opdata & 0b000111000000) >> 6)
+ case 0b1000: return BRI(flag=(opdata & 0b011000000000) >> 9,
+ addr_imm=opdata & 0b000111111111)
+ case 0b1001: return LPC(target_reg=(opdata & 0b111000000000) >> 9)
+ case 0b1010: return NOP()
+ case 0b1011: return NOP()
+ case 0b1100: return ISRP0()
+ case 0b1101: return ISRP1()
+ case 0b1110: return RTI()
case 0b1111: return HLT()
- # TODO: Implement other opcodes
- raise ValueError(f"Invalid instruction: {instr:>16b}")
+ raise ValueError(f"Invalid instruction: {instr:>016b}")
diff --git a/atk16_emu/opcodes.py b/atk16_emu/opcodes.py
index 268c873..6353ed5 100644
--- a/atk16_emu/opcodes.py
+++ b/atk16_emu/opcodes.py
@@ -34,5 +34,58 @@ class ALI():
alu_code: int
@dataclass
+class LDR():
+ to_reg: int
+ addr_reg: int
+
+@dataclass
+class STR():
+ from_reg: int
+ addr_reg: int
+
+@dataclass
+class LDI():
+ to_reg: int
+ imm: int
+
+@dataclass
+class JPR():
+ addr_reg: int
+
+@dataclass
+class JPI():
+ imm: int
+
+@dataclass
+class BRR():
+ flag: int
+ addr_reg: int
+
+@dataclass
+class BRI():
+ flag: int
+ addr_imm: int
+
+@dataclass
+class LPC():
+ target_reg: int
+
+@dataclass
+class NOP():
+ pass
+
+@dataclass
+class ISRP0():
+ pass
+
+@dataclass
+class ISRP1():
+ pass
+
+@dataclass
+class RTI():
+ pass
+
+@dataclass
class HLT():
pass
diff --git a/atk16_emu/test/test_opcode_alr.py b/atk16_emu/test/test_opcode_alr.py
index 8561ff6..e7fe1a7 100644
--- a/atk16_emu/test/test_opcode_alr.py
+++ b/atk16_emu/test/test_opcode_alr.py
@@ -1,7 +1,7 @@
from ..emu import Machine
from .utils import make_rom
-def test_alr_s0_small_unsigned():
+def test_alr_add_small_unsigned():
machine = Machine()
rom_image = make_rom([0b0000_010_000_001_000]) # ALR target=RC left=RA right=RB alu_code=0
@@ -20,7 +20,7 @@ def test_alr_s0_small_unsigned():
assert machine.fr.zero == 0
assert machine.fr.sign == 0
-def test_alr_s0_large_unsigned():
+def test_alr_add_large_unsigned():
machine = Machine()
rom_image = make_rom([0b0000_010_000_001_000]) # ALR target=RC left=RA right=RB alu_code=0
@@ -39,7 +39,7 @@ def test_alr_s0_large_unsigned():
assert machine.fr.zero == 0
assert machine.fr.sign == 0
-def test_alr_s0_small_signed():
+def test_alr_add_small_signed():
machine = Machine()
rom_image = make_rom([0b0000_010_000_001_000]) # ALR target=RC left=RA right=RB alu_code=0
@@ -58,7 +58,7 @@ def test_alr_s0_small_signed():
assert machine.fr.zero == 1
assert machine.fr.sign == 0
-def test_alr_s0_large_signed():
+def test_alr_add_large_signed():
machine = Machine()
rom_image = make_rom([0b0000_010_000_001_000]) # ALR target=RC left=RA right=RB alu_code=0
@@ -75,4 +75,80 @@ def test_alr_s0_large_signed():
assert machine.fr.carry == 0
assert machine.fr.overflow == 1
assert machine.fr.zero == 0
- assert machine.fr.sign == 1 \ No newline at end of file
+ assert machine.fr.sign == 1
+
+def test_alr_sub_small_unsigned():
+ machine = Machine()
+
+ rom_image = make_rom([0b0000_010_000_001_001]) # ALR target=RC left=RA right=RB alu_code=1
+ machine.load_rom_image(rom_image)
+ machine.reset()
+
+ machine.ra.value = 10
+ machine.rb.value = 20
+
+ machine.run()
+ machine.step()
+
+ assert machine.rc.value == 0xFFF6 # -10
+ assert machine.fr.carry == 1
+ assert machine.fr.overflow == 0
+ assert machine.fr.zero == 0
+ assert machine.fr.sign == 1
+
+def test_alr_sub_large_unsigned():
+ machine = Machine()
+
+ rom_image = make_rom([0b0000_010_000_001_001]) # ALR target=RC left=RA right=RB alu_code=1
+ machine.load_rom_image(rom_image)
+ machine.reset()
+
+ machine.ra.value = 0xFFFF
+ machine.rb.value = 0xFFFF
+
+ machine.run()
+ machine.step()
+
+ assert machine.rc.value == 0x0000
+ assert machine.fr.carry == 0
+ assert machine.fr.overflow == 0
+ assert machine.fr.zero == 1
+ assert machine.fr.sign == 0
+
+def test_alr_sub_small_signed():
+ machine = Machine()
+
+ rom_image = make_rom([0b0000_010_000_001_001]) # ALR target=RC left=RA right=RB alu_code=1
+ machine.load_rom_image(rom_image)
+ machine.reset()
+
+ machine.ra.value = 0x0001 # 1
+ machine.rb.value = 0xFFFF # -1
+
+ machine.run()
+ machine.step()
+
+ assert machine.rc.value == 0x0002
+ assert machine.fr.carry == 1
+ assert machine.fr.overflow == 0
+ assert machine.fr.zero == 0
+ assert machine.fr.sign == 0
+
+def test_alr_sub_large_signed():
+ machine = Machine()
+
+ rom_image = make_rom([0b0000_010_000_001_001]) # ALR target=RC left=RA right=RB alu_code=1
+ machine.load_rom_image(rom_image)
+ machine.reset()
+
+ machine.ra.value = 0x7FFF # 32767
+ machine.rb.value = 0xFFFF # -1
+
+ machine.run()
+ machine.step()
+
+ assert machine.rc.value == 0x8000 # -32768
+ assert machine.fr.carry == 1
+ assert machine.fr.overflow == 1
+ assert machine.fr.zero == 0
+ assert machine.fr.sign == 1
diff --git a/atk16_utils/pad_bin.py b/atk16_utils/pad_bin.py
index 9f014d1..1c3c24d 100644
--- a/atk16_utils/pad_bin.py
+++ b/atk16_utils/pad_bin.py
@@ -14,6 +14,9 @@ def pad_binary(file_path, target_size) -> int:
# Calculate needed padding
padding_size = target_size - current_size
+ if padding_size < 0:
+ raise ValueError(f"File is larger than the target size ({current_size} > {target_size}).")
+
# Append zeros if needed
if padding_size > 0:
with open(file_path, 'ab') as file:
diff --git a/resources/asm/sum.atk16 b/resources/asm/sum.atk16
index 1bc3277..a724568 100644
--- a/resources/asm/sum.atk16
+++ b/resources/asm/sum.atk16
@@ -1,4 +1,3 @@
-@use ext_std:*
; Program: sum two values and store the result in RAM
; ROM (and program execution) starts at offset 0x0
@@ -11,16 +10,16 @@
@label program
ldi 10 RA ; RA := 10
ldi 20 RB ; RB := 20
- add RA RB RC ; RC := RA + RB
+ alr al_plus RA RB RC ; RC := RA + RB
ldi ram_offset RD ; store address of ram_offset in RD
ldr RD RD ; dereference ram_offset address
@label debug
str RC RD ; store RC in RAM
; Check that 10 + 20 = 30
- mov RC RA ; RA := result of sum
+ ali al_plus RC 0 RA ; RA := result of sum
ldi 30 RB ; RB := 30
- sub RA RB RC ; RC := RA - RB
+ alr al_minus RA RB RC ; RC := RA - RB
bri zero success ; if result is zero, jump to success
ldi 2 RA ; RA := 2 to signal failure