!!!SPL
a Forth-style concatenative stack language. The compiler is (currently) written in Python and emits 6502 assembly code.
[http://sourceforge.net/projects/spl65/|Originally created by Ron Kneusel] for the Apple II, adapted for the Atari 8bit and enhanced by Carsten Strotmann (cas@strotmann.de).
SPL is still work-in-progress, it has some "rough-edges" and might fail in certain places. It has many bugs. However, it works fine for some developers.
Applications in SPL:
* [ATR Copy Center ACC] - A copy tool for the SIO2USB device
!! SPL Source (Python)
{{{
#!/usr/bin/python
#
# SPL compiler
# RTK, 03-Sep-2007
# Last update: 10-Aug-2012
#
# $Id$
#
# This code is freeware, do as you please with it.
#
##########################################################
import os
import sys
import math
import time
from types import *
# Modification date
LAST_MOD = "10-Aug-2012"
#-----------------------------------------------------------------------
# Configuration section. Modify these values to reflect
# your working environment and target system.
#-----------------------------------------------------------------------
# Assembler name and/or path. If the executable is in your
# $PATH just the name is needed. If on Windows, a good place
# to put the as6502.exe file is in the C:\WINDOWS\ directory in
# which case all you need is "as6502" here.
ASSEMBLER = "atasm"
# Library and include paths. If you have environment variables set
# for these, they will be used. If you do not set environment variables
# the default value will be used. Therefore, set the default value to
# the full pathname, trailing / or \ included, to the place where you
# set up the directories, if not using environment variables.
LIB_DIR = os.getenv("SPL_LIB_PATH", default="lib/")
INCLUDE_DIR = os.getenv("SPL_INCLUDE_PATH", default="include/")
# Pathname of blank ProDOS disk image
DISK_IMAGE = LIB_DIR + "blank.dsk"
# Default base output file name and type
BASE_NAME = "out"
BASE_TYPE = "bin"
# Compiler version number
VERSION = "1.0atari"
# Header for assembly output files, must be a list of strings
HEADER = [
"",
"Output from SPL compiler version " + VERSION,
"Generated on " + time.ctime(time.time()),
""
]
# Code start and stack location in RAM
ORG = 0x2200
# Start of variable space, grows downwards
VARTOP= 0x8000 # doesn't clobber the ProDOS BASIC interpreter
# Library equates - case matters
EQUATES = {
"stack" : ORG, # stack address
"sp" : 0x80, # stack pointer
"ex" : 0x81, # xreg value
"ey" : 0x82, # yreg value
"ea" : 0x83, # areg value
"ta" : 0x84, # and 7, scratch 1
"tb" : 0x86, # and 9, scratch 2
"op1" : 0x87, # 1st operand (4 bytes)
"op2" : 0x8B, # 2nd operand (4 bytes)
"res" : 0x8F, # result (8 bytes)
"rem" : 0x96, # remainder (4 bytes)
"tmp" : 0x9A, # scratch (4 bytes)
"sign" : 0x9F, # sign (1 byte)
"outbuf" : 0xA0, # output text buffer (12 bytes)
"inbuf" : 0x400 # input text buffer
}
#
# Library words dependency table. New library words (ie, in lib/)
# must be added to this table. What is listed is the name of every
# routine called (JSR or JMP) by the library word. This table is
# used to include only the routines necessary for the program being compiled.
#
DEPENDENCIES = {
"abs" : ["get_ab","comp_tb","push"],
"accept" : ["get_ta","push"],
"add1" : [],
"add" : ["get_tb","pop","push"],
"addstore" : ["get_ab"],
"and" : ["get_ab","push"],
"booland" : ["get_ab","push"],
"areg" : ["pop"],
"fetch" : ["get_ta","push"],
"dfetch" : ["get_ta","push"],
"cfetch" : ["get_ta", "push"],
"b_and" : ["get_tb","pop","push"],
"bclr" : ["get_ab","push"],
"b_or" : ["get_tb","push"],
"bset" : ["get_ab","push"],
"btest" : ["get_ab","push"],
"btoa" : ["ptrout"],
"btod" : [],
"b_xor" : ["get_tb","pop","push"],
"bye" : [],
"call" : ["pop"],
"chout" : [],
"ch" : ["pop"],
"cls" : [],
"cmove" : ["get_ta", "get_tb"],
"cmoveb" : ["get_ta", "get_op1"],
"comp" : ["pop","push"],
"comp_ta" : [],
"comp_tb" : [],
"copy" : [],
"count" : ["get_ta", "push"],
"cprhex" : [],
"cr" : [],
"crson" : [],
"crsoff" : [],
"ctoggle" : ["pop", "get_ta"],
"csub" : [],
"cv" : ["pop"],
"d32" : [],
"dabs" : ["get_op1","push_op1"],
"d_add" : [],
"dadd" : ["get_ops","push_res"],
"date" : ["push"],
"ddivide" : ["ddiv","push"],
"ddivmod" : ["ddiv","push"],
"ddiv" : ["get_ops","d32","neg"],
"depth" : ["push"],
"d_eq" : ["zerores"],
"deq" : ["get_ops","d_eq","push"],
"d_ge" : ["d_eq","d_gt"],
"dge" : ["get_ops","d_ge","push"],
"d_gt" : ["d_sub","iszero","zerores"],
"dgt" : ["get_ops","d_gt","push"],
"disp" : ["get_ta","chout","udiv16","push"],
"div" : ["get_ta","comp_ta","comp_tb","udiv16","push"],
"d_le" : ["d_eq","d_lt"],
"dle" : ["get_ops","d_le","push"],
"d_lt" : ["d_sub","zerores"],
"dlt" : ["get_ops","d_lt","push"],
"dmod" : ["ddiv","push"],
"dmult" : ["get_ops","neg","m32","push_res"],
"dnegate" : ["get_op1","neg","op1res","push_res"],
"d_ne" : ["d_eq"],
"dne" : ["get_ops","d_ne","push"],
"decaddr" : ["get_ta"],
"deccaddr" : ["pop"],
"dnumber" : ["pop","neg","push"],
"dprhex" : ["get_op1", "cprhex"],
"dprint" : ["get_op2","chout","neg","pntres"],
"drop2" : ["pop"],
"drop" : ["pop"],
"d_sqrt" : ["d_sub"],
"dsqrt" : ["get_op1","d_sqrt","push_res"],
"d_sub" : [],
"dsub" : ["get_ops","d_sub","push_res"],
"dtos" : ["pop"],
"dup2" : ["push"],
"duprint" : ["get_op2","btod","pntres"],
"dprint" : ["get_op2","chout","neg","btod","pntres"],
"dup" : ["push"],
"emit" : ["pop","chout"],
"eq" : ["get_ab","push"],
"exit" : [],
"execute" : ["pop"],
"erase" : ["pop","get_ab"],
"fill" : ["pop","get_ab"],
"fclose" : ["pop","push"],
"fcreate" : ["pop","push"],
"fdestroy" : ["pop","push"],
"feof" : ["pop","push"],
"fetchend" : ["push"],
"fflush" : ["pop","push"],
"fgetc" : ["pop","push"],
"fread" : ["pop","push"],
"fopen" : ["pop","push","pdos_addr"],
"fputc" : ["pop","push"],
"fseek" : ["pop","push"],
"ftell" : ["pop","push"],
"fwrite" : ["pop","push"],
"ge" : ["get_ab","csub","push"],
"get_ab" : ["get_tb","get_ta"],
"get_op1" : ["pop"],
"get_op2" : ["pop"],
"get_ops" : ["get_op2","get_op1"],
"get_ta" : ["pop"],
"get_tb" : ["pop"],
"get_file_info" : ["pop","push"],
"getcwd" : ["pop","push"],
"gt" : ["get_ab","csub","push"],
"incaddr" : ["get_ta"],
"inccaddr" : ["pop"],
"input_s" : ["push"],
"inverse" : [],
"iszero" : [],
"keyp" : ["rdykey","push"],
"key" : ["rdkey","push"],
"le" : ["get_ab","csub","push"],
"lt" : ["get_ab","csub","push"],
"m32" : ["zerores"],
"minus1" : ["pop","push"],
"minus2" : ["pop","push"],
"mod" : ["get_ab","comp_ta","comp_tb","udiv16","push"],
"mult" : ["get_ab","comp_ta","comp_tb","umult16","push"],
"negate" : ["get_ta","comp_ta","push"],
"neg" : ["add1"],
"ne" : ["get_ab","push"],
"nip" : ["get_ta","pop","push"],
"normal" : [],
"not" : ["get_ta","push"],
"n_str" : ["get_ta","ptrout","comp_ta","u_str"],
"number" : ["pop","comp_ta","push"],
"op1res" : [],
"or" : ["get_ab","push"],
"over" : ["push"],
"pad" : ["push"],
"pdos_addr": [],
"plus1" : ["get_ta","push"],
"plus2" : ["get_ta","push"],
"pntres" : ["chout"],
"pop" : [],
"pos" : ["pop"],
"prbuf" : ["chout"],
"prhex2" : ["pop", "cprhex"],
"prhex" : ["pop", "cprhex"],
"primm" : ["chout"],
"print" : ["n_str","prbuf"],
"ptrout" : [],
"push_op1" : ["push"],
"push_op2" : ["push"],
"push_rem" : ["push"],
"push_res" : ["push"],
"push" : [],
"quit" : [],
"read_block" : ["pop","push"],
"rename" : ["pop","push"],
"rdkey" : [],
"rdykey" : [],
"reset" : [],
"rot" : ["get_ta","get_tb","pop","push"],
"setcwd" : ["pop","push"],
"space" : ["chout"],
"set_file_info" : ["pop","push"],
"spfetch" : ["push"],
"stod" : ["push"],
"strcmp" : ["get_ab","push"],
"strcpy" : ["get_ab"],
"strlen" : ["get_ta","push"],
"strmatch" : ["get_ab","push"],
"strpos" : ["get_ab","push"],
"sub1" : [],
"sub" : ["get_ab","csub","push"],
"swapcell" : [],
"swap2" : ["get_ta","get_tb","pop","push"],
"swap" : ["get_ta","get_tb","push"],
"time" : ["push"],
"to_r" : ["pop"],
"from_r" : ["push"],
"r_fetch" : ["to_r", "from_r", "dup"],
"tooutbuf" : ["u_str"],
"store16" : ["get_ta","pop"],
"store32" : ["get_ta","pop"],
"store8" : ["get_ta","pop"],
"udiv16" : [],
"udiv" : ["get_ab","udiv16","push"],
"ugt" : ["get_ab","push"],
"ult" : ["get_ab","push"],
"umod" : ["get_ab","udiv16","push"],
"umult16" : [],
"umult" : ["get_ab","umult16","push"],
"uncount" : ["pop","push"],
"uprint" : ["u_str","prbuf"],
"ushiftl" : ["pop","push"],
"ushiftr" : ["pop","push"],
"u_str" : ["get_ta","btoa"],
"within" : ["over", "to_r", "from_r", "ult"],
"write_block": ["pop","push"],
"xor" : ["get_ab","push"],
"xreg" : ["pop"],
"yreg" : ["pop"],
"zerores" : []
}
#
# Mapping between library words and assembly labels.
#
LIBRARYMAP = {
"abs" : "abs",
"accept" : "accept",
"+" : "add",
"++" : "incaddr",
"c++" : "inccaddr",
"+!" : "addstore",
"areg" : "areg",
"at" : "pos",
"@" : "fetch",
"d@" : "dfetch",
"c@" : "cfetch",
">outbuf" : "tooutbuf",
"and" : "b_and",
"bclr" : "bclr",
"or" : "b_or",
"bset" : "bset",
"btest" : "btest",
"bye" : "bye",
"xor" : "b_xor",
"call" : "call",
"ch" : "ch",
"cls" : "cls",
"cmove" : "cmove",
"cmove>" : "cmoveb",
"count" : "count",
"~" : "comp",
"cr" : "cr",
"crson" : "crson",
"crsoff" : "crsoff",
"ctoggle" : "ctoggle",
"cv" : "cv",
"dabs" : "dabs",
"date" : "date",
"d+" : "dadd",
"d/" : "ddivide",
"d/mod" : "ddivmod",
"depth" : "depth",
"d=" : "deq",
"d>=" : "dge",
"d>" : "dgt",
"disp" : "disp",
"d<=" : "dle",
"d<" : "dlt",
"dmod" : "dmod",
"d*" : "dmult",
"dnegate" : "dnegate",
"d<>" : "dne",
"dnumber" : "dnumber",
"d.$" : "dprhex",
"d." : "dprint",
"2drop" : "drop2",
"drop" : "drop",
"dsqrt" : "dsqrt",
"d-" : "dsub",
"2dup" : "dup2",
"du." : "duprint",
"dup" : "dup",
"emit" : "emit",
"end@" : "fetchend",
"exit" : "exit",
"erase" : "erase",
"=" : "eq",
"execute" : "execute",
"fclose" : "fclose",
"fdestroy" : "fdestroy",
"feof" : "feof",
"fflush" : "fflush",
"finfo@" : "get_file_info",
"finfo!" : "set_file_info",
"fgetc" : "fgetc",
"fill" : "fill",
"fopen" : "fopen",
"fputc" : "fputc",
"fread" : "fread",
"fseek" : "fseek",
"fwrite" : "fwrite",
"fcreate" : "fcreate",
"ftell" : "ftell",
"getcwd" : "getcwd",
">=" : "ge",
">" : "gt",
"input" : "input_s",
"inverse" : "inverse",
"keyp" : "keyp",
"key" : "key",
"/" : "div",
"<=" : "le",
"<" : "lt",
"1-" : "minus1",
"2-" : "minus2",
"mod" : "mod",
"*" : "mult",
"negate" : "negate",
"<>" : "ne",
"nip" : "nip",
"normal" : "normal",
"not" : "not",
"number" : "number",
"over" : "over",
"pad" : "pad",
"1+" : "plus1",
"2+" : "plus2",
".2$" : "prhex2",
".$" : "prhex",
"." : "print",
"pos" : "pos",
"quit" : "quit",
"read_block" : "read_block",
"rename" : "rename",
"reset" : "reset",
"rot" : "rot",
">r" : "to_r",
"r>" : "from_r",
"r@" : "r_fetch",
"setcwd" : "setcwd",
"space" : "space",
"sp@" : "spfetch",
"strcmp" : "strcmp",
"strcpy" : "strcpy",
"strlen" : "strlen",
"strmatch" : "strmatch",
"strpos" : "strpos",
"time" : "time",
"-" : "sub",
"--" : "decaddr",
"c--" : "deccaddr",
"2swap" : "swap2",
"swap" : "swap",
"!" : "store16",
"d!" : "store32",
"c!" : "store8",
"uncount" : "uncount",
"u/" : "udiv",
"u>" : "ugt",
"u<" : "ult",
"umod" : "umod",
"u*" : "umult",
"u." : "uprint",
"<<" : "ushiftl",
">>" : "ushiftr",
"><" : "swapcell",
"within" : "within",
"write_block": "write_block",
"xreg" : "xreg",
"yreg" : "yreg"
}
#-----------------------------------------------------------------------
# Compiler source code follows.
#-----------------------------------------------------------------------
######################################################
# SPLCompiler
#
class SPLCompiler:
"""Implements a compiler from SPL to 6502 assembly code."""
#-------------------------------------------------
# GetLabel
#
def GetLabel(self, name):
"""Return a unique label."""
label = "A%05d" % self.counter
self.counter += 1
return name+"_"+label
#-------------------------------------------------
# Decimal
#
def Decimal(self, s):
"""Interpret s as a decimal number."""
sign = +1
start = 0
if (s[0] == "-"):
sign = -1
start = 1
elif (s[0] == "+"):
start = 1
i = start
n = 0
msg = "Illegal decimal number: "
while (i < len(s)):
if (s[i] in "0123456789"):
n = 10*n + int(s[i])
elif (s[i] == "#") or (s[i] == "%"):
if (i != len(s)-1):
self.Error(msg + s)
else:
self.Error(msg + s)
i += 1
return n*sign
#-------------------------------------------------
# Binary
#
def Binary(self, s):
"""Interpret s as a binary number."""
n = i = 0
msg = "Illegal binary number: "
while (i < len(s)):
if (s[i] in "01"):
n = 2*n + int(s[i])
elif (s[i] == "#") or (s[i] == "%"):
if (i != len(s)-1):
self.Error(msg + s)
else:
self.Error(msg + s)
i += 1
return n
#-------------------------------------------------
# Hexadecimal
#
def Hexadecimal(self, s):
"""Interpret s as a hexadecimal number."""
n = i = 0
msg = "Illegal hexadecimal number: "
while (i < len(s)):
if (s[i] in "0123456789"):
n = 16*n + int(s[i])
elif (s[i].upper() in "ABCDEF"):
n = 16*n + (ord(s[i].upper()) - ord("A") + 10)
elif (s[i] == "#") or (s[i] == "%"):
if (i != len(s)-1):
self.Error(msg + s)
else:
self.Error(msg + s)
i += 1
return n
#-------------------------------------------------
# Number
#
def Number(self, s):
"""Return s as a number, if possible."""
if (type(s) != StringType):
n = 0
elif (s == ""):
n = 0
elif (len(s) < 2):
n = self.Decimal(s)
elif (s[0:2].upper() == "0X"):
n = self.Hexadecimal(s[2:])
elif (s[0] == "$"):
n = self.Hexadecimal(s[1:])
elif (s[0:2].upper() == "0B"):
n = self.Binary(s[2:])
elif (s[0] == "%"):
n = self.Binary(s[1:])
else:
n = self.Decimal(s)
return n
#-------------------------------------------------
# CheckDecimal
#
def CheckDecimal(self, s):
"""True if string s is a valid decimal number."""
try:
n = int(s)
except:
return False
return True
#-------------------------------------------------
# CheckHexadecimal
#
def CheckHexadecimal(self, s):
"""True if string s is a valid hex number."""
for t in s.upper():
if (t not in "0123456789ABCDEF"):
return False
return True
#-------------------------------------------------
# CheckBinary
#
def CheckBinary(self, s):
"""True if string s is a valid binary number."""
for t in s:
if (t not in "01"):
return False
return True
#-------------------------------------------------
# isNumber
#
def isNumber(self, s):
"""Return true if string s is a valid number."""
# It must be a string
if (type(s) != StringType):
return False
elif (s == ""):
return False
elif (len(s) < 2):
return self.CheckDecimal(s)
elif (s[0:2].upper() == "0X"):
return self.CheckHexadecimal(s[2:])
elif(s[0] == "$"):
return self.CheckHexadecimal(s[2:])
elif (s[0:2].upper() == "0B"):
return self.CheckBinary(s[2:])
elif (s[0] == "%"):
return self.CheckBinary(s[2:])
else:
return True
#-------------------------------------------------
# InvalidName
#
def InvalidName(self, name):
"""Returns true if the given name is not valid."""
if (type(name) != StringType):
return True
if (name == ""):
return True
if ((name[0].upper() < "A") or (name[0].upper() > 'Z')) and (name[0] != "_"):
return True
for c in name:
if (c.upper() not in "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_"):
return True
return False
#-------------------------------------------------
# Error
#
def Error(self, msg):
"""Output an error message and quit."""
raise ValueError("In " + self.funcName + " : " + self.Token + " : " + str(msg))
#-------------------------------------------------
# ParseCommandLine
#
def ParseCommandLine(self):
"""Parse the command line arguments."""
# Defaults
self.sys = False
self.warn = False
# List of all source code names
self.files = []
# Default output file base filename and type
self.outname = BASE_NAME
self.outtype = BASE_TYPE
next = 0
for s in sys.argv[1:]:
if (s == "-o"):
next = 1
elif (s == "-t"):
next = 2
elif (s == "-org"):
next = 3
elif (s == "-var"):
next = 4
elif (s == "-stack"):
next = 5
elif (s == "-sys"):
self.sys = True
elif (s == "-warn"):
self.warn = True
elif (s == "-prodos"):
self.VARTOP = 0x89ff
else:
if (next == 1):
self.outname = s
next = 0
elif (next == 2):
self.outtype = s
next = 0
elif (next == 3):
self.cmdOrigin = self.Number(s)
next = 0
elif (next == 4):
self.VARTOP = self.Number(s)
next = 0
elif (next == 5):
EQUATES["stack"] = self.Number(s)
next = 0
else:
self.files.append(s)
#-------------------------------------------------
# LoadFiles
#
def LoadFiles(self):
"""Load the source code on the command line."""
self.source = ""
for fname in self.files:
# If no .spl extension, add it
if fname.find(".spl") == -1:
fname += ".spl"
try:
# Open the file as given on the command line
f = open(fname,"r")
except:
# Is it in the include directory?
try:
f = open(INCLUDE_DIR + fname, "r")
except:
self.Error("Unable to locate %s" % fname)
# Append to the source string
self.source += (f.read() + "\n")
f.close()
#-------------------------------------------------
# Tokenize
#
def Tokenize(self):
"""Tokenize the input source code."""
if self.source == "":
return
self.tokens = []
inToken = inString = inComment = inCode = False
delim = ""
t = ""
for c in self.source:
if (inString):
if (c == delim):
t += c
self.tokens.append(t)
t = ""
inString = False
else:
t += c
elif (inComment):
if (c == "\n"):
inComment = False
elif (c == ")"):
inComment = False
elif (inToken):
if (c <= " "):
inToken = False
self.tokens.append(t)
t = ""
else:
t += c
else:
if (c == "["):
inCode = True
if (c == "]"):
inCode = False
if (c == '"') or (c == "'"):
inString = True
delim = c
t += c
elif (c == "#") and not inCode:
inComment = True
elif (c == "(") and not inCode:
inComment = True
elif (c == ")") and not inCode:
inComment = False
elif (c <= " "):
pass # ignore whitespace
else:
t += c
inToken = True
#-------------------------------------------------
# SetOrigin
#
def SetOrigin(self):
"""Set the output origin position."""
# Use the command line setting, if given
if (self.cmdOrigin != -1):
self.org = self.cmdOrigin
else:
self.org = ORG
next = indef = False
for i in self.tokens:
if (i == "org"):
if (indef):
self.Error("Origin statements cannot be within functions.")
next = True
elif (i == "def"):
indef = True
elif (i == ":"):
indef = True
elif (i == "end"):
indef = False
elif (i == ";"):
indef = False
elif (next):
self.org = self.Number(i)
if (self.org < 0) or (self.org > 65535):
self.Error("Illegal origin address: " + str(self.org))
return
#-------------------------------------------------
# AddName
#
def AddName(self, name):
"""Add a new name to the names dictionary."""
if self.names.has_key(name):
self.Error("Duplicate name found: " + str(name))
# self.names[name] = self.GetLabel(name)
self.names[name] = name
#-------------------------------------------------
# Declarations
#
def Declarations(self):
"""Locate all defined variables, constants and strings."""
# Dictionaries of all defined variables, and numeric and string constants
# accessed by name as key.
self.vars = {}
self.consts = {}
self.str = {}
indef = False
i = 0
while (i < len(self.tokens)):
if (self.tokens[i] == "var"):
if (indef):
self.Error("Variables cannot be defined within functions.")
if (i+2) >= len(self.tokens):
self.Error("Syntax error: too few tokens for variable declaration.")
name = self.tokens[i+1]
bytes= self.Number(self.tokens[i+2])
i += 2
if (self.InvalidName(name)):
self.Error("Illegal variable name: " + str(name))
if (bytes <= 0) or (bytes >= 65535):
self.Error("Illegal variable size: " + name + ", size = " + str(bytes))
self.vars[name] = bytes
self.symtbl[name] = "VAR"
self.AddName(name)
elif (self.tokens[i] == "const"):
if (indef):
self.Error("Constants cannot be defined within functions.")
if (i+2) >= len(self.tokens):
self.Error("Syntax error: too few tokens for constant declaration.")
name = self.tokens[i+1]
val = self.Number(self.tokens[i+2])
i += 2
if (self.InvalidName(name)):
self.Error("Illegal constant name: " + str(name))
if (val < -32768) or (val >= 65535):
self.Error("Illegal constant value: " + name + ", value = " + str(val))
self.consts[name] = val
self.symtbl[name] = "CONST"
self.AddName(name)
elif (self.tokens[i] == "str"):
if (indef):
self.Error("Strings cannot be defined within functions.")
if (i+2) >= len(self.tokens):
self.Error("Syntax error: too few tokens for string declaration.")
name = self.tokens[i+1]
val = self.tokens[i+2]
i += 2
if (self.InvalidName(name)):
self.Error("Illegal string name: " + str(name))
if (val == ""):
self.Error("Illegal string value, name = " + str(name))
self.str[name] = val
self.symtbl[name] = "STR"
self.AddName(name)
elif (self.tokens[i] == "def"):
indef = True
elif (self.tokens[i] == ":"):
indef = True
elif (self.tokens[i] == "end"):
indef = False
elif (self.tokens[i] == ";"):
indef = False
i += 1
#-------------------------------------------------
# ParseDataBlocks
#
def ParseDataBlocks(self):
"""Get all data blocks."""
# Dictionary of all data blocks. Each entry stores the data values
# for that block.
self.dataBlocks = {}
indata = False
i = 0
name = ""
data = []
while (i < len(self.tokens)):
if (self.tokens[i] == "data"):
if (indata):
self.Error("Data blocks may not be nested.")
indata = True
data = []
if (i+1) >= len(self.tokens):
self.Error("Too few tokens for data block declaration.")
name = self.tokens[i+1]
if (self.InvalidName(name)):
print i
self.Error("Illegal data block name: " + str(name))
i += 2
elif (self.tokens[i] == "end") and (indata):
indata = False
self.dataBlocks[name] = data
self.symtbl[name] = "DATA"
self.AddName(name)
i += 1
elif (indata):
data.append(self.tokens[i])
i += 1
else:
i += 1
#-------------------------------------------------
# ParseCodeBlocks
#
def ParseCodeBlocks(self):
"""Get a code block."""
# Dictionary of all code blocks. Each entry stores the code values
# for that block.
self.codeBlocks = {}
incode = False
instatement = False
i = 0
name = ""
statement = ""
code = []
while (i < len(self.tokens)):
if (self.tokens[i] == "code"):
if (incode):
self.Error("Code blocks may not be nested.")
incode = True
code = []
if (i+1) >= len(self.tokens):
self.Error("Too few tokens for code block declaration.")
name = self.tokens[i+1]
if (self.InvalidName(name)):
print i
self.Error("Illegal code block name: " + str(name))
i += 2
elif (self.tokens[i] == "end") and (incode):
incode = False
self.codeBlocks[name] = code
self.symtbl[name] = "CODE"
self.AddName(name)
i += 1
elif (incode):
if (self.tokens[i] == "["):
instatement = True
statement = " "
elif (self.tokens[i] == "]"):
code.append(statement)
else:
statement = statement + " " + self.tokens[i]
i += 1
else:
i += 1
#-------------------------------------------------
# ParseFunctions
#
def ParseFunctions(self):
"""Parse all functions."""
# Dictionary of all functions. Each entry stores the tokens associated with
# that function, accessible by function name as key.
self.funcs = {}
indef = False
i = 0
name = ""
func = []
while (i < len(self.tokens)):
if ((self.tokens[i] == "def") or (self.tokens[i] == ":")):
if (indef):
self.Error("Function declarations may not be nested.")
indef = True
func = []
if (i+1) >= len(self.tokens):
self.Error("Too few tokens for function declaration.")
name = self.tokens[i+1]
if (self.InvalidName(name)):
print i
self.Error("Illegal function name: " + str(name))
i += 2
elif ((self.tokens[i] == "end") or (self.tokens[i] == ";")) and (indef):
indef = False
self.funcs[name] = func
self.symtbl[name] = "FUNC"
self.referenced[name] = False
self.AddName(name)
i += 1
elif (indef):
func.append(self.tokens[i])
i += 1
else:
i += 1
#-------------------------------------------------
# TagFunctions
#
def TagFunctions(self):
"""Mark all functions that are referenced."""
for f in self.funcs:
for token in self.funcs[f]:
if (self.symtbl.has_key(token)):
if (self.symtbl[token] == "FUNC"):
self.referenced[token] = True
#-------------------------------------------------
# isStringConstant
#
def isStringConstant(self, token):
"""Return true if this token is a string constant."""
return (token[0] == token[-1]) and ((token[0] == '"') or (token[0] == "'"))
#-------------------------------------------------
# StringConstantName
#
def StringConstantName(self):
"""Generate a unique name for this string constant."""
name = "STR_%04X" % self.stringCount
self.stringCount += 1
return name
#-------------------------------------------------
# LocateStringConstants
#
def LocateStringConstants(self):
"""Locate string constants inside of functions and replace them with
references to STR constants."""
# Always reference "main"
self.referenced["main"] = True
# Check all tokens of all defined functions
for key in self.funcs:
if (self.referenced[key]):
i = 0
while (i < len(self.funcs[key])):
token = self.funcs[key][i]
if self.isStringConstant(token): # if it is a string constant
name = self.StringConstantName() # generate a name for it
self.str[name] = token # and add it to the string constant dictionary
self.symtbl[name] = "STR" # plus the symbol table
self.funcs[key][i] = name # and change the token to the generated name
self.AddName(name) # add the new name
i += 1
#-------------------------------------------------
# Dependencies
#
def Dependencies(self, names):
"""Add all dependencies for the given list of dependencies."""
for d in names: # for all dependencies
if (d not in self.dependencies): # if not in global list
self.dependencies.append(d) # add it
self.Dependencies(DEPENDENCIES[d]) # and all of its dependencies, too
#-------------------------------------------------
# LibraryRoutines
#
def LibraryRoutines(self):
"""Locate all library routines used."""
# List of all library routines used
self.lib = []
# Check all tokens of all defined functions
for key in self.funcs:
for token in self.funcs[key]:
if LIBRARYMAP.has_key(token): # if it is a library routine
if (token not in self.lib): # and hasn't been added yet
self.lib.append(token) # add it to the list
self.symtbl[token] = "LIB" # and the symbol table
# Now add all the dependencies
depends = []
for routine in self.lib: # for every identified library routine
name = LIBRARYMAP[routine] # get the library file name
for d in DEPENDENCIES[name]: # check its dependencies
if (d not in depends): # if not already added
depends.append(d) # add it to the list
# Now add the dependencies of the dependencies
self.Dependencies(depends)
#-------------------------------------------------
# CompileNumber
#
def CompileNumber(self, token):
"""Compile a number by pushing it on the stack."""
n = self.Number(token)
# Low 16-bits
self.fasm.append(["","lda","#<$"+hex(n)[2:]])
self.fasm.append(["","ldx","#>$"+hex(n)[2:]])
self.fasm.append(["","jsr","push"])
# If marked as double-precision, push the upper 16-bits, too
if ("#" in token):
self.fasm.append(["","lda","#"+hex((n >> 16) & 0xFF)])
self.fasm.append(["","ldx","#"+hex((n >> 24) & 0xFF)])
self.fasm.append(["","jsr","push"])
# Make sure we include push and pop in the dependencies
if ("push" not in self.dependencies):
self.dependencies.append("push")
if ("pop" not in self.dependencies):
self.dependencies.append("pop")
#-------------------------------------------------
# CompileVariableRef
#
def CompileVariableRef(self, token):
"""Compile a variable ref by putting its address on the stack."""
name = self.names[token]
self.fasm.append(["","lda","#<"+name])
self.fasm.append(["","ldx","#>"+name])
self.fasm.append(["","jsr","push"])
#-------------------------------------------------
# CompileDataBlockRef
#
def CompileDataBlockRef(self, token):
"""Compile a data block ref by putting its address on the stack."""
name = self.names[token]
self.fasm.append(["","lda","#<"+name])
self.fasm.append(["","ldx","#>"+name])
self.fasm.append(["","jsr","push"])
#-------------------------------------------------
# CompileCodeBlockRef
#
def CompileCodeBlockRef(self, token):
"""Compile a code block ref by jsr to its address."""
name = self.names[token]
self.fasm.append(["","jsr", name])
#-------------------------------------------------
# CompileStringConstantRef
#
def CompileStringConstantRef(self, token):
"""Compile a reference to a string constant by putting its
address on the stack."""
name = self.names[token]
self.fasm.append(["","lda","#<"+name])
self.fasm.append(["","ldx","#>"+name])
self.fasm.append(["","jsr","push"])
#-------------------------------------------------
# CompileLoopBegin
#
def CompileLoopBegin(self):
"""Compile the start of a loop."""
t = self.GetLabel("loop")
self.fasm.append([t,"",""])
self.loop.append([t, self.GetLabel("loop2")])
#-------------------------------------------------
# CompileLoopEnd
#
def CompileLoopEnd(self):
"""Compile the end of a loop."""
if self.loop == []:
self.Error("Loop underflow!")
t = self.loop[-1]
self.loop = self.loop[0:-1]
self.fasm.append(["","jmp",t[0]])
self.fasm.append([t[1],"",""])
#-------------------------------------------------
# CompileIf
#
def CompileIf(self):
"""Compile an if statement."""
t_else = self.GetLabel("else")
t_then = self.GetLabel("then")
t = self.GetLabel("t")
t_end = self.GetLabel("tend")
self.compare.append([t_else, t_then, False, t_end])
self.fasm.append(["","jsr","pop"])
self.fasm.append(["","cmp","#0"])
self.fasm.append(["","beq", t])
self.fasm.append(["","bne", t_then])
self.fasm.append([t, "jmp", t_else])
self.fasm.append([t_then, "", ""])
#-------------------------------------------------
# CompileNotIf
#
def CompileNotIf(self):
"""Compile a not if statement."""
t_else = self.GetLabel("else")
t_then = self.GetLabel("then")
t = self.GetLabel("t")
t_end = self.GetLabel("tend")
self.compare.append([t_else, t_then, False, t_end])
self.fasm.append(["","jsr","pop"])
self.fasm.append(["","cmp","#0"])
self.fasm.append(["","bne", t])
self.fasm.append(["","beq", t_then])
self.fasm.append([t, "jmp", t_else])
self.fasm.append([t_then, "", ""])
#-------------------------------------------------
# CompileElse
#
def CompileElse(self):
"""Compile an else statement."""
self.compare[-1][2] = True
t_else, t_then, flag, t_end = self.compare[-1]
self.fasm.append(["","jmp",t_end])
self.fasm.append([t_else,"",""])
#-------------------------------------------------
# CompileThen
#
def CompileThen(self):
"""Compile a then statement."""
if self.compare == []:
self.Error("Compare underflow!")
t_else, t_then, flag, t_end = self.compare[-1]
self.compare = self.compare[0:-1]
if not flag:
self.fasm.append([t_else,"",""])
else:
self.fasm.append([t_end,"",""])
#-------------------------------------------------
# CompileBreak
#
def CompileBreak(self):
"""Comple a break statement."""
t = self.loop[-1]
self.fasm.append(["","jmp",t[1]])
#-------------------------------------------------
# CompileCont
#
def CompileCont(self):
"""Compile a continue statement."""
t = self.loop[-1]
self.fasam.append(["","jmp",t[0]])
#-------------------------------------------------
# CompileIfBreak
#
def CompileIfBreak(self):
"""Compile a conditional break."""
t = self.loop[-1]
q = self.GetLabel("break")
self.fasm.append(["","jsr","pop"])
self.fasm.append(["","cmp","#0"])
self.fasm.append(["","beq", q])
self.fasm.append(["","jmp", t[1]])
self.fasm.append([q,"",""])
#-------------------------------------------------
# CompileIfCont
#
def CompileIfCont(self):
"""Compile a conditional continue."""
t = self.loop[-1]
q = self.GetLabel("ifcont")
self.fasm.append(["","jsr","pop"])
self.fasm.append(["","cmp","#0"])
self.fasm.append(["","beq", q])
self.fasm.append(["","jmp", t[0]])
self.fasm.append([q,"",""])
#-------------------------------------------------
# CompileNotIfBreak
#
def CompileNotIfBreak(self):
"""Compile a negated conditional break."""
t = self.loop[-1]
q = self.GetLabel("notifbreak")
self.fasm.append(["","jsr","pop"])
self.fasm.append(["","cmp","#0"])
self.fasm.append(["","bne", q])
self.fasm.append(["","jmp", t[1]])
self.fasm.append([q,"",""])
#-------------------------------------------------
# CompileNotIfCont
#
def CompileNotIfCont(self):
"""Compile a negated conditional continue."""
t = self.loop[-1]
q = self.GetLabel("notifcont")
self.fasm.append(["","jsr","pop"])
self.fasm.append(["","cmp","#0"])
self.fasm.append(["","bne", q])
self.fasm.append(["","jmp", t[0]])
self.fasm.append([q,"",""])
#-------------------------------------------------
# CompileLibraryRef
#
def CompileLibraryRef(self, token):
"""Compile a reference to a library word."""
self.fasm.append(["","jsr", LIBRARYMAP[token]])
#-------------------------------------------------
# CompileReturn
#
def CompileReturn(self):
"""Compile a return statement."""
# Return from subroutine
self.fasm.append(["","rts",""])
#-------------------------------------------------
# FunctionAddress
#
def FunctionAddress(self):
"""Mark the next function reference to push the address
on the stack."""
self.functionAddress = True
#-------------------------------------------------
# Keywords
#
def Keywords(self):
"""Place all keywords in the symbol table and
create the compile helper function dictionary."""
self.symtbl["{"] = "KWD"
self.symtbl["}"] = "KWD"
self.symtbl["if" ] = "KWD"
self.symtbl["0if"] = "KWD"
self.symtbl["else"] = "KWD"
self.symtbl["then"] = "KWD"
self.symtbl["break"] = "KWD"
self.symtbl["cont"] = "KWD"
self.symtbl["?break"] = "KWD"
self.symtbl["?cont"] = "KWD"
self.symtbl["?0break"] = "KWD"
self.symtbl["?0cont"] = "KWD"
self.symtbl["&"] = "KWD"
self.symtbl["return"] = "KWD"
# Compile helper dictionary
self.keywords = {
"{" : self.CompileLoopBegin,
"}" : self.CompileLoopEnd,
"if" : self.CompileIf,
"0if" : self.CompileNotIf,
"else" : self.CompileElse,
"then" : self.CompileThen,
"break" : self.CompileBreak,
"cont" : self.CompileCont,
"?break" : self.CompileIfBreak,
"?cont" : self.CompileIfCont,
"?0break" : self.CompileNotIfBreak,
"?0cont" : self.CompileNotIfCont,
"return" : self.CompileReturn,
"&" : self.FunctionAddress
}
#-------------------------------------------------
# CompileDataBlocks
#
def CompileDataBlocks(self):
"""Create assembly instructions for all data blocks."""
# Holds the assembly code for the data blocks
self.dasm = []
# Compile each block
for f in self.dataBlocks:
self.dasm.append([self.names[f],"",""]) # Data block label
for number in self.dataBlocks[f]:
n = self.Number(number)
if ("#" in number):
if (n < -32768):
n += 2**32
self.dasm.append(["",".byte",str(n & 0xff)])
self.dasm.append(["",".byte",str((n >> 8) & 0xff)])
self.dasm.append(["",".byte",str((n >> 16) & 0xff)])
self.dasm.append(["",".byte",str((n >> 24) & 0xff)])
elif ("%" in number):
if (n < 0):
n += 2**16
self.dasm.append(["",".byte",str(n & 0xff)])
self.dasm.append(["",".byte",str((n >> 8) & 0xff)])
else:
if (n >= -32768) and (n < 0):
n += 2**16
if (n < -32768):
n += 2**32
if (n >= 0) and (n < 256):
self.dasm.append(["",".byte",str(n)])
elif (n >= 0) and (n <= 65535):
self.dasm.append(["",".byte",str(n & 0xff)])
self.dasm.append(["",".byte",str((n >> 8) & 0xff)])
elif (n > 65535):
self.dasm.append(["",".byte",str(n & 0xff)])
self.dasm.append(["",".byte",str((n >> 8) & 0xff)])
self.dasm.append(["",".byte",str((n >> 16) & 0xff)])
self.dasm.append(["",".byte",str((n >> 24) & 0xff)])
#-------------------------------------------------
# CompileCodeBlocks
#
def CompileCodeBlocks(self):
"""Create assembly instructions for all code blocks."""
# Holds the assembly code for the data blocks
self.casm = []
# Compile each block
for f in self.codeBlocks:
self.casm.append([self.names[f],"",""]) # Code block label
for line in self.codeBlocks[f]:
self.casm.append(["",line,""])
#-------------------------------------------------
# CompileFunctions
#
def CompileFunctions(self):
"""Compile all defined functions."""
# Holds the assembly code for the functions
self.fasm = []
# Compile each function
for f in self.funcs:
if (self.referenced[f]):
self.loop = []
self.compare = []
self.funcName = f # Currently compiling
self.fasm.append([self.names[f],"",""]) # Subroutine label
for token in self.funcs[f]:
self.Token = token # Current token
if (self.symtbl.has_key(token)):
if (self.symtbl[token] == "FUNC"):
if (self.functionAddress):
# Push the function address
self.fasm.append(["","lda", "#<"+self.names[token]])
self.fasm.append(["","ldx", "#>"+self.names[token]])
self.fasm.append(["","jsr", "push"])
self.functionAddress = False
else:
# Compile a subroutine call
self.fasm.append(["","jsr",self.names[token]])
elif (self.symtbl[token] == "VAR"):
self.CompileVariableRef(token) # Compile a variable reference
elif (self.symtbl[token] == "DATA"):
self.CompileDataBlockRef(token) # Compile a reference to a data block
elif (self.symtbl[token] == "CODE"):
self.CompileCodeBlockRef(token)
elif (self.symtbl[token] == "CONST"):
self.CompileNumber(str(self.consts[token])) # Compile a constant reference
elif (self.symtbl[token] == "STR"):
self.CompileStringConstantRef(token) # Compile a reference to a string constant
elif (self.symtbl[token] == "KWD"):
self.keywords[token]() # Compile a keyword
elif (self.symtbl[token] == "LIB"):
if (self.functionAddress):
# Push the address of the library routine
self.fasm.append(["","lda", "#<"+token])
self.fasm.append(["","ldx", "#>"+token])
self.fasm.append(["","jsr", "push"])
self.functionAddress = False
else:
# Compile a library word call
self.CompileLibraryRef(token) # Compile a library word reference
else:
self.Error("Unknown symbol table type: " + str(token) + ", type = " + \
str(self.symtbl[token]) + ", function = " + f)
elif (self.isNumber(token)):
self.CompileNumber(token)
else:
self.Error("Unknown token: " + str(token) + ", function = " + f)
self.fasm.append(["","rts",""]) # Ending RTS instruction
#-------------------------------------------------
# pp
#
def pp(self, f, t):
"""Write an instruction to the assembly output file."""
f.write(t[0])
f.write("\t")
f.write(t[1])
f.write(" ")
f.write(t[2])
f.write("\n")
#-------------------------------------------------
# AssemblyOutput
#
def AssemblyOutput(self):
"""Generate the output assembly code."""
# Check for a main function
if not self.symtbl.has_key('main'):
self.Error("No main function defined.")
if (self.symtbl["main"] != "FUNC"):
self.Error("No main function defined.")
# Open the output assembly source code file
f = open(self.outname + ".s", "w")
# Write the header
for s in HEADER:
f.write("; "+s+"\n")
# Origin
if (self.sys):
self.org = 0x2000
self.pp(f, ["","*=","$%04X" % self.org])
f.write("\n")
# Library equates
for s in EQUATES:
self.pp(f, [s,"=","$%04X" % EQUATES[s]])
f.write("\n")
# Variables
offset = self.VARTOP
for v in self.vars:
offset -= self.vars[v]
self.pp(f, [self.names[v], "=", "$%04X" % offset])
f.write("\n")
# Main call
self.pp(f, ["", "lda", "#0"])
self.pp(f, ["", "sta", "sp"]) # zero stack pointer
self.pp(f, ["", "jmp", self.names["main"]])
f.write("\n")
# Data blocks
for s in self.dasm:
self.pp(f, s)
f.write("\n")
# Code blocks
for s in self.casm:
self.pp(f, s)
f.write("\n")
# String constants
for s in self.str:
self.pp(f, [self.names[s], ".byte", str(len(self.str[s])-2) +","+ self.str[s]+",$9B"])
f.write("\n")
# Dependencies
for s in self.dependencies:
g = open(LIB_DIR+s+".s", "r")
f.write(g.read())
g.close()
f.write("\n")
# Library routines
for s in self.lib:
g = open(LIB_DIR+LIBRARYMAP[s]+".s", "r")
f.write(g.read())
g.close()
f.write("\n")
# Functions
for s in self.fasm:
self.pp(f, s)
f.write("\n")
# end label
f.write("_end\n")
f.close()
#-------------------------------------------------
# ConvertToAppleII
#
def ConvertToAppleII(self):
"""Convert the binary output file to an Apple II
text file of EXEC-ing into memory."""
# Get the binary data
f = open(self.outname + ".bin", "rb")
d = f.read()
f.close()
# Write to a new output file
f = open(self.outname + ".txt", "w")
f.write("CALL -151")
# Write each byte
i = 0
while (i < len(d)):
if ((i % 8) == 0):
f.write("\n%04X: " % (self.org + i))
f.write("%02X " % ord(d[i]))
i += 1
# Close the file
if (self.sys):
f.write("\nBSAVE %s, A%d, L%d, TSYS\n" % (self.outname.upper(), self.org, len(d)))
else:
f.write("\nBSAVE %s, A%d, L%d\n" % (self.outname.upper(), self.org, len(d)))
#-------------------------------------------------
# BlockToTrackSector
#
def BlockToTrackSector(self, blk):
"""Convert a block number to the corresponding track and
sector numbers."""
trk = int(blk/8)
sec = ([[0,14],[13,12],[11,10],[9,8],[7,6],[5,4],[3,2],[1,15]])[blk % 8]
return [trk, sec]
#-------------------------------------------------
# TrackSectorOffset
#
def TrackSectorOffset(self, trk, sec):
"""Convert a given track and sector to an offset in
the disk image."""
return 256*(16*trk + sec)
#-------------------------------------------------
# ReadBlock
#
def ReadBlock(self, dsk, blk):
"""Return a 512 byte block from dsk at blk."""
# Get the track and sectors for this block
trk, sec = self.BlockToTrackSector(blk)
# Get the offsets
off1 = self.TrackSectorOffset(trk, sec[0])
off2 = self.TrackSectorOffset(trk, sec[1])
# Get the 256 bytes at each of these locations
# as a single list and return it
return dsk[off1:(off1+256)] + dsk[off2:(off2+256)]
#-------------------------------------------------
# WriteBlock
#
def WriteBlock(self, dsk, blk, data):
"""Write 512 bytes of data to dsk at block blk."""
# Data must be exactly 512 long
if (len(data) != 512):
self.Error("Illegal block length.")
# Get the track and sectors for this block
trk, sec = self.BlockToTrackSector(blk)
# Get the points into the dsk image
off1 = self.TrackSectorOffset(trk, sec[0])
off2 = self.TrackSectorOffset(trk, sec[1])
# Update the 256 values at off1 with the first
# 256 values of data and the values at off2 with
# the next 256 values of data
for i in xrange(256):
dsk[off1+i] = data[i]
dsk[off2+i] = data[i+256]
#-------------------------------------------------
# Date
#
def Date(self):
"""Return the date in ProDOS format."""
t = time.localtime(time.time())
dlow = ((t[1] & 0x07) << 5) | t[2]
dhigh = ((t[0] - 2000) << 1) | ((t[1] & 0x08) >> 3)
return [dlow, dhigh]
#-------------------------------------------------
# Time
#
def Time(self):
"""Return the time in ProDOS format."""
t = time.localtime(time.time())
return [t[4], t[3]]
#-------------------------------------------------
# MarkBlock
#
def MarkBlock(self, blk, blocknum):
"""Mark blocknum as being used."""
# Byte number containing blocknum
bytenum = blocknum / 8
# Bit number containing blocknum
bitnum = 7 - (blocknum % 8)
# Set to used
blk[bytenum] = blk[bytenum] & ~(1 << bitnum)
#-------------------------------------------------
# ConvertToDSK
#
def ConvertToDSK(self):
"""Convert the binary output file to a .dsk file."""
# Get the binary data
f = open(self.outname + ".bin", "rb")
d = f.read()
f.close()
data = []
for c in d:
data.append(ord(c))
# Read the blank disk image file as list of bytes.
# It is assumed that this file is a blank ProDOS 140k
# floppy image stored in DOS 3.3 order.
f = open(DISK_IMAGE, "rb")
t = f.read()
f.close()
dsk = []
for c in t:
dsk.append(ord(c))
#
# Create the directory entry for this file
#
# File storage type and name length
blk = self.ReadBlock(dsk, 2)
offset = 0x2b # Offset to start of second directory entry
if (len(data) <= 512):
blk[offset] = 0x15 # seedling file
seedling = True
else:
blk[offset] = 0x25 # sapling file
seedling = False
# File name, always "A.OUT"
blk[offset+1] = ord("A")
blk[offset+2] = ord(".")
blk[offset+3] = ord("O")
blk[offset+4] = ord("U")
blk[offset+5] = ord("T")
# File type
if (self.sys):
blk[offset+0x10] = 255
else:
blk[offset+0x10] = 6
# Key pointer, block 7
blk[offset+0x11] = 7
blk[offset+0x12] = 0
# File size
blocks = int(math.ceil(len(data)/512.0))
if (blocks == 0):
blocks = 1
blk[offset+0x13] = blocks % 256
blk[offset+0x14] = blocks / 256
blk[offset+0x15] = len(data) % 256
blk[offset+0x16] = len(data) / 256
blk[offset+0x17] = 0
# Date and time
dlow, dhigh = self.Date()
blk[offset+0x18] = dlow
blk[offset+0x19] = dhigh
m, h = self.Time()
blk[offset+0x1a] = m
blk[offset+0x1b] = h
# ProDOS versions
blk[offset+0x1c] = 0
blk[offset+0x1d] = 0
# Access code
blk[offset+0x1e] = 0xc3
# Aux type code (program start in memory)
blk[offset+0x1f] = self.org % 256
blk[offset+0x20] = self.org / 256
# Last accessed time
blk[offset+0x21] = dlow
blk[offset+0x22] = dhigh
blk[offset+0x23] = m
blk[offset+0x24] = h
# Key block for directory
blk[offset+0x25] = 2
blk[offset+0x26] = 0
# Number of files in this directory
blk[0x25] = blk[0x25] + 1
# Write the block
self.WriteBlock(dsk, 2, blk)
#
# Index block
#
blk = self.ReadBlock(dsk, 7)
if (seedling):
# Write the data
i = 0
while (i < len(data)):
blk[i] = data[i]
i += 1
# Block used
used = [7]
else:
# Reserve the necessary number of blocks
used = []
for i in xrange(blocks):
used.append(i+8)
j = 0
for i in used:
blk[j] = i % 256
blk[j+256] = i / 256
j += 1
# Write the index block
self.WriteBlock(dsk, 7, blk)
# Write the file data if not a seedling file
if (not seedling):
# Pad data to a multiple of 512
data = data + [0]*(512 - (len(data) % 512))
j = 0
for b in used:
blk = self.ReadBlock(dsk, b)
for i in xrange(512):
blk[i] = data[j+i]
self.WriteBlock(dsk, b, blk)
j += 512
# Include block 7, the index block, so it will
# be marked as used in the volume bitmap
used.append(7)
#
# Volume bitmap updates
#
blk = self.ReadBlock(dsk, 6)
for i in used:
self.MarkBlock(blk, i)
self.WriteBlock(dsk, 6, blk)
#
# Write the entire image to disk
#
f = open(self.outname + ".dsk", "wb")
for i in dsk:
f.write(chr(i))
f.close()
#-------------------------------------------------
# ConvertToR65
#
def ConvertToR65(self):
"""Make an r65 file."""
# Get the binary data
f = open(self.outname + ".bin", "rb")
d = f.read()
f.close()
data = []
for c in d:
data.append(ord(c))
# Create the output file
f = open(self.outname + ".r65", "wb")
f.write(chr(self.org % 256))
f.write(chr(self.org / 256))
for c in data:
f.write(chr(c))
f.close()
#-------------------------------------------------
# ConvertToAtariCom
#
def ConvertToCom(self):
"""Make an Atari COMpound file."""
# Get the binary data
f = open(self.outname + ".bin", "rb")
d = f.read()
f.close()
data = []
for c in d:
data.append(ord(c))
# Create the output file
f = open(self.outname + ".com", "wb")
f.write(chr(0xff)) # compound marker $FFFF
f.write(chr(0xff))
f.write(chr(self.org % 256)) # startaddress
f.write(chr(self.org / 256))
f.write(chr((self.org + len(data)+1)% 256)) # endaddress
f.write(chr((self.org + len(data)+1)/ 256))
for c in data:
f.write(chr(c))
f.write(chr(0xff))
f.write(chr(0xff))
f.write(chr(0xe0)) # RUNAD $02E0
f.write(chr(0x02))
f.write(chr(0xe1))
f.write(chr(0x02))
f.write(chr(self.org % 256)) # startaddress
f.write(chr(self.org / 256))
f.close()
#-------------------------------------------------
# GenerateFinalOutput
#
def GenerateFinalOutput(self):
"""Create the final output file."""
# Assemble the .s file
if (self.outtype != "asm"):
rc = os.system(ASSEMBLER + " -r " + self.outname + ".s")
if (rc != 0):
self.Error("Error during assembly!")
# Convert to desired output file format
if (self.outtype == "bin"):
return
elif (self.outtype == "a2t"):
self.ConvertToAppleII()
elif (self.outtype == "dsk"):
self.ConvertToDSK()
elif (self.outtype == "r65"):
self.ConvertToR65()
elif (self.outtype == "atari"):
self.ConvertToCom()
#-------------------------------------------------
# Compile
#
def Compile(self):
"""Compile the files on the command line."""
self.funcName = "$MAIN$" # Name of currently compiling function
self.Token = "<na>" # Currently compiling token
self.VARTOP = VARTOP # Variable starting address
self.cmdOrigin = -1 # -org value, if any
self.counter = 0 # Start labels from zero
self.stringCount = 0 # String constant name counter
self.symtbl = {} # Ready the symbol table
self.names = {} # Map names to labels
self.dependencies = [] # Library word dependencies
self.functionAddress = False # If true, push a function address
self.referenced = {} # True if that function referenced by another
# only functions actually referenced are compiled
self.ParseCommandLine() # Parse the command line arguments
self.Keywords() # Put all keywords into the symbol table
self.LoadFiles() # Load all source code into self.src
self.Tokenize() # Break up into tokens in self.tokens
self.SetOrigin() # Determine the origin for the output code
self.Declarations() # Find all variables and constants
self.ParseDataBlocks() # Get all data blocks and their data
self.ParseCodeBlocks() # Get all code blocks
self.ParseFunctions() # Get all the functions and their tokens
self.TagFunctions() # Tag used functions
self.LocateStringConstants() # Locate string constants in functions
self.LibraryRoutines() # Determine all library routines used
self.CompileDataBlocks() # Generate assembly code for all data blocks
self.CompileCodeBlocks() # Generate assembly code for all code blocks
self.CompileFunctions() # Generate assembly code for all functions
self.AssemblyOutput() # Output all assembly code and assemble it
self.GenerateFinalOutput() # Convert the assembler output into the final desired format
#--------------------------------------------------
# __init__
#
def __init__(self):
"""Build the compiler object."""
# Help message
if (len(sys.argv) == 1):
print "\nSPL Compiler (" + LAST_MOD + ")\n"
print "Use: spl file1 [file2 ...] [-o outbase] [-t type] [-sys] [-org n] [-var m] " + \
"[-stack p] [-prodos]\n"
print "Where:\n"
print " file1 = first source code file (.spl extension assumed)"
print " file2... = additional source code files"
print " outbase = base file name for output files (NO extension)"
print " type = output file type:"
print " bin = compiled binary"
print " asm = assembly source only"
print " a2t = text file for EXEC on Apple II"
print " dsk = a disk file for Apple II emulators"
print " r65 = source file for the r65 65C02 simulator"
print " atari = compound file for Atari XL/XE DOS"
print " sys = create a ProDOS system file (-t a2t and -t dsk only)"
print " org = set the origin address to n"
print " var = set the VARTOP value (variables grow down from here)"
print " stack = set the stack address (up to 256 bytes)"
print " prodos = set VARTOP to allow room for 3 file buffers (Apple II)\n"
sys.exit(1)
# Otherwise, compile the files
self.Compile()
# Run if not imported
if __name__ == '__main__':
app = SPLCompiler()
#
# end spl.py
#
}}}