from assembly import FETCH, INSTRUCTIONS import assembly as ASM import pin META_DATA = "v3.0 hex words addressed\n" CJMPS = [ASM.JO, ASM.JNO, ASM.JZ, ASM.JNZ, ASM.JP, ASM.JNP] def chunk(l, n): for i in range(0, len(l), n): yield l[i:i + n] def get_hex_str(codes, width): width = int(width) data = [] for _code in codes: hex_str = hex(_code)[2:] zero_str = "" for _ in range(width - len(hex_str)): zero_str += "0" data.append(zero_str + hex_str) return data def save_hex_str(hex_str_lis, chunk_count: int, outfile_name: str): # 写入数据到 bin文件 lines = [META_DATA, ] idx = 0 for slice in chunk(hex_str_lis, chunk_count): hex_idx_str = hex(idx)[2:] zero_str = "" for _ in range(4 - len(hex_idx_str)): zero_str += "0" hex_idx_str = zero_str + hex_idx_str line = f"{hex_idx_str}:" for _ in slice: line += f" {_}" line += "\n" lines.append(line) idx += chunk_count # 下一行的地址 with open(outfile_name, "w") as f: f.writelines(lines) def compile_addr2(addr, ir, psw, idx): # addr: 0b10000100_0000_0110 # ir psw cyc # ir: 1xxx[aa][bb] # idx: 当前指令周期的下标 global base_bin_data op = ir & 0b1111_0000 # 需要进行的操作 amd = (ir >> 2) & 0b0000_0011 # 目的操作数 也是就是 aa ams = ir & 0b0000_0011 # 原地址操作数 bb INST = INSTRUCTIONS[2] # 如果还未实现该指令 if op not in INST: base_bin_data[addr] = pin.CYC # 当前指令周期清零, 执行下个指令周期吧 return # print(bin(addr), hex(addr), bin(op), bin(list(INST.keys())[0]), list(INST.keys())) am = (amd, ams) # 寻址方式判断 [aa][bb] if am not in INST[op]: base_bin_data[addr] = pin.CYC # 当前指令周期清零, 执行下个指令周期吧 return # print(bin(addr), hex(addr), bin(op), bin(amd), bin(ams)) EXEC = INST[op][am] # 当前指令周期 长度塞不下了 if idx < len(EXEC): # print(f"{hex(addr)} 写入指令: {bin(EXEC[idx])}") base_bin_data[addr] = EXEC[idx] else: # 这个指令周期放不下了 base_bin_data[addr] = pin.CYC # 当前指令周期清零, 执行下个指令周期吧 return def get_condition_jump(EXEC, op, psw): overflow = psw & 0b001 zero = psw & 0b010 partiy = psw & 0b100 if op == ASM.JO and overflow: return EXEC elif op == ASM.JNO and not overflow: return EXEC elif op == ASM.JZ and zero: return EXEC elif op == ASM.JNZ and not zero: return EXEC elif op == ASM.JP and partiy: return EXEC elif op == ASM.JNP and not partiy: return EXEC else: return [pin.CYC] def get_interrupt(EXEC, op, psw): allow_int = psw & 0b1000 # 如果是中断有效位的rom内存区域 if allow_int: return EXEC else: return [pin.CYC] def compile_addr1(addr, ir, psw, idx): # 01xxxx[aa] global base_bin_data op = ir & 0b1111_1100 # 需要进行的操作 amd = ir & 0b0000_0011 # 目的操作数 也是就是 aa INST = INSTRUCTIONS[1] if op not in INST: base_bin_data[addr] = pin.CYC return if amd not in INST[op]: base_bin_data[addr] = pin.CYC return EXEC = INST[op][amd] # 判断是否是条件转移指令, 处在的 bin段 # 如果不是就用cyc填充 if op in CJMPS: EXEC = get_condition_jump(EXEC, op, psw) # 中断 if op == ASM.INT: EXEC = get_interrupt(EXEC, op, psw) # 当前指令周期 长度塞不下了 if idx < len(EXEC): # print(f"{hex(addr)} 写入指令: {bin(EXEC[idx])}") base_bin_data[addr] = EXEC[idx] else: # 这个指令周期放不下了 base_bin_data[addr] = pin.CYC # 当前指令周期清零, 执行下个指令周期吧 return def compile_addr0(addr, ir, psw, idx): # 00xxxxxx op = ir # 需要进行的操作 INST = INSTRUCTIONS[0] # 如果还未实现该指令 if op not in INST: base_bin_data[addr] = pin.CYC # 当前指令周期清零, 执行下个指令周期吧 return EXEC = INST[op] # 当前指令周期 长度塞不下了 if idx < len(EXEC): base_bin_data[addr] = EXEC[idx] else: # 这个指令周期放不下了 base_bin_data[addr] = pin.CYC # 当前指令周期清零, 执行下个指令周期吧 return if __name__ == "__main__": data_width = 32 addr_width = 16 # 我们现在只支持16个指令 base_bin_data = [pin.HLT for _ in range(1 << addr_width)] for addr in range(1 << addr_width): # addr: 0b10000100_0000_0110 ir = addr >> 8 psw = (addr >> 4) & 0b1111 cyc = addr & 0b1111 # 最大支持2的4次方个指令 # 取指令周期 if cyc < len(FETCH): base_bin_data[addr] = FETCH[cyc] continue idx = cyc - len(FETCH) # 从取指令周期之后开始计数, 直接减去去指令周期的大小即可, 因为cyc是 addr来的 # 数据都就位, 处理IR寄存器的值 if ir & 0b1000_0000: compile_addr2(addr, ir, psw, idx) elif ir & 0b0100_0000: compile_addr1(addr, ir, psw, idx) else: compile_addr0(addr, ir, psw, idx) save_hex_str(get_hex_str(base_bin_data, data_width / 4), 8, "./ROM.bin")