diff options
| author | Jan Tuomi <jans.tuomi@gmail.com> | 2024-02-21 12:07:07 +0200 |
|---|---|---|
| committer | Jan Tuomi <jans.tuomi@gmail.com> | 2024-02-21 12:07:07 +0200 |
| commit | 927c018f12e73c254cec32c0f60e1c01b9fc0319 (patch) | |
| tree | 21fbb975e3567cb0d9ab3fb34519c50325571d6c /atk16_utils | |
| parent | a0b4a1a6107e4dc905b21858d8ac76c47293cfb4 (diff) | |
Refactor names
Diffstat (limited to 'atk16_utils')
| -rw-r--r-- | atk16_utils/charmem.py | 63 | ||||
| -rw-r--r-- | atk16_utils/convert_ttf.py | 52 | ||||
| -rw-r--r-- | atk16_utils/dig_install.py | 60 | ||||
| -rw-r--r-- | atk16_utils/pad_bin.py | 59 | ||||
| -rwxr-xr-x | atk16_utils/ucode.py | 112 |
5 files changed, 346 insertions, 0 deletions
diff --git a/atk16_utils/charmem.py b/atk16_utils/charmem.py new file mode 100644 index 0000000..aaac122 --- /dev/null +++ b/atk16_utils/charmem.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 +# Generate .bin file with ATK16 CPU TPU charmem data + +# Addressed by _CCC CCCC CYYY +# Data width 8bit: ___X XXXX +# where _ = unused, C = 8bit character code, Y = character y coordinate, X = pixel value + +import sys + +if len(sys.argv) != 2: + print("usage: charmem.py <outfile.bin>") + sys.exit(1) + +outfile_path = sys.argv[1] + +with open("charset.txt", "r") as f: + lines = [s.strip("\n") for s in f.readlines()] + +header_chars = lines[0] + +charset: list[list[str]] = [] + +CHAR_HEIGHT = 8 +CHAR_WIDTH = 8 + +i = 0 +while i < len(lines): + i += 1 # skip comment line + char = lines[i:i+CHAR_HEIGHT] + for row in char: + if len(row) != CHAR_WIDTH: + raise Exception(f"char row number {i} has width != {CHAR_WIDTH}: {len(row)}\n" + row + "\n" + "\n".join(char)) + charset.append(char) + i += CHAR_HEIGHT + 1 + +CHAR_N = len(charset) + +TOTAL_BYTEARRAY_SIZE = CHAR_N * CHAR_HEIGHT + +def string_to_byte(input_string: str): + # Replace spaces with '0' and '#' with '1' + binary_string = input_string.replace(' ', '0').replace('█', '1') + + assert(len(input_string) == CHAR_WIDTH) + + # Check if the string contains only '0' or '1' + if not all(c in '01' for c in binary_string): + raise ValueError("Input string must contain only spaces and '█' characters.") + + # Convert the binary string to an unsigned integer byte + return int(binary_string[::-1], 2) + + +res_b = bytearray(TOTAL_BYTEARRAY_SIZE) +for cn, char in enumerate(charset): + for cy in range(0, CHAR_HEIGHT): + addr = (cn << 3) + cy + byte = string_to_byte(char[cy]) + print(f"{byte:>08b}") + res_b[addr] = byte + +with open(outfile_path, "wb") as f: + f.write(res_b) diff --git a/atk16_utils/convert_ttf.py b/atk16_utils/convert_ttf.py new file mode 100644 index 0000000..c1135cb --- /dev/null +++ b/atk16_utils/convert_ttf.py @@ -0,0 +1,52 @@ +from PIL import Image, ImageFont, ImageDraw + +import sys + +if len(sys.argv) != 3: + print("usage: convert_ttf.py <infile.ttf> <outfile.txt>") + sys.exit(1) + +# Define the font file and size +font_file = sys.argv[1] +out_file = sys.argv[2] +font_size = 8 # 8x8 pixels + +# Create a font object +font = ImageFont.truetype(font_file, font_size) + + +# backspace 0x8, tab 0x9, newline 0xA, space 0x20 + +# Define the characters to render +#characters = ' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ' + +def to_char_code(i: int) -> str: + if i == 10: + return " " + return chr(i) + +characters = [to_char_code(i) for i in range(0, 256)] + + +# Open a file to write the bitmaps to +with open(out_file, 'w') as bitmap_file: + for char_idx, char in enumerate(characters): + # Create a new image with a white background + image = Image.new('1', (8, 8), 1) + + # Create a drawing context + draw = ImageDraw.Draw(image) + + # Draw the character + draw.text((0, 0), char, font=font, fill=0) + + # Convert the image to a list of 0's and 1's + pixels = image.getdata() + bitmap = [1 if pixel == 0 else 0 for pixel in pixels] + + # Write the bitmap to the file + bitmap_file.write(f'Character {char_idx}: {char}\n') + for i in range(0, 64, 8): + row = bitmap[i:i+8] + bitmap_file.write(''.join(str(bit) for bit in row) + '\n') + bitmap_file.write('\n') diff --git a/atk16_utils/dig_install.py b/atk16_utils/dig_install.py new file mode 100644 index 0000000..c32229b --- /dev/null +++ b/atk16_utils/dig_install.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 +# Install a generated .bin file into a memory chip in a .dig circuit file + +import sys +from typing import cast +import xml.etree.ElementTree as ET + +if len(sys.argv) != 4: + print("usage: dig_install.py <infile.bin> <circuitfile.dig> <chip_label>") + sys.exit(1) + +infile = sys.argv[1] +circuitfile = sys.argv[2] +chip_label = sys.argv[3] + +with open(infile, "rb") as f: + program_bytes = f.read() + +i = 0 +program_words: list[str] = [] +while i < len(program_bytes): + hi_byte = program_bytes[i] + lo_byte = program_bytes[i + 1] + word = f"{hi_byte:>02x}{lo_byte:>02x}" + #print(word) + program_words.append(word) + i += 2 + +program_data = ",".join(program_words) +print("program_data:", program_data) + +tree = ET.parse(circuitfile) +root = tree.getroot() + +# Iterate through each 'visualElement' element +def find_data_element(root: ET.Element) -> ET.Element: + is_correct_visual_element = False + for visual_element in root.findall('.//visualElement'): + element_attributes = visual_element.find('elementAttributes') + if element_attributes is None: continue + + entries = list(element_attributes) + for entry in entries: + strings = entry.findall("string") + is_label_entry = len(strings) == 2 and strings[0].text == "Label" and strings[1].text == chip_label + if is_label_entry and not is_correct_visual_element: + is_correct_visual_element = True + continue + + is_data_entry = len(strings) == 1 and strings[0].text == "Data" + if is_data_entry and is_correct_visual_element: + data_element = entry.findall("data")[0] + return data_element + + raise Exception(f"No data entry matching label {chip_label} found in tree") + +data_element = find_data_element(root) +data_element.text = program_data + +tree.write(circuitfile)
\ No newline at end of file diff --git a/atk16_utils/pad_bin.py b/atk16_utils/pad_bin.py new file mode 100644 index 0000000..9f014d1 --- /dev/null +++ b/atk16_utils/pad_bin.py @@ -0,0 +1,59 @@ +import os + +def pad_binary(file_path, target_size) -> int: + """ + Pads a binary file with zeros until it reaches a specified size in KiB. + + :param file_path: Path to the binary file to be padded. + :param target_size: Target size in bytes. + """ + + # Check current file size + current_size = os.path.getsize(file_path) + + # Calculate needed padding + padding_size = target_size - current_size + + # Append zeros if needed + if padding_size > 0: + with open(file_path, 'ab') as file: + file.write(b'\x00' * padding_size) + + return padding_size + +def convert_size_to_bytes(size_str: str) -> int: + """ + Converts a size string with K, M, or G suffix to bytes. + + :param size_str: Size string (e.g., "64K", "1M", "2G"). + :return: Size in bytes. + """ + size_str = size_str.upper() + if size_str.endswith('K'): + return int(size_str[:-1]) * 1024 + elif size_str.endswith('M'): + return int(size_str[:-1]) * 1024 ** 2 + elif size_str.endswith('G'): + return int(size_str[:-1]) * 1024 ** 3 + else: + return int(size_str) + +if __name__ == "__main__": + import argparse + + # Setup argument parser + parser = argparse.ArgumentParser(description='Pad a binary file with zeros until it reaches a specified size.') + parser.add_argument('file', type=str, help='Path to the binary file to be padded.') + parser.add_argument('--to', dest='size', type=str, help='Target size (e.g., 64K, 1M, 2G).', required=True) + + args = parser.parse_args() + + # Convert size argument to bytes + target_size_bytes = convert_size_to_bytes(args.size) + + padded_bytes_n = pad_binary(args.file, target_size_bytes) + + if padded_bytes_n > 0: + print(f"File has been padded with {padded_bytes_n} zeros.") + else: + print("File is already equal to or larger than the target size. No padding added.") diff --git a/atk16_utils/ucode.py b/atk16_utils/ucode.py new file mode 100755 index 0000000..22a2ce2 --- /dev/null +++ b/atk16_utils/ucode.py @@ -0,0 +1,112 @@ +#!/usr/bin/env python3 +# Generate .bin file with ATK16 CPU microcode + +# Addressed by OOOO BUUU +# where O = opcode, B = branch flag, U = microsequencer value + +import sys + +if len(sys.argv) != 2: + print("usage: ucode.py <outfile.bin>") + sys.exit(1) + +outfile_path = sys.argv[1] + +PC_CO = 1 << 0 +PC_IE = 1 << 1 +PC_OE = 1 << 2 +MAR_IE = 1 << 3 +MEM_IE = 1 << 4 +MEM_OE = 1 << 5 +RW_IE = 1 << 6 +R1_OE = 1 << 7 +R2_OE = 1 << 8 +IR_IE = 1 << 9 +IM_M = 1 << 10 +LI_OE = 1 << 11 +ALU_OE = 1 << 12 +FR_IE = 1 << 13 +HALT = 1 << 14 +US_RS = 1 << 15 +ISRA_OE = 1 << 16 +IM_DS = 1 << 17 +IM_EN = 1 << 18 +IPC_IE = 1 << 19 +IPC_OE = 1 << 20 +NOP5 = 1 << 21 +NOP6 = 1 << 22 +NOP7 = 1 << 23 + +BRANCH_FLAG_STATES_N = 2 +UCODE_N: int = 2**3 +CONTROL_WORD_SIZE = 3 + +def not_branch(bs: list[int]) -> list[list[int]]: + return BRANCH_FLAG_STATES_N * [bs] + +def branch(false_branch: list[int], true_branch: list[int]) -> list[list[int]]: + return [false_branch, true_branch] + +fetch = [PC_OE|MAR_IE, MEM_OE|IR_IE|PC_CO] + +def nop(): + return not_branch([*fetch, US_RS, 0, 0, 0, 0, 0]) + +ucode = [ + # ALR 0000 TTTL LLRR RSSS + not_branch([*fetch, ALU_OE|FR_IE|RW_IE, US_RS, 0, 0, 0, 0]), + # ALI 0001 TTTL LLII ISSS + not_branch([*fetch, IM_M|ALU_OE|FR_IE|RW_IE, US_RS, 0, 0, 0, 0]), + # LDR 0010 TTTR RRXX XXXX + not_branch([*fetch, R1_OE|MAR_IE, MEM_OE|RW_IE, US_RS, 0, 0, 0]), + # STR 0011 XXXL LLRR RXXX + not_branch([*fetch, R1_OE|MAR_IE, R2_OE|MEM_IE, US_RS, 0, 0, 0]), + # LDI 0100 TTTI IIII IIII + not_branch([*fetch, LI_OE|RW_IE, US_RS, 0, 0, 0, 0]), + # JPR 0101 XXXR RRXX XXXX + not_branch([*fetch, R1_OE|PC_IE, US_RS, 0, 0, 0, 0]), + # JPI 0110 XXXI IIII IIII + not_branch([*fetch, IM_M|LI_OE|PC_IE, US_RS, 0, 0, 0, 0]), + # BRR 0111 XFFR RRXX XXXX + branch([*fetch, US_RS, 0, 0, 0, 0, 0], + [*fetch, R1_OE|PC_IE, US_RS, 0, 0, 0, 0]), + # BRI 1000 XFFI IIII IIII + branch([*fetch, US_RS, 0, 0, 0, 0, 0], + [*fetch, IM_M|LI_OE|PC_IE, US_RS, 0, 0, 0, 0]), + # LPC 1001 TTTX XXXX XXXX + not_branch([*fetch, PC_OE|RW_IE, US_RS, 0, 0, 0, 0]), + # NOP 1010 XXXX XXXX XXXX + nop(), + # NOP 1011 XXXX XXXX XXXX + nop(), + # ISRP0 1100 XXXX XXXX XXXX + not_branch([IM_EN|PC_OE|IPC_IE, ISRA_OE|MAR_IE, MEM_OE|PC_IE, US_RS, 0, 0, 0, 0]), + # ISRP1 1101 XXXX XXXX XXXX + not_branch([IM_EN|PC_OE|IPC_IE, ISRA_OE|MAR_IE, MEM_OE|PC_IE, US_RS, 0, 0, 0, 0]), + # RTI 1110 XXXX XXXX XXXX + not_branch([*fetch, IM_DS|IPC_OE|PC_IE, US_RS, 0, 0, 0, 0]), + # HLT 1111 XXXX XXXX XXXX + not_branch([*fetch, HALT, 0, 0, 0, 0, 0]), +] + +INST_N = len(ucode) +TOTAL_BYTEARRAY_SIZE = INST_N * BRANCH_FLAG_STATES_N * UCODE_N * CONTROL_WORD_SIZE + +res_b = bytearray(TOTAL_BYTEARRAY_SIZE) + +for i in range(INST_N): + for j in range(BRANCH_FLAG_STATES_N): + for k in range(UCODE_N): + for l in range(CONTROL_WORD_SIZE): + idx = l + \ + k * CONTROL_WORD_SIZE + \ + j * CONTROL_WORD_SIZE * UCODE_N + \ + i * CONTROL_WORD_SIZE * UCODE_N * BRANCH_FLAG_STATES_N + cword = ucode[i][j][k] + assert len(ucode[i][j]) == 8 + cbyte = (cword >> (8 * (CONTROL_WORD_SIZE - l - 1))) & 0xff + print(f"inst: {i:>04b}, idx: {idx:>04x}, cbyte: {cbyte:>08b}") + res_b[idx] = cbyte + +with open(outfile_path, "wb") as f: + f.write(res_b) |
