prog8/python/il65/main.py

129 lines
5.8 KiB
Python
Raw Normal View History

"""
2017-12-25 15:00:25 +00:00
Programming Language for 6502/6510 microprocessors, codename 'Sick'
This is the main program that drives the rest.
2018-01-08 02:31:23 +00:00
Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
"""
import time
import os
2018-01-14 14:18:50 +00:00
import re
import argparse
2017-12-27 22:45:22 +00:00
import subprocess
2018-01-09 01:40:32 +00:00
from .compile import PlyParser
from .optimize import optimize
from .plylex import print_bold
2018-03-10 19:32:34 +00:00
from .plyparse import ProgramFormat, Module
2018-01-14 14:18:50 +00:00
class Assembler64Tass:
def __init__(self, format: ProgramFormat) -> None:
self.format = format
def assemble(self, inputfilename: str, outputfilename: str) -> None:
args = ["64tass", "--ascii", "--case-sensitive", "-Wall", "-Wno-strict-bool",
"--dump-labels", "--vice-labels", "-l", outputfilename+".vice-mon-list",
2018-01-23 20:20:01 +00:00
"--no-monitor", "--output", outputfilename, inputfilename]
2018-01-14 14:18:50 +00:00
if self.format in (ProgramFormat.PRG, ProgramFormat.BASIC):
args.append("--cbm-prg")
elif self.format == ProgramFormat.RAW:
args.append("--nostart")
else:
raise ValueError("don't know how to create code format "+str(self.format))
try:
if self.format == ProgramFormat.PRG:
print("\nCreating C-64 prg.")
elif self.format == ProgramFormat.RAW:
print("\nCreating raw binary.")
try:
subprocess.check_call(args)
except FileNotFoundError as x:
raise SystemExit("ERROR: cannot run assembler program: "+str(x))
except subprocess.CalledProcessError as x:
raise SystemExit("assembler failed with returncode " + str(x.returncode))
def generate_breakpoint_list(self, program_filename: str) -> str:
breakpoints = []
2018-01-23 20:20:01 +00:00
vice_mon_file = program_filename + ".vice-mon-list"
with open(vice_mon_file, "rU") as f:
2018-01-14 14:18:50 +00:00
for line in f:
2018-01-23 20:20:01 +00:00
match = re.fullmatch(r"al (?P<address>\w+) \S+_il65_breakpoint_\d+.?", line, re.DOTALL)
2018-01-14 14:18:50 +00:00
if match:
breakpoints.append("$" + match.group("address"))
2018-01-23 20:20:01 +00:00
with open(vice_mon_file, "at") as f:
2018-01-14 14:18:50 +00:00
print("; vice monitor breakpoint list now follows", file=f)
print("; {:d} breakpoints have been defined here".format(len(breakpoints)), file=f)
print("del", file=f)
for b in breakpoints:
print("break", b, file=f)
2018-01-23 20:20:01 +00:00
return vice_mon_file
def main() -> None:
description = "Compiler for IL65 language, code name 'Sick'"
ap = argparse.ArgumentParser(description=description)
ap.add_argument("-o", "--output", help="output directory")
2018-03-10 19:32:34 +00:00
ap.add_argument("-c", "--codegenerator", choices=["6502", "tinyvm"], default="tinyvm", help="what code generator to use")
2018-02-02 21:42:09 +00:00
ap.add_argument("-f", "--enablefloat", action="store_true", help="enable C64 (mflpt5) floating point operations")
2017-12-27 22:45:22 +00:00
ap.add_argument("-no", "--nooptimize", action="store_true", help="do not optimize the parse tree")
ap.add_argument("-sv", "--startvice", action="store_true", help="autostart vice x64 emulator after compilation")
ap.add_argument("sourcefile", help="the source .ill/.il65 file to compile")
args = ap.parse_args()
assembly_filename = os.path.splitext(args.sourcefile)[0] + ".asm"
program_filename = os.path.splitext(args.sourcefile)[0] + ".prg"
if args.output:
os.makedirs(args.output, mode=0o700, exist_ok=True)
assembly_filename = os.path.join(args.output, os.path.split(assembly_filename)[1])
program_filename = os.path.join(args.output, os.path.split(program_filename)[1])
print("\n" + description)
start = time.perf_counter()
2018-01-09 01:40:32 +00:00
print("\nParsing program source code.")
2018-02-02 21:42:09 +00:00
parser = PlyParser(enable_floats=args.enablefloat)
2018-01-09 01:40:32 +00:00
parsed_module = parser.parse_file(args.sourcefile)
if parsed_module:
2017-12-27 22:45:22 +00:00
if args.nooptimize:
2018-01-29 20:10:06 +00:00
print_bold("Optimizations disabled!")
2017-12-21 22:05:35 +00:00
else:
2018-01-09 23:44:11 +00:00
print("\nOptimizing code.")
2018-01-09 01:40:32 +00:00
optimize(parsed_module)
2018-03-10 19:32:34 +00:00
if args.codegenerator == "tinyvm":
generate_tinyvm_code(parsed_module, args.enablefloat, assembly_filename, start)
else:
generate_6502_code(parsed_module, args.enablefloat, args.startvice, program_filename, assembly_filename, start)
def generate_tinyvm_code(module: Module, float_enabled: bool, assembly_filename: str, compilationstart: float) -> None:
print("\nGenerating tinyvm code.")
from il65.codegen.tinyvm.generate import AssemblyGenerator
cg = AssemblyGenerator(module, float_enabled)
cg.generate(assembly_filename)
duration_total = time.perf_counter() - compilationstart
print("Compile duration: {:.2f} seconds".format(duration_total))
print()
def generate_6502_code(module: Module, float_enabled: bool, startvice: bool,
program_filename: str, assembly_filename: str, compilationstart: float) -> None:
print("\nGenerating 6502 assembly code.")
from il65.codegen.mos6502.generate import AssemblyGenerator
cg = AssemblyGenerator(module, float_enabled)
cg.generate(assembly_filename)
assembler = Assembler64Tass(module.format)
assembler.assemble(assembly_filename, program_filename)
mon_command_file = assembler.generate_breakpoint_list(program_filename)
duration_total = time.perf_counter() - compilationstart
print("Compile duration: {:.2f} seconds".format(duration_total))
size = os.path.getsize(program_filename)
print("Output size: {:d} bytes".format(size))
print_bold("Output file: " + program_filename)
print()
if startvice:
print("Autostart vice emulator...")
# "-remotemonitor"
cmdline = ["x64", "-moncommands", mon_command_file,
"-autostartprgmode", "1", "-autostart-warp", "-autostart", program_filename]
with open(os.devnull, "wb") as shutup:
subprocess.call(cmdline, stdout=shutup)