Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ Below is a concise, categorized summary of the Tiny8 instruction set (mnemonics
- JMP label / RJMP offset — unconditional jump
- CALL label / RCALL offset — call subroutine (push return address)
- RET / RETI — return from subroutine / return from interrupt (sets I)
- BRNE / BREQ / BRCS / BRCC — conditional branches based on flags
- BRNE / BREQ / BRCS / BRCC / BRGE / BRLT — conditional branches based on flags
- CP Rd, Rr / CPI Rd, K — compare (sets flags)
- CPSE Rd, Rr — compare and skip if equal

Expand Down
36 changes: 35 additions & 1 deletion src/tiny8/cpu.py
Original file line number Diff line number Diff line change
Expand Up @@ -549,7 +549,19 @@ def op_out(self, port: int, rr: int):
val = self.read_reg(rr)
self.write_ram(port, val)

def op_jmp(self, label: str):
def op_jmp(self, label: str | int):
"""Jump to a given label or numeric address by updating the program counter.

This operation sets the CPU's program counter (self.pc) to the target address minus one.
The subtraction of one accounts for the fact that the instruction dispatcher will typically
increment the program counter after the current instruction completes.

Args:
label (str | int): The jump target. If a string, it is treated as a symbolic label
and looked up in self.labels to obtain its numeric address. If an int (or any
value convertible to int), it is used directly as the numeric address.
"""

if isinstance(label, str):
if label not in self.labels:
raise KeyError(f"Label {label} not found")
Expand Down Expand Up @@ -893,6 +905,28 @@ def op_brcc(self, label: str):
if not c:
self.op_jmp(label)

def op_brge(self, label: str | int):
"""BRGE - Branch if Greater or Equal (Signed)

Args:
label: Destination label or address to jump to if the condition is met.
"""

s = self.get_flag(SREG_S)
if not s:
self.op_jmp(label)

def op_brlt(self, label: str | int):
"""BRLT - Branch if Less Than (Signed).

Args:
label: Destination label or address to jump to if the condition is met.
"""

s = self.get_flag(SREG_S)
if s:
self.op_jmp(label)

def op_push(self, rr: int):
"""Push a register value onto the stack.

Expand Down
36 changes: 36 additions & 0 deletions tests/test_control_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,39 @@ def test_cpse_skips_next(self):
# If cpse skipped the next instruction, r6 should remain zero and r7 should be loaded
self.assertEqual(cpu.read_reg(6), 0x00)
self.assertEqual(cpu.read_reg(7), 0xBB)

def test_brlt(self):
# BRLT: branch when signed (Rd < Rr). Use -1 (0xFF) < 1 (0x01)
src = """
ldi r0, $FF ; -1
ldi r1, $01 ; 1
cp r0, r1
brlt less
ldi r3, $01
jmp done
less:
ldi r3, $02
done:
nop
"""
cpu = self.run_asm(src)
# -1 < 1 so branch taken -> r3 == 2
self.assertEqual(cpu.read_reg(3), 0x02)

def test_brge(self):
# BRGE: branch when signed (Rd >= Rr). Use 1 >= -1
src2 = """
ldi r0, $01 ; 1
ldi r1, $FF ; -1
cp r0, r1
brge ge
ldi r3, $01
jmp done
ge:
ldi r3, $02
done:
nop
"""
cpu2 = self.run_asm(src2)
# 1 >= -1 so branch taken -> r3 == 2
self.assertEqual(cpu2.read_reg(3), 0x02)