| 
									
										
										
										
											2023-03-21 01:26:03 +09:00
										 |  |  | import binascii | 
					
						
							|  |  |  | from typing import Optional, Dict, ItemsView, List, Union, Tuple | 
					
						
							|  |  |  | import unicodedata | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | from . import utils | 
					
						
							|  |  |  | import re | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | REGS8 = {"A": 7, "B": 0, "C": 1, "D": 2, "E": 3, "H": 4, "L": 5, "[HL]": 6} | 
					
						
							|  |  |  | REGS16A = {"BC": 0, "DE": 1, "HL": 2, "SP": 3} | 
					
						
							|  |  |  | REGS16B = {"BC": 0, "DE": 1, "HL": 2, "AF": 3} | 
					
						
							|  |  |  | FLAGS = {"NZ": 0x00, "Z": 0x08, "NC": 0x10, "C": 0x18} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | CONST_MAP: Dict[str, int] = {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class ExprBase: | 
					
						
							|  |  |  |     def asReg8(self) -> Optional[int]: | 
					
						
							|  |  |  |         return None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def isA(self, kind: str, value: Optional[str] = None) -> bool: | 
					
						
							|  |  |  |         return False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class Token(ExprBase): | 
					
						
							|  |  |  |     def __init__(self, kind: str, value: Union[str, int], line_nr: int) -> None: | 
					
						
							|  |  |  |         self.kind = kind | 
					
						
							|  |  |  |         self.value = value | 
					
						
							|  |  |  |         self.line_nr = line_nr | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def isA(self, kind: str, value: Optional[str] = None) -> bool: | 
					
						
							|  |  |  |         return self.kind == kind and (value is None or value == self.value) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __repr__(self) -> str: | 
					
						
							|  |  |  |         return "[%s:%s:%d]" % (self.kind, self.value, self.line_nr) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def asReg8(self) -> Optional[int]: | 
					
						
							|  |  |  |         if self.kind == 'ID': | 
					
						
							|  |  |  |             return REGS8.get(str(self.value), None) | 
					
						
							|  |  |  |         return None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class REF(ExprBase): | 
					
						
							|  |  |  |     def __init__(self, expr: ExprBase) -> None: | 
					
						
							|  |  |  |         self.expr = expr | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def asReg8(self) -> Optional[int]: | 
					
						
							|  |  |  |         if self.expr.isA('ID', 'HL'): | 
					
						
							|  |  |  |             return REGS8['[HL]'] | 
					
						
							|  |  |  |         return None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __repr__(self) -> str: | 
					
						
							|  |  |  |         return "[%s]" % (self.expr) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class OP(ExprBase): | 
					
						
							|  |  |  |     def __init__(self, op: str, left: ExprBase, right: Optional[ExprBase] = None): | 
					
						
							|  |  |  |         self.op = op | 
					
						
							|  |  |  |         self.left = left | 
					
						
							|  |  |  |         self.right = right | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __repr__(self) -> str: | 
					
						
							|  |  |  |         return "%s %s %s" % (self.left, self.op, self.right) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @staticmethod | 
					
						
							|  |  |  |     def make(op: str, left: ExprBase, right: Optional[ExprBase] = None) -> ExprBase: | 
					
						
							|  |  |  |         if left.isA('NUMBER') and right is not None and right.isA('NUMBER'): | 
					
						
							|  |  |  |             assert isinstance(right, Token) and isinstance(right.value, int) | 
					
						
							|  |  |  |             assert isinstance(left, Token) and isinstance(left.value, int) | 
					
						
							|  |  |  |             if op == '+': | 
					
						
							|  |  |  |                 left.value += right.value | 
					
						
							|  |  |  |                 return left | 
					
						
							|  |  |  |             if op == '-': | 
					
						
							|  |  |  |                 left.value -= right.value | 
					
						
							|  |  |  |                 return left | 
					
						
							|  |  |  |             if op == '*': | 
					
						
							|  |  |  |                 left.value *= right.value | 
					
						
							|  |  |  |                 return left | 
					
						
							|  |  |  |             if op == '/': | 
					
						
							|  |  |  |                 left.value //= right.value | 
					
						
							|  |  |  |                 return left | 
					
						
							|  |  |  |         if left.isA('NUMBER') and right is None: | 
					
						
							|  |  |  |             assert isinstance(left, Token) and isinstance(left.value, int) | 
					
						
							|  |  |  |             if op == '+': | 
					
						
							|  |  |  |                 return left | 
					
						
							|  |  |  |             if op == '-': | 
					
						
							|  |  |  |                 left.value = -left.value | 
					
						
							|  |  |  |                 return left | 
					
						
							|  |  |  |         return OP(op, left, right) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class Tokenizer: | 
					
						
							|  |  |  |     TOKEN_REGEX = re.compile('|'.join('(?P<%s>%s)' % pair for pair in [ | 
					
						
							|  |  |  |         ('NUMBER', r'\d+(\.\d*)?'), | 
					
						
							|  |  |  |         ('HEX', r'\$[0-9A-Fa-f]+'), | 
					
						
							|  |  |  |         ('ASSIGN', r':='), | 
					
						
							|  |  |  |         ('COMMENT', r';[^\n]+'), | 
					
						
							|  |  |  |         ('LABEL', r':'), | 
					
						
							|  |  |  |         ('DIRECTIVE', r'#[A-Za-z_]+'), | 
					
						
							|  |  |  |         ('STRING', '[a-zA-Z]?"[^"]*"'), | 
					
						
							|  |  |  |         ('ID', r'\.?[A-Za-z_][A-Za-z0-9_\.]*'), | 
					
						
							|  |  |  |         ('OP', r'[+\-*/,\(\)]'), | 
					
						
							|  |  |  |         ('REFOPEN', r'\['), | 
					
						
							|  |  |  |         ('REFCLOSE', r'\]'), | 
					
						
							|  |  |  |         ('NEWLINE', r'\n'), | 
					
						
							|  |  |  |         ('SKIP', r'[ \t]+'), | 
					
						
							|  |  |  |         ('MISMATCH', r'.'), | 
					
						
							|  |  |  |     ])) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, code: str) -> None: | 
					
						
							|  |  |  |         self.__tokens: List[Token] = [] | 
					
						
							|  |  |  |         line_num = 1 | 
					
						
							|  |  |  |         for mo in self.TOKEN_REGEX.finditer(code): | 
					
						
							|  |  |  |             kind = mo.lastgroup | 
					
						
							|  |  |  |             assert kind is not None | 
					
						
							|  |  |  |             value: Union[str, int] = mo.group() | 
					
						
							|  |  |  |             if kind == 'MISMATCH': | 
					
						
							| 
									
										
										
										
											2023-04-06 11:06:34 -07:00
										 |  |  |                 line = code.split("\n")[line_num - 1] | 
					
						
							|  |  |  |                 raise RuntimeError(f"Syntax error on line: {line_num}: {kind}:`{line}`") | 
					
						
							| 
									
										
										
										
											2023-03-21 01:26:03 +09:00
										 |  |  |             elif kind == 'SKIP': | 
					
						
							|  |  |  |                 pass | 
					
						
							|  |  |  |             elif kind == 'COMMENT': | 
					
						
							|  |  |  |                 pass | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 if kind == 'NUMBER': | 
					
						
							|  |  |  |                     value = int(value) | 
					
						
							|  |  |  |                 elif kind == 'HEX': | 
					
						
							|  |  |  |                     value = int(str(value)[1:], 16) | 
					
						
							|  |  |  |                     kind = 'NUMBER' | 
					
						
							|  |  |  |                 elif kind == 'ID': | 
					
						
							|  |  |  |                     value = str(value).upper() | 
					
						
							|  |  |  |                 self.__tokens.append(Token(kind, value, line_num)) | 
					
						
							|  |  |  |                 if kind == 'NEWLINE': | 
					
						
							|  |  |  |                     line_num += 1 | 
					
						
							|  |  |  |         self.__tokens.append(Token('NEWLINE', '\n', line_num)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def peek(self) -> Token: | 
					
						
							|  |  |  |         return self.__tokens[0] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def pop(self) -> Token: | 
					
						
							|  |  |  |         return self.__tokens.pop(0) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def expect(self, kind: str, value: Optional[str] = None) -> None: | 
					
						
							|  |  |  |         pop = self.pop() | 
					
						
							|  |  |  |         if not pop.isA(kind, value): | 
					
						
							|  |  |  |             if value is not None: | 
					
						
							|  |  |  |                 raise SyntaxError("%s != %s:%s" % (pop, kind, value)) | 
					
						
							|  |  |  |             raise SyntaxError("%s != %s" % (pop, kind)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __bool__(self) -> bool: | 
					
						
							|  |  |  |         return bool(self.__tokens) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class Assembler: | 
					
						
							|  |  |  |     SIMPLE_INSTR = { | 
					
						
							|  |  |  |         'NOP':  0x00, | 
					
						
							|  |  |  |         'RLCA': 0x07, | 
					
						
							|  |  |  |         'RRCA': 0x0F, | 
					
						
							|  |  |  |         'STOP': 0x010, | 
					
						
							|  |  |  |         'RLA':  0x17, | 
					
						
							|  |  |  |         'RRA':  0x1F, | 
					
						
							|  |  |  |         'DAA':  0x27, | 
					
						
							|  |  |  |         'CPL':  0x2F, | 
					
						
							|  |  |  |         'SCF':  0x37, | 
					
						
							|  |  |  |         'CCF':  0x3F, | 
					
						
							|  |  |  |         'HALT': 0x76, | 
					
						
							|  |  |  |         'RETI': 0xD9, | 
					
						
							|  |  |  |         'DI':   0xF3, | 
					
						
							|  |  |  |         'EI':   0xFB, | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     LINK_REL8 = 0 | 
					
						
							|  |  |  |     LINK_ABS8 = 1 | 
					
						
							|  |  |  |     LINK_ABS16 = 2 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, base_address: Optional[int] = None) -> None: | 
					
						
							|  |  |  |         self.__base_address = base_address or -1 | 
					
						
							|  |  |  |         self.__result = bytearray() | 
					
						
							|  |  |  |         self.__label: Dict[str, int] = {} | 
					
						
							|  |  |  |         self.__constant: Dict[str, int] = {} | 
					
						
							|  |  |  |         self.__link: Dict[int, Tuple[int, ExprBase]] = {} | 
					
						
							|  |  |  |         self.__scope: Optional[str] = None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.__tok = Tokenizer("") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def process(self, code: str) -> None: | 
					
						
							|  |  |  |         conditional_stack = [True] | 
					
						
							|  |  |  |         self.__tok = Tokenizer(code) | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             while self.__tok: | 
					
						
							|  |  |  |                 start = self.__tok.pop() | 
					
						
							|  |  |  |                 if start.kind == 'NEWLINE': | 
					
						
							|  |  |  |                     pass  # Empty newline | 
					
						
							|  |  |  |                 elif start.kind == 'DIRECTIVE': | 
					
						
							|  |  |  |                     if start.value == '#IF': | 
					
						
							|  |  |  |                         t = self.parseExpression() | 
					
						
							|  |  |  |                         assert isinstance(t, Token) | 
					
						
							|  |  |  |                         conditional_stack.append(conditional_stack[-1] and t.value != 0) | 
					
						
							|  |  |  |                         self.__tok.expect('NEWLINE') | 
					
						
							|  |  |  |                     elif start.value == '#ELSE': | 
					
						
							|  |  |  |                         conditional_stack[-1] = not conditional_stack[-1] and conditional_stack[-2] | 
					
						
							|  |  |  |                         self.__tok.expect('NEWLINE') | 
					
						
							|  |  |  |                     elif start.value == '#ENDIF': | 
					
						
							|  |  |  |                         conditional_stack.pop() | 
					
						
							|  |  |  |                         assert conditional_stack | 
					
						
							|  |  |  |                         self.__tok.expect('NEWLINE') | 
					
						
							|  |  |  |                     else: | 
					
						
							|  |  |  |                         raise SyntaxError(start) | 
					
						
							|  |  |  |                 elif not conditional_stack[-1]: | 
					
						
							|  |  |  |                     while not self.__tok.pop().isA('NEWLINE'): | 
					
						
							|  |  |  |                         pass | 
					
						
							|  |  |  |                 elif start.kind == 'ID': | 
					
						
							|  |  |  |                     if start.value == 'DB': | 
					
						
							|  |  |  |                         self.instrDB() | 
					
						
							|  |  |  |                         self.__tok.expect('NEWLINE') | 
					
						
							|  |  |  |                     elif start.value == 'DW': | 
					
						
							|  |  |  |                         self.instrDW() | 
					
						
							|  |  |  |                         self.__tok.expect('NEWLINE') | 
					
						
							|  |  |  |                     elif start.value == 'LD': | 
					
						
							|  |  |  |                         self.instrLD() | 
					
						
							|  |  |  |                         self.__tok.expect('NEWLINE') | 
					
						
							|  |  |  |                     elif start.value == 'LDH': | 
					
						
							|  |  |  |                         self.instrLDH() | 
					
						
							|  |  |  |                         self.__tok.expect('NEWLINE') | 
					
						
							|  |  |  |                     elif start.value == 'LDI': | 
					
						
							|  |  |  |                         self.instrLDI() | 
					
						
							|  |  |  |                         self.__tok.expect('NEWLINE') | 
					
						
							|  |  |  |                     elif start.value == 'LDD': | 
					
						
							|  |  |  |                         self.instrLDD() | 
					
						
							|  |  |  |                         self.__tok.expect('NEWLINE') | 
					
						
							|  |  |  |                     elif start.value == 'INC': | 
					
						
							|  |  |  |                         self.instrINC() | 
					
						
							|  |  |  |                         self.__tok.expect('NEWLINE') | 
					
						
							|  |  |  |                     elif start.value == 'DEC': | 
					
						
							|  |  |  |                         self.instrDEC() | 
					
						
							|  |  |  |                         self.__tok.expect('NEWLINE') | 
					
						
							|  |  |  |                     elif start.value == 'ADD': | 
					
						
							|  |  |  |                         self.instrADD() | 
					
						
							|  |  |  |                         self.__tok.expect('NEWLINE') | 
					
						
							|  |  |  |                     elif start.value == 'ADC': | 
					
						
							|  |  |  |                         self.instrALU(0x88) | 
					
						
							|  |  |  |                         self.__tok.expect('NEWLINE') | 
					
						
							|  |  |  |                     elif start.value == 'SUB': | 
					
						
							|  |  |  |                         self.instrALU(0x90) | 
					
						
							|  |  |  |                         self.__tok.expect('NEWLINE') | 
					
						
							|  |  |  |                     elif start.value == 'SBC': | 
					
						
							|  |  |  |                         self.instrALU(0x98) | 
					
						
							|  |  |  |                         self.__tok.expect('NEWLINE') | 
					
						
							|  |  |  |                     elif start.value == 'AND': | 
					
						
							|  |  |  |                         self.instrALU(0xA0) | 
					
						
							|  |  |  |                         self.__tok.expect('NEWLINE') | 
					
						
							|  |  |  |                     elif start.value == 'XOR': | 
					
						
							|  |  |  |                         self.instrALU(0xA8) | 
					
						
							|  |  |  |                         self.__tok.expect('NEWLINE') | 
					
						
							|  |  |  |                     elif start.value == 'OR': | 
					
						
							|  |  |  |                         self.instrALU(0xB0) | 
					
						
							|  |  |  |                         self.__tok.expect('NEWLINE') | 
					
						
							|  |  |  |                     elif start.value == 'CP': | 
					
						
							|  |  |  |                         self.instrALU(0xB8) | 
					
						
							|  |  |  |                         self.__tok.expect('NEWLINE') | 
					
						
							|  |  |  |                     elif start.value == 'BIT': | 
					
						
							|  |  |  |                         self.instrBIT(0x40) | 
					
						
							|  |  |  |                         self.__tok.expect('NEWLINE') | 
					
						
							|  |  |  |                     elif start.value == 'RES': | 
					
						
							|  |  |  |                         self.instrBIT(0x80) | 
					
						
							|  |  |  |                         self.__tok.expect('NEWLINE') | 
					
						
							|  |  |  |                     elif start.value == 'SET': | 
					
						
							|  |  |  |                         self.instrBIT(0xC0) | 
					
						
							|  |  |  |                         self.__tok.expect('NEWLINE') | 
					
						
							|  |  |  |                     elif start.value == 'RET': | 
					
						
							|  |  |  |                         self.instrRET() | 
					
						
							|  |  |  |                         self.__tok.expect('NEWLINE') | 
					
						
							|  |  |  |                     elif start.value == 'CALL': | 
					
						
							|  |  |  |                         self.instrCALL() | 
					
						
							|  |  |  |                         self.__tok.expect('NEWLINE') | 
					
						
							|  |  |  |                     elif start.value == 'RLC': | 
					
						
							|  |  |  |                         self.instrCB(0x00) | 
					
						
							|  |  |  |                         self.__tok.expect('NEWLINE') | 
					
						
							|  |  |  |                     elif start.value == 'RRC': | 
					
						
							|  |  |  |                         self.instrCB(0x08) | 
					
						
							|  |  |  |                         self.__tok.expect('NEWLINE') | 
					
						
							|  |  |  |                     elif start.value == 'RL': | 
					
						
							|  |  |  |                         self.instrCB(0x10) | 
					
						
							|  |  |  |                         self.__tok.expect('NEWLINE') | 
					
						
							|  |  |  |                     elif start.value == 'RR': | 
					
						
							|  |  |  |                         self.instrCB(0x18) | 
					
						
							|  |  |  |                         self.__tok.expect('NEWLINE') | 
					
						
							|  |  |  |                     elif start.value == 'SLA': | 
					
						
							|  |  |  |                         self.instrCB(0x20) | 
					
						
							|  |  |  |                         self.__tok.expect('NEWLINE') | 
					
						
							|  |  |  |                     elif start.value == 'SRA': | 
					
						
							|  |  |  |                         self.instrCB(0x28) | 
					
						
							|  |  |  |                         self.__tok.expect('NEWLINE') | 
					
						
							|  |  |  |                     elif start.value == 'SWAP': | 
					
						
							|  |  |  |                         self.instrCB(0x30) | 
					
						
							|  |  |  |                         self.__tok.expect('NEWLINE') | 
					
						
							|  |  |  |                     elif start.value == 'SRL': | 
					
						
							|  |  |  |                         self.instrCB(0x38) | 
					
						
							|  |  |  |                         self.__tok.expect('NEWLINE') | 
					
						
							|  |  |  |                     elif start.value == 'RST': | 
					
						
							|  |  |  |                         self.instrRST() | 
					
						
							|  |  |  |                         self.__tok.expect('NEWLINE') | 
					
						
							|  |  |  |                     elif start.value == 'JP': | 
					
						
							|  |  |  |                         self.instrJP() | 
					
						
							|  |  |  |                         self.__tok.expect('NEWLINE') | 
					
						
							|  |  |  |                     elif start.value == 'JR': | 
					
						
							|  |  |  |                         self.instrJR() | 
					
						
							|  |  |  |                         self.__tok.expect('NEWLINE') | 
					
						
							|  |  |  |                     elif start.value == 'PUSH': | 
					
						
							|  |  |  |                         self.instrPUSHPOP(0xC5) | 
					
						
							|  |  |  |                         self.__tok.expect('NEWLINE') | 
					
						
							|  |  |  |                     elif start.value == 'POP': | 
					
						
							|  |  |  |                         self.instrPUSHPOP(0xC1) | 
					
						
							|  |  |  |                         self.__tok.expect('NEWLINE') | 
					
						
							|  |  |  |                     elif start.value in self.SIMPLE_INSTR: | 
					
						
							|  |  |  |                         self.__result.append(self.SIMPLE_INSTR[str(start.value)]) | 
					
						
							|  |  |  |                         self.__tok.expect('NEWLINE') | 
					
						
							|  |  |  |                     elif self.__tok.peek().kind == 'LABEL': | 
					
						
							|  |  |  |                         self.__tok.pop() | 
					
						
							|  |  |  |                         self.addLabel(str(start.value)) | 
					
						
							|  |  |  |                     elif self.__tok.peek().kind == 'ASSIGN': | 
					
						
							|  |  |  |                         self.__tok.pop() | 
					
						
							|  |  |  |                         value = self.__tok.pop() | 
					
						
							|  |  |  |                         if value.kind != 'NUMBER': | 
					
						
							|  |  |  |                             raise SyntaxError(start) | 
					
						
							|  |  |  |                         self.addConstant(str(start.value), int(value.value)) | 
					
						
							|  |  |  |                     else: | 
					
						
							|  |  |  |                         raise SyntaxError(start) | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     raise SyntaxError(start) | 
					
						
							|  |  |  |         except SyntaxError: | 
					
						
							|  |  |  |             print("Syntax error on line: %s" % code.split("\n")[self.__tok.peek().line_nr-1]) | 
					
						
							|  |  |  |             raise | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def insert8(self, expr: ExprBase) -> None: | 
					
						
							|  |  |  |         if expr.isA('NUMBER'): | 
					
						
							|  |  |  |             assert isinstance(expr, Token) | 
					
						
							|  |  |  |             value = int(expr.value) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             self.__link[len(self.__result)] = (Assembler.LINK_ABS8, expr) | 
					
						
							|  |  |  |             value = 0 | 
					
						
							|  |  |  |         assert 0 <= value < 256 | 
					
						
							|  |  |  |         self.__result.append(value) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def insertRel8(self, expr: ExprBase) -> None: | 
					
						
							|  |  |  |         if expr.isA('NUMBER'): | 
					
						
							|  |  |  |             assert isinstance(expr, Token) | 
					
						
							|  |  |  |             self.__result.append(int(expr.value)) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             self.__link[len(self.__result)] = (Assembler.LINK_REL8, expr) | 
					
						
							|  |  |  |             self.__result.append(0x00) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def insert16(self, expr: ExprBase) -> None: | 
					
						
							|  |  |  |         if expr.isA('NUMBER'): | 
					
						
							|  |  |  |             assert isinstance(expr, Token) | 
					
						
							|  |  |  |             value = int(expr.value) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             self.__link[len(self.__result)] = (Assembler.LINK_ABS16, expr) | 
					
						
							|  |  |  |             value = 0 | 
					
						
							|  |  |  |         assert 0 <= value <= 0xFFFF | 
					
						
							|  |  |  |         self.__result.append(value & 0xFF) | 
					
						
							|  |  |  |         self.__result.append(value >> 8) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def insertString(self, string: str) -> None: | 
					
						
							|  |  |  |         if string.startswith('"') and string.endswith('"'): | 
					
						
							|  |  |  |             string = string[1:-1] | 
					
						
							|  |  |  |             string = unicodedata.normalize('NFKD', string) | 
					
						
							|  |  |  |             self.__result += string.encode("latin1", "ignore") | 
					
						
							|  |  |  |         elif string.startswith("m\"") and string.endswith("\""): | 
					
						
							|  |  |  |             self.__result += utils.formatText(string[2:-1].replace("|", "\n")) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             raise SyntaxError | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def instrLD(self) -> None: | 
					
						
							|  |  |  |         left_param = self.parseParam() | 
					
						
							|  |  |  |         self.__tok.expect('OP', ',') | 
					
						
							|  |  |  |         right_param = self.parseParam() | 
					
						
							|  |  |  |         lr8 = left_param.asReg8() | 
					
						
							|  |  |  |         rr8 = right_param.asReg8() | 
					
						
							|  |  |  |         if lr8 is not None and rr8 is not None: | 
					
						
							|  |  |  |             self.__result.append(0x40 | (lr8 << 3) | rr8) | 
					
						
							|  |  |  |         elif left_param.isA('ID', 'A') and isinstance(right_param, REF): | 
					
						
							|  |  |  |             if right_param.expr.isA('ID', 'BC'): | 
					
						
							|  |  |  |                 self.__result.append(0x0A) | 
					
						
							|  |  |  |             elif right_param.expr.isA('ID', 'DE'): | 
					
						
							|  |  |  |                 self.__result.append(0x1A) | 
					
						
							|  |  |  |             elif right_param.expr.isA('ID', 'HL+'):  # TODO | 
					
						
							|  |  |  |                 self.__result.append(0x2A) | 
					
						
							|  |  |  |             elif right_param.expr.isA('ID', 'HL-'):  # TODO | 
					
						
							|  |  |  |                 self.__result.append(0x3A) | 
					
						
							|  |  |  |             elif right_param.expr.isA('ID', 'C'): | 
					
						
							|  |  |  |                 self.__result.append(0xF2) | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 self.__result.append(0xFA) | 
					
						
							|  |  |  |                 self.insert16(right_param.expr) | 
					
						
							|  |  |  |         elif right_param.isA('ID', 'A') and isinstance(left_param, REF): | 
					
						
							|  |  |  |             if left_param.expr.isA('ID', 'BC'): | 
					
						
							|  |  |  |                 self.__result.append(0x02) | 
					
						
							|  |  |  |             elif left_param.expr.isA('ID', 'DE'): | 
					
						
							|  |  |  |                 self.__result.append(0x12) | 
					
						
							|  |  |  |             elif left_param.expr.isA('ID', 'HL+'):  # TODO | 
					
						
							|  |  |  |                 self.__result.append(0x22) | 
					
						
							|  |  |  |             elif left_param.expr.isA('ID', 'HL-'):  # TODO | 
					
						
							|  |  |  |                 self.__result.append(0x32) | 
					
						
							|  |  |  |             elif left_param.expr.isA('ID', 'C'): | 
					
						
							|  |  |  |                 self.__result.append(0xE2) | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 self.__result.append(0xEA) | 
					
						
							|  |  |  |                 self.insert16(left_param.expr) | 
					
						
							|  |  |  |         elif left_param.isA('ID', 'BC'): | 
					
						
							|  |  |  |             self.__result.append(0x01) | 
					
						
							|  |  |  |             self.insert16(right_param) | 
					
						
							|  |  |  |         elif left_param.isA('ID', 'DE'): | 
					
						
							|  |  |  |             self.__result.append(0x11) | 
					
						
							|  |  |  |             self.insert16(right_param) | 
					
						
							|  |  |  |         elif left_param.isA('ID', 'HL'): | 
					
						
							|  |  |  |             self.__result.append(0x21) | 
					
						
							|  |  |  |             self.insert16(right_param) | 
					
						
							|  |  |  |         elif left_param.isA('ID', 'SP'): | 
					
						
							|  |  |  |             if right_param.isA('ID', 'HL'): | 
					
						
							|  |  |  |                 self.__result.append(0xF9) | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 self.__result.append(0x31) | 
					
						
							|  |  |  |                 self.insert16(right_param) | 
					
						
							|  |  |  |         elif right_param.isA('ID', 'SP') and isinstance(left_param, REF): | 
					
						
							|  |  |  |             self.__result.append(0x08) | 
					
						
							|  |  |  |             self.insert16(left_param.expr) | 
					
						
							|  |  |  |         elif lr8 is not None: | 
					
						
							|  |  |  |             self.__result.append(0x06 | (lr8 << 3)) | 
					
						
							|  |  |  |             self.insert8(right_param) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             raise SyntaxError | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def instrLDH(self) -> None: | 
					
						
							|  |  |  |         left_param = self.parseParam() | 
					
						
							|  |  |  |         self.__tok.expect('OP', ',') | 
					
						
							|  |  |  |         right_param = self.parseParam() | 
					
						
							|  |  |  |         if left_param.isA('ID', 'A') and isinstance(right_param, REF): | 
					
						
							|  |  |  |             if right_param.expr.isA('ID', 'C'): | 
					
						
							|  |  |  |                 self.__result.append(0xF2) | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 self.__result.append(0xF0) | 
					
						
							|  |  |  |                 self.insert8(right_param.expr) | 
					
						
							|  |  |  |         elif right_param.isA('ID', 'A') and isinstance(left_param, REF): | 
					
						
							|  |  |  |             if left_param.expr.isA('ID', 'C'): | 
					
						
							|  |  |  |                 self.__result.append(0xE2) | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 self.__result.append(0xE0) | 
					
						
							|  |  |  |                 self.insert8(left_param.expr) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             raise SyntaxError | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def instrLDI(self) -> None: | 
					
						
							|  |  |  |         left_param = self.parseParam() | 
					
						
							|  |  |  |         self.__tok.expect('OP', ',') | 
					
						
							|  |  |  |         right_param = self.parseParam() | 
					
						
							|  |  |  |         if left_param.isA('ID', 'A') and isinstance(right_param, REF) and right_param.expr.isA('ID', 'HL'): | 
					
						
							|  |  |  |             self.__result.append(0x2A) | 
					
						
							|  |  |  |         elif right_param.isA('ID', 'A') and isinstance(left_param, REF) and left_param.expr.isA('ID', 'HL'): | 
					
						
							|  |  |  |             self.__result.append(0x22) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             raise SyntaxError | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def instrLDD(self) -> None: | 
					
						
							|  |  |  |         left_param = self.parseParam() | 
					
						
							|  |  |  |         self.__tok.expect('OP', ',') | 
					
						
							|  |  |  |         right_param = self.parseParam() | 
					
						
							|  |  |  |         if left_param.isA('ID', 'A') and isinstance(right_param, REF) and right_param.expr.isA('ID', 'HL'): | 
					
						
							|  |  |  |             self.__result.append(0x3A) | 
					
						
							|  |  |  |         elif right_param.isA('ID', 'A') and isinstance(left_param, REF) and left_param.expr.isA('ID', 'HL'): | 
					
						
							|  |  |  |             self.__result.append(0x32) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             raise SyntaxError | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def instrINC(self) -> None: | 
					
						
							|  |  |  |         param = self.parseParam() | 
					
						
							|  |  |  |         r8 = param.asReg8() | 
					
						
							|  |  |  |         if r8 is not None: | 
					
						
							|  |  |  |             self.__result.append(0x04 | (r8 << 3)) | 
					
						
							|  |  |  |         elif param.isA('ID', 'BC'): | 
					
						
							|  |  |  |             self.__result.append(0x03) | 
					
						
							|  |  |  |         elif param.isA('ID', 'DE'): | 
					
						
							|  |  |  |             self.__result.append(0x13) | 
					
						
							|  |  |  |         elif param.isA('ID', 'HL'): | 
					
						
							|  |  |  |             self.__result.append(0x23) | 
					
						
							|  |  |  |         elif param.isA('ID', 'SP'): | 
					
						
							|  |  |  |             self.__result.append(0x33) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             raise SyntaxError | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def instrDEC(self) -> None: | 
					
						
							|  |  |  |         param = self.parseParam() | 
					
						
							|  |  |  |         r8 = param.asReg8() | 
					
						
							|  |  |  |         if r8 is not None: | 
					
						
							|  |  |  |             self.__result.append(0x05 | (r8 << 3)) | 
					
						
							|  |  |  |         elif param.isA('ID', 'BC'): | 
					
						
							|  |  |  |             self.__result.append(0x0B) | 
					
						
							|  |  |  |         elif param.isA('ID', 'DE'): | 
					
						
							|  |  |  |             self.__result.append(0x1B) | 
					
						
							|  |  |  |         elif param.isA('ID', 'HL'): | 
					
						
							|  |  |  |             self.__result.append(0x2B) | 
					
						
							|  |  |  |         elif param.isA('ID', 'SP'): | 
					
						
							|  |  |  |             self.__result.append(0x3B) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             raise SyntaxError | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def instrADD(self) -> None: | 
					
						
							|  |  |  |         left_param = self.parseParam() | 
					
						
							|  |  |  |         self.__tok.expect('OP', ',') | 
					
						
							|  |  |  |         right_param = self.parseParam() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if left_param.isA('ID', 'A'): | 
					
						
							|  |  |  |             rr8 = right_param.asReg8() | 
					
						
							|  |  |  |             if rr8 is not None: | 
					
						
							|  |  |  |                 self.__result.append(0x80 | rr8) | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 self.__result.append(0xC6) | 
					
						
							|  |  |  |                 self.insert8(right_param) | 
					
						
							|  |  |  |         elif left_param.isA('ID', 'HL') and right_param.isA('ID') and isinstance(right_param, Token) and right_param.value in REGS16A: | 
					
						
							|  |  |  |             self.__result.append(0x09 | REGS16A[str(right_param.value)] << 4) | 
					
						
							|  |  |  |         elif left_param.isA('ID', 'SP'): | 
					
						
							|  |  |  |             self.__result.append(0xE8) | 
					
						
							|  |  |  |             self.insert8(right_param) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             raise SyntaxError | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def instrALU(self, code_value: int) -> None: | 
					
						
							|  |  |  |         param = self.parseParam() | 
					
						
							|  |  |  |         if param.isA('ID', 'A') and self.__tok.peek().isA('OP', ','): | 
					
						
							|  |  |  |             self.__tok.pop() | 
					
						
							|  |  |  |             param = self.parseParam() | 
					
						
							|  |  |  |         r8 = param.asReg8() | 
					
						
							|  |  |  |         if r8 is not None: | 
					
						
							|  |  |  |             self.__result.append(code_value | r8) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             self.__result.append(code_value | 0x46) | 
					
						
							|  |  |  |             self.insert8(param) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def instrRST(self) -> None: | 
					
						
							|  |  |  |         param = self.parseParam() | 
					
						
							|  |  |  |         if param.isA('NUMBER') and isinstance(param, Token) and (int(param.value) & ~0x38) == 0: | 
					
						
							|  |  |  |             self.__result.append(0xC7 | int(param.value)) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             raise SyntaxError | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def instrPUSHPOP(self, code_value: int) -> None: | 
					
						
							|  |  |  |         param = self.parseParam() | 
					
						
							|  |  |  |         if param.isA('ID') and isinstance(param, Token) and str(param.value) in REGS16B: | 
					
						
							|  |  |  |             self.__result.append(code_value | (REGS16B[str(param.value)] << 4)) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             raise SyntaxError | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def instrJR(self) -> None: | 
					
						
							|  |  |  |         param = self.parseParam() | 
					
						
							|  |  |  |         if self.__tok.peek().isA('OP', ','): | 
					
						
							|  |  |  |             self.__tok.pop() | 
					
						
							|  |  |  |             condition = param | 
					
						
							|  |  |  |             param = self.parseParam() | 
					
						
							|  |  |  |             if condition.isA('ID') and isinstance(condition, Token) and str(condition.value) in FLAGS: | 
					
						
							|  |  |  |                 self.__result.append(0x20 | FLAGS[str(condition.value)]) | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 raise SyntaxError | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             self.__result.append(0x18) | 
					
						
							|  |  |  |         self.insertRel8(param) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def instrCB(self, code_value: int) -> None: | 
					
						
							|  |  |  |         param = self.parseParam() | 
					
						
							|  |  |  |         r8 = param.asReg8() | 
					
						
							|  |  |  |         if r8 is not None: | 
					
						
							|  |  |  |             self.__result.append(0xCB) | 
					
						
							|  |  |  |             self.__result.append(code_value | r8) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             raise SyntaxError | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def instrBIT(self, code_value: int) -> None: | 
					
						
							|  |  |  |         left_param = self.parseParam() | 
					
						
							|  |  |  |         self.__tok.expect('OP', ',') | 
					
						
							|  |  |  |         right_param = self.parseParam() | 
					
						
							|  |  |  |         rr8 = right_param.asReg8() | 
					
						
							|  |  |  |         if left_param.isA('NUMBER') and isinstance(left_param, Token) and rr8 is not None: | 
					
						
							|  |  |  |             self.__result.append(0xCB) | 
					
						
							|  |  |  |             self.__result.append(code_value | (int(left_param.value) << 3) | rr8) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             raise SyntaxError | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def instrRET(self) -> None: | 
					
						
							|  |  |  |         if self.__tok.peek().isA('ID'): | 
					
						
							|  |  |  |             condition = self.__tok.pop() | 
					
						
							|  |  |  |             if condition.isA('ID') and condition.value in FLAGS: | 
					
						
							|  |  |  |                 self.__result.append(0xC0 | FLAGS[str(condition.value)]) | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 raise SyntaxError | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             self.__result.append(0xC9) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def instrCALL(self) -> None: | 
					
						
							|  |  |  |         param = self.parseParam() | 
					
						
							|  |  |  |         if self.__tok.peek().isA('OP', ','): | 
					
						
							|  |  |  |             self.__tok.pop() | 
					
						
							|  |  |  |             condition = param | 
					
						
							|  |  |  |             param = self.parseParam() | 
					
						
							|  |  |  |             if condition.isA('ID') and isinstance(condition, Token) and condition.value in FLAGS: | 
					
						
							|  |  |  |                 self.__result.append(0xC4 | FLAGS[str(condition.value)]) | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 raise SyntaxError | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             self.__result.append(0xCD) | 
					
						
							|  |  |  |         self.insert16(param) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def instrJP(self) -> None: | 
					
						
							|  |  |  |         param = self.parseParam() | 
					
						
							|  |  |  |         if self.__tok.peek().isA('OP', ','): | 
					
						
							|  |  |  |             self.__tok.pop() | 
					
						
							|  |  |  |             condition = param | 
					
						
							|  |  |  |             param = self.parseParam() | 
					
						
							|  |  |  |             if condition.isA('ID') and isinstance(condition, Token) and condition.value in FLAGS: | 
					
						
							|  |  |  |                 self.__result.append(0xC2 | FLAGS[str(condition.value)]) | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 raise SyntaxError | 
					
						
							|  |  |  |         elif param.isA('ID', 'HL'): | 
					
						
							|  |  |  |             self.__result.append(0xE9) | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             self.__result.append(0xC3) | 
					
						
							|  |  |  |         self.insert16(param) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def instrDW(self) -> None: | 
					
						
							|  |  |  |         param = self.parseExpression() | 
					
						
							|  |  |  |         self.insert16(param) | 
					
						
							|  |  |  |         while self.__tok.peek().isA('OP', ','): | 
					
						
							|  |  |  |             self.__tok.pop() | 
					
						
							|  |  |  |             param = self.parseExpression() | 
					
						
							|  |  |  |             self.insert16(param) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def instrDB(self) -> None: | 
					
						
							|  |  |  |         param = self.parseExpression() | 
					
						
							|  |  |  |         if param.isA('STRING'): | 
					
						
							|  |  |  |             assert isinstance(param, Token) | 
					
						
							|  |  |  |             self.insertString(str(param.value)) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             self.insert8(param) | 
					
						
							|  |  |  |         while self.__tok.peek().isA('OP', ','): | 
					
						
							|  |  |  |             self.__tok.pop() | 
					
						
							|  |  |  |             param = self.parseExpression() | 
					
						
							|  |  |  |             if param.isA('STRING'): | 
					
						
							|  |  |  |                 assert isinstance(param, Token) | 
					
						
							|  |  |  |                 self.insertString(str(param.value)) | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 self.insert8(param) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def addLabel(self, label: str) -> None: | 
					
						
							|  |  |  |         if label.startswith("."): | 
					
						
							|  |  |  |             assert self.__scope is not None | 
					
						
							|  |  |  |             label = self.__scope + label | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             assert "." not in label, label | 
					
						
							|  |  |  |             self.__scope = label | 
					
						
							|  |  |  |         assert label not in self.__label, "Duplicate label: %s" % (label) | 
					
						
							|  |  |  |         assert label not in self.__constant, "Duplicate label: %s" % (label) | 
					
						
							|  |  |  |         self.__label[label] = len(self.__result) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def addConstant(self, name: str, value: int) -> None: | 
					
						
							|  |  |  |         assert name not in self.__constant, "Duplicate constant: %s" % (name) | 
					
						
							|  |  |  |         assert name not in self.__label, "Duplicate constant: %s" % (name) | 
					
						
							|  |  |  |         self.__constant[name] = value | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def parseParam(self) -> ExprBase: | 
					
						
							|  |  |  |         t = self.__tok.peek() | 
					
						
							|  |  |  |         if t.kind == 'REFOPEN': | 
					
						
							|  |  |  |             self.__tok.pop() | 
					
						
							|  |  |  |             expr = self.parseExpression() | 
					
						
							|  |  |  |             self.__tok.expect('REFCLOSE') | 
					
						
							|  |  |  |             return REF(expr) | 
					
						
							|  |  |  |         return self.parseExpression() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def parseExpression(self) -> ExprBase: | 
					
						
							|  |  |  |         t = self.parseAddSub() | 
					
						
							|  |  |  |         return t | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def parseAddSub(self) -> ExprBase: | 
					
						
							|  |  |  |         t = self.parseFactor() | 
					
						
							|  |  |  |         p = self.__tok.peek() | 
					
						
							|  |  |  |         if p.isA('OP', '+') or p.isA('OP', '-'): | 
					
						
							|  |  |  |             self.__tok.pop() | 
					
						
							|  |  |  |             return OP.make(str(p.value), t, self.parseAddSub()) | 
					
						
							|  |  |  |         return t | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def parseFactor(self) -> ExprBase: | 
					
						
							|  |  |  |         t = self.parseUnary() | 
					
						
							|  |  |  |         p = self.__tok.peek() | 
					
						
							|  |  |  |         if p.isA('OP', '*') or p.isA('OP', '/'): | 
					
						
							|  |  |  |             self.__tok.pop() | 
					
						
							|  |  |  |             return OP.make(str(p.value), t, self.parseFactor()) | 
					
						
							|  |  |  |         return t | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def parseUnary(self) -> ExprBase: | 
					
						
							|  |  |  |         t = self.__tok.pop() | 
					
						
							|  |  |  |         if t.isA('OP', '-') or t.isA('OP', '+'): | 
					
						
							|  |  |  |             return OP.make(str(t.value), self.parseUnary()) | 
					
						
							|  |  |  |         elif t.isA('OP', '('): | 
					
						
							|  |  |  |             result = self.parseExpression() | 
					
						
							|  |  |  |             self.__tok.expect('OP', ')') | 
					
						
							|  |  |  |             return result | 
					
						
							|  |  |  |         if t.kind not in ('ID', 'NUMBER', 'STRING'): | 
					
						
							|  |  |  |             raise SyntaxError | 
					
						
							|  |  |  |         if t.isA('ID') and t.value in CONST_MAP: | 
					
						
							|  |  |  |             t.kind = 'NUMBER' | 
					
						
							|  |  |  |             t.value = CONST_MAP[str(t.value)] | 
					
						
							|  |  |  |         elif t.isA('ID') and t.value in self.__constant: | 
					
						
							|  |  |  |             t.kind = 'NUMBER' | 
					
						
							|  |  |  |             t.value = self.__constant[str(t.value)] | 
					
						
							|  |  |  |         elif t.isA('ID') and str(t.value).startswith("."): | 
					
						
							|  |  |  |             assert self.__scope is not None | 
					
						
							|  |  |  |             t.value = self.__scope + str(t.value) | 
					
						
							|  |  |  |         return t | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def link(self) -> None: | 
					
						
							|  |  |  |         for offset, (link_type, link_expr) in self.__link.items(): | 
					
						
							|  |  |  |             expr = self.resolveExpr(link_expr) | 
					
						
							|  |  |  |             assert expr is not None | 
					
						
							|  |  |  |             assert expr.isA('NUMBER'), expr | 
					
						
							|  |  |  |             assert isinstance(expr, Token) | 
					
						
							|  |  |  |             value = int(expr.value) | 
					
						
							|  |  |  |             if link_type == Assembler.LINK_REL8: | 
					
						
							|  |  |  |                 byte = (value - self.__base_address) - offset - 1 | 
					
						
							|  |  |  |                 assert -128 <= byte <= 127, expr | 
					
						
							|  |  |  |                 self.__result[offset] = byte & 0xFF | 
					
						
							|  |  |  |             elif link_type == Assembler.LINK_ABS8: | 
					
						
							|  |  |  |                 assert 0 <= value <= 0xFF | 
					
						
							|  |  |  |                 self.__result[offset] = value & 0xFF | 
					
						
							|  |  |  |             elif link_type == Assembler.LINK_ABS16: | 
					
						
							|  |  |  |                 assert self.__base_address >= 0, "Cannot place absolute values in a relocatable code piece" | 
					
						
							|  |  |  |                 assert 0 <= value <= 0xFFFF | 
					
						
							|  |  |  |                 self.__result[offset] = value & 0xFF | 
					
						
							|  |  |  |                 self.__result[offset + 1] = value >> 8 | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 raise RuntimeError | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def resolveExpr(self, expr: Optional[ExprBase]) -> Optional[ExprBase]: | 
					
						
							|  |  |  |         if expr is None: | 
					
						
							|  |  |  |             return None | 
					
						
							|  |  |  |         elif isinstance(expr, OP): | 
					
						
							|  |  |  |             left = self.resolveExpr(expr.left) | 
					
						
							|  |  |  |             assert left is not None | 
					
						
							|  |  |  |             return OP.make(expr.op, left, self.resolveExpr(expr.right)) | 
					
						
							|  |  |  |         elif isinstance(expr, Token) and expr.isA('ID') and isinstance(expr, Token) and expr.value in self.__label: | 
					
						
							|  |  |  |             return Token('NUMBER', self.__label[str(expr.value)] + self.__base_address, expr.line_nr) | 
					
						
							|  |  |  |         return expr | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def getResult(self) -> bytearray: | 
					
						
							|  |  |  |         return self.__result | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def getLabels(self) -> ItemsView[str, int]: | 
					
						
							|  |  |  |         return self.__label.items() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def const(name: str, value: int) -> None: | 
					
						
							|  |  |  |     name = name.upper() | 
					
						
							| 
									
										
										
										
											2024-04-13 18:21:55 -07:00
										 |  |  |     assert name not in CONST_MAP or CONST_MAP[name] == value | 
					
						
							| 
									
										
										
										
											2023-03-21 01:26:03 +09:00
										 |  |  |     CONST_MAP[name] = value | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def resetConsts() -> None: | 
					
						
							|  |  |  |     CONST_MAP.clear() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def ASM(code: str, base_address: Optional[int] = None, labels_result: Optional[Dict[str, int]] = None) -> bytes: | 
					
						
							|  |  |  |     asm = Assembler(base_address) | 
					
						
							|  |  |  |     asm.process(code) | 
					
						
							|  |  |  |     asm.link() | 
					
						
							|  |  |  |     if labels_result is not None: | 
					
						
							|  |  |  |         assert base_address is not None | 
					
						
							|  |  |  |         for label, offset in asm.getLabels(): | 
					
						
							|  |  |  |             labels_result[label] = base_address + offset | 
					
						
							|  |  |  |     return binascii.hexlify(asm.getResult()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def allOpcodesTest() -> None: | 
					
						
							|  |  |  |     import json | 
					
						
							|  |  |  |     opcodes = json.load(open("Opcodes.json", "rt")) | 
					
						
							|  |  |  |     for label in (False, True): | 
					
						
							|  |  |  |         for prefix, codes in opcodes.items(): | 
					
						
							|  |  |  |             for num, op in codes.items(): | 
					
						
							|  |  |  |                 if op['mnemonic'].startswith('ILLEGAL_') or op['mnemonic'] == 'PREFIX': | 
					
						
							|  |  |  |                     continue | 
					
						
							|  |  |  |                 params = [] | 
					
						
							|  |  |  |                 postfix = '' | 
					
						
							|  |  |  |                 for o in op['operands']: | 
					
						
							|  |  |  |                     name = o['name'] | 
					
						
							|  |  |  |                     if name == 'd16' or name == 'a16': | 
					
						
							|  |  |  |                         if label: | 
					
						
							|  |  |  |                             name = 'LABEL' | 
					
						
							|  |  |  |                         else: | 
					
						
							|  |  |  |                             name = '$0000' | 
					
						
							|  |  |  |                     if name == 'd8' or name == 'a8': | 
					
						
							|  |  |  |                         name = '$00' | 
					
						
							|  |  |  |                     if name == 'r8': | 
					
						
							|  |  |  |                         if label and num != '0xE8': | 
					
						
							|  |  |  |                             name = 'LABEL' | 
					
						
							|  |  |  |                         else: | 
					
						
							|  |  |  |                             name = '$00' | 
					
						
							|  |  |  |                     if name[-1] == 'H' and name[0].isnumeric(): | 
					
						
							|  |  |  |                         name = '$' + name[:-1] | 
					
						
							|  |  |  |                     if o['immediate']: | 
					
						
							|  |  |  |                         params.append(name) | 
					
						
							|  |  |  |                     else: | 
					
						
							|  |  |  |                         params.append("[%s]" % (name)) | 
					
						
							|  |  |  |                     if 'increment' in o and o['increment']: | 
					
						
							|  |  |  |                         postfix = 'I' | 
					
						
							|  |  |  |                     if 'decrement' in o and o['decrement']: | 
					
						
							|  |  |  |                         postfix = 'D' | 
					
						
							|  |  |  |                 code = op["mnemonic"] + postfix + " " + ", ".join(params) | 
					
						
							|  |  |  |                 code = code.strip() | 
					
						
							|  |  |  |                 try: | 
					
						
							|  |  |  |                     data = ASM("LABEL:\n%s" % (code), 0x0000) | 
					
						
							|  |  |  |                     if prefix == 'cbprefixed': | 
					
						
							|  |  |  |                         assert data[0:2] == b'cb' | 
					
						
							|  |  |  |                         data = data[2:] | 
					
						
							|  |  |  |                     assert data[0:2] == num[2:].encode('ascii').lower(), data[0:2] + b"!=" + num[2:].encode('ascii').lower() | 
					
						
							|  |  |  |                 except Exception as e: | 
					
						
							|  |  |  |                     print("%s\t\t|%r|\t%s" % (code, e, num)) | 
					
						
							|  |  |  |                     print(op) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if __name__ == "__main__": | 
					
						
							|  |  |  |     #allOpcodesTest() | 
					
						
							|  |  |  |     const("CONST1", 1) | 
					
						
							|  |  |  |     const("CONST2", 2) | 
					
						
							|  |  |  |     ASM("""
 | 
					
						
							|  |  |  |     ld a, (123) | 
					
						
							|  |  |  |     ld hl, $1234 + 456 | 
					
						
							|  |  |  |     ld hl, $1234 + CONST1 | 
					
						
							|  |  |  |     ld hl, label | 
					
						
							|  |  |  |     ld hl, label.end - label | 
					
						
							|  |  |  |     ld c, label.end - label | 
					
						
							|  |  |  | label: | 
					
						
							|  |  |  |     nop | 
					
						
							|  |  |  | .end: | 
					
						
							|  |  |  |     """, 0)
 | 
					
						
							|  |  |  |     ASM("""
 | 
					
						
							|  |  |  |     jr label | 
					
						
							|  |  |  | label: | 
					
						
							|  |  |  |     """)
 | 
					
						
							|  |  |  |     assert ASM("db 1 + 2 * 3") == b'07' |