diff --git a/examples/hello_vasyl.py b/examples/hello_vasyl.py new file mode 100644 index 0000000..90acfda --- /dev/null +++ b/examples/hello_vasyl.py @@ -0,0 +1,68 @@ +# if shazzam not installed +import sys +sys.path.append(".") + +from reloading import reloading +from shazzam.Segment import SegmentType +from shazzam.py64gen import * +from shazzam.py64gen import RegisterX as x, RegisterY as y, RegisterACC as a +from shazzam.macros.aliases import color, vic +import shazzam.macros.sys as sys +from shazzam.macros.vasyl import * +import shazzam.macros.vlib as vlib +from shazzam.drivers.assemblers.CC65 import CC65 + +# define your cross assembler +assembler = CC65("cc65", "third_party/cc65/bin/cl65") +program_name = os.path.splitext(os.path.basename(__file__))[0] + +@reloading +def code(): + + # define here or anywhere, doesn't matter, your variables + + import examples.vasyl.logo_dlist + + # CC65 generates basic header, no macro needed just to define the CODE segment + with segment(0x0801, "start") as s: + + sys.basic_start() + label("init") + + jsr(at("knock_knock")) + + lda(at(53265)) + sta(at("preserve_ctrl1")) + lda(at(0xd020)) + sta(at("preserve_ec")) + lda(imm(0)) # turn off VIC-II display fetches + sta(at(53265)) # so that badlines do not interfere + + jsr(at("copy_and_activate_dlist")) + label("loop") + jsr(at(0xffe4)) # check if key pressed + beq(rel_at("loop")) + + lda(imm(0)) # turn off the display list + sta(at(VREG_CONTROL)) + lda(at("preserve_ctrl1")) + sta(at(53265)) + lda(at("preserve_ec")) + sta(at(0xd020)) + rts() + + vasyl_segment_load = 0x0 + vasyl_segment_size = get_segment_addresses(assembler.get_vasyl_segment()).end_address + + # include vlib routines + vlib.init(vasyl_segment_load, vasyl_segment_size) + vlib.copy_and_activate_dlist(vasyl_segment_load, vasyl_segment_size) + + # generate listing + gen_code(assembler, gen_listing=True) + + # finally assemble segments to PRG using cross assembler then crunch it! + assemble_prg(assembler, start_address=0x0801) + +if __name__ == "__main__": + generate(code, program_name) diff --git a/examples/vasyl/logo_dlist.py b/examples/vasyl/logo_dlist.py new file mode 100644 index 0000000..793dbc6 --- /dev/null +++ b/examples/vasyl/logo_dlist.py @@ -0,0 +1,625 @@ +from shazzam.py64gen import * +from shazzam.macros.vasyl import * +from shazzam.Assembler import Assembler + +# BeamRacer * https://beamracer.net +# Video and Display List coprocessor board for the Commodore 64 +# Copyright (C)2019-2020 Mad Hackers Lab +# +# https://github.com/madhackerslab/beamracer-examples +# +# Logo display list + +with segment(0x00, "VASYL") as s: + + + label("dl_start", is_global=True) + WAIT(48 ,0) + + dl_line_0 = label("dl_line_0") + MASKV(0) + WAIT(0, 15) + MOV(0x20, 0) + DELAYV(1) + SKIP() + WAIT(55 , 59) + BRA(dl_line_0) + WAIT(56 ,0) + + dl_line_1 = label("dl_line_1") + MASKV(0) + WAIT(0, 15) + MOV(0x20, 14) + DELAYH(5) + MOV(0x20, 6) + MOV(0x20, 0) + MOV(0x20, 14) + DELAYH(6) + MOV(0x20, 6) + MOV(0x20, 0) + DELAYH(1) + MOV(0x20, 14) + DELAYH(4) + MOV(0x20, 6) + MOV(0x20, 14) + DELAYH(2) + MOV(0x20, 6) + MOV(0x20, 14) + DELAYH(2) + MOV(0x20, 6) + MOV(0x20, 0) + DELAYV(1) + SKIP() + WAIT(63 , 59) + BRA(dl_line_1) +# WAIT(64 ,0) + + dl_line_2 = label("dl_line_2") + MASKV(0) + WAIT(0, 15) + MOV(0x20, 0) + DELAYH(4) + MOV(0x20, 14) + DELAYH(1) + MOV(0x20, 6) + MOV(0x20, 0) + DELAYH(8) + MOV(0x20, 14) + DELAYH(1) + MOV(0x20, 6) + MOV(0x20, 0) + DELAYH(3) + MOV(0x20, 14) + DELAYH(6) + MOV(0x20, 6) + MOV(0x20, 0) + DELAYV(1) + SKIP() + WAIT(71 , 59) + BRA(dl_line_2) +# WAIT(72 ,0) + + dl_line_3 = label("dl_line_3") + MASKV(0) + WAIT(0, 15) + MOV(0x20, 14) + DELAYH(5) + MOV(0x20, 6) + MOV(0x20, 0) + MOV(0x20, 14) + DELAYH(5) + MOV(0x20, 6) + MOV(0x20, 0) + MOV(0x20, 14) + DELAYH(2) + MOV(0x20, 6) + MOV(0x20, 0) + MOV(0x20, 14) + DELAYH(1) + MOV(0x20, 6) + MOV(0x20, 14) + DELAYH(6) + MOV(0x20, 6) + MOV(0x20, 0) + DELAYV(1) + SKIP() + WAIT(79 , 59) + BRA(dl_line_3) +# WAIT(80 ,0) + + dl_line_4 = label("dl_line_4") + MASKV(0) + WAIT(0, 15) + MOV(0x20, 14) + DELAYH(2) + MOV(0x20, 6) + MOV(0x20, 0) + MOV(0x20, 14) + DELAYH(1) + MOV(0x20, 6) + MOV(0x20, 14) + DELAYH(2) + MOV(0x20, 6) + MOV(0x20, 0) + DELAYH(3) + MOV(0x20, 14) + DELAYH(2) + MOV(0x20, 6) + MOV(0x20, 0) + MOV(0x20, 14) + DELAYH(1) + MOV(0x20, 6) + MOV(0x20, 14) + DELAYH(2) + MOV(0x20, 6) + MOV(0x20, 0) + MOV(0x20, 14) + DELAYH(1) + MOV(0x20, 6) + MOV(0x20, 0) + DELAYV(1) + SKIP() + WAIT(87 , 59) + BRA(dl_line_4) +# WAIT(88 ,0) + + dl_line_5 = label("dl_line_5") + MASKV(0) + WAIT(0, 15) + MOV(0x20, 14) + DELAYH(2) + MOV(0x20, 6) + MOV(0x20, 0) + MOV(0x20, 14) + DELAYH(1) + MOV(0x20, 6) + MOV(0x20, 14) + DELAYH(2) + MOV(0x20, 6) + MOV(0x20, 0) + DELAYH(3) + MOV(0x20, 14) + DELAYH(6) + MOV(0x20, 6) + MOV(0x20, 14) + DELAYH(2) + MOV(0x20, 6) + MOV(0x20, 0) + MOV(0x20, 14) + DELAYH(1) + MOV(0x20, 6) + MOV(0x20, 0) + DELAYV(1) + SKIP() + WAIT(95 , 59) + BRA(dl_line_5) +# WAIT(96 ,0) + + dl_line_6 = label("dl_line_6") + MASKV(0) + WAIT(0, 15) + MOV(0x20, 14) + DELAYH(2) + MOV(0x20, 6) + MOV(0x20, 0) + MOV(0x20, 14) + DELAYH(1) + MOV(0x20, 6) + MOV(0x20, 14) + DELAYH(2) + MOV(0x20, 6) + MOV(0x20, 0) + DELAYH(3) + MOV(0x20, 14) + DELAYH(2) + MOV(0x20, 6) + MOV(0x20, 0) + MOV(0x20, 14) + DELAYH(1) + MOV(0x20, 6) + MOV(0x20, 14) + DELAYH(2) + MOV(0x20, 6) + MOV(0x20, 0) + MOV(0x20, 14) + DELAYH(1) + MOV(0x20, 6) + MOV(0x20, 0) + DELAYV(1) + SKIP() + WAIT(103 , 59) + BRA(dl_line_6) + WAIT(104 ,0) + + dl_line_7 = label("dl_line_7") + MASKV(0) + WAIT(0, 15) + MOV(0x20, 14) + DELAYH(5) + MOV(0x20, 6) + MOV(0x20, 0) + MOV(0x20, 14) + DELAYH(6) + MOV(0x20, 6) + MOV(0x20, 14) + DELAYH(2) + MOV(0x20, 6) + MOV(0x20, 0) + MOV(0x20, 14) + DELAYH(1) + MOV(0x20, 6) + MOV(0x20, 14) + DELAYH(2) + MOV(0x20, 6) + MOV(0x20, 0) + MOV(0x20, 14) + DELAYH(1) + MOV(0x20, 6) + MOV(0x20, 0) + DELAYV(1) + SKIP() + WAIT(111 , 59) + BRA(dl_line_7) + WAIT(112 ,0) + + dl_line_8 = label("dl_line_8") + MASKV(0) + WAIT(0, 15) + MOV(0x20, 0) + DELAYV(1) + SKIP() + WAIT(119 , 59) + BRA(dl_line_8) + WAIT(120 ,0) + + dl_line_9 = label("dl_line_9") + MASKV(0) + WAIT(0, 15) + MOV(0x20, 0) + DELAYH(37) + MOV(0x20, 7) + MOV(0x20, 0) + DELAYV(1) + SKIP() + WAIT(127 , 59) + BRA(dl_line_9) + WAIT(128 ,0) + + dl_line_10 = label("dl_line_10") + MASKV(0) + WAIT(0, 15) + MOV(0x20, 7) + DELAYH(37) + MOV(0x20, 1) + MOV(0x20, 7) + MOV(0x20, 0) + DELAYV(1) + SKIP() + WAIT(135 , 59) + BRA(dl_line_10) + WAIT(136 ,0) + + dl_line_11 = label("dl_line_11") + MASKV(0) + WAIT(0, 15) + MOV(0x20, 4) + DELAYH(37) + MOV(0x20, 1) + MOV(0x20, 0) + DELAYV(1) + SKIP() + WAIT(143 , 59) + BRA(dl_line_11) + WAIT(144 ,0) + + dl_line_12 = label("dl_line_12") + MASKV(0) + WAIT(0, 15) + MOV(0x20, 0) + DELAYH(37) + MOV(0x20, 7) + MOV(0x20, 0) + DELAYV(1) + SKIP() + WAIT(151 , 59) + BRA(dl_line_12) +# WAIT(152 ,0) +#dl_line_13") +# MASKV(0) +# WAIT(0, 15) +# MOV(0x20, 0) +# DELAYV(1) +# SKIP() +# WAIT(159 , 59) +# MOV VREG_DL2STROBE, 0) + WAIT(160 ,0) + + dl_line_14 = label("dl_line_14") + MASKV(0) + WAIT(0, 15) + MOV(0x20, 14) + DELAYH(5) + MOV(0x20, 6) + MOV(0x20, 0) + DELAYH(3) + MOV(0x20, 14) + DELAYH(4) + MOV(0x20, 6) + MOV(0x20, 0) + MOV(0x20, 14) + DELAYH(4) + MOV(0x20, 6) + MOV(0x20, 14) + DELAYH(6) + MOV(0x20, 6) + MOV(0x20, 14) + DELAYH(5) + MOV(0x20, 6) + MOV(0x20, 0) + DELAYV(1) + SKIP() + WAIT(167 , 59) + BRA(dl_line_14) + WAIT(168 ,0) + + dl_line_15 = label("dl_line_15") + MASKV(0) + WAIT(0, 15) + MOV(0x20, 0) + DELAYH(4) + MOV(0x20, 14) + DELAYH(1) + MOV(0x20, 6) + MOV(0x20, 0) + DELAYH(1) + MOV(0x20, 14) + DELAYH(1) + MOV(0x20, 6) + MOV(0x20, 0) + DELAYH(23) + MOV(0x20, 14) + DELAYH(1) + MOV(0x20, 6) + MOV(0x20, 0) + DELAYV(1) + SKIP() + WAIT(175 , 59) + BRA(dl_line_15) + WAIT(176 ,0) + + dl_line_16 = label("dl_line_16") + MASKV(0) + WAIT(0, 15) + MOV(0x20, 14) + DELAYH(2) + MOV(0x20, 6) + MOV(0x20, 0) + MOV(0x20, 14) + DELAYH(1) + MOV(0x20, 6) + MOV(0x20, 0) + MOV(0x20, 14) + DELAYH(2) + MOV(0x20, 6) + MOV(0x20, 0) + MOV(0x20, 14) + DELAYH(1) + MOV(0x20, 6) + MOV(0x20, 14) + DELAYH(2) + MOV(0x20, 6) + MOV(0x20, 0) + DELAYH(2) + MOV(0x20, 14) + DELAYH(5) + MOV(0x20, 6) + MOV(0x20, 0) + MOV(0x20, 14) + DELAYH(2) + MOV(0x20, 6) + MOV(0x20, 0) + MOV(0x20, 14) + DELAYH(1) + MOV(0x20, 6) + MOV(0x20, 0) + DELAYV(1) + SKIP() + WAIT(183 , 59) + BRA(dl_line_16) + WAIT(184 ,0) + + dl_line_17 = label("dl_line_17") + MASKV(0) + WAIT(0, 15) + MOV(0x20, 14) + DELAYH(5) + MOV(0x20, 6) + MOV(0x20, 0) + DELAYH(1) + MOV(0x20, 14) + DELAYH(2) + MOV(0x20, 6) + MOV(0x20, 0) + MOV(0x20, 14) + DELAYH(1) + MOV(0x20, 6) + MOV(0x20, 14) + DELAYH(2) + MOV(0x20, 6) + MOV(0x20, 0) + DELAYH(2) + MOV(0x20, 14) + DELAYH(2) + MOV(0x20, 6) + MOV(0x20, 0) + DELAYH(3) + MOV(0x20, 14) + DELAYH(5) + MOV(0x20, 6) + MOV(0x20, 0) + DELAYV(1) + SKIP() + WAIT(191 , 59) + BRA(dl_line_17) + WAIT(192 ,0) + + dl_line_18 = label("dl_line_18") + MASKV(0) + WAIT(0, 15) + MOV(0x20, 14) + DELAYH(2) + MOV(0x20, 6) + MOV(0x20, 14) + DELAYH(1) + MOV(0x20, 6) + MOV(0x20, 0) + DELAYH(1) + MOV(0x20, 14) + DELAYH(6) + MOV(0x20, 6) + MOV(0x20, 14) + DELAYH(2) + MOV(0x20, 6) + MOV(0x20, 0) + DELAYH(2) + MOV(0x20, 14) + DELAYH(2) + MOV(0x20, 6) + MOV(0x20, 0) + DELAYH(3) + MOV(0x20, 14) + DELAYH(2) + MOV(0x20, 0) + MOV(0x20, 14) + DELAYH(1) + MOV(0x20, 6) + MOV(0x20, 0) + DELAYV(1) + SKIP() + WAIT(199 , 59) + BRA(dl_line_18) + WAIT(200 ,0) + + dl_line_19 = label("dl_line_19") + MASKV(0) + WAIT(0, 15) + MOV(0x20, 14) + DELAYH(2) + MOV(0x20, 6) + MOV(0x20, 0) + MOV(0x20, 14) + DELAYH(1) + MOV(0x20, 6) + MOV(0x20, 0) + MOV(0x20, 14) + DELAYH(2) + MOV(0x20, 6) + MOV(0x20, 0) + MOV(0x20, 14) + DELAYH(1) + MOV(0x20, 6) + MOV(0x20, 14) + DELAYH(5) + MOV(0x20, 6) + MOV(0x20, 14) + DELAYH(2) + MOV(0x20, 6) + MOV(0x20, 0) + DELAYH(3) + MOV(0x20, 14) + DELAYH(2) + MOV(0x20, 0) + DELAYH(1) + MOV(0x20, 14) + DELAYH(1) + MOV(0x20, 6) + MOV(0x20, 0) + DELAYV(1) + SKIP() + WAIT(207 , 59) + BRA(dl_line_19) + WAIT(208 ,0) + + dl_line_20 = label("dl_line_20") + MASKV(0) + WAIT(0, 15) + MOV(0x20, 14) + DELAYH(2) + MOV(0x20, 6) + MOV(0x20, 0) + DELAYH(1) + MOV(0x20, 14) + DELAYH(1) + MOV(0x20, 6) + MOV(0x20, 14) + DELAYH(2) + MOV(0x20, 6) + MOV(0x20, 0) + MOV(0x20, 14) + DELAYH(1) + MOV(0x20, 6) + MOV(0x20, 0) + MOV(0x20, 14) + DELAYH(4) + MOV(0x20, 6) + MOV(0x20, 14) + DELAYH(6) + MOV(0x20, 0) + MOV(0x20, 14) + DELAYH(2) + MOV(0x20, 0) + DELAYH(2) + MOV(0x20, 14) + DELAYH(1) + MOV(0x20, 6) + MOV(0x20, 0) + DELAYV(1) + SKIP() + WAIT(215 , 59) + BRA(dl_line_20) + + dl_line_21 = label("dl_line_21") + WAIT(224 ,0) + + dl_line_22 = label("dl_line_22") + MASKV(0) + WAIT(0, 15) + MOV(0x20, 0) + DELAYH(27) + MOV(0x20, 2) + DELAYH(1) + MOV(0x20, 0) + DELAYH(2) + MOV(0x20, 2) + DELAYH(2) + MOV(0x20, 0) + DELAYV(1) + SKIP() + WAIT(231 , 59) + BRA(dl_line_22) + WAIT(232 ,0) + + dl_line_23 = label("dl_line_23") + MASKV(0) + WAIT(0, 15) + MOV(0x20, 0) + DELAYH(28) + MOV(0x20, 2) + MOV(0x20, 0) + DELAYH(2) + MOV(0x20, 2) + MOV(0x20, 0) + MOV(0x20, 2) + MOV(0x20, 0) + DELAYV(1) + SKIP() + WAIT(239 , 59) + BRA(dl_line_23) + WAIT(240 ,0) + + dl_line_24 = label("dl_line_24") + MASKV(0) + WAIT(0, 15) + MOV(0x20, 0) + DELAYH(28) + MOV(0x20, 2) + MOV(0x20, 0) + MOV(0x20, 2) + MOV(0x20, 0) + MOV(0x20, 2) + DELAYH(2) + MOV(0x20, 0) + DELAYV(1) + SKIP() + WAIT(247 , 59) + BRA(dl_line_24) + END() + + label("preserve_ctrl1", is_global=True) + byte(0) + + label("preserve_ec", is_global=True) + byte(0) + + for i in range(0x40): + byte(255) \ No newline at end of file diff --git a/shazzam/Assembler.py b/shazzam/Assembler.py index 6b5e773..8ff78d4 100644 --- a/shazzam/Assembler.py +++ b/shazzam/Assembler.py @@ -7,7 +7,6 @@ class Assembler(): """ This class defines the interface to be implemented by cross-assembler drivers. - The default one is CC65. """ def __init__(self, name: str, exe_path: str): @@ -23,6 +22,14 @@ def get_code_segment(self) -> str: """ pass + def get_vasyl_segment(self) -> str: + """get_code_segment + + Returns: + str: [description] + """ + return "VASYL" + def get_code_format(self): """get_code_format""" pass diff --git a/shazzam/Segment.py b/shazzam/Segment.py index 63e6969..3d76b87 100644 --- a/shazzam/Segment.py +++ b/shazzam/Segment.py @@ -368,8 +368,8 @@ def _gen_listing(self, code, label_size: int): "address": 0, "bytecode": 8 if self.show_address else 0, "label": 40 if self.show_bytecode else 8 if self.show_address else 0, - "instruction": 40+label_size if self.show_bytecode else 8+label_size if self.show_address else label_size, - "cycles": 40+label_size+20 if self.show_bytecode else 38+label_size+20 if self.show_address else label_size+20, + "instruction": 40+label_size+4 if self.show_bytecode else 8+label_size+4 if self.show_address else label_size+4, + "cycles": 40+(label_size*2) if self.show_bytecode else 38+(label_size*2) if self.show_address else (label_size*2)+20, } remaining_bytes_to_process = 0 @@ -504,8 +504,8 @@ def _gen_assembly(self, code, label_size: int): self.logger.debug("Generating assembly code") code_template_index = { "label": 0, - "instruction": label_size, - "cycles": label_size+30, + "instruction": label_size + 5, + "cycles": (label_size*2)+10, } remaining_bytes_to_process = 0 @@ -643,12 +643,12 @@ def gen_code(self, listing: bool = False) -> None: code.append(f'\t\t{self.directive_prefix}segment {self.directive_delimiter}{self.name}{self.directive_delimiter}\n') # get longest label - if len(locals_labels) > 0: - label_size = max(10, len(max(locals_labels)) + 8) + all_labels = locals_labels + globals_labels + if len(all_labels) > 0: + label_size = len(sorted(all_labels, key=len, reverse=True)[0]) else: label_size = 10 - - self.logger.debug(f"Label size: {label_size}") + self.logger.debug(f"Max label size: {label_size}") if listing: code = self._gen_listing(code, label_size) diff --git a/shazzam/macros/vasyl.py b/shazzam/macros/vasyl.py new file mode 100644 index 0000000..7a82cf6 --- /dev/null +++ b/shazzam/macros/vasyl.py @@ -0,0 +1,231 @@ +import math +import logging +from shazzam.py64gen import * +from shazzam.py64gen import RegisterX as x, RegisterY as y, RegisterACC as a +from shazzam.macros.aliases import color, vic + +logger = logging.getLogger("shazzam") + +# Beam Racer * https://beamracer.net +# Video and Display List coprocessor board for the Commodore 64 +# Copyright (C)2019-2020 Mad Hackers Lab +# +# https://github.com/madhackerslab/ +# +# Header file for BeamRacer VASYL chip +# See https://docs.beamracer.net/ for more information. +# +# Compatible with ca65 assembler (part of https://github.com/cc65). + +# Macros for assembling VASYL opcodes. + +# Registers (0xd031-0xd03f, read-write) +VIC_BASE = 0xd000 + +VREG_BASE = 0xd030 +VREG_INT = 0x40 +VREG_MAX = 0x4f +VREG_CONTROL = VREG_BASE + 0x01 +VREG_DLIST = VREG_BASE + 0x02 +VREG_DLISTL = VREG_BASE + 0x02 +VREG_DLISTH = VREG_BASE + 0x03 +VREG_ADR0 = VREG_BASE + 0x04 +VREG_STEP0 = VREG_BASE + 0x06 +VREG_PORT0 = VREG_BASE + 0x07 +VREG_ADR1 = VREG_BASE + 0x08 +VREG_STEP1 = VREG_BASE + 0x0a +VREG_PORT1 = VREG_BASE + 0x0b +VREG_REP0 = VREG_BASE + 0x0c +VREG_REP1 = VREG_BASE + 0x0d +VREG_DLSTROBE = VREG_BASE + 0x0e +VREG_RESERVED = VREG_BASE + 0x0f + +# Internal registers (0xd040-0xd04f, write-only, not system-bus accessible) +VREG_PBS_CONTROL = VREG_BASE + 0x10 +VREG_DLIST2 = VREG_BASE + 0x11 +VREG_DLIST2L = VREG_BASE + 0x11 +VREG_DLIST2H = VREG_BASE + 0x12 +VREG_DL2STROBE = VREG_BASE + 0x13 +VREG_PBS_BASEL = VREG_BASE + 0x14 +VREG_PBS_BASEH = VREG_BASE + 0x15 +VREG_PBS_CYCLE_START = VREG_BASE + 0x16 +VREG_PBS_CYCLE_STOP = VREG_BASE + 0x17 +VREG_PBS_STEPL = VREG_BASE + 0x18 +VREG_PBS_STEPH = VREG_BASE + 0x19 +VREG_PBS_PADDINGL = VREG_BASE + 0x1a +VREG_PBS_PADDINGH = VREG_BASE + 0x1b +VREG_PBS_XORBYTE = VREG_BASE + 0x1c +VREG_PBS_RESERVED0 = VREG_BASE + 0x1d +VREG_PBS_RESERVED1 = VREG_BASE + 0x1e +VREG_PBS_RESERVED2 = VREG_BASE + 0x1f + + +# Opcode masks and values. +# +# *_MASK has "one" bits in locations which must be matching with +# corresponding *_VALUE bits to recognize given instruction. For instance: +# lda #opcode +# and #VASYL_BADLINE_MASK +# cmp #VASYL_BADLINE_VALUE +# beq opcode_is_badline_instruction + +VASYL_BADLINE_MASK = 0b11111000 +VASYL_BADLINE_VALUE = 0b10101000 +VASYL_BRA_MASK = 0b11111111 +VASYL_BRA_VALUE = 0b10100011 +VASYL_DECAB_MASK = 0b11111110 +VASYL_DECAB_VALUE = 0b10100000 +VASYL_DELAYH_MASK = 0b11111000 # also includes the mask for MASKH +VASYL_DELAYH_VALUE = 0b10110000 +VASYL_DELAYV_MASK = 0b11111000 # also includes the mask for MASKV +VASYL_DELAYV_VALUE = 0b10111000 +VASYL_IRQ_MASK = 0b11111111 +VASYL_IRQ_VALUE = 0b10100010 +VASYL_MASKH_MASK = 0b11111110 +VASYL_MASKH_VALUE = 0b10110100 +VASYL_MASKPH_MASK = 0b11111110 +VASYL_MASKPH_VALUE = 0b10110110 +VASYL_MASKPV_MASK = 0b11111110 +VASYL_MASKPV_VALUE = 0b10111110 +VASYL_MASKV_MASK = 0b11111110 +VASYL_MASKV_VALUE = 0b10111100 +VASYL_MOVI_MASK = 0b11100000 +VASYL_MOVI_VALUE = 0b10000000 +VASYL_MOV_MASK = 0b11000000 +VASYL_MOV_VALUE = 0b11000000 +VASYL_VNOP_MASK = 0b11111111 +VASYL_VNOP_VALUE = 0b10100111 +VASYL_SETAB_MASK = 0b11111110 +VASYL_SETAB_VALUE = 0b10110010 +VASYL_SKIP_MASK = 0b11111111 +VASYL_SKIP_VALUE = 0b10100110 +VASYL_WAITBAD_MASK = 0b11111111 +VASYL_WAITBAD_VALUE = 0b10100100 +VASYL_WAITREP_MASK = 0b11111110 +VASYL_WAITREP_VALUE = 0b10111010 +VASYL_WAIT_MASK = 0b10000000 +VASYL_WAIT_VALUE = 0b00000000 +VASYL_XFER_MASK = 0b11111111 +VASYL_XFER_VALUE = 0b10100101 + + +# Constants +MEMBANK_COUNT = 8 + +CONTROL_RAMBANK_BIT = 0 # bits 0-2 +CONTROL_DLIST_ON_BIT = 3 +CONTROL_RAMBANK_MASK = (0b111 << CONTROL_RAMBANK_BIT) +CONTROL_PORT_READ_ENABLE_BIT = 4 +CONTROL_GRAYDOT_KILL_BIT = 5 +CONTROL_PORT_MODE_BIT = 6 +CONTROL_PORT_MODE_COPY = (0b01 << CONTROL_PORT_MODE_BIT) +CONTROL_PORT_MODE_MASK = (0b11 << CONTROL_PORT_MODE_BIT) + +PBS_CONTROL_ACTIVE_BIT = 3 +PBS_CONTROL_RAMBANK_BIT = 0 # bits 0-2 +PBS_CONTROL_RAMBANK_MASK = (0b111 << PBS_CONTROL_RAMBANK_BIT) +PBS_CONTROL_UPDATE_BIT = 4 # bits 4-5 +PBS_CONTROL_UPDATE_MASK = (0b11 << PBS_CONTROL_UPDATE_BIT) +PBS_CONTROL_UPDATE_NONE = (0b00 << PBS_CONTROL_UPDATE_BIT) +PBS_CONTROL_UPDATE_EOL = (0b01 << PBS_CONTROL_UPDATE_BIT) +PBS_CONTROL_UPDATE_ALWAYS = (0b10 << PBS_CONTROL_UPDATE_BIT) +PBS_CONTROL_SWIZZLE_BIT = 6 # bits 6-7 +PBS_CONTROL_SWIZZLE_MASK = (0b11 << PBS_CONTROL_SWIZZLE_BIT) +PBS_CONTROL_SWIZZLE_NONE = (0b00 << PBS_CONTROL_SWIZZLE_BIT) +PBS_CONTROL_SWIZZLE_MIRROR = (0b01 << PBS_CONTROL_SWIZZLE_BIT) +PBS_CONTROL_SWIZZLE_MULTIMIRROR = (0b10 << PBS_CONTROL_SWIZZLE_BIT) + +def WAIT(v, h): + word(((v) & 0x01ff) | (((h) & 0x3f) << 9)) + +def DELAYH(v, h=None): + if h is None: # this means only one arg was given so "v" actually the horizontal delay + byte([0b10110000, ((v) & 0x3f)]) + else: + byte([0b10110000, (((v) & 0x03) << 6) | ((h) & 0x3f)]) + +def DELAYV(v): + word((0b10111000 << 8) | ((v) & 0x01ff)) + +def MASKH(h): + byte(0b10110100, ((h) & 0x3f)) + +def MASKV(v): + word((0b10111100 << 8) | ((v) & 0x01ff)) + +def MASKPH(h): + byte(0b10110110, ((h) & 0x3f)) + +def MASKPV(v): + word((0b10111110 << 8)| ((v) & 0x01ff)) + +def SETA(v): + byte(0b10110010, ((v) & 0xff)) + +def SETB(v): + byte(0b10110011, ((v) & 0xff)) + +def DECA(): + byte(0b10100000) + +def DECB(): + byte(0b10100001) + +def MOV(reg, value): + if ((reg) & 0xff) < VREG_MAX and (((reg & 0xff00) == VIC_BASE) or ((reg & 0xff00) == 0x0) ): + if ((reg) & 0xff) < VREG_INT: + byte([0xc0 | ((reg) & 0x3f), ((value) & 0xff)]) + else: + byte([0x80 | (((reg) - VREG_INT) & 0x0f), ((value) & 0xff)]) + else: + raise ValueError(f"MOV: register out of range: {reg:02X}") + +def SKIP(): + byte(0b10100110) + +def IRQ(): + byte(0b10100010) + +def VNOP(): + byte(0b10100111) + +def WAITBAD(): + byte(0b10100100) + +def BADLINE(l): + byte(0b10101000 | ((l) & 0x07)) + +def XFER(reg, c): + if ((reg) & 0xff) <= VREG_MAX and ((((reg) & 0xff00) == VIC_BASE) or (((reg) & 0xff00) == 0x0)): + byte(0b10100101, (((c) & 1) << 7) | ((reg) & 0xff)) + else: + raise ValueError(f"XFER: register out of range: {reg:04X}") + +def BRA(target): + if target.value is not None: + if get_current_address() > target.value: + rel_adr = -(get_current_address() + 2 - target.value) + else: + rel_adr = target.value - (get_current_address() + 2) + + if rel_adr >= -128 & rel_adr <= 127: + byte([0b10100011, (rel_adr & 0xff)]) + else: + raise ValueError(f"BRA: target out of range: {rel_adr} bytes away") + else: + raise NotImplementedError() + + #TODO: Fix this part + # ifndef target + # byte(0b10100011, ((target) - (* + 1)) & 0xff) + # logger.warning("Forward BRA assembled without bounds checking.") + # else: + # if ((target) -(* + 1)) >= -128 & ((target) -(* + 1)) <= 127: + # byte(0b10100011, ((target) - (* + 1)) & 0xff) + # else: + # raise ValueError(f"BRA: target out of range: {target} is {(target) -(* + 1)} bytes away") + +def END(): + WAIT(0x1ff, 0x3f) + + diff --git a/shazzam/macros/vlib.py b/shazzam/macros/vlib.py new file mode 100644 index 0000000..b6bad46 --- /dev/null +++ b/shazzam/macros/vlib.py @@ -0,0 +1,247 @@ +import math +import logging +from shazzam.py64gen import * +from shazzam.py64gen import RegisterX as x, RegisterY as y, RegisterACC as a +from shazzam.macros.aliases import color, vic +from shazzam.macros.vasyl import * + +logger = logging.getLogger("shazzam") + +# BeamRacer * https://beamracer.net +# Video and Display List coprocessor board for the Commodore 64 +# Copyright (C)2019-2020 Mad Hackers Lab +# +# https://github.com/madhackerslab/ +# +# Header file for BeamRacer VASYL chip +# See https://docs.beamracer.net/ for more information. +# +# Compatible with ca65 assembler (part of https://github.com/cc65). + +tmp_ptr = 251 +tmp_ptr2 = 253 + +def init(vasyl_segment_load, vasyl_segment_size, autostart : bool = False): + + import shazzam.macros.vasyl + + # this only gets assembled if there is no knocking ahead of it + # .ifnref knock_knock + if autostart: + copy_and_activate_dlist() + + label("autostart") + jsr(at("knock_knock")) + + ldx(imm(0x2e)) + label("vlib_preserve_loop") + lda(at(0xd000), x) + sta(at("vlib_preserve_vic"), x) + dex() + bpl(rel_at("vlib_preserve_loop")) + + jsr(at("copy_and_activate_dlist")) + label("vlib_keyloop") + jsr(at(0xffe4)) # check if key pressed + beq(rel_at("vlib_keyloop")) + + cmp(imm(3)) + beq(rel_at("vlib_no_restore")) + + lda(imm(0)) # turn off display list + sta(at(VREG_CONTROL)) + + ldx(imm(0x2e)) + label("vlib_preserve_loop2") + lda(at("vlib_preserve_vic"), x) + sta(at(0xd000), x) + dex() + bpl(rel_at("vlib_preserve_loop2")) + label("vlib_no_restore") + rts() + label("vlib_preserve_vic") + byte([0] * 47) # .res 47 + # .endif + + +# .ifref knock_knock + # Attempt activation of the BeamRacer, on failure (BeamRacer missing) exits the program + label("knock_knock", is_global=True) + ldx(imm(255)) + cpx(at(VREG_CONTROL)) + bne(rel_at("vlib_active")) + + lda(imm(0x42)) + sta(at(VREG_CONTROL)) + + lda(imm(0x52)) + sta(at(VREG_CONTROL)) + cpx(at(VREG_CONTROL)) + bne(rel_at("vlib_active")) + + # exit the program + pla() + pla() + label("vlib_active") + # jsr(at("print_info")) + rts() +# .endif + +def copy_and_activate_dlist(vasyl_segment_load, vasyl_segment_size): + copy_dlist(vasyl_segment_load, vasyl_segment_size) + +# .ifref copy_and_activate_dlist + # Copy a dlist to local RAM and activate it: + # the contents of segment "VASYL" is copied to address 0 in local RAM and then the display list is activated. + label("copy_and_activate_dlist") + jsr(at("copy_dlist")) + + # start using the new Display List + lda(imm("dl_start")) + sta(at(VREG_DLIST + 1)) + lda(imm(1 << CONTROL_DLIST_ON_BIT)) + sta(at(VREG_CONTROL)) + rts() +# .endif + +def copy_dlist(vasyl_segment_load, vasyl_segment_size): + copy_to_lmem() + +# .ifref copy_dlist + # Copy a dlist to local RAM: + # the contents of segment "VASYL" is copied to address 0 in local RAM. + label("copy_dlist") + lda(imm(vasyl_segment_load & 0xff)) + sta(at(tmp_ptr)) + lda(imm(vasyl_segment_load >> 8)) + sta(at(tmp_ptr + 1)) + lda(imm(0)) + sta(at(tmp_ptr2)) + sta(at(tmp_ptr2) + 1) + + lda(imm(vasyl_segment_size % 0xff)) + ldx(imm(vasyl_segment_size >> 8)) + jmp(at("copy_to_lmem")) +# .endif + +def copy_to_lmem(): +# .ifref copy_to_lmem + # Copy data to lram: + # 0xFA/0xFB - source + # 0xFC/0xFD - destination (in VASYL's local RAM) + # AX - lo/hi byte count + label("copy_to_lmem") + ldy(at(tmp_ptr2)) + sty(at(VREG_ADR0)) + ldy(at(tmp_ptr2) + 1) + sty(at(VREG_ADR0) + 1) + ldy(imm(1)) + sty(at(VREG_STEP0)) + + clc() + adc(at(tmp_ptr)) + sta(at(tmp_ptr2)) + txa() + adc(at(tmp_ptr) + 1) + sta(at(tmp_ptr2) + 1) + + ldy(imm(0)) + label("vlib_loop") + lda(ind_at(tmp_ptr), y) + sta(at(VREG_PORT0)) + inc(at(tmp_ptr)) + bne(rel_at("vlib_no_carry")) + inc(at(tmp_ptr) + 1) + label("vlib_no_carry") + lda(at(tmp_ptr2)) + cmp(at(tmp_ptr)) + bne(rel_at("vlib_loop")) + lda(at(tmp_ptr2) + 1) + cmp(at(tmp_ptr) + 1) + bne(rel_at("vlib_loop")) + rts() +# .endif + +def set_pages(): +# .ifref set_pages + # Set VASYL memory pages to a value. + # A - value + # Y - starting page + # X - page count + UNROLL_FACTOR = 8 + label("set_pages") + sty(at(VREG_ADR0) + 1) + ldy(imm(1)) + sty(at(VREG_STEP0)) + dey() # 0 + sty(at(VREG_ADR0)) + dey() # 255 + sta(at(VREG_PORT0)) # Store one byte. + label("vlib_next_page") + sty(at(VREG_REP0)) # Repeat 255 times on first iteration, 256 on subsequent ones. + label("vlib_waitrep") + ldy(at(VREG_REP0)) # Wait for REP transfer to end. + bne(rel_at("vlib_waitrep")) + dex() + bne(rel_at("vlib_next_page")) + rts() +# .endif + +def print_info(): +# .ifref print_info + # Print information about VASYL and VIC-II versions. + label("print_info") + lda(imm("version_text")) + jsr(at(0xab1e)) # print null terminated string + lda(at(VREG_DLSTROBE)) + lsr(a) + lsr(a) + lsr(a) + tax() + + lda(imm(0)) + jsr(at(0xbdcd)) # print XA as unsigned integer + lda(imm("type_text")) + jsr(at(0xab1e)) # print null terminated string + + lda(at(VREG_DLSTROBE)) + andr(imm(0x07)) + tax() + lda(at("type_table_lo"), x) + ldy(at("type_table_hi"), x) + jmp(at(0xab1e)) # print null terminated string + + label("version_text") + byte(["vasyl ID: ", 0]) + + label("type_text") + byte([ 0xd,"vic-ii type : ", 0]) + + l_ntsc = label("type_ntsc") + byte(["ntsc (6567r8 or 8562)", 0]) + + l_pal = label("type_pal") + byte(["pal (6569 or 8565)", 0]) + + l_paln = label("type_paln") + byte(["pal-n (6572)", 0]) + + l_ntsc_old = label("type_ntsc_old") + byte(["ntsc (6567r56a)", 0]) + + l_unknown = label("type_unknown") + byte(["unknown", 0]) + + label("type_table_lo") + byte([l_ntsc.value & 0xff, l_unknown.value & 0xff, l_unknown.value & 0xff, l_ntsc_old.value & 0xff]) + byte([l_paln.value & 0xff, l_unknown.value & 0xff, l_pal.value & 0xff, l_unknown.value & 0xff]) + + label("type_table_hi") + byte([l_ntsc.value >> 8, l_unknown.value >> 8, l_unknown.value >> 8, l_ntsc_old.value >> 8]) + byte([l_paln.value >> 8, l_unknown.value >> 8, l_pal.value >> 8, l_unknown.value >> 8]) + +# .endif diff --git a/shazzam/py64gen.py b/shazzam/py64gen.py index 91fe21d..86f57ca 100644 --- a/shazzam/py64gen.py +++ b/shazzam/py64gen.py @@ -520,32 +520,27 @@ def byte(value: Any) -> bytearray: elif isinstance(value, list): ret_array = [] for v in value: - ret_array.append(g._CURRENT_CONTEXT.add_byte(Immediate(value=v))) + # ret_array.append(g._CURRENT_CONTEXT.add_byte(Immediate(value=v))) + ret_array.append(byte(v)) return ret_array else: raise ValueError("Immediate must be an int or a string") -def word(value: int, label: str) -> None: +def word(value: int) -> None: """[summary] Args: value (int): [description] - label (str): [description] Raises: RuntimeError: [description] ValueError: [description] """ - global _CURRENT_CONTEXT - if g._CURRENT_CONTEXT is None: - raise RuntimeError("No segment defined!") - if value > 0xffff: raise ValueError(f"Value exceed word size: {value}") - g._CURRENT_CONTEXT.add_word(value, label) - + byte([value >> 8, value & 0xff]) def incbin(data: bytearray) -> None: """[summary] @@ -618,9 +613,6 @@ def _check_segments_overlap(code_segment: str = "CODE") -> None: raise ValueError( f"The segments {g._PROGRAM.segments[i-1].name} and {g._PROGRAM.segments[i].name} overlap!") -# --------------------------------------------------------------------- -# Utils -# --------------------------------------------------------------------- def _get_segment_by_address(address: int, segments: List[Segment]) -> Segment: """_get_segment_by_address @@ -640,6 +632,10 @@ def _get_segment_by_address(address: int, segments: List[Segment]) -> Segment: raise ValueError(f"No Segment found starting at {address:04X}") +# --------------------------------------------------------------------- +# Utils +# --------------------------------------------------------------------- + _funcs = {} def generate(func, program_name: str) -> None: """generate