|
1 | | -#!/usr/bin/env python |
2 | | -# -*- coding: utf-8 -*- |
3 | | -# vim: ts=4:et:sw=4 |
| 1 | +from typing import Any, NamedTuple |
4 | 2 |
|
5 | | -from src.zxbasm.z80 import Z80SET |
6 | | -from src.api.errors import Error |
7 | | -import re |
8 | | - |
9 | | -# Reg. Exp. for counting N args in an asm mnemonic |
10 | | -ARGre = re.compile(r'\bN+\b') |
11 | | - |
12 | | -Z80_8REGS = ('A', 'B', 'C', 'D', 'E', 'H', 'L', |
13 | | - 'IXh', 'IYh', 'IXl', 'IYl', 'I', 'R') |
14 | | - |
15 | | -Z80_16REGS = {'AF': ('A', 'F'), 'BC': ('B', 'C'), 'DE': ('D', 'E'), |
16 | | - 'HL': ('H', 'L'), 'SP': (), |
17 | | - 'IX': ('IXh', 'IXl'), 'IY': ('IYh', 'IYl') |
18 | | - } |
19 | | - |
20 | | - |
21 | | -def num2bytes(x, bytes_): |
22 | | - """ Returns x converted to a little-endian t-uple of bytes. |
23 | | - E.g. num2bytes(255, 4) = (255, 0, 0, 0) |
24 | | - """ |
25 | | - if not isinstance(x, int): # If it is another "thing", just return ZEROs |
26 | | - return tuple([0] * bytes_) |
| 3 | +from src.api import errmsg |
| 4 | +from src.api import global_ as gl |
27 | 5 |
|
28 | | - x = x & ((2 << (bytes_ * 8)) - 1) # mask the initial value |
29 | | - result = () |
30 | | - |
31 | | - for i in range(bytes_): |
32 | | - result += (x & 0xFF,) |
33 | | - x >>= 8 |
34 | | - |
35 | | - return result |
| 6 | +from src.api.errors import Error |
| 7 | +from src.zxbasm.expr import Expr |
| 8 | +from src.zxbasm.asm_instruction import AsmInstruction |
36 | 9 |
|
37 | 10 |
|
38 | | -class InvalidMnemonicError(Error): |
39 | | - """ Exception raised when an invalid Mnemonic has been emitted. |
| 11 | +class Container(NamedTuple): |
| 12 | + """ Single class container |
40 | 13 | """ |
| 14 | + item: Any |
| 15 | + lineno: int |
41 | 16 |
|
42 | | - def __init__(self, mnemo): |
43 | | - self.msg = "Invalid mnemonic '%s'" % mnemo |
44 | | - self.mnemo = mnemo |
45 | 17 |
|
| 18 | +class Asm(AsmInstruction): |
| 19 | + """ Class extension to AsmInstruction with a short name :-P |
| 20 | + and will trap some exceptions and convert them to error msgs. |
46 | 21 |
|
47 | | -class InvalidArgError(Error): |
48 | | - """ Exception raised when an invalid argument has been emitted. |
| 22 | + It will also record source line |
49 | 23 | """ |
50 | 24 |
|
51 | | - def __init__(self, arg): |
52 | | - self.msg = "Invalid argument '%s'. It must be an integer." % str(arg) |
53 | | - self.mnemo = arg |
| 25 | + def __init__(self, lineno, asm, arg=None): |
| 26 | + self.lineno = lineno |
54 | 27 |
|
| 28 | + if asm not in ('DEFB', 'DEFS', 'DEFW'): |
| 29 | + try: |
| 30 | + super().__init__(asm, arg) |
| 31 | + except Error as v: |
| 32 | + errmsg.error(lineno, v.msg) |
| 33 | + return |
55 | 34 |
|
56 | | -class InternalMismatchSizeError(Error): |
57 | | - """ Exception raised when an invalid instruction length has been emitted. |
58 | | - """ |
59 | | - |
60 | | - def __init__(self, current_size, asm): |
61 | | - a = '' if current_size == 1 else 's' |
62 | | - b = '' if asm.size == 1 else 's' |
| 35 | + self.pending = len([x for x in self.arg if isinstance(x, Expr) and x.try_eval() is None]) > 0 |
63 | 36 |
|
64 | | - self.msg = ("Invalid instruction [%s] size (%i byte%s). " |
65 | | - "It should be %i byte%s." % (asm.asm, current_size, a, |
66 | | - asm.size, b)) |
67 | | - self.current_size = current_size |
68 | | - self.asm = asm |
| 37 | + if not self.pending: |
| 38 | + self.arg = self.argval() |
| 39 | + else: |
| 40 | + self.asm = asm |
| 41 | + self.pending = True |
69 | 42 |
|
| 43 | + if isinstance(arg, str): |
| 44 | + self.arg = tuple([Expr(Container(ord(x), lineno)) for x in arg]) |
| 45 | + else: |
| 46 | + self.arg = arg |
70 | 47 |
|
71 | | -class AsmInstruction: |
72 | | - """ Derives from Opcode. This one checks for opcode validity. |
73 | | - """ |
74 | | - def __init__(self, asm, arg=None): |
75 | | - """ Parses the given asm instruction and validates |
76 | | - it against the Z80SET table. Raises InvalidMnemonicError |
77 | | - if not valid. |
| 48 | + self.arg_num = len(self.arg) |
78 | 49 |
|
79 | | - It uses the Z80SET global dictionary. Args is an optional |
80 | | - argument (it can be a Label object or a value) |
| 50 | + def bytes(self) -> bytearray: |
| 51 | + """ Returns opcodes |
81 | 52 | """ |
82 | | - if isinstance(arg, list): |
83 | | - arg = tuple(arg) |
| 53 | + if self.asm not in ('DEFB', 'DEFS', 'DEFW'): |
| 54 | + if self.pending: |
| 55 | + tmp = self.arg # Saves current arg temporarily |
| 56 | + self.arg = (0, ) * self.arg_num |
| 57 | + result = super(Asm, self).bytes() |
| 58 | + self.arg = tmp # And recovers it |
84 | 59 |
|
85 | | - if arg is None: |
86 | | - arg = () |
| 60 | + return result |
87 | 61 |
|
88 | | - if arg is not None and not isinstance(arg, tuple): |
89 | | - arg = (arg,) |
| 62 | + return super(Asm, self).bytes() |
90 | 63 |
|
91 | | - asm = asm.split(';', 1) # Try to get comments out, if any |
92 | | - if len(asm) > 1: |
93 | | - self.comments = ';' + asm[1] |
94 | | - else: |
95 | | - self.comments = '' |
| 64 | + if self.asm == 'DEFB': |
| 65 | + if self.pending: |
| 66 | + return bytearray((0, ) * self.arg_num) |
96 | 67 |
|
97 | | - asm = asm[0] |
| 68 | + return bytearray(x & 0xFF for x in self.argval()) |
98 | 69 |
|
99 | | - self.mnemo = asm.upper() |
100 | | - if self.mnemo not in Z80SET.keys(): |
101 | | - raise InvalidMnemonicError(asm) |
| 70 | + if self.asm == 'DEFS': |
| 71 | + if self.pending: |
| 72 | + N = self.arg[0] |
| 73 | + if isinstance(N, Expr): |
| 74 | + N = N.eval() |
| 75 | + return (0, ) * N |
102 | 76 |
|
103 | | - Z80 = Z80SET[self.mnemo] |
| 77 | + args = self.argval() |
| 78 | + arg0 = args[0] |
| 79 | + arg1 = args[1] |
| 80 | + assert isinstance(arg0, int) |
| 81 | + assert isinstance(arg1, int) |
104 | 82 |
|
105 | | - self.asm = asm |
106 | | - self.size = Z80.size |
107 | | - self.T = Z80.T |
108 | | - self.opcode = Z80.opcode |
109 | | - self.argbytes = tuple([len(x) for x in ARGre.findall(asm)]) |
110 | | - self.arg = arg |
111 | | - self.arg_num = len(ARGre.findall(asm)) |
| 83 | + if arg1 > 255: |
| 84 | + errmsg.warning_value_will_be_truncated(self.lineno) |
| 85 | + num = arg1 & 0xFF |
| 86 | + return bytearray((num, ) * arg0) |
112 | 87 |
|
113 | | - def argval(self): |
114 | | - """ Returns the value of the arg (if any) or None. |
115 | | - If the arg. is not an integer, an error be triggered. |
116 | | - """ |
117 | | - if self.arg is None or any(x is None for x in self.arg): |
118 | | - return None |
| 88 | + if self.pending: # DEFW |
| 89 | + return bytearray((0, ) * 2 * self.arg_num) |
119 | 90 |
|
120 | | - for x in self.arg: |
121 | | - if not isinstance(x, int): |
122 | | - raise InvalidArgError(self.arg) |
| 91 | + result = bytearray() |
| 92 | + for i in self.argval(): |
| 93 | + x = i & 0xFFFF |
| 94 | + result.extend([x & 0xFF, x >> 8]) |
123 | 95 |
|
124 | | - return self.arg |
| 96 | + return bytearray(result) |
125 | 97 |
|
126 | | - def bytes(self) -> bytearray: |
127 | | - """ Returns a t-uple with instruction bytes (integers) |
| 98 | + def argval(self): |
| 99 | + """ Solve args values or raise errors if not |
| 100 | + defined yet |
128 | 101 | """ |
129 | | - result = [] |
130 | | - op = self.opcode.split(' ') |
131 | | - argi = 0 |
132 | | - |
133 | | - while op: |
134 | | - q = op.pop(0) |
| 102 | + if gl.has_errors: |
| 103 | + return [None] |
135 | 104 |
|
136 | | - if q == 'XX': |
137 | | - for k in range(self.argbytes[argi] - 1): |
138 | | - op.pop(0) |
| 105 | + if self.asm in ('DEFB', 'DEFS', 'DEFW'): |
| 106 | + result = tuple([x.eval() if isinstance(x, Expr) else x for x in self.arg]) |
| 107 | + if self.asm == 'DEFB' and any(x > 255 for x in result): |
| 108 | + errmsg.warning_value_will_be_truncated(self.lineno) |
| 109 | + return result |
139 | 110 |
|
140 | | - result.extend(num2bytes(self.argval()[argi], self.argbytes[argi])) |
141 | | - argi += 1 |
142 | | - else: |
143 | | - result.append(int(q, 16)) # Add opcode |
144 | | - |
145 | | - if len(result) != self.size: |
146 | | - raise InternalMismatchSizeError(len(result), self) |
| 111 | + self.arg = tuple([x if not isinstance(x, Expr) else x.eval() for x in self.arg]) |
| 112 | + if gl.has_errors: |
| 113 | + return [None] |
147 | 114 |
|
148 | | - return bytearray(result) |
| 115 | + if self.asm.split(' ')[0] in ('JR', 'DJNZ'): # A relative jump? |
| 116 | + if self.arg[0] < -128 or self.arg[0] > 127: |
| 117 | + errmsg.error(self.lineno, 'Relative jump out of range') |
| 118 | + return [None] |
149 | 119 |
|
150 | | - def __str__(self): |
151 | | - return self.asm |
| 120 | + return super(Asm, self).argval() |
0 commit comments