a Forth-style concatenative stack language. The compiler is (currently) written in Python and emits 6502 assembly code.
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
#!/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 #