Raw File
#!/usr/bin/python3
# Copyright 2019 Ronald S. Burkey <info@sandroid.org>
#
# This file is part of yaAGC.
#
# yaAGC is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
# yaAGC is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with yaAGC; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# Filename:    yaASM.py
# Purpose:     An LVDC assembler, intended to replace yaASM.c for LVDC
#              (but not for OBC) assemblies.  It's actually just something
#              I'm playing around with to see if I can get something a
#              little cleaner and thus easier for me to maintain than yaASM.c, 
#              so I shouldn't really say I _intend_ it as a replacement.
# Reference:   http://www.ibibio.org/apollo
# Mods:        2019-07-10 RSB  Began playing around with the concept.
#
# The usage is just
#              yaASM.py <INPUT.lvdc >OUTPUT.listing
# If the assembly is successful, an executable file for the assembler is 
# output, called lvdc.bin.

import sys

operators = {
    "HOP": { "opcode":0b0000 }, 
    "HOP*": { "opcode":0b0000 }, 
    "MPY": { "opcode":0b0001 }, 
    "SUB": { "opcode":0b0010 }, 
    "DIV": { "opcode":0b0011 }, 
    "TNZ": { "opcode":0b0100 }, 
    "MPH": { "opcode":0b0101 }, 
    "AND": { "opcode":0b0110 }, 
    "ADD": { "opcode":0b0111 },
    "TRA": { "opcode":0b1000 }, 
    "XOR": { "opcode":0b1001 }, 
    "PIO": { "opcode":0b1010 }, 
    "STO": { "opcode":0b1011 }, 
    "TMI": { "opcode":0b1100 }, 
    "RSU": { "opcode":0b1101 }, 
    "CDS": { "opcode":0b1110, "a9":0 }, 
    "SHF": { "opcode":0b1110, "a9":1, "a8":0 }, 
    "EXM": { "opcode":0b0000, "a9":1, "a8":0 },
    "CLA": { "opcode":0b1111 }, 
    "SHR": { "opcode":0b1110, "a9":1, "a8":0 }, 
    "SHL": { "opcode":0b1110, "a9":1, "a8":0 }
}
# I have no actual info about the available pseudo-ops, so it's likely that
# everything relating to the pseudo-ops will have to be ripped out later and
# replaced.  But I'm putting in some pseudo-op functionality as a temporary
# measure.
pseudos = ["CODE", "DATA", "OCT", "DEC"]

#----------------------------------------------------------------------------
#           Read the source code and do simple preprocessing
#----------------------------------------------------------------------------
# First step is just to read the entire input file into memory, doing some
# simple preprocessing at the same time, such as getting rid of blank lines,
# expanding the CALL macro, etc.  The object is basically to parse the whole
# mess into an easy-to-understand dictionary called inputFile. 
#
# As far as I know right now, it looks like we can painlessly assign a HOP 
# to each line of input that needs one as well.  If not, then HOP assignment
# will have to be removed from this pass and one or more additional passes 
# added later to determine the HOPs.
inputFile = []
IM = 0
IS = 0
S = 1
LOC = 0
DM = 0
DS = 0
for line in sys.stdin:
    line = line.rstrip()
    inputLine = { "raw": line }
    
    # Split the line into fields.
    if line[:1] in [" ", "\t"] and not line.isspace():
        line = "@" + line
    fields = line.split()
    if len(fields) > 0 and fields[0] == "@":
        fields[0] = ""
    
    # Remove comments from the fields.
    for n in range(0, len(fields)):
        if fields[n][:1] == "#" or (n <= 1 and fields[n][:1] == "*"):
            if n == 1 and fields[0] == "":
                n = 0
            del fields[n:]
            break
    
    if len(fields) == 3:
        # Operator/operand "CALL a,b" is really shorthand for 
        # "CLA b / HOP* a", so expand it in that manner.
        if fields[1] == "CALL":
            opfields = fields[2].split(",")
            if len(opfields) == 2:
                inputLine["macro"] = True
                inputFile.append(inputLine)
                inputLine = { 
                    "expanded": True, 
                    "raw": "\tCLA\t" + opfields[1],
                    "operator": "CLA",
                    "operand": opfields[1],
                    "hop": {"IM":IM, "IS":IS, "S":S, "LOC":LOC, "DM":DM, "DS":DS}
                }
                LOC += 1
                if fields[0] != "":
                    inputLine["lhs"] = fields[0]
                inputFile.append(inputLine)
                inputLine = { 
                    "expanded": True, 
                    "raw": "\tHOP*\t" + opfields[0],
                    "operator": "HOP*",
                    "operand": opfields[0],
                    "hop": {"IM":IM, "IS":IS, "S":S, "LOC":LOC, "DM":DM, "DS":DS}
                }
                LOC += 1
            else:
                inputLine["error"] = "Wrong number of arguments"
        # Okay, not a "CALL", so we can just handle "normal" input lines.
        elif fields[1] in operators:
            if fields[0] != "":
                inputLine["lhs"] = fields[0]
            inputLine["operator"] = fields[1]
            inputLine["operand"] = fields[2]
            inputLine["hop"] = {"IM":IM, "IS":IS, "S":S, "LOC":LOC, "DM":DM, "DS":DS}
            LOC += 1
        elif fields[1] in pseudos:
            if fields[0] != "":
                inputLine["lhs"] = fields[0]
            inputLine["pseudo"] = fields[1]
            inputLine["parameter"] = fields[2]
            if fields[1] == "CODE":
                opfields = fields[2].split("-")
                if len(opfields) == 4:
                    IM = int(opfields[0], 8)
                    IS = int(opfields[1], 8)
                    S = int(opfields[2], 8)
                    LOC = int(opfields[3], 8)
                else:
                    inputLine["error"] = "Wrong number of fields"
            elif fields[1] == "DATA":
                opfields = fields[2].split("-")
                if len(opfields) == 4:
                    DM = int(opfields[0], 8)
                    DS = int(opfields[1], 8)
                else:
                    inputLine["error"] = "Wrong number of fields"
            # All of the following pseudo-ops use up one word of memory.
            elif fields[1] in pseudos:
                inputLine["hop"] = {"IM":IM, "IS":IS, "S":S, "LOC":LOC, "DM":DM, "DS":DS}
                LOC += 1
        else:
            inputLine["error"] = "Unrecognized operator"
    elif len(fields) == 1:
        inputLine["lhs"] = fields[0]
        inputLine["hop"] = {"IM":IM, "IS":IS, "S":S, "LOC":LOC, "DM":DM, "DS":DS}
        LOC += 1
    elif len(fields) != 0:
        inputLine["error"] = "Wrong number of fields"
    inputFile.append(inputLine)

#----------------------------------------------------------------------------
# If it turns out later that the code above can't assign all HOPs in its
# single pass, insert code to do that here.  However, with my incomplete 
# state of knowledge right now, it doesn't seem as thought that will be
# necessary.

#----------------------------------------------------------------------------
#                           Create a symbol table
#----------------------------------------------------------------------------
# Create a table to quickly look up addresses of symbols.
symbols = {}
for inputLine in inputFile:
    if "lhs" in inputLine:
        if "hop" in inputLine:
            lhs = inputLine["lhs"]
            if lhs in symbols:
                if "error" in inputLine:
                    inputLine["error"] += ". " + "Symbol already defined"
                else:
                    inputLine["error"] = "Symbol already defined"
            symbols[lhs] = inputLine["hop"]
        else:
            if "error" in inputLine:
                inputLine["error"] += ". " + "Symbol without HOP."
            else:
                inputLine["error"] = "Symbol without HOP."

#----------------------------------------------------------------------------
#                           Complete the assembly
#----------------------------------------------------------------------------
# At this point we have a dictionary called inputFile in which the entire 
# input source file has been parsed into a relatively simple structure.  The
# addresses of all symbols (constants, variables, code) are known.  We should
# therefore be able to actually complete the entire assembly.
print("IM IS S LOC DM DS  A8-A1 A9 OP    CONSTANT    SOURCE STATEMENT")
for inputLine in inputFile:
    if "error" in inputLine:
        print("Error: " + inputLine["error"])
    
    if "hop" in inputLine:
        hop = inputLine["hop"]
        line = " %o %02o %o %03o  %o %02o  " % (hop["IM"], hop["IS"], hop["S"], 
                                                hop["LOC"], hop["DM"], 
                                                hop["DS"])
    else:
        line = "                  "
    
    if "operator" in inputLine:
        operator = inputLine["operator"]
        if operator == "SHR":
            n = 1
        elif operator == "SHL":
            n = 1
        elif operator == "SHF":
            n = 1
        elif operator == "EXM":
            n = 1
        else:
            n = 1
    
    print(line + "\t" + inputLine["raw"])

#----------------------------------------------------------------------------
#                           Print a symbol table
#----------------------------------------------------------------------------
print("\n\nSymbol Table:")
for key in sorted(symbols):
    hop = symbols[key]
    print(key + "\t" + " %o %02o %o %03o" % (hop["IM"], hop["IS"], hop["S"], 
                                                hop["LOC"]))
back to top