def test_suboperation_get_opcodeinfo_scenario2(mnemonic): """Tests for get_opcodeinfo function.""" chip = Processor() opcode = '{"opcode": -1, "mnemonic": "N/A"}' chip_opcode = str(get_opcodeinfo(chip, '', mnemonic)).replace('\'', '"') assert chip_opcode == opcode
def deal_with_custom_opcode(chip: Processor, parts: list, constant: bool, opcode: str, address: int, p_line: int) -> Tuple[str, int, int]: err = False if (parts[0][len(parts[0]) - 1] != ',' and not constant): if (opcode == 'ld()' or opcode[:2] == 'ld'): opcode = 'ld ' if opcode not in ('org', '/', 'end', 'pin', '='): opcodeinfo = get_opcodeinfo(chip, 'S', opcode) if opcodeinfo == {'opcode': -1, 'mnemonic': 'N/A'}: err = "FATAL: Pass 1: Invalid mnemonic '" + \ opcode + "' at line: " + str(p_line + 1) else: address = address + opcodeinfo['words'] return err, opcode, address
def test_suboperation_get_opcodeinfo_scenario1(values): """Tests for get_opcodeinfo function.""" chip = Processor() opcode = values[0] mnemonic = values[1] exetime = values[2] bit1 = values[3] bit2 = values[4] words = values[5] opcode = '{"opcode": ' + str(opcode) + ', "mnemonic": "' + \ mnemonic + '", "exe": ' + str(exetime) + ',' opcode = opcode + ' "bits": ["' + bit1 + '", "' + \ bit2 + '"], "words": ' + str(words) + '}' chip_opcode = str(get_opcodeinfo(chip, '', mnemonic)).replace('\'', '"') assert chip_opcode == opcode
def assemble(program_name: str, object_file: str, chip: Processor, quiet: bool, type: str) -> bool: """ Main two-pass assembler for i4004 code. Parameters ---------- program_name: str, mandatory Name of the source file to load object_file: str, mandatory Name of the output file in which to place the object code chip: Processor, mandatory Instance of a processor to place the assembled code in. quiet: bool, mandatory Determines whether quiet mode is on i.e. no output. type: str, mandatory Determines the type of output file(s) to create. Returns ------- True if the code assembles correctly False if there are errors in the code Raises ------ N/A Notes ----- N/A """ # mccabe: MC0001 / assemble is too complex (44) - start # ... # mccabe: MC0001 / assemble is too complex (10) # SonarLint: S3776: assemble is too complex (25) - start # Pass 0 - Initialise label tables, program storage etc _labels, tps, tfile = pass0(chip) # Program Line Count count = 0 # Pass 1 err, _labels, tps, tfile, address = pass1(chip, program_name, _labels, tps, tfile, quiet) if err: do_error(err + "\nProgram Assembly halted @ Pass 1\n\n") return False # Pass 2 print_messages(quiet, 'ASM', chip, '') org_found = False location = '' while True: line = tfile[count].strip() if len(line) == 0: break # End of code x = line.split() label = '' # Check for initial comments if line[0] == '/': asm_comment(label, count, line, quiet) else: opcode = x[0] if x[0][-1] == ',': label = x[0] opcode = x[1] # Check to see if we are assembling a label if '0' <= str(opcode)[:1] <= '9': tps = asm_label(tps, address, x, count, label) break opcodeinfo = get_opcodeinfo(chip, 'S', opcode) chip, x, _labels, address, tps, opcodeinfo, label, count, \ err, org_found, location = \ asm_main(chip, x, _labels, address, tps, opcode, opcodeinfo, label, count, org_found, location, quiet) if err: do_error(err) break count = count + 1 if err: print("Program Assembly halted") return False # Wrap up assembly process and write to file if necessary. chip = wrap_up(chip, location, tps, _labels, object_file, quiet, type) return True
def asm_others(chip: Processor, x: list, count: int, opcode: str, tps: list, address: int, label: str, quiet: bool) \ -> Tuple[list, int]: """ Assemble othe instructions. Parameters ---------- chip : Processor, mandatory The instance of the processor containing the registers, accumulator etc x: list, mandatory Line of program code (split into component parts). count: int, mandatory Current assembly program line opcode: int, mandatory Value of the opcode for assembly tps: list, mandatory Assembled code address: int, mandatory address to assemble to label: str, mandatory Label of the current line (if any) quiet: bool, mandatory If quiet mode is on Returns ------- tps: list Assembled code address: int the address to assemble to Raises ------ N/A Notes ----- N/A """ d_type = '' if int(x[2]) <= 256: d_type = 'data8' val_left, val_right = split_address8(int(x[2])) address_left, address_right = split_address8(address) f_opcode = opcode + "(" + x[1] + "," + d_type + ")" opcodeinfo = get_opcodeinfo(chip, 'L', f_opcode) bit1, bit2 = get_bits(opcodeinfo) tps[address] = opcodeinfo['opcode'] tps[address + 1] = int(x[2]) if not quiet: print_ln(address, label, address_left, address_right, bit1, bit2, val_left, val_right, str(tps[address]) + "," + str(tps[address + 1]), '', '', str(count), opcode, str(x[1]), str(x[2]), '', '') address = address + opcodeinfo['words'] return tps, address
def work_with_a_line_of_asm(chip: Processor, line: str, _labels: list, p_line: int, address: int, tfile: list) \ -> Tuple[Any, list, int, int, list]: """ Analyse a single line of code. Parameters ---------- chip: Processor, mandatory The instance of the processor containing the registers, accumulator etc line: str, mandatory line of assembly code read in from a file _labels: list, Mandatory List for containing labels p_line: int, mandatory numbered line of assembly language program address: int, Mandatory Current address of memory for assembly tfile: list, Mandatory Assembly language store Returns ------- err: False if no error, error text if error tfile: list Assembly language store p_line: int numbered line of assembly language program address: int Current address of memory for assembly _labels: list List for containing labels Raises ------ N/A Notes ----- N/A """ constant = False err = False # Work with a line of assembly code parts = line.split() if parts[0][len(parts[0]) - 1] == ',': # Found a label, now add it to the label table if add_label(_labels, parts[0]) == -1: err = ('FATAL: Pass 1: Duplicate label: ' + parts[0] + ' at line ' + str(p_line + 1)) return err, tfile, p_line, 0, _labels # Attach value to a label if '0' <= str(parts[1])[:1] <= '9': constant = True label_content = parts[1] else: label_content = address # An EQUATE statement (indicated by "=") if parts[1] == '=': constant = True label_content = int(parts[2]) match_label(_labels, parts[0], label_content) # Set opcode opcode = parts[1][:3] if opcode != '=': opcodeinfo = get_opcodeinfo(chip, 'S', opcode) address = address + opcodeinfo['words'] else: # Set opcode opcode = parts[0][:3] # Deal with custom opcode err, opcode, address = \ deal_with_custom_opcode(chip, parts, constant, opcode, address, p_line) tfile[p_line] = line.strip() p_line = p_line + 1 return err, tfile, p_line, address, _labels
def assemble_2(chip: Processor, x: list, opcode: str, address: int, tps: list, _labels: list, address_left: str, address_right: str, label: str, count: int, quiet: bool) \ -> Tuple[int, list, list]: """ Function to assemble specific instructions. Parameters ---------- chip : Processor, mandatory The instance of the processor containing the registers, accumulator etc x: list, mandatory The current line of code being assembled split into individual elements opcode: str, mandatory The textual opcode (LD, LDM etc..) address: int, mandatory Address in memory to place the newly assembled instruction tps: list, mandatory List representing the memory of the i4004 into which the newly assembled instructions will be placed. _labels: list, mandatory List of valid labels address_left, address_right: str, mandatory Binary representation of 2 4-bit words representing "address" label: str, mandatory If there is a label associated with this instruction, it will be here, "" otherwise. count: int, mandatory Assembly line number (used for printing during assembly) quiet: bool, mandatory If quiet mode is on Returns ------- address: int After the instruction has been assembled, the incoming address is incremented by the number of words in the assembled instruction. tps: list List representing the memory of the i4004 into which the newly assembled instruction has just been placed. _labels: list Addresses of the labels (pass through only) Raises ------ N/A Notes ----- N/A """ # pad out for the only 2-character mnemonic if opcode == 'ld': opcode = 'ld ' addx = get_label_addr(_labels, x[1]) if addx == -1: addx = x[1] f_opcode = opcode + '(' + str(addx) + ')' if opcode in ('jun', 'jms'): # Special case for JUN and JMS if opcode == 'jun': decimal_code = 64 if opcode == 'jms': decimal_code = 80 f_opcode = opcode + '(address12)' opcodeinfo = get_opcodeinfo(chip, 'L', f_opcode) label_addr = get_label_addr(_labels, x[1]) label_addr12 = zfl(str(bin(decimal_code))[2:], 8)[:4] + \ zfl(str(bin(label_addr)[2:]), 12) bit1 = label_addr12[:8] bit2 = label_addr12[8:] tps[address] = int(str(bit1), 2) tps[address + 1] = int(str(bit2), 2) if not quiet: print_ln(address, label, address_left, address_right, bit1[:4], bit1[4:], bit2[:4], bit2[4:], str(tps[address]) + ',' + str(tps[address + 1]), '', '', str(count), opcode, str(x[1]), '', '', '') address = address + opcodeinfo['words'] else: if opcode == 'src': register = x[1].lower().replace('p', '').replace('r', '') f_opcode = 'src(' + register + ')' opcodeinfo = get_opcodeinfo(chip, 'L', f_opcode) bit1, bit2 = get_bits(opcodeinfo) tps[address] = opcodeinfo['opcode'] if not quiet: print_ln(address, label, address_left, address_right, bit1, bit2, '', '', tps[address], '', '', str(count), opcode, str(x[1]), '', '', '') address = address + opcodeinfo['words'] return address, tps, _labels
def assemble_jcn(self: Processor, x: list, _labels: list, tps: list, address: int, address_left: str, address_right: str, label: str, count: int, quiet: bool) -> Tuple[int, list, list]: """ Function to assemble JCN instructions. Parameters ---------- self : Processor, mandatory The instance of the processor containing the registers, accumulator etc x: list, mandatory The current line of code being assembled split into individual elements _labels: list, mandatory List of valid labels tps: list, mandatory List representing the memory of the i4004 into which the newly assembled instructions will be placed. address: int, mandatory Address in memory to place the newly assembled instruction address_left, address_right: str, mandatory Binary representation of 2 4-bit words representing "address" label: str, mandatory If there is a label associated with this instruction, it will be here, "" otherwise. count: int, mandatory Assembly line number (used for printing during assembly) quiet: bool, mandatory If quiet mode is on Returns ------- address: int After the instruction has been assembled, the incoming address is incremented by the number of words in the assembled instruction. tps: list List representing the memory of the i4004 into which the newly assembled instruction has just been placed. _labels: list Addresses of the labels (pass through only) Raises ------ N/A Notes ----- N/A """ conditions = x[1].upper() dest_label = x[2] if '0' <= conditions <= '9': bin_conditions = conditions else: bin_conditions = decode_conditions(conditions) f_opcode = 'jcn(' + str(bin_conditions) + ',address8)' opcodeinfo = get_opcodeinfo(self, 'L', f_opcode) label_addr = int(get_label_addr(_labels, dest_label)) vl, vr = split_address8(label_addr) bit1, bit2 = get_bits(opcodeinfo) tps[address] = opcodeinfo['opcode'] tps[address + 1] = label_addr if not quiet: print_ln(address, label, address_left, address_right, bit1, bit2, vl, vr, str(tps[address]) + "," + str(tps[address + 1]), '', '', str(count), x[0], str(x[1]), str(x[2]), '', '') address = address + opcodeinfo['words'] return address, tps, _labels
def assemble_fim(self: Processor, x: list, _labels: list, tps: list, address: int, label: str, count: int, quiet: bool) -> Tuple[int, list, list]: """ Function to assemble FIM instruction. Parameters ---------- self : Processor, mandatory The instance of the processor containing the registers, accumulator etc x: list, mandatory The current line of code being assembled split into individual elements _labels: list, mandatory List of valid labels tps: list, mandatory List representing the memory of the i4004 into which the newly assembled instructions will be placed. address: int, mandatory Current program counter address label: str, mandatory If there is a label associated with this instruction, it will be here, "" otherwise. count: int, mandatory Assembly line number (used for printing during assembly) quiet: bool, mandatory If quiet mode is on Returns ------- address: int After the instruction has been assembled, the incoming address is incremented by the number of words in the assembled instruction. tps: list List representing the memory of the i4004 into which the newly assembled instruction has just been placed. _labels: list Addresses of the labels (pass through only) Raises ------ N/A Notes ----- N/A """ f_opcode = x[0] + '(' + x[1] + ',data8)' opcodeinfo = get_opcodeinfo(self, 'L', f_opcode) tps[address] = opcodeinfo['opcode'] tps[address + 1] = int(x[2]) bit1, bit2 = get_bits(opcodeinfo) if not quiet: print_ln(address, label, '', '', bit1, bit2, '', '', str(tps[address]) + "," + str(tps[address + 1]), '', '', str(count), x[0], str(x[1]), '', str(x[2]), '') address = address + opcodeinfo['words'] return address, tps, _labels