Skip to content

Commit

Permalink
Implement many assembler features: sprite, sprite literals, sprite re…
Browse files Browse the repository at this point in the history
…ferences, word, arrays, texture, scanner support for include. Initial concept for interactive processor app using tkinter.
  • Loading branch information
alcatrazEscapee committed Feb 13, 2022
1 parent 915d09d commit 49719a8
Show file tree
Hide file tree
Showing 45 changed files with 661 additions and 261 deletions.
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
*
!*/
!/.gitignore

!/spec/**/*.ssl
!/requirements.txt

!/processorv5/**/*.py
!/test/**/*.py

!/test/assets/**/*.s
!/test/assets/**/*.trace
!/test/assets/textures/*.png

__pycache__
*.pyc
85 changes: 85 additions & 0 deletions processorv5/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
from argparse import ArgumentParser, Namespace
from tkinter import Tk, Frame, Button, Label, Canvas, Toplevel, filedialog
from threading import Thread

from assembler import Assembler
from processor import Processor

import os
import processor


def parse_command_line_args():
parser = ArgumentParser('ProcessorV5 Architecture')
parser.add_argument('-i', action='store_true', dest='interactive', help='Open an interactive window')

return parser.parse_args()


def main(args: Namespace):
App()


class App:

def __init__(self):
self.root = Tk()
self.root.title('ProcessorV5 Interactive')
self.root.rowconfigure(0, minsize=100, weight=1)
self.root.columnconfigure(1, minsize=320, weight=1)

self.buttons = Frame(self.root)
self.buttons.grid(row=0, column=0, sticky='ns')

self.load_button = Button(self.buttons, text='Load', command=self.on_load)
self.load_button.grid(row=0, column=0, sticky='ew', padx=5, pady=5)

self.run_button = Button(self.buttons, text='Run')
self.run_button.grid(row=1, column=0, sticky='ew', padx=5, pady=5)

self.screen = Canvas(self.root, bg='white', height=320, width=320)
self.screen.grid(row=0, column=1, padx=5, pady=5)

self.processor = None
self.processor_thread = None

self.root.mainloop()

def on_load(self):
path = filedialog.askopenfilename(
filetypes=[('Assembly Files', '*.s'), ('All Files', '*.*')],
initialdir=os.path.join(os.getcwd(), '../')
)
if not path:
return

with open(path, 'r', encoding='utf-8') as f:
text = f.read()
asm = Assembler(text, 'interpreted')
if not asm.assemble():
error = Toplevel(self.root)
error.grab_set()

text = Label(error, text=asm.error)
text.pack()
return

self.processor = Processor(asm.asserts, self.assert_handle)

def on_run(self):
if self.processor is not None:
self.processor_thread = Thread(target=self.run_processor)
self.processor_thread.run()

def run_processor(self):
pass

def assert_handle(self):
raise RuntimeError(processor.create_assert_debug_view(self.processor))





if __name__ == '__main__':
main(parse_command_line_args())
65 changes: 61 additions & 4 deletions processorv5/assembler.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
# This is an Assembler from assembly code to binary level instructions for the ProcessorV5 architecture
# The purpose is to be able to write programs for the final implementation and hardware model levels

from typing import Optional, Tuple, List, Dict, Literal
from typing import Optional, Tuple, List, Dict, Literal, Any

from phases import Scanner, Parser, CodeGen

import sys
import utils
import argparse
import blueprint
import dissasembler


Expand All @@ -17,6 +18,8 @@ def read_command_line_args():
parser.add_argument('file', type=str, help='The assembly file to be compiled')
parser.add_argument('-o', action='store_true', dest='output_binary', help='Output a binary file')
parser.add_argument('-v', action='store_true', dest='output_viewable', help='Output a hybrid view/disassembly file')
parser.add_argument('-b', action='store_true', dest='output_blueprint', help='Output a blueprint string for a 60KB ROM')
parser.add_argument('--out', type=str, help='The output file name')
return parser.parse_args()

def main(args: argparse.Namespace):
Expand All @@ -26,16 +29,70 @@ def main(args: argparse.Namespace):
print(asm.error)
sys.exit(1)

output_file = args.file if args.out is None else args.out

if args.output_binary:
with open(args.file + '.o', 'wb') as f:
with open(output_file + '.o', 'wb') as f:
for c in asm.code:
f.write(c.to_bytes(8, byteorder='big'))

if args.output_viewable:
dis = dissasembler.decode(asm.code)
with open(args.file + '.v', 'w', encoding='utf-8') as f:
with open(output_file + '.v', 'w', encoding='utf-8') as f:
for c, line in zip(asm.code, dis):
f.write('%-12d %-12d | %s\n' % (utils.signed_bitfield_64_to_32(c, 32, 32), utils.signed_bitfield_64_to_32(c, 0, 32), line))
f.write('%s %s | %s\n' % (
bin(utils.bitfield_uint64(c, 32, 32))[2:].zfill(32),
bin(utils.bitfield_uint64(c, 0, 32))[2:].zfill(32),
line
))

if args.output_blueprint:
data = encode_as_blueprint(asm.code)
with open(output_file + '.blueprint', 'w', encoding='utf-8') as f:
f.write(data)


def encode_as_blueprint(code: List[int]):
with open('./assets/60kb_rom.blueprint') as f:
data: Any = blueprint.decode_blueprint_string(f.read())

# Index combinator positions and signals
entities = data['blueprint']['entities']
x_positions, y_positions = set(), set()

for e in entities:
pos = e['position']
x_positions.add(pos['x'])
y_positions.add(pos['y'])

x_index = {x: i for i, x in enumerate(sorted(x_positions))}
y_index = {y: i for i, y in enumerate(sorted(y_positions))}

pos_index = {}
for e in entities:
pos = e['position']
filters = e['control_behavior']['filters']
signal_index = {}
for f in filters:
signal_index[f['index'] - 1] = f
pos_index[x_index[pos['x']], y_index[pos['y']]] = signal_index

# Map memory space onto physical space
for address, word in enumerate(code):
for port_x, value in enumerate((utils.signed_bitfield_64_to_32(word, 0, 32), utils.signed_bitfield_64_to_32(word, 32, 32))):
index = address
index, row_index_y = index // 20, index % 20
index, row_minor_y = index // 4, index % 4
index, col_major_x = index // 16, index % 16
index, row_major_y = index // 6, index % 6

assert index == 0, 'Address out of bounds for memory size'

signal = pos_index[(port_x * 16) + col_major_x, (row_major_y * 6) + row_minor_y]
signal = signal[row_index_y]
signal['count'] = int(value)

return blueprint.encode_blueprint_string(data)


class Assembler:
Expand Down
39 changes: 38 additions & 1 deletion processorv5/constants.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
from enum import Enum, IntEnum


GPU_MEMORY_SIZE = 64
MAIN_MEMORY_SIZE = 1024
INSTRUCTION_MEMORY_SIZE = 7680

FIRST_GENERAL_MEMORY_ADDRESS = 20


class Instructions(Enum):
"""
Assembly level instructions, identified by their asm name
Expand Down Expand Up @@ -95,6 +102,16 @@ def names():
NOOP = 'noop' # noop -> add r0 r0 r0
SET = 'set' # set X Y -> add X Y r0
SETI = 'seti' # set X #M -> addi X r0 #M
BR = 'br' # br L -> beq r0 r0 L

GFLUSH = 'gflush'
GLSI = 'glsi'
GLSM = 'glsm'
GLSS = 'glss'
GCB = 'gcb'
GCI = 'gci'
GMV = 'gvm'
GMVI = 'gmvi'


class Opcodes(IntEnum):
Expand Down Expand Up @@ -158,6 +175,7 @@ class Opcodes(IntEnum):
RET = 50
HALT = 51
ASSERT = 52
GPU = 53


class Registers(IntEnum):
Expand All @@ -181,4 +199,23 @@ class Registers(IntEnum):
R13 = 13
R14 = 14
R15 = 15
R16 = 16
R16 = 16


class GPUFunction(IntEnum):
G_CLEAR = 0
G_NOR = 1
G_ERASE = 2
G_DRAW_NEGATIVE = 3
G_HIGHLIGHT = 4
G_NEGATIVE = 5
G_TOGGLE = 6
G_NAND = 7
G_ERASE_NEGATIVE = 8
G_TOGGLE_NEGATIVE = 9
G_NOOP = 10
G_DRAW_ALPHA_NEGATIVE = 11
G_DRAW = 12
G_HIGHLIGHT_NEGATIVE = 13
G_DRAW_ALPHA = 14
G_FULL = 15
Loading

0 comments on commit 49719a8

Please sign in to comment.