SPL#
a Forth-style concatenative stack language. The compiler is (currently) written in Python and emits 6502 assembly code.
http://sourceforge.net/projects/spl65/ 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 #