Skoraj koncano, manjka generiranje obj kode

This commit is contained in:
Timon 2026-01-03 21:41:45 +01:00
parent d50c62106e
commit 4b2a7a3cc2
47 changed files with 1034 additions and 0 deletions

0
ass3/zbirnik/README.md Normal file
View file

18
ass3/zbirnik/poetry.lock generated Normal file
View file

@ -0,0 +1,18 @@
# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand.
[[package]]
name = "ply"
version = "3.11"
description = "Python Lex & Yacc"
optional = false
python-versions = "*"
groups = ["main"]
files = [
{file = "ply-3.11-py2.py3-none-any.whl", hash = "sha256:096f9b8350b65ebd2fd1346b12452efe5b9607f7482813ffca50c22722a807ce"},
{file = "ply-3.11.tar.gz", hash = "sha256:00c7c1aaa88358b9c765b6d3000c6eec0ba42abca5351b095321aef446081da3"},
]
[metadata]
lock-version = "2.1"
python-versions = ">=3.10"
content-hash = "aeee2cd18440094d85604201949f31ea16b086488be3992891c1777b5c89b058"

View file

@ -0,0 +1,21 @@
[project]
name = "zbirnik"
version = "0.1.0"
description = ""
authors = [
{ name = "Timon", email = "timonbub@gmail.com" }
]
readme = "README.md"
requires-python = ">=3.10"
dependencies = [
"ply>=3.11,<4.0"
]
[tool.poetry]
packages = [
{ include = "zbirnik", from = "src" }
]
[build-system]
requires = ["poetry-core>=2.0.0,<3.0.0"]
build-backend = "poetry.core.masonry.api"

View file

@ -0,0 +1,6 @@
class EmitContext:
def __init__(self, opcodes, registers, symtab):
self.opcodes = opcodes
self.registers = registers
self.symtab = symtab
self.base = None

View file

View file

@ -0,0 +1,7 @@
# addressing.py
from enum import Enum, auto
class AddrMode(Enum):
SIMPLE = auto()
IMMEDIATE = auto()
INDIRECT = auto()

View file

@ -0,0 +1,62 @@
from zbirnik.ukazi.node import Node
class Code:
REGISTERS = {'A': 0, 'X': 1, 'L': 2, 'B': 3, 'S': 4, 'T': 5, 'F': 6 }
def __init__(self, name: str | None = None):
self.name = name
self.nodes = []
self.locctr = 0
self.symtab = {}
self.start_adress = 0
self.entry_point = 0
self.program_length = 0
self.obj = ""
def add(self, node: Node):
self.nodes.append(node)
def pass1(self):
locctr = 0
self.start_address = 0
self.entry_point = None
for node in self.nodes:
# START
if node.__class__.__name__ == "directive" and node.name == "START":
self.start_address = node.value
locctr = self.start_address
self.name = node.label
node.address = locctr
continue
# naslov node-a
node.address = locctr
# label → SYMTAB
if node.label:
if node.label in self.symtab:
raise ValueError(f"Duplicate symbol: {node.label}")
self.symtab[node.label] = locctr
# END
if node.__class__.__name__ == "directive" and node.name == "END":
if node.value is not None:
self.entry_point = node.value
break
locctr += node.size()
self.locctr = locctr
self.program_length = locctr - self.start_address
def pass2(self, ctx):
output = bytearray()
for node in self.nodes:
chunk = node.emit(ctx)
if chunk:
output.extend(chunk)
return bytes(output)

View file

@ -0,0 +1,11 @@
from abc import ABC, abstractmethod
class Mnemonic(ABC):
def __init__(self, name: str, opcode: int | None = None, description: str = ""):
self.name = name
self.opcode = opcode
self.description = description
@abstractmethod
def parse(self, parser):
pass

View file

@ -0,0 +1,8 @@
from zbirnik.ukazi.directive import directive
from mnemonic import Mnemonic
from zbirnik.parserctx import ParserContext
class MnemonicD(Mnemonic):
def parse(self, parser: ParserContext):
return directive(direktiva=self.name, label=parser.label,
operand=None)

View file

@ -0,0 +1,8 @@
from zbirnik.ukazi.directive import directive
from mnemonic import Mnemonic
from zbirnik.parserctx import ParserContext
class MnemonicDn(Mnemonic):
def parse(self, parser: ParserContext):
return directive(direktiva=self.name, label=parser.label,
operand=parser.read_num_sym())

View file

@ -0,0 +1,6 @@
from mnemonic import Mnemonic
from zbirnik.ukazi.f1 import f1
class MnemonicF1(Mnemonic):
def parse(self, parser):
return f1(label=parser.label, mnemonic=self.name)

View file

@ -0,0 +1,9 @@
from zbirnik.mnemoniki.mnemonic import Mnemonic
from zbirnik.ukazi.f2 import f2
from zbirnik.parserctx import ParserContext
class mnemonicF2(Mnemonic):
def parse(self, parser: ParserContext):
return f2(r1=parser.read_reg(), r2=parser.read_reg(),
mnemonic=self.name, label=parser.label)

View file

@ -0,0 +1,8 @@
from zbirnik.mnemoniki.mnemonic import Mnemonic
from zbirnik.ukazi.f2 import f2
from zbirnik.parserctx import ParserContext
class MnemonicF2n(Mnemonic):
def parse(self, parser: ParserContext):
return f2(r1=parser.read_reg(), r2 = None,
mnemonic=self.name, label=parser.label)

View file

@ -0,0 +1,9 @@
from zbirnik.mnemoniki.mnemonic import Mnemonic
from zbirnik.ukazi.f2 import f2
from zbirnik.parserctx import ParserContext
class MnemonicF2r(Mnemonic):
def parse(self, parser: ParserContext):
return f2(r1=parser.read_reg(), r2=None,
mnemonic=self.name, label=parser.label)

View file

@ -0,0 +1,8 @@
from zbirnik.mnemoniki.mnemonic import Mnemonic
from zbirnik.ukazi.f2 import f2
from zbirnik.parserctx import ParserContext
class MnemonicF2rn(Mnemonic):
def parse(self, parser: ParserContext):
return f2(r1=parser.read_reg(), r2=parser.read_num_sym()-1,
mnemonic=self.name, label=parser.label)

View file

@ -0,0 +1,9 @@
from zbirnik.mnemoniki.mnemonic import Mnemonic
from zbirnik.ukazi.f2 import f2
from zbirnik.parserctx import ParserContext
class MnemonicF2rr(Mnemonic):
def parse(self, parser: ParserContext):
return f2(r1=parser.read_reg(), r2=parser.read_reg(),
mnemonic=self.name, label=parser.label)

View file

@ -0,0 +1,8 @@
from zbirnik.mnemoniki.mnemonic import Mnemonic
from zbirnik.ukazi.f3 import f3
from zbirnik.parserctx import ParserContext
class MnemonicF3(Mnemonic):
def parse(self, parser: ParserContext):
return f3(operand=None, adr_mode=None, index=None,
mnemonic=self.name, label=parser.label)

View file

@ -0,0 +1,9 @@
from zbirnik.mnemoniki.mnemonic import Mnemonic
from zbirnik.ukazi.f3 import f3
from zbirnik.parserctx import ParserContext
class MnemonicF3m(Mnemonic):
def parse(self, parser: ParserContext):
addr_mode, value, indexed = parser.read_addressed_operand()
return f3(operand=value, mnemonic=self.name, label=parser.label,
adr_mode=addr_mode, index=indexed)

View file

@ -0,0 +1,10 @@
from zbirnik.mnemoniki.mnemonic import Mnemonic
from zbirnik.ukazi.f4 import f4
from zbirnik.parserctx import ParserContext
class MnemonicF4m(Mnemonic):
def parse(self, parser: ParserContext):
addr_mode, value, indexed = parser.read_addressed_operand()
return f4(operand=value, mnemonic=self.name, label=parser.label,
adr_mode=addr_mode, index=indexed)

View file

@ -0,0 +1,7 @@
from zbirnik.mnemoniki.mnemonic import Mnemonic
from zbirnik.ukazi.storage import storage
from zbirnik.parserctx import ParserContext
class MnemonicSd(Mnemonic):
def parse(self, parser: ParserContext):
return storage(label=parser.label, name=self.name, val=parser.read_num_sym())

View file

@ -0,0 +1,8 @@
from zbirnik.mnemoniki.mnemonic import Mnemonic
from zbirnik.ukazi.storage import storage
from zbirnik.parserctx import ParserContext
class MnemonicSn(Mnemonic):
def parse(self, parser: ParserContext):
return storage(label=parser.label, name=self.name, val=parser.read_num_sym())

View file

@ -0,0 +1,70 @@
from mnemoniki import (
mnemonicD, mnemonicDn,
mnemonicF1, mnemonicF2n, mnemonicF2r, mnemonicF2rn, mnemonicF2rr,
mnemonicF3, mnemonicF3m, mnemonicF4m,
mnemonicSd, mnemonicSn
)
# Centralna tabela mnemonikov
MNEMONICS = {
# Direktive (brez operandov)
'NOBASE': mnemonicD('NOBASE'),
'LTORG': mnemonicD('LTORG'),
# Direktive (en operand)
'START': mnemonicDn('START'),
'END': mnemonicDn('END'),
'BASE': mnemonicDn('BASE'),
# Format 1 (brez operandov)
'FIX': mnemonicF1('FIX', opcode=0xC4),
'FLOAT': mnemonicF1('FLOAT', opcode=0xC0),
'HIO': mnemonicF1('HIO', opcode=0xF4),
'NORM': mnemonicF1('NORM', opcode=0xC8),
'SIO': mnemonicF1('SIO', opcode=0xF0),
'TIO': mnemonicF1('TIO', opcode=0xF8),
# Format 2
# F2 – en številčni operand
'SVC': mnemonicF2n('SVC', opcode=0xB0),
# F2 – en register
'CLEAR': mnemonicF2r('CLEAR', opcode=0xB4),
'TIXR': mnemonicF2r('TIXR', opcode=0xB8),
# F2 – register + število
'SHIFTL': mnemonicF2rn('SHIFTL', opcode=0xA4),
'SHIFTR': mnemonicF2rn('SHIFTR', opcode=0xA8),
# F2 – dva registra
'ADDR': mnemonicF2rr('ADDR', opcode=0x90),
'SUBR': mnemonicF2rr('SUBR', opcode=0x94),
'MULR': mnemonicF2rr('MULR', opcode=0x98),
'DIVR': mnemonicF2rr('DIVR', opcode=0x9C),
'COMPR': mnemonicF2rr('COMPR', opcode=0xA0),
# Format 3
'RSUB': mnemonicF3('RSUB', opcode=0x4C),
'LDA': mnemonicF3m('LDA', opcode=0x00),
'LDX': mnemonicF3m('LDX', opcode=0x04),
'LDL': mnemonicF3m('LDL', opcode=0x08),
'STA': mnemonicF3m('STA', opcode=0x0C),
'STX': mnemonicF3m('STX', opcode=0x10),
'STL': mnemonicF3m('STL', opcode=0x14),
'ADD': mnemonicF3m('ADD', opcode=0x18),
'SUB': mnemonicF3m('SUB', opcode=0x1C),
'MUL': mnemonicF3m('MUL', opcode=0x20),
'DIV': mnemonicF3m('DIV', opcode=0x24),
'COMP': mnemonicF3m('COMP', opcode=0x28),
'J': mnemonicF3m('J', opcode=0x3C),
'JEQ': mnemonicF3m('JEQ', opcode=0x30),
'JGT': mnemonicF3m('JGT', opcode=0x34),
'JLT': mnemonicF3m('JLT', opcode=0x38),
'JSUB': mnemonicF3m('JSUB', opcode=0x48),
'TD': mnemonicF3m('TD', opcode=0xE0),
'RD': mnemonicF3m('RD', opcode=0xD8),
'WD': mnemonicF3m('WD', opcode=0xDC),
# Format 4 (razširjeni)
'+LDA': mnemonicF4m('+LDA', opcode=0x00),
'+JSUB': mnemonicF4m('+JSUB', opcode=0x48),
# Pomnilniške direktive
# podatki
'BYTE': mnemonicSd('BYTE'),
'WORD': mnemonicSd('WORD'),
# rezervacija
'RESB': mnemonicSn('RESB'),
'RESW': mnemonicSn('RESW'),
}

View file

@ -0,0 +1,219 @@
Created by PLY version 3.11 (http://www.dabeaz.com/ply)
Grammar
Rule 0 S' -> start
Rule 1 start -> LABEL command
Rule 2 start -> command
Rule 3 command -> MNEMONIC
Rule 4 command -> MNEMONIC args
Rule 5 args -> operand
Rule 6 args -> operand COMMA operand
Rule 7 operand -> REGISTER
Rule 8 operand -> AT address
Rule 9 operand -> HASH address
Rule 10 operand -> address
Rule 11 address -> NUMBER
Rule 12 address -> SYMBOL
Terminals, with rules where they appear
AT : 8
COMMA : 6
HASH : 9
LABEL : 1
MNEMONIC : 3 4
NUMBER : 11
REGISTER : 7
SYMBOL : 12
error :
Nonterminals, with rules where they appear
address : 8 9 10
args : 4
command : 1 2
operand : 5 6 6
start : 0
Parsing method: LALR
state 0
(0) S' -> . start
(1) start -> . LABEL command
(2) start -> . command
(3) command -> . MNEMONIC
(4) command -> . MNEMONIC args
LABEL shift and go to state 2
MNEMONIC shift and go to state 4
start shift and go to state 1
command shift and go to state 3
state 1
(0) S' -> start .
state 2
(1) start -> LABEL . command
(3) command -> . MNEMONIC
(4) command -> . MNEMONIC args
MNEMONIC shift and go to state 4
command shift and go to state 5
state 3
(2) start -> command .
$end reduce using rule 2 (start -> command .)
state 4
(3) command -> MNEMONIC .
(4) command -> MNEMONIC . args
(5) args -> . operand
(6) args -> . operand COMMA operand
(7) operand -> . REGISTER
(8) operand -> . AT address
(9) operand -> . HASH address
(10) operand -> . address
(11) address -> . NUMBER
(12) address -> . SYMBOL
$end reduce using rule 3 (command -> MNEMONIC .)
REGISTER shift and go to state 8
AT shift and go to state 9
HASH shift and go to state 11
NUMBER shift and go to state 12
SYMBOL shift and go to state 13
args shift and go to state 6
operand shift and go to state 7
address shift and go to state 10
state 5
(1) start -> LABEL command .
$end reduce using rule 1 (start -> LABEL command .)
state 6
(4) command -> MNEMONIC args .
$end reduce using rule 4 (command -> MNEMONIC args .)
state 7
(5) args -> operand .
(6) args -> operand . COMMA operand
$end reduce using rule 5 (args -> operand .)
COMMA shift and go to state 14
state 8
(7) operand -> REGISTER .
COMMA reduce using rule 7 (operand -> REGISTER .)
$end reduce using rule 7 (operand -> REGISTER .)
state 9
(8) operand -> AT . address
(11) address -> . NUMBER
(12) address -> . SYMBOL
NUMBER shift and go to state 12
SYMBOL shift and go to state 13
address shift and go to state 15
state 10
(10) operand -> address .
COMMA reduce using rule 10 (operand -> address .)
$end reduce using rule 10 (operand -> address .)
state 11
(9) operand -> HASH . address
(11) address -> . NUMBER
(12) address -> . SYMBOL
NUMBER shift and go to state 12
SYMBOL shift and go to state 13
address shift and go to state 16
state 12
(11) address -> NUMBER .
COMMA reduce using rule 11 (address -> NUMBER .)
$end reduce using rule 11 (address -> NUMBER .)
state 13
(12) address -> SYMBOL .
COMMA reduce using rule 12 (address -> SYMBOL .)
$end reduce using rule 12 (address -> SYMBOL .)
state 14
(6) args -> operand COMMA . operand
(7) operand -> . REGISTER
(8) operand -> . AT address
(9) operand -> . HASH address
(10) operand -> . address
(11) address -> . NUMBER
(12) address -> . SYMBOL
REGISTER shift and go to state 8
AT shift and go to state 9
HASH shift and go to state 11
NUMBER shift and go to state 12
SYMBOL shift and go to state 13
operand shift and go to state 17
address shift and go to state 10
state 15
(8) operand -> AT address .
COMMA reduce using rule 8 (operand -> AT address .)
$end reduce using rule 8 (operand -> AT address .)
state 16
(9) operand -> HASH address .
COMMA reduce using rule 9 (operand -> HASH address .)
$end reduce using rule 9 (operand -> HASH address .)
state 17
(6) args -> operand COMMA operand .
$end reduce using rule 6 (args -> operand COMMA operand .)

View file

@ -0,0 +1,92 @@
import ply.lex
import ply.yacc
import sys
# Lexer.
tokens = (
'AT',
'COMMA',
'HASH',
'LABEL',
'REGISTER',
'MNEMONIC',
'SYMBOL',
'NUMBER',
'COMMENT',
)
t_AT = r'@'
t_COMMA = r','
t_HASH = r'\#'
t_LABEL = r'^[a-z_0-9]+'
t_REGISTER = r'\b[ABFLSTX]\b'
t_MNEMONIC = r'\b[A-Z]+\b'
t_SYMBOL = r'[a-z_0-9]+'
def t_NUMBER(t):
r'-?\d+'
t.value = int(t.value)
return t
def t_COMMENT(t):
r'\..*'
t.value = t.value[1:].strip()
return t
t_ignore = ' \t\n'
def t_error(t):
print(f'illegal character {t}')
t.lexer.skip(1)
lexer = ply.lex.lex()
# Parser.
def p_start(p):
r'''start : LABEL command
| COMMENT
| command'''
if len(p) == 2:
if isinstance(p[1], str):
p[0] = ('COMMENT', p[1])
else:
p[0] = (None, p[1])
else:
p[0] = tuple(p[1:])
def p_command(p):
r'''command : MNEMONIC
| MNEMONIC args'''
p[0] = p[1:]
def p_args(p):
r'''args : operand
| operand COMMA operand'''
match len(p):
case 2: p[0] = (p[1],)
case 4: p[0] = (p[1], p[3])
def p_operand(p):
r'''operand : REGISTER
| AT address
| HASH address
| address'''
p[0] = p[1:]
def p_address(p):
r'''address : NUMBER
| SYMBOL'''
p[0] = p[1]
def p_error(p):
if p:
print('Syntax error at token', p)
parser.errok()
else:
print('Syntax error at EOF')
if __name__ == '__main__':
import sys
parser = ply.yacc.yacc()
for line in sys.stdin:
print(parser.parse(line))

View file

@ -0,0 +1,62 @@
class ParserContext:
def __init__(self, parsed):
"""
parsed je rezultat PLY parserja, npr.:
('loop', ('LDA', (('#', 5),)))
(None, ('FIX',))
('COMMENT', 'to je komentar')
"""
if isinstance(parsed, tuple) and parsed[0] == 'COMMENT':
self.label = None
self.mnemonic = 'COMMENT'
self.operands = []
self.comment = parsed[1]
return
self.label, command = parsed
self.mnemonic = command[0]
self.operands = list(command[1]) if len(command) > 1 else []
self.comment = None
def has_operand(self) -> bool:
return len(self.operands) > 0
def next_op(self):
if not self.operands:
raise ValueError("Manjka operand")
return self.operands.pop(0)
def read_reg(self) -> str:
op = self.next_op()
return op[0]
def read_num_sym(self):
op = self.next_op()
if (len(op) == 1):
return op[0]
return op[1]
def read_addressed_operand(self):
from adressing import AddrMode
indexed = False
addr_mode = AddrMode.SIMPLE
op = self.next_operand()
# immediate / indirect
if len(op) == 2:
prefix, value = op
addr_mode = {'#': AddrMode.IMMEDIATE, '@': AddrMode.INDIRECT}[prefix]
else:
value = op[0]
#indeksiranje
if self.operands and self.operands[0] == ('X',):
self.operands.pop(0)
indexed = True
return addr_mode, value, indexed

View file

@ -0,0 +1,42 @@
# parsetab.py
# This file is automatically generated. Do not edit.
# pylint: disable=W,C,R
_tabversion = '3.10'
_lr_method = 'LALR'
_lr_signature = 'AT COMMA HASH LABEL MNEMONIC NUMBER REGISTER SYMBOLstart : LABEL command\n | commandcommand : MNEMONIC\n | MNEMONIC argsargs : operand\n | operand COMMA operandoperand : REGISTER\n | AT address\n | HASH address\n | addressaddress : NUMBER\n | SYMBOL'
_lr_action_items = {'LABEL':([0,],[2,]),'MNEMONIC':([0,2,],[4,4,]),'$end':([1,3,4,5,6,7,8,10,12,13,15,16,17,],[0,-2,-3,-1,-4,-5,-7,-10,-11,-12,-8,-9,-6,]),'REGISTER':([4,14,],[8,8,]),'AT':([4,14,],[9,9,]),'HASH':([4,14,],[11,11,]),'NUMBER':([4,9,11,14,],[12,12,12,12,]),'SYMBOL':([4,9,11,14,],[13,13,13,13,]),'COMMA':([7,8,10,12,13,15,16,],[14,-7,-10,-11,-12,-8,-9,]),}
_lr_action = {}
for _k, _v in _lr_action_items.items():
for _x,_y in zip(_v[0],_v[1]):
if not _x in _lr_action: _lr_action[_x] = {}
_lr_action[_x][_k] = _y
del _lr_action_items
_lr_goto_items = {'start':([0,],[1,]),'command':([0,2,],[3,5,]),'args':([4,],[6,]),'operand':([4,14,],[7,17,]),'address':([4,9,11,14,],[10,15,16,10,]),}
_lr_goto = {}
for _k, _v in _lr_goto_items.items():
for _x, _y in zip(_v[0], _v[1]):
if not _x in _lr_goto: _lr_goto[_x] = {}
_lr_goto[_x][_k] = _y
del _lr_goto_items
_lr_productions = [
("S' -> start","S'",1,None,None,None),
('start -> LABEL command','start',2,'p_start','parser.py',41),
('start -> command','start',1,'p_start','parser.py',42),
('command -> MNEMONIC','command',1,'p_command','parser.py',48),
('command -> MNEMONIC args','command',2,'p_command','parser.py',49),
('args -> operand','args',1,'p_args','parser.py',53),
('args -> operand COMMA operand','args',3,'p_args','parser.py',54),
('operand -> REGISTER','operand',1,'p_operand','parser.py',60),
('operand -> AT address','operand',2,'p_operand','parser.py',61),
('operand -> HASH address','operand',2,'p_operand','parser.py',62),
('operand -> address','operand',1,'p_operand','parser.py',63),
('address -> NUMBER','address',1,'p_address','parser.py',67),
('address -> SYMBOL','address',1,'p_address','parser.py',68),
]

View file

@ -0,0 +1,72 @@
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, {})
node = f1("FIX", None)
result = node.emit(ctx)
print(result.hex())
from zbirnik.ukazi.f2 import f2
node = f2("A", "X", "ADDR")
print(node.emit(ctx).hex())
from zbirnik.ukazi.f3 import f3
from zbirnik.adressing import AddrMode
#IMMEDIATE
node = f3(
operand=5,
mnemonic="LDA",
label=None,
index=False,
adr_mode=AddrMode.IMMEDIATE
)
node.address = 0x1000
print(node.emit(ctx).hex())
#PC RELATIVE
ctx.symtab["NUM"] = 0x1003
node = f3(
operand="NUM",
mnemonic="LDA",
label=None,
index=False,
adr_mode=AddrMode.SIMPLE
)
node.address = 0x1000
print(node.emit(ctx).hex())
from zbirnik.ukazi.f4 import f4
#F4
ctx.symtab["SUBR"] = 0x12345
node = f4(
operand="SUBR",
mnemonic="JSUB",
label=None,
index=False,
adr_mode=AddrMode.SIMPLE
)
print(node.emit(ctx).hex())

View file

@ -0,0 +1,8 @@
from zbirnik.ukazi.node import Node
class Comment(Node):
def __init__(self, text: str):
self.text = text
def size(self):
return 0

View file

@ -0,0 +1,29 @@
from zbirnik.ukazi.node import Node
class directive(Node):
def __init__(self, direktiva : str, operand : str | int | None, label : str | None = None):
self.direktiva = direktiva
super().__init__(label)
self.operand = operand
def size(self) -> int:
return 0
def emit(self, ctx):
# BASE directive
if self.name == "BASE":
if isinstance(self.value, str):
ctx.base = ctx.symtab[self.value]
elif isinstance(self.value, int):
ctx.base = self.value
else:
raise ValueError("Invalid BASE operand")
return None
# NOBASE directive
if self.name == "NOBASE":
ctx.base = None
return None
# START, END, LTORG, ...
return None

View file

@ -0,0 +1,13 @@
from zbirnik.ukazi.node import Node
class f1(Node):
def __init__(self, mnemnonic : str, label : str | None = None):
self.mnemonic = mnemnonic
super().__init__(label)
def size(self) -> int:
return 1
def emit(self, ctx):
opcode = ctx.opcodes[self.mnemonic]
return bytes([opcode])

View file

@ -0,0 +1,30 @@
from zbirnik.ukazi.node import Node
class f2(Node):
def __init__(self, r1: str, r2: str | None, mnemonic: str, label : str | None = None):
self.r1 = r1
self.r2 = r2
self.mnemonic = mnemonic
super().__init__(label)
def size(self) -> int:
return 2
def emit(self, ctx):
opcode = ctx.opcodes[self.mnemonic]
if isinstance(self.r1, str):
r1_val = ctx.registers[self.r1]
else:
r1_val = self.r1
if self.r2 is None:
r2_val = 0
elif isinstance(self.r2, str):
r2_val = ctx.registers[self.r2]
else:
r2_val = self.r2
byte2 = (r1_val << 4) | r2_val
return bytes([opcode, byte2])

View file

@ -0,0 +1,62 @@
from zbirnik.ukazi.node import Node
from zbirnik.adressing import AddrMode
class f3(Node):
def __init__(self, operand: str | int | None, mnemonic: str, index : bool, adr_mode: str | None, label : str | None = None):
super().__init__(label)
self.operand = operand
self.mnemonic = mnemonic
self.index = index
self.adr_mode = adr_mode
def size(self) -> int:
return 3
def emit(self, ctx):
opcode = ctx.opcodes[self.mnemonic]
base_opcode = opcode & 0b11111100
if self.adr_mode == AddrMode.SIMPLE:
n, i = 1, 1
elif self.adr_mode == AddrMode.IMMEDIATE:
n, i = 0, 1
elif self.adr_mode == AddrMode.INDIRECT:
n, i = 1, 0
else:
raise ValueError("Invalid addressing mode")
byte1 = base_opcode | (n << 1) | i
x = 1 if self.index else 0
if isinstance(self.operand, int):
target = self.operand
else:
target = ctx.symtab[self.operand]
pc = self.address + 3
disp = target - pc
b = 0
p = 1
#PC relativno
if -2048 <= disp <= 2047:
b = 0
p = 1
#Bazno relativno
elif ctx.base is not None:
disp = target - ctx.base
if 0 <= disp <= 4095:
b = 1
p = 0
else:
raise ValueError("Displacement out of range")
e = 0
byte2 = ((x << 7) | (b << 6) | (p << 5) | (e << 4) | ((disp >> 8) & 0x0F))
byte3 = disp & 0xFF
return bytes([byte1, byte2, byte3])

View file

@ -0,0 +1,51 @@
from zbirnik.ukazi.node import Node
from zbirnik.adressing import AddrMode
class f4(Node):
def __init__(
self,
operand: str | int | None,
mnemonic: str,
label: str | None = None,
index: bool = False,
adr_mode: AddrMode = AddrMode.SIMPLE
):
super().__init__(label)
self.operand = operand
self.mnemonic = mnemonic
self.index = index
self.adr_mode = adr_mode
def size(self) -> int:
return 4
def emit(self, ctx):
opcode = ctx.opcodes[self.mnemonic]
base_opcode = opcode & 0b11111100
if self.adr_mode == AddrMode.SIMPLE:
n, i = 1, 1
elif self.adr_mode == AddrMode.IMMEDIATE:
n, i = 0, 1
elif self.adr_mode == AddrMode.INDIRECT:
n, i = 1, 0
else:
raise ValueError("Invalid addressing mode")
byte1 = base_opcode | (n << 1) | i
x = 1 if self.index else 0
b = 0
p = 0
e = 1
if isinstance(self.operand, int):
target = self.operand
else:
target = ctx.symtab[self.operand]
byte2 = ((x << 7) | (b << 6) | (p << 5) | (e << 4) | ((target >> 16) & 0x0F))
byte3 = (target >> 8) & 0xFF
byte4 = target & 0xFF
return bytes([byte1, byte2, byte3, byte4])

View file

@ -0,0 +1,9 @@
class Node:
def __init__(self, label=None):
self.label = label
self.address = None
def size(self):
raise NotImplementedError
def emit(self):
raise NotImplementedError

View file

@ -0,0 +1,43 @@
from zbirnik.ukazi.node import Node
class storage(Node):
def __init__(self, val : str | int, name : str, label : str | None = None):
super().__init__(label)
self.val = val
self.name = name
def size(self) -> int:
return 0
def emit(self, ctx):
if self.name == "WORD":
if isinstance(self.value, str):
val = ctx.symtab[self.value]
else:
val = self.value
val &= 0xFFFFFF # 24-bit signed
b1 = (val >> 16) & 0xFF
b2 = (val >> 8) & 0xFF
b3 = val & 0xFF
return bytes([b1, b2, b3])
if self.name == "BYTE":
s = self.value
# C'EOF'
if s.startswith("C'") and s.endswith("'"):
chars = s[2:-1]
return bytes(ord(c) for c in chars)
# X'F1A3'
if s.startswith("X'") and s.endswith("'"):
hexstr = s[2:-1]
return bytes.fromhex(hexstr)
raise ValueError("Invalid BYTE constant")
# RESB / RESW → no code
if self.name in ("RESB", "RESW"):
return b""
return b""

View file