Skip to content

Commit f8cef32

Browse files
committed
refact: move methods and globals out of __init__.py
1 parent 02d2159 commit f8cef32

8 files changed

Lines changed: 1358 additions & 1320 deletions

File tree

src/arch/z80/backend/__init__.py

Lines changed: 372 additions & 1175 deletions
Large diffs are not rendered by default.

src/arch/z80/backend/common.py

Lines changed: 298 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,80 @@
33
# vim ts=4:et:sw=4:ai
44

55
import math
6+
import re
67

78
from typing import List
89
from typing import Set
910

1011
import src.api.global_ as gl
1112
import src.api.errors
12-
13-
from .runtime import RUNTIME_LABELS
14-
from .runtime import LABEL_REQUIRED_MODULES
15-
13+
import src.arch
14+
from src.api import global_
15+
16+
from src.arch.z80.backend import errors
17+
from src.arch.z80.backend.errors import InvalidICError as InvalidIC
18+
from src.arch.z80.backend.runtime import RUNTIME_LABELS, LABEL_REQUIRED_MODULES, NAMESPACE, Labels as RuntimeLabel
19+
20+
21+
# List of modules (in alphabetical order) that, if included, should call MEM_INIT
22+
MEMINITS = {
23+
"alloc.asm",
24+
"loadstr.asm",
25+
"storestr2.asm",
26+
"storestr.asm",
27+
"strcat.asm",
28+
"strcpy.asm",
29+
"string.asm",
30+
"strslice.asm",
31+
}
32+
33+
# Internal data types definition, with its size in bytes, or -1 if it is variable (string)
34+
# Compound types are only arrays, and have the t
35+
YY_TYPES = {
36+
"u8": 1, # 8 bit unsigned integer
37+
"u16": 2, # 16 bit unsigned integer
38+
"u32": 4, # 32 bit unsigned integer
39+
"i8": 1, # 8 bit SIGNED integer
40+
"i16": 2, # 16 bit SIGNED integer
41+
"i32": 4, # 32 bit SIGNED integer
42+
"f16": 4, # -32768.9999 to 32767.9999 -aprox.- fixed point decimal (step = 1/2^16)
43+
"f": 5, # Floating point
44+
}
45+
46+
# Matches a boolean instruction like 'equ16' or 'andi32'
47+
RE_BOOL = re.compile(r"^(eq|ne|lt|le|gt|ge|and|or|xor|not)(([ui](8|16|32))|(f16|f|str))$")
48+
49+
# Marches an hexadecimal number
50+
RE_HEXA = re.compile(r"^[0-9A-F]+$")
51+
52+
# (ix +/- ...) regexp
53+
RE_IX_IDX = re.compile(r"^\([ \t]*ix[ \t]*[-+][ \t]*.+\)$")
54+
55+
# Label for the program START end EXIT
56+
START_LABEL = f"{NAMESPACE}.__START_PROGRAM"
57+
END_LABEL = f"{NAMESPACE}.__END_PROGRAM"
58+
59+
CALL_BACK = f"{NAMESPACE}.__CALL_BACK__"
60+
MAIN_LABEL = f"{NAMESPACE}.__MAIN_PROGRAM__"
61+
DATA_LABEL = global_.ZXBASIC_USER_DATA
62+
DATA_END_LABEL = f"{DATA_LABEL}_END"
63+
64+
# -------------------------------------------------------
65+
# Runtime flags. Must be initialized on each compilation
66+
# -------------------------------------------------------
67+
68+
# Whether the FunctionExit scheme has been already used or not
69+
FLAG_use_function_exit = False
70+
71+
# Whether an 'end' has already been emitted or not
72+
FLAG_end_emitted = False
73+
74+
# This will be appended at the end of code emission (useful for lvard, for example)
75+
AT_END = []
76+
77+
# A table with ASM block entered by the USER (these won't be optimized)
78+
ASMS = {}
79+
ASMCOUNT = 0 # ASM blocks counter
1680

1781
MEMORY = [] # Must be initialized by with init()
1882

@@ -35,19 +99,16 @@
3599
# GENERATED labels __LABELXX
36100
TMP_LABELS: Set[str] = set()
37101

102+
# ------------------------------------
103+
# Table describing operations
104+
# 'OPERATOR' -> [Number of arguments]
105+
# ------------------------------------
106+
QUADS = {}
38107

39-
def init():
40-
global LABEL_COUNTER
41-
global TMP_COUNTER
42-
43-
LABEL_COUNTER = 0
44-
TMP_COUNTER = 0
45108

46-
del MEMORY[:]
47-
del TMP_STORAGES[:]
48-
REQUIRES.clear()
49-
INITS.clear()
50-
TMP_LABELS.clear()
109+
# ------------------------------------
110+
# Shared functions
111+
# ------------------------------------
51112

52113

53114
def log2(x: float) -> float:
@@ -170,3 +231,225 @@ def _f_ops(op1, op2, swap=True):
170231
def is_int_type(stype: str) -> bool:
171232
"""Returns whether a given type is integer"""
172233
return stype[0] in ("u", "i")
234+
235+
236+
class Quad:
237+
"""Implements a Quad code instruction."""
238+
239+
def __init__(self, *args):
240+
"""Creates a quad-uple checking it has the current params.
241+
Operators should be passed as Quad('+', tSymbol, val1, val2)
242+
"""
243+
if not args:
244+
raise InvalidIC("<null>")
245+
246+
if args[0] not in QUADS.keys():
247+
errors.throw_invalid_quad_code(args[0])
248+
249+
if len(args) - 1 != QUADS[args[0]][0]:
250+
errors.throw_invalid_quad_params(args[0], len(args) - 1, QUADS[args[0]][0])
251+
252+
args = tuple([str(x) for x in args]) # Convert it to strings
253+
254+
self.quad = args
255+
self.op = args[0]
256+
257+
def __str__(self):
258+
"""String representation"""
259+
return str(self.quad)
260+
261+
262+
def init():
263+
global LABEL_COUNTER
264+
global TMP_COUNTER
265+
global ASMCOUNT
266+
global FLAG_end_emitted
267+
global FLAG_use_function_exit
268+
269+
LABEL_COUNTER = 0
270+
TMP_COUNTER = 0
271+
ASMCOUNT = 0
272+
273+
MEMORY.clear()
274+
TMP_STORAGES.clear()
275+
REQUIRES.clear()
276+
INITS.clear()
277+
TMP_LABELS.clear()
278+
ASMS.clear()
279+
AT_END.clear()
280+
281+
FLAG_use_function_exit = False
282+
FLAG_end_emitted = False
283+
284+
285+
# ------------------------------------------------------------------
286+
# Typecast conversions
287+
# ------------------------------------------------------------------
288+
289+
290+
def get_bytes(elements: List[str]) -> List[str]:
291+
"""Returns a list a default set of bytes/words in hexadecimal
292+
(starting with an hex number) or literals (starting with #).
293+
Numeric values with more than 2 digits represents a WORD (2 bytes) value.
294+
E.g. '01' => 01h, '001' => 1, 0 bytes (0001h)
295+
Literal values starts with # (1 byte) or ## (2 bytes)
296+
E.g. '#label + 1' => (label + 1) & 0xFF
297+
'##(label + 1)' => (label + 1) & 0xFFFF
298+
"""
299+
output = []
300+
301+
for x in elements:
302+
if x.startswith("##"): # 2-byte literal
303+
output.append("({}) & 0xFF".format(x[2:]))
304+
output.append("(({}) >> 8) & 0xFF".format(x[2:]))
305+
continue
306+
307+
if x.startswith("#"): # 1-byte literal
308+
output.append("({}) & 0xFF".format(x[1:]))
309+
continue
310+
311+
# must be an hex number
312+
assert RE_HEXA.match(x), 'expected an hex number, got "%s"' % x
313+
output.append("%02X" % int(x[-2:], 16))
314+
if len(x) > 2:
315+
output.append("%02X" % int(x[-4:-2:], 16))
316+
317+
return output
318+
319+
320+
def get_bytes_size(elements: List[str]) -> int:
321+
"""Defines a memory space with a default set of bytes/words in hexadecimal
322+
(starting with an hex number) or literals (starting with #).
323+
Numeric values with more than 2 digits represents a WORD (2 bytes) value.
324+
E.g. '01' => 01h, '001' => 1, 0 bytes (0001h)
325+
Literal values starts with # (1 byte) or ## (2 bytes)
326+
E.g. '#label + 1' => (label + 1) & 0xFF
327+
'##(label + 1)' => (label + 1) & 0xFFFF
328+
"""
329+
return len(get_bytes(elements))
330+
331+
332+
def to_byte(stype):
333+
"""Returns the instruction sequence for converting from
334+
the given type to byte.
335+
"""
336+
output = []
337+
338+
if stype in ("i8", "u8"):
339+
return []
340+
341+
if is_int_type(stype):
342+
output.append("ld a, l")
343+
elif stype == "f16":
344+
output.append("ld a, e")
345+
elif stype == "f": # Converts C ED LH to byte
346+
output.append(runtime_call(RuntimeLabel.FTOU32REG))
347+
output.append("ld a, l")
348+
349+
return output
350+
351+
352+
def to_word(stype):
353+
"""Returns the instruction sequence for converting the given
354+
type stored in DE,HL to word (unsigned) HL.
355+
"""
356+
output = [] # List of instructions
357+
358+
if stype == "u8": # Byte to word
359+
output.append("ld l, a")
360+
output.append("ld h, 0")
361+
362+
elif stype == "i8": # Signed byte to word
363+
output.append("ld l, a")
364+
output.append("add a, a")
365+
output.append("sbc a, a")
366+
output.append("ld h, a")
367+
368+
elif stype == "f16": # Must MOVE HL into DE
369+
output.append("ex de, hl")
370+
371+
elif stype == "f":
372+
output.append(runtime_call(RuntimeLabel.FTOU32REG))
373+
374+
return output
375+
376+
377+
def to_long(stype):
378+
"""Returns the instruction sequence for converting the given
379+
type stored in DE,HL to long (DE, HL).
380+
"""
381+
output = [] # List of instructions
382+
383+
if stype in ("i8", "u8", "f16"): # Byte to word
384+
output = to_word(stype)
385+
386+
if stype != "f16": # If its a byte, just copy H to D,E
387+
output.append("ld e, h")
388+
output.append("ld d, h")
389+
390+
if stype in ("i16", "f16"): # Signed byte or fixed to word
391+
output.append("ld a, h")
392+
output.append("add a, a")
393+
output.append("sbc a, a")
394+
output.append("ld e, a")
395+
output.append("ld d, a")
396+
397+
elif stype == "u16":
398+
output.append("ld de, 0")
399+
400+
elif stype == "f":
401+
output.append(runtime_call(RuntimeLabel.FTOU32REG))
402+
403+
return output
404+
405+
406+
def to_fixed(stype):
407+
"""Returns the instruction sequence for converting the given
408+
type stored in DE,HL to fixed DE,HL.
409+
"""
410+
output = [] # List of instructions
411+
412+
if is_int_type(stype):
413+
output = to_word(stype)
414+
output.append("ex de, hl")
415+
output.append("ld hl, 0") # 'Truncate' the fixed point
416+
elif stype == "f":
417+
output.append(runtime_call(RuntimeLabel.FTOF16REG))
418+
419+
return output
420+
421+
422+
def to_float(stype: str) -> List[str]:
423+
"""Returns the instruction sequence for converting the given
424+
type stored in DE,HL to fixed DE,HL.
425+
"""
426+
output: List[str] = [] # List of instructions
427+
428+
if stype == "f":
429+
return output # Nothing to do
430+
431+
if stype == "f16":
432+
output.append(runtime_call(RuntimeLabel.F16TOFREG))
433+
return output
434+
435+
# If we reach this point, it's an integer type
436+
if stype == "u8":
437+
output.append(runtime_call(RuntimeLabel.U8TOFREG))
438+
elif stype == "i8":
439+
output.append(runtime_call(RuntimeLabel.I8TOFREG))
440+
else:
441+
output = to_long(stype)
442+
if stype in ("i16", "i32"):
443+
output.append(runtime_call(RuntimeLabel.I32TOFREG))
444+
else:
445+
output.append(runtime_call(RuntimeLabel.U32TOFREG))
446+
447+
return output
448+
449+
450+
def new_ASMID():
451+
"""Returns a new unique ASM block id"""
452+
453+
result = "##ASM%i" % src.arch.z80.backend.common.ASMCOUNT
454+
src.arch.z80.backend.common.ASMCOUNT += 1
455+
return result

0 commit comments

Comments
 (0)