Dodal generator za obj datoteke, in teste, popravil parserctx da pravilno obravnava RSUB
This commit is contained in:
parent
8ea00ddb32
commit
b6e5f3d593
30 changed files with 177 additions and 57 deletions
|
|
@ -1,9 +1,8 @@
|
||||||
from zbirnik.opcodes import OPCODES
|
from zbirnik.opcodes import OPCODES
|
||||||
|
|
||||||
class EmitContext:
|
class EmitContext:
|
||||||
REGISTERS = {
|
|
||||||
'A': 0, 'X': 1, 'L': 2,
|
REGISTERS = {'A': 0, 'X': 1, 'L': 2,'B': 3, 'S': 4, 'T': 5, 'F': 6}
|
||||||
'B': 3, 'S': 4, 'T': 5, 'F': 6
|
|
||||||
}
|
|
||||||
|
|
||||||
def __init__(self, symtab):
|
def __init__(self, symtab):
|
||||||
self.opcodes = OPCODES
|
self.opcodes = OPCODES
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
ass3/zbirnik/src/zbirnik/__pycache__/gen_obj.cpython-310.pyc
Normal file
BIN
ass3/zbirnik/src/zbirnik/__pycache__/gen_obj.cpython-310.pyc
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -5,3 +5,4 @@ class AddrMode(Enum):
|
||||||
SIMPLE = auto()
|
SIMPLE = auto()
|
||||||
IMMEDIATE = auto()
|
IMMEDIATE = auto()
|
||||||
INDIRECT = auto()
|
INDIRECT = auto()
|
||||||
|
EXTENDED = auto()
|
||||||
|
|
|
||||||
89
ass3/zbirnik/src/zbirnik/gen_obj.py
Normal file
89
ass3/zbirnik/src/zbirnik/gen_obj.py
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
from zbirnik.code import Code
|
||||||
|
from zbirnik.EmitCtx import EmitContext
|
||||||
|
|
||||||
|
class Generate_obj:
|
||||||
|
|
||||||
|
def __init__(self, code: Code, emit_ctx: EmitContext):
|
||||||
|
self.code = code
|
||||||
|
self.records = []
|
||||||
|
self.emit_ctx = emit_ctx
|
||||||
|
|
||||||
|
def generate(self) -> str:
|
||||||
|
h_record = self.generate_h_record()
|
||||||
|
t_records = self.generate_t_records()
|
||||||
|
#m_records = self.generate_m_records() nimam podpore za EXTREF tako da ne bo M recordou
|
||||||
|
e_record = self.generate_e_record()
|
||||||
|
|
||||||
|
return '\n'.join([h_record] + t_records + [e_record])
|
||||||
|
|
||||||
|
def generate_h_record(self) -> str:
|
||||||
|
ime = (self.code.name).ljust(6)
|
||||||
|
start = f"{self.code.start_address:06X}"
|
||||||
|
dolzina= f"{self.code.program_length:06X}"
|
||||||
|
return f"H^{ime}^{start}^{dolzina}"
|
||||||
|
|
||||||
|
|
||||||
|
def generate_t_records(self) -> list[str]:
|
||||||
|
t_records = []
|
||||||
|
current_start_addr = None
|
||||||
|
current_bytes = bytearray()
|
||||||
|
|
||||||
|
for node in self.code.nodes:
|
||||||
|
chunk = node.emit(self.emit_ctx)
|
||||||
|
# If node generates no code (like RESB, RESW, START, END)
|
||||||
|
if not chunk:
|
||||||
|
# Flush current T record if we have accumulated bytes
|
||||||
|
if current_bytes:
|
||||||
|
t_record = self._make_t_record(current_start_addr, current_bytes)
|
||||||
|
t_records.append(t_record)
|
||||||
|
current_bytes = bytearray()
|
||||||
|
current_start_addr = None
|
||||||
|
continue
|
||||||
|
|
||||||
|
# If this is the first byte in a new T record, set starting address
|
||||||
|
if current_start_addr is None:
|
||||||
|
current_start_addr = node.address
|
||||||
|
|
||||||
|
# Add this node's bytes to current accumulation
|
||||||
|
current_bytes.extend(chunk)
|
||||||
|
|
||||||
|
# If we've accumulated 30 or more bytes, flush a T record
|
||||||
|
while len(current_bytes) >= 30:
|
||||||
|
# Take first 30 bytes
|
||||||
|
record_bytes = current_bytes[:30]
|
||||||
|
t_record = self._make_t_record(current_start_addr, record_bytes)
|
||||||
|
t_records.append(t_record)
|
||||||
|
|
||||||
|
# Remove those bytes and update start address
|
||||||
|
current_bytes = current_bytes[30:]
|
||||||
|
current_start_addr += 30
|
||||||
|
|
||||||
|
# Flush any remaining bytes
|
||||||
|
if current_bytes:
|
||||||
|
t_record = self._make_t_record(current_start_addr, current_bytes)
|
||||||
|
t_records.append(t_record)
|
||||||
|
|
||||||
|
return t_records
|
||||||
|
|
||||||
|
def _make_t_record(self, start_address: int, bytes_data: bytearray) -> str:
|
||||||
|
start = f"{start_address:06X}"
|
||||||
|
length = f"{len(bytes_data):02X}"
|
||||||
|
hex_code = bytes_data.hex().upper()
|
||||||
|
|
||||||
|
return f"T^{start}^{length}^{hex_code}"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def generate_m_records(self) -> list[str]:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def generate_e_record(self) -> str:
|
||||||
|
entry = self.code.entry_point
|
||||||
|
|
||||||
|
if isinstance(entry, str):
|
||||||
|
entry = self.code.symtab.get(entry, self.code.start_address)
|
||||||
|
|
||||||
|
if entry is None:
|
||||||
|
entry = self.code.start_address
|
||||||
|
|
||||||
|
return f"E^{entry:06X}"
|
||||||
|
|
@ -3,17 +3,18 @@ from zbirnik.code import Code
|
||||||
from zbirnik.EmitCtx import EmitContext
|
from zbirnik.EmitCtx import EmitContext
|
||||||
from zbirnik.mnemoniki.mnemoniki_tabela import MNEMONICS
|
from zbirnik.mnemoniki.mnemoniki_tabela import MNEMONICS
|
||||||
from zbirnik.parserctx import ParserContext
|
from zbirnik.parserctx import ParserContext
|
||||||
|
from zbirnik.gen_obj import Generate_obj
|
||||||
import os
|
import os
|
||||||
|
|
||||||
ime = input("Vnesite ime programa (.asm): ")
|
ime = input("Vnesite ime programa (.asm): ")
|
||||||
ime = os.path.join(os.path.dirname(__file__), ime)
|
ime1 = os.path.join(os.path.dirname(__file__), ime)
|
||||||
|
|
||||||
if not ime.endswith(".asm"):
|
if not ime1.endswith(".asm"):
|
||||||
raise ValueError("Ime programa ni v pravi obliki, mora biti: ime.asm")
|
raise ValueError("Ime programa ni v pravi obliki, mora biti: ime.asm")
|
||||||
|
|
||||||
code = Code()
|
code = Code()
|
||||||
|
|
||||||
with open(ime) as f:
|
with open(ime1) as f:
|
||||||
for line in f:
|
for line in f:
|
||||||
print("LINE:", line.rstrip())
|
print("LINE:", line.rstrip())
|
||||||
|
|
||||||
|
|
@ -35,8 +36,19 @@ with open(ime) as f:
|
||||||
|
|
||||||
|
|
||||||
code.pass1()
|
code.pass1()
|
||||||
|
|
||||||
ctx_emit = EmitContext(symtab=code.symtab)
|
ctx_emit = EmitContext(symtab=code.symtab)
|
||||||
|
|
||||||
binary = code.pass2(ctx_emit)
|
binary = code.pass2(ctx_emit)
|
||||||
print(binary.hex())
|
|
||||||
|
print("Rezultat pass2: " + binary.hex())
|
||||||
|
print("-"*40)
|
||||||
|
obj_generator = Generate_obj(code=code, emit_ctx=ctx_emit)
|
||||||
|
|
||||||
|
obj_data = obj_generator.generate()
|
||||||
|
|
||||||
|
izhod = "output/"+ ime.replace(".asm", ".obj")
|
||||||
|
izhod = os.path.join(os.path.dirname(__file__), izhod)
|
||||||
|
|
||||||
|
with open(izhod, "w") as obj:
|
||||||
|
obj.write(obj_data)
|
||||||
|
|
||||||
|
print(obj_data)
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -63,6 +63,7 @@ MNEMONICS = {
|
||||||
'TD': MnemonicF3m('TD', opcode=0xE0),
|
'TD': MnemonicF3m('TD', opcode=0xE0),
|
||||||
'RD': MnemonicF3m('RD', opcode=0xD8),
|
'RD': MnemonicF3m('RD', opcode=0xD8),
|
||||||
'WD': MnemonicF3m('WD', opcode=0xDC),
|
'WD': MnemonicF3m('WD', opcode=0xDC),
|
||||||
|
'TIX': MnemonicF3m('TIX', opcode=0x2C),
|
||||||
# Format 4 (razširjeni)
|
# Format 4 (razširjeni)
|
||||||
'+LDA': MnemonicF4m('+LDA', opcode=0x00),
|
'+LDA': MnemonicF4m('+LDA', opcode=0x00),
|
||||||
'+JSUB': MnemonicF4m('+JSUB', opcode=0x48),
|
'+JSUB': MnemonicF4m('+JSUB', opcode=0x48),
|
||||||
|
|
|
||||||
3
ass3/zbirnik/src/zbirnik/output/test1.obj
Normal file
3
ass3/zbirnik/src/zbirnik/output/test1.obj
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
H^TEST ^000000^00000C
|
||||||
|
T^000000^09^0320030F2003000005
|
||||||
|
E^000000
|
||||||
6
ass3/zbirnik/src/zbirnik/output/test2.obj
Normal file
6
ass3/zbirnik/src/zbirnik/output/test2.obj
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
H^COPY ^0003E8^00005A
|
||||||
|
T^0003E8^1E^0320271B20270F202707202703A02A192C070FA0242F201E3B2FF1022039
|
||||||
|
T^000406^12^292C5B3320033F2FE54F000000000A000014
|
||||||
|
T^00041B^06^000000000005
|
||||||
|
T^00043F^03^000418
|
||||||
|
E^0003E8
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import ply.lex
|
import ply.lex
|
||||||
import ply.yacc
|
import ply.yacc
|
||||||
#import sys
|
|
||||||
|
|
||||||
global at_line_start, seen_mnemonic_or_directive
|
global at_line_start, seen_mnemonic_or_directive
|
||||||
# Lexer
|
# Lexer
|
||||||
|
|
@ -179,13 +178,3 @@ def p_error(p):
|
||||||
print('Syntax error at EOF')
|
print('Syntax error at EOF')
|
||||||
|
|
||||||
parser = ply.yacc.yacc()
|
parser = ply.yacc.yacc()
|
||||||
|
|
||||||
|
|
||||||
#if __name__ == '__main__':
|
|
||||||
# for line in sys.stdin:
|
|
||||||
# line = line.rstrip('\n\r')
|
|
||||||
# if line and not line.lstrip().startswith('.'):
|
|
||||||
# at_line_start = True
|
|
||||||
# seen_mnemonic_or_directive = False
|
|
||||||
# result = parser.parse(line, lexer=lexer)
|
|
||||||
# print(result)
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
class ParserContext:
|
class ParserContext:
|
||||||
def __init__(self, parsed):
|
def __init__(self, parsed):
|
||||||
# Handle COMMENT special case (if you still need it)
|
#Comment
|
||||||
if isinstance(parsed, tuple) and parsed[0] == 'COMMENT':
|
if isinstance(parsed, tuple) and parsed[0] == 'COMMENT':
|
||||||
self.label = None
|
self.label = None
|
||||||
self.mnemonic = 'COMMENT'
|
self.mnemonic = 'COMMENT'
|
||||||
|
|
@ -8,13 +8,13 @@ class ParserContext:
|
||||||
self.comment = parsed[1]
|
self.comment = parsed[1]
|
||||||
return
|
return
|
||||||
|
|
||||||
# New parser format: (label, mnemonic, *operands)
|
#parsed=(label, mnemonic, *operands)
|
||||||
if not isinstance(parsed, tuple) or len(parsed) < 2:
|
if not isinstance(parsed, tuple) or len(parsed) < 2:
|
||||||
raise ValueError(f"Invalid parsed format: {parsed}")
|
raise ValueError(f"Invalid parsed format: {parsed}")
|
||||||
|
|
||||||
self.label = parsed[0] # Can be None
|
self.label = parsed[0]
|
||||||
self.mnemonic = parsed[1]
|
self.mnemonic = parsed[1]
|
||||||
# All remaining elements are operands
|
#Ostali so operandi
|
||||||
self.operands = list(parsed[2:]) if len(parsed) > 2 else []
|
self.operands = list(parsed[2:]) if len(parsed) > 2 else []
|
||||||
self.comment = None
|
self.comment = None
|
||||||
|
|
||||||
|
|
@ -29,12 +29,10 @@ class ParserContext:
|
||||||
def read_reg(self) -> str:
|
def read_reg(self) -> str:
|
||||||
"""Read a register operand (e.g., 'A', 'X', 'L')"""
|
"""Read a register operand (e.g., 'A', 'X', 'L')"""
|
||||||
op = self.next_op()
|
op = self.next_op()
|
||||||
# If it's a plain string, return it
|
|
||||||
if isinstance(op, str):
|
if isinstance(op, str):
|
||||||
return op
|
return op
|
||||||
# If it's a tuple (shouldn't happen for registers), take first element
|
|
||||||
if isinstance(op, tuple):
|
|
||||||
return op[0]
|
|
||||||
return str(op)
|
return str(op)
|
||||||
|
|
||||||
def read_num_sym(self):
|
def read_num_sym(self):
|
||||||
|
|
@ -59,7 +57,7 @@ class ParserContext:
|
||||||
addr_mode = AddrMode.SIMPLE
|
addr_mode = AddrMode.SIMPLE
|
||||||
op = self.next_op()
|
op = self.next_op()
|
||||||
|
|
||||||
# Check if operand has a prefix (immediate/indirect/indexed)
|
# Check for prefix (immediate/indirect/indexed)
|
||||||
if isinstance(op, tuple) and len(op) == 2:
|
if isinstance(op, tuple) and len(op) == 2:
|
||||||
prefix, value = op
|
prefix, value = op
|
||||||
if prefix == '#':
|
if prefix == '#':
|
||||||
|
|
@ -67,17 +65,14 @@ class ParserContext:
|
||||||
elif prefix == '@':
|
elif prefix == '@':
|
||||||
addr_mode = AddrMode.INDIRECT
|
addr_mode = AddrMode.INDIRECT
|
||||||
elif prefix == '+':
|
elif prefix == '+':
|
||||||
# Extended format (SIC/XE)
|
|
||||||
addr_mode = AddrMode.EXTENDED
|
addr_mode = AddrMode.EXTENDED
|
||||||
else:
|
else:
|
||||||
# Unknown prefix, treat as simple
|
|
||||||
value = op
|
value = op
|
||||||
else:
|
else:
|
||||||
# Simple operand (no prefix)
|
# Simple
|
||||||
value = op
|
value = op
|
||||||
|
|
||||||
# Check for indexed addressing (X register)
|
# Check for indexed addressing (X register)
|
||||||
# In new parser, indexed would be a second operand that's just 'X'
|
|
||||||
if self.operands and self.operands[0] == 'X':
|
if self.operands and self.operands[0] == 'X':
|
||||||
self.operands.pop(0)
|
self.operands.pop(0)
|
||||||
indexed = True
|
indexed = True
|
||||||
|
|
|
||||||
3
ass3/zbirnik/src/zbirnik/program.obj
Normal file
3
ass3/zbirnik/src/zbirnik/program.obj
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
H^TEST ^000000^00000C
|
||||||
|
T^000000^09^0320030F2003000005
|
||||||
|
E^000000
|
||||||
23
ass3/zbirnik/src/zbirnik/test2.asm
Normal file
23
ass3/zbirnik/src/zbirnik/test2.asm
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
COPY START 1000
|
||||||
|
LDA ALPHA
|
||||||
|
ADD BETA
|
||||||
|
STA GAMMA
|
||||||
|
LDX ZERO
|
||||||
|
LOOP LDA BUFFER,X
|
||||||
|
ADD #1
|
||||||
|
STA BUFFER,X
|
||||||
|
TIX COUNT
|
||||||
|
JLT LOOP
|
||||||
|
LDA @PTR
|
||||||
|
COMP #100
|
||||||
|
JEQ DONE
|
||||||
|
J LOOP
|
||||||
|
DONE RSUB
|
||||||
|
ALPHA WORD 10
|
||||||
|
BETA WORD 20
|
||||||
|
GAMMA RESW 1
|
||||||
|
ZERO WORD 0
|
||||||
|
COUNT WORD 5
|
||||||
|
BUFFER RESW 10
|
||||||
|
PTR WORD GAMMA
|
||||||
|
END COPY
|
||||||
|
|
@ -1,25 +1,7 @@
|
||||||
OPCODES = {
|
|
||||||
"FIX": 0xC4,
|
|
||||||
"ADDR": 0x90,
|
|
||||||
"LDA": 0x00,
|
|
||||||
"STA": 0x0C,
|
|
||||||
"JSUB": 0x48,
|
|
||||||
}
|
|
||||||
|
|
||||||
REGISTERS = {
|
|
||||||
"A": 0,
|
|
||||||
"X": 1,
|
|
||||||
"L": 2,
|
|
||||||
"B": 3,
|
|
||||||
"S": 4,
|
|
||||||
"T": 5,
|
|
||||||
"F": 6,
|
|
||||||
}
|
|
||||||
|
|
||||||
from zbirnik.ukazi.f1 import f1
|
from zbirnik.ukazi.f1 import f1
|
||||||
from zbirnik.EmitCtx import EmitContext
|
from zbirnik.EmitCtx import EmitContext
|
||||||
|
|
||||||
ctx = EmitContext(OPCODES, REGISTERS, {})
|
ctx = EmitContext({})
|
||||||
|
|
||||||
node = f1("FIX", None)
|
node = f1("FIX", None)
|
||||||
result = node.emit(ctx)
|
result = node.emit(ctx)
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -10,7 +10,7 @@ class directive(Node):
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
def emit(self, ctx):
|
def emit(self, ctx):
|
||||||
# BASE directive
|
# BASE
|
||||||
if self.name == "BASE":
|
if self.name == "BASE":
|
||||||
if isinstance(self.operand, str):
|
if isinstance(self.operand, str):
|
||||||
ctx.base = ctx.symtab[self.operand]
|
ctx.base = ctx.symtab[self.operand]
|
||||||
|
|
@ -20,7 +20,7 @@ class directive(Node):
|
||||||
raise ValueError("Invalid BASE operand")
|
raise ValueError("Invalid BASE operand")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# NOBASE directive
|
# NOBASE
|
||||||
if self.name == "NOBASE":
|
if self.name == "NOBASE":
|
||||||
ctx.base = None
|
ctx.base = None
|
||||||
return None
|
return None
|
||||||
|
|
|
||||||
|
|
@ -13,15 +13,20 @@ class f2(Node):
|
||||||
def emit(self, ctx):
|
def emit(self, ctx):
|
||||||
opcode = ctx.opcodes[self.mnemonic]
|
opcode = ctx.opcodes[self.mnemonic]
|
||||||
|
|
||||||
|
#f2r,f2rr,f2rn
|
||||||
if isinstance(self.r1, str):
|
if isinstance(self.r1, str):
|
||||||
r1_val = ctx.REGISTERS[self.r1]
|
r1_val = ctx.REGISTERS[self.r1]
|
||||||
|
#f2n
|
||||||
else:
|
else:
|
||||||
r1_val = self.r1
|
r1_val = self.r1
|
||||||
|
|
||||||
|
#f2r
|
||||||
if self.r2 is None:
|
if self.r2 is None:
|
||||||
r2_val = 0
|
r2_val = 0
|
||||||
|
#f2rr
|
||||||
elif isinstance(self.r2, str):
|
elif isinstance(self.r2, str):
|
||||||
r2_val = ctx.REGISTERS[self.r2]
|
r2_val = ctx.REGISTERS[self.r2]
|
||||||
|
#f2rn
|
||||||
else:
|
else:
|
||||||
r2_val = self.r2
|
r2_val = self.r2
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,16 @@ class f3(Node):
|
||||||
|
|
||||||
def emit(self, ctx):
|
def emit(self, ctx):
|
||||||
opcode = ctx.opcodes[self.mnemonic]
|
opcode = ctx.opcodes[self.mnemonic]
|
||||||
|
|
||||||
|
# RSUB
|
||||||
|
if self.mnemonic == "RSUB":
|
||||||
|
base_opcode = opcode & 0b11111100
|
||||||
|
n, i = 1, 1
|
||||||
|
byte1 = base_opcode | (n << 1) | i
|
||||||
|
byte2 = 0x00
|
||||||
|
byte3 = 0x00
|
||||||
|
return bytes([byte1, byte2, byte3])
|
||||||
|
|
||||||
base_opcode = opcode & 0b11111100
|
base_opcode = opcode & 0b11111100
|
||||||
|
|
||||||
if self.adr_mode == AddrMode.SIMPLE:
|
if self.adr_mode == AddrMode.SIMPLE:
|
||||||
|
|
@ -38,6 +48,7 @@ class f3(Node):
|
||||||
disp = target - pc
|
disp = target - pc
|
||||||
b = 0
|
b = 0
|
||||||
p = 1
|
p = 1
|
||||||
|
|
||||||
#PC relativno
|
#PC relativno
|
||||||
if -2048 <= disp <= 2047:
|
if -2048 <= disp <= 2047:
|
||||||
b = 0
|
b = 0
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ class f4(Node):
|
||||||
raise ValueError("Invalid addressing mode")
|
raise ValueError("Invalid addressing mode")
|
||||||
|
|
||||||
byte1 = base_opcode | (n << 1) | i
|
byte1 = base_opcode | (n << 1) | i
|
||||||
|
|
||||||
x = 1 if self.index else 0
|
x = 1 if self.index else 0
|
||||||
b = 0
|
b = 0
|
||||||
p = 0
|
p = 0
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ class storage(Node):
|
||||||
if s.startswith("C'") and s.endswith("'"):
|
if s.startswith("C'") and s.endswith("'"):
|
||||||
chars = s[2:-1]
|
chars = s[2:-1]
|
||||||
return bytes(ord(c) for c in chars)
|
return bytes(ord(c) for c in chars)
|
||||||
# X'F1A3'
|
# X'FA3'
|
||||||
if s.startswith("X'") and s.endswith("'"):
|
if s.startswith("X'") and s.endswith("'"):
|
||||||
hexstr = s[2:-1]
|
hexstr = s[2:-1]
|
||||||
return bytes.fromhex(hexstr)
|
return bytes.fromhex(hexstr)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue