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
|
||||
|
||||
class EmitContext:
|
||||
REGISTERS = {
|
||||
'A': 0, 'X': 1, 'L': 2,
|
||||
'B': 3, 'S': 4, 'T': 5, 'F': 6
|
||||
}
|
||||
|
||||
REGISTERS = {'A': 0, 'X': 1, 'L': 2,'B': 3, 'S': 4, 'T': 5, 'F': 6}
|
||||
|
||||
def __init__(self, symtab):
|
||||
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()
|
||||
IMMEDIATE = 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.mnemoniki.mnemoniki_tabela import MNEMONICS
|
||||
from zbirnik.parserctx import ParserContext
|
||||
from zbirnik.gen_obj import Generate_obj
|
||||
import os
|
||||
|
||||
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")
|
||||
|
||||
code = Code()
|
||||
|
||||
with open(ime) as f:
|
||||
with open(ime1) as f:
|
||||
for line in f:
|
||||
print("LINE:", line.rstrip())
|
||||
|
||||
|
|
@ -35,8 +36,19 @@ with open(ime) as f:
|
|||
|
||||
|
||||
code.pass1()
|
||||
|
||||
ctx_emit = EmitContext(symtab=code.symtab)
|
||||
|
||||
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),
|
||||
'RD': MnemonicF3m('RD', opcode=0xD8),
|
||||
'WD': MnemonicF3m('WD', opcode=0xDC),
|
||||
'TIX': MnemonicF3m('TIX', opcode=0x2C),
|
||||
# Format 4 (razširjeni)
|
||||
'+LDA': MnemonicF4m('+LDA', opcode=0x00),
|
||||
'+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.yacc
|
||||
#import sys
|
||||
|
||||
global at_line_start, seen_mnemonic_or_directive
|
||||
# Lexer
|
||||
|
|
@ -179,13 +178,3 @@ def p_error(p):
|
|||
print('Syntax error at EOF')
|
||||
|
||||
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:
|
||||
def __init__(self, parsed):
|
||||
# Handle COMMENT special case (if you still need it)
|
||||
#Comment
|
||||
if isinstance(parsed, tuple) and parsed[0] == 'COMMENT':
|
||||
self.label = None
|
||||
self.mnemonic = 'COMMENT'
|
||||
|
|
@ -8,13 +8,13 @@ class ParserContext:
|
|||
self.comment = parsed[1]
|
||||
return
|
||||
|
||||
# New parser format: (label, mnemonic, *operands)
|
||||
#parsed=(label, mnemonic, *operands)
|
||||
if not isinstance(parsed, tuple) or len(parsed) < 2:
|
||||
raise ValueError(f"Invalid parsed format: {parsed}")
|
||||
|
||||
self.label = parsed[0] # Can be None
|
||||
self.label = parsed[0]
|
||||
self.mnemonic = parsed[1]
|
||||
# All remaining elements are operands
|
||||
#Ostali so operandi
|
||||
self.operands = list(parsed[2:]) if len(parsed) > 2 else []
|
||||
self.comment = None
|
||||
|
||||
|
|
@ -29,12 +29,10 @@ class ParserContext:
|
|||
def read_reg(self) -> str:
|
||||
"""Read a register operand (e.g., 'A', 'X', 'L')"""
|
||||
op = self.next_op()
|
||||
# If it's a plain string, return it
|
||||
|
||||
if isinstance(op, str):
|
||||
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)
|
||||
|
||||
def read_num_sym(self):
|
||||
|
|
@ -59,25 +57,22 @@ class ParserContext:
|
|||
addr_mode = AddrMode.SIMPLE
|
||||
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:
|
||||
prefix, value = op
|
||||
if prefix == '#':
|
||||
addr_mode = AddrMode.IMMEDIATE
|
||||
elif prefix == '@':
|
||||
addr_mode = AddrMode.INDIRECT
|
||||
elif prefix == '+':
|
||||
# Extended format (SIC/XE)
|
||||
elif prefix == '+':
|
||||
addr_mode = AddrMode.EXTENDED
|
||||
else:
|
||||
# Unknown prefix, treat as simple
|
||||
value = op
|
||||
else:
|
||||
# Simple operand (no prefix)
|
||||
# Simple
|
||||
value = op
|
||||
|
||||
# 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':
|
||||
self.operands.pop(0)
|
||||
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.EmitCtx import EmitContext
|
||||
|
||||
ctx = EmitContext(OPCODES, REGISTERS, {})
|
||||
ctx = EmitContext({})
|
||||
|
||||
node = f1("FIX", None)
|
||||
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
|
||||
|
||||
def emit(self, ctx):
|
||||
# BASE directive
|
||||
# BASE
|
||||
if self.name == "BASE":
|
||||
if isinstance(self.operand, str):
|
||||
ctx.base = ctx.symtab[self.operand]
|
||||
|
|
@ -20,7 +20,7 @@ class directive(Node):
|
|||
raise ValueError("Invalid BASE operand")
|
||||
return None
|
||||
|
||||
# NOBASE directive
|
||||
# NOBASE
|
||||
if self.name == "NOBASE":
|
||||
ctx.base = None
|
||||
return None
|
||||
|
|
|
|||
|
|
@ -13,15 +13,20 @@ class f2(Node):
|
|||
def emit(self, ctx):
|
||||
opcode = ctx.opcodes[self.mnemonic]
|
||||
|
||||
#f2r,f2rr,f2rn
|
||||
if isinstance(self.r1, str):
|
||||
r1_val = ctx.REGISTERS[self.r1]
|
||||
#f2n
|
||||
else:
|
||||
r1_val = self.r1
|
||||
|
||||
#f2r
|
||||
if self.r2 is None:
|
||||
r2_val = 0
|
||||
#f2rr
|
||||
elif isinstance(self.r2, str):
|
||||
r2_val = ctx.REGISTERS[self.r2]
|
||||
#f2rn
|
||||
else:
|
||||
r2_val = self.r2
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,16 @@ class f3(Node):
|
|||
|
||||
def emit(self, ctx):
|
||||
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
|
||||
|
||||
if self.adr_mode == AddrMode.SIMPLE:
|
||||
|
|
@ -38,6 +48,7 @@ class f3(Node):
|
|||
disp = target - pc
|
||||
b = 0
|
||||
p = 1
|
||||
|
||||
#PC relativno
|
||||
if -2048 <= disp <= 2047:
|
||||
b = 0
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ class f4(Node):
|
|||
raise ValueError("Invalid addressing mode")
|
||||
|
||||
byte1 = base_opcode | (n << 1) | i
|
||||
|
||||
x = 1 if self.index else 0
|
||||
b = 0
|
||||
p = 0
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ class storage(Node):
|
|||
if s.startswith("C'") and s.endswith("'"):
|
||||
chars = s[2:-1]
|
||||
return bytes(ord(c) for c in chars)
|
||||
# X'F1A3'
|
||||
# X'FA3'
|
||||
if s.startswith("X'") and s.endswith("'"):
|
||||
hexstr = s[2:-1]
|
||||
return bytes.fromhex(hexstr)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue