├── CPU.xlsx ├── ROM.xlsx ├── instructionSet.xlsx ├── sample programs ├── PAGODA.BIN ├── cycle.s └── bouncingBall.s ├── Excel-ASM16.xml ├── README.md ├── LICENSE └── compileExcelASM16.py /CPU.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codewithsadaf/excelCPU/HEAD/CPU.xlsx -------------------------------------------------------------------------------- /ROM.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codewithsadaf/excelCPU/HEAD/ROM.xlsx -------------------------------------------------------------------------------- /instructionSet.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codewithsadaf/excelCPU/HEAD/instructionSet.xlsx -------------------------------------------------------------------------------- /sample programs/PAGODA.BIN: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codewithsadaf/excelCPU/HEAD/sample programs/PAGODA.BIN -------------------------------------------------------------------------------- /sample programs/cycle.s: -------------------------------------------------------------------------------- 1 | .DATA 2 | SCREEN = $F000 3 | .CODE 4 | LOAD R0 SCREEN 5 | LOAD R1 $0123 6 | LOAD R2 $4567 7 | LOAD R3 $89AB 8 | LOAD R4 $CDEF 9 | 10 | LOOP: 11 | STORE R1 R0 12 | INC R0 13 | STORE R2 R0 14 | INC R0 15 | STORE R3 R0 16 | INC R0 17 | STORE R4 R0 18 | INC R0 19 | JMP LOOP 20 | 21 | 22 | ;ORG $F000 23 | .INC "PAGODA.BIN" -------------------------------------------------------------------------------- /sample programs/bouncingBall.s: -------------------------------------------------------------------------------- 1 | ;4x4 pixel ball bounces around the screen 2 | 3 | .DATA 4 | SCREEN = $F000 5 | DIRX = $0000 6 | DIRY = $0000 7 | COLOR = $1111 8 | LOCATION = $FC81 9 | .CODE 10 | LOAD R0 #0 11 | LOAD R15 SCREEN 12 | LOAD R1 #124 13 | LOAD R2 COLOR 14 | LOAD R3 DIRX 15 | LOAD R4 DIRY 16 | LOAD R5 LOCATION 17 | ;TMP VARS 18 | LOAD R13 $20 19 | 20 | BOUNCE: 21 | ;DIR 0 IS -- 22 | ;DIR 1 IS ++ 23 | JMP DRAWBALL 24 | FROMDRAWBALL: 25 | ;JMP ERASEBALL 26 | ;FROMERASEBALL: 27 | JMP MOVEBALL 28 | 29 | DRAWBALL: 30 | TRAN R5 R14 31 | STORE R2 R14 32 | CLC 33 | ADD R14 R13 34 | STORE R2 R14 35 | ADD R14 R13 36 | STORE R2 R14 37 | ADD R14 R13 38 | STORE R2 R14 39 | JMP FROMDRAWBALL 40 | 41 | ERASEBALL: 42 | TRAN R5 R14 43 | STORE R0 R14 44 | CLC 45 | ADD R14 R13 46 | STORE R0 R14 47 | ADD R14 R13 48 | STORE R0 R14 49 | ADD R14 R13 50 | STORE R0 R14 51 | ;JMP FROMERASEBALL 52 | 53 | MOVEBALL: 54 | ;CHECK Y 55 | TRAN R5 R14 56 | LOAD R12 $0FFF 57 | AND R14 R12 58 | LOAD R12 $0080 59 | DIV R14 R12 60 | TRAN R12 R11 ;KEEP X LOCATION IN R11 61 | JMP YMOVE 62 | AFTERYMOVE: 63 | CMP R14 R0 64 | JEQ SWITCHDIRY 65 | LOAD R12 $001F 66 | CMP R14 R12 67 | JEQ SWITCHDIRY 68 | 69 | RESETLOCY: 70 | LOAD R12 $0080 71 | MULT R14 R12 72 | LOAD R12 $F000 73 | OR R14 R12 74 | TRAN R14 R5 75 | 76 | JMP XMOVE 77 | AFTERXMOVE: 78 | CMP R11 R0 79 | JEQ SWITCHDIRX 80 | LOAD R12 $001F 81 | CMP R11 R12 82 | JEQ SWITCHDIRX 83 | 84 | RESETLOCX: 85 | CLC 86 | ADD R5 R11 87 | 88 | ADJUSTCOLOR: 89 | LOAD R12 $1111 90 | CLC 91 | ADD R2 R12 92 | 93 | JMP BOUNCE 94 | 95 | 96 | YMOVE: 97 | CMP R4 R0 98 | JEQ YUP; IF DIRY = 0 99 | INC R14 100 | JMP AFTERYMOVE 101 | YUP: 102 | DEC R14 103 | JMP AFTERYMOVE 104 | 105 | XMOVE: 106 | CMP R3 R0 107 | JEQ XLEFT; IF DIRX = 0 108 | INC R11 109 | JMP AFTERXMOVE 110 | XLEFT: 111 | DEC R11 112 | JMP AFTERXMOVE 113 | 114 | SWITCHDIRY: 115 | LOAD R12 $0001 116 | XOR R4 R12 117 | JMP RESETLOCY 118 | 119 | SWITCHDIRX: 120 | LOAD R12 $0001 121 | XOR R3 R12 122 | JMP RESETLOCX -------------------------------------------------------------------------------- /Excel-ASM16.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 00; 01 02 03 04 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | JMP JEQ JLT JGE LOAD STORE TRAN ADD SUB MULT DIV INC DEC AND OR XOR NOT NOT ROL ROR CMP CLC STC NOP ORG 28 | R 29 | $ # @ 30 | .DATA .CODE .INC 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Excel 16-Bit CPU 2 | The Excel 16-Bit CPU repository contains the following main files: 3 | ``` 4 | CPU.xlsx - The main spreadsheet which contains the CPU 5 | ROM.xlsx - The ROM spreadsheet used read by the CPU when the read ROM switch is turned on 6 | InstructionSet.xlsx - Explains the ISA of the CPU 7 | compileExcelASM16.py - The Excel-ASM16 compiler 8 | Excel-ASM16.xml - Markdown for the Excel-ASM16 language compatible with Notepad++ 9 | Sample Programs - Folder of sample programs for the Excel CPU 10 | ``` 11 | 12 | The CPU.xlsx file features a 16-bit CPU, 16 general purpose registers, 128KB of RAM, and a 128x128 display. 13 | 14 | Iterative Calcuation must be turned on. This can be done by going to File -> Options -> Formulas -> then Enable Iterative Calculation and **set Maximum Iterations to 1** 15 | 16 | The CPU runs off a clock signal set in B2. This clock signal will update under the normal conditions of recalculation within an Excel spreadsheet. Pressing the F9 key will recalculate the spreadsheet. 17 | 18 | The Reset Button in the F2 cell, if set to true, will reset the PC register back to 0. 19 | 20 | The computer in the CPU.xlsx file can be controlled either in automatic or manual mode. This is controlled by the button in J2. If set to true, when the clock signal from B2 is high, then the CPU will carry out the operation specified in the override slot in the Fetch Unit in cell D8. If false, then the CPU will execute the operation retrieved from the memory table as specified by the PC register. 21 | 22 | The Reset RAM button, if set to true, will reset every memory unit to 0. 23 | 24 | The Read ROM button, if set to true, will copy the values of the memory table in the ROM.xlsx spreadsheet onto the RAM table of the CPU.xlsx spreadsheet. 25 | 26 | Normal operation of the CPU consists of setting the Reset Button to high, either flipping the Reset RAM or Read ROM buttons on and off again (causing the RAM to be reset or the ROM to be read into the RAM table), and then turning off the Reset Button. The CPU is then set up to either run a program in Manual mode, or will carry out the program specified in RAM. 27 | 28 | 29 | The CPU is designed to run according to the instruction set architecture specified in the InstructionSet.xlsx spreadsheet. 30 | 31 | Warning: It is not possible to simply mash the F9 key as fast as possible, it takes time for Excel to update so many cells, it is recommended to wait until the text "Ready" can be seen in the bottom left corner of Excel can be seen before continuing to press the F9 key. 32 | 33 | 34 | Alternatively, programs can be written in the Excel-ASM16 language and compiled to the ROM.xlsx spreadsheet. 35 | 36 | Excel-ASM16 features 24 different case-insensitive instructions. 37 | There are three different operands that are used in each instruction 38 | ``` 39 | REG ; refers to any of the 16 general purpose registers 40 | E.G. R0, R1, R15 &c. 41 | 42 | MEM ; refers to any 16-bit addressable memory unit (formatted in hexadecimal) 43 | E.G. @0000, @F000, @FFFF, &c. 44 | 45 | IMD ; refers to an immediate number usually 16-bits long, except in the case of ROL and ROR 46 | ; can be defined either in decimal or hexadecimal 47 | E.G. #0000, $0CCC, #60340, $FF10, &c. 48 | ``` 49 | ### LOAD 50 | ``` 51 | LOAD REG MEM ; loads the specified memory unit into REG 52 | LOAD REG IMD ; load specified 16-bit immediate value into REG 53 | LOAD REG REG ; loads memory unit at the address stored in REGB into REGA 54 | ``` 55 | ### STORE 56 | ``` 57 | STORE REG MEM ; stores the value of REG to the address specified 58 | STORE REG REG ; stores the value of REGA into the memory unit at the address in REGB 59 | ``` 60 | ### JUMP 61 | ``` 62 | JMP IMD ; sets PC to the immediate 16-bit value 63 | JEQ IMD ; if ZF = 0, sets PC to the immediate 16-bit value 64 | JLT IMD ; if CF = 0, sets PC to the immediate 16-bit value 65 | JGE IMD ; if CF = 1 or ZF = 1, sets PC to the immediate 16-bit value 66 | ``` 67 | ### TRAN 68 | ``` 69 | TRAN REG REG ; transfers value from REGA to REGB 70 | ``` 71 | ### ALGEBRAIC INSTRUCTIONS 72 | ### ADD 73 | ``` 74 | ADD REG REG ; REGA + REGB + CF, result stored in REGA 75 | ``` 76 | ### SUB 77 | ``` 78 | SUB REG REG ; (REGA - REGB) - CF, result stored in REGA 79 | ``` 80 | ### MULT 81 | ``` 82 | MULT REG REG ; REGA * REGB, low 16-bit result stored in REGA, high 16-bit result stored in REGB 83 | ``` 84 | ### DIV 85 | ``` 86 | DIV REG REG ; REGA / REGB result stored in REGA, REGA MOD REGB stored in REGB 87 | ``` 88 | ### INC 89 | ``` 90 | INC REG ; REGA++, CF not affected 91 | ``` 92 | ### DEC 93 | ``` 94 | DEC REG ; REGA--, CF not affected 95 | ``` 96 | ### BITWISE INSTRUCTIONS 97 | ### AND 98 | ``` 99 | AND REG REG ; REGA AND REGB, result stored in REGA 100 | ``` 101 | ### OR 102 | ``` 103 | OR REG REG ; REGA OR REGB, result stored in REGA 104 | ``` 105 | ### XOR 106 | ``` 107 | XOR REG REG ; REGA XOR REGB, result stored in REGA 108 | ``` 109 | ### NOT 110 | ``` 111 | NOT REG ; NOT REGA, result stored in REGA 112 | ``` 113 | ### ROLL INSTRUCTIONS 114 | ### ROL 115 | ``` 116 | ROL REG IMD ; leftwise roll of bits of REGA carried out IMD times 117 | ; IMD is a 4-bit value 118 | ``` 119 | ### ROR 120 | ``` 121 | ROR REG IMD ; rightwise roll of bits of REGA carried out IMD times 122 | ; IMD is a 4-bit value 123 | ``` 124 | ### Flag instructions 125 | ``` 126 | CLC ; sets CF to 0 127 | STC ; sets CF to 1 128 | ``` 129 | ### NOP 130 | ``` 131 | NOP ; does not effect any registers or memory 132 | ``` 133 | ### ORG 134 | ``` 135 | ORG IMD ; sets the location of the next instruction 136 | ; must be further than the current length of program 137 | ``` 138 | ### INC 139 | ``` 140 | INC "file.bin" ; copies the binary file into the program 141 | ``` 142 | 143 | ### Compiling 144 | After having written a program, it is compiled with the commandline instruction 145 | ``` 146 | py compileExcelASM16.py program.s ROM.xlsx 147 | ``` 148 | Where **program.s** is the user's program file, and ROM.xlsx is the ROM spreadsheet 149 | 150 | After compiling successfully, the program can be transferred into the CPU.xlsx program by flipping the Read ROM button at the top of the spreadsheet. Note, the ROM.xlsx file must be open for the data to update correctly. 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. 122 | -------------------------------------------------------------------------------- /compileExcelASM16.py: -------------------------------------------------------------------------------- 1 | #COMPILER FOR EXCEL-ASM16 2 | #USE: py compileExcelASM16.py [program.s] ROM.xlsx 3 | 4 | import sys 5 | import os 6 | import time 7 | import math 8 | from openpyxl import load_workbook 9 | 10 | compiled = False; 11 | filePath = "" 12 | spreadsheet = "" 13 | startTime = 0 14 | data = [] 15 | program = [] 16 | output = [] 17 | 18 | labelOpen = False 19 | labelToUse = "" 20 | RED = '\033[91m'; 21 | ENDCOLOR = '\033[0m'; 22 | 23 | def integerError(lineNumber): 24 | print(RED + "\tInteger outside of expected range, line: " + str(lineNumber)+ ENDCOLOR) 25 | compileResults() 26 | 27 | def syntaxError(lineNumber): 28 | print(RED + "\tSyntax Error, line: " + str(lineNumber)+ ENDCOLOR) 29 | compileResults() 30 | 31 | def labelError(lineNumber): 32 | print(RED + "\tDouble label detected, line " + str(lineNumber) + ENDCOLOR) 33 | compileResults() 34 | 35 | def referenceNotFoundError(labelName): 36 | print(RED + "\tReference to variable or label not found, " + str(labelName) + ENDCOLOR) 37 | compileResults() 38 | 39 | def unrecognizedError(lineNumber): 40 | print(RED + "\tUnrecognized Instruction, line " + str(lineNumber) + ENDCOLOR) 41 | compileResults() 42 | 43 | def varSequenceError(lineNumber): 44 | print(RED + "\tVariables must be defined before program code, line " + str(lineNumber) + ENDCOLOR) 45 | compileResults() 46 | 47 | def varUseError(varName): 48 | print(RED + "\tVariable cannot be used like label, var: " + str(lineNumber) + ENDCOLOR) 49 | compileResults() 50 | 51 | def orgError(lineNumber): 52 | print(RED + "\tProgram Count exceeds target address, line " + str(lineNumber) + ENDCOLOR) 53 | compileResults() 54 | 55 | def incResourceError(resourceName, lineNumber): 56 | print(RED + "\tResource " + resourceName + " could not be found, line " + str(lineNumber) + ENDCOLOR) 57 | compileResults() 58 | 59 | def lengthError(exceededWords): 60 | print(RED + "\tProgram length exceeds available RAM by " + str(exceededWords) + " words" + ENDCOLOR) 61 | compileResults() 62 | 63 | def ROMbookError(): 64 | print(RED + "\tCould not save to specified workbook, make sure the file is closed and try again" + ENDCOLOR) 65 | exit() 66 | 67 | def createLine(label, operations): 68 | return [label, operations] 69 | 70 | def getCurrentAddress(): 71 | address = len(data) 72 | for operations in program: 73 | address = address + len(operations[1]) 74 | return address 75 | 76 | def getLocationOfLabel(labelName): 77 | location = 0 78 | for var in data: 79 | if (labelName == var[0]): 80 | varUseError(labelName) 81 | location = location + 1 82 | for operations in program: 83 | if (labelName == operations[0]): 84 | return location 85 | location = location + len(operations[1]) 86 | return -1 87 | 88 | def getVarIndex(varName): 89 | i = 0 90 | for var in data: 91 | if (varName == var[0]): 92 | return i 93 | i = i + 1 94 | return -1 95 | 96 | def includeBIN(fileName): 97 | with open(fileName, "rb") as incFile: 98 | cycle = False 99 | lastValue = 0 100 | while (word := incFile.read(1)): 101 | value = int.from_bytes(word, "big") 102 | if (cycle): 103 | value = (lastValue * 256) + value 104 | if (value >= pow(2, 16)): 105 | input() 106 | program.append(createLine("", [value])) 107 | cycle = False 108 | else: 109 | lastValue = value 110 | cycle = True 111 | if (cycle): #catching last single byte value 112 | program.append(createLine("", [(lastValue * 256)])) 113 | 114 | return 115 | 116 | def parseNumber(numberString, lineNumber): 117 | prefix = numberString[0] 118 | numberString = numberString[1:] 119 | result = 0 120 | if (prefix == "$" or prefix == "@"): 121 | result = int(numberString, 16) 122 | if (result > 65535): 123 | integerError(lineNumber) 124 | elif (prefix == "#"): 125 | result = int(numberString) 126 | if (result > 65535): 127 | integerError(lineNumber) 128 | elif (prefix == "R" and numberString.isdigit() and int(numberString) <= 15): 129 | result = int(numberString) 130 | if (result > 15): 131 | integerError(lineNumber) 132 | else: #is a label 133 | result = getLocationOfLabel(prefix + numberString) 134 | if (result == -1 and lineNumber == -1): #second time around 135 | referenceNotFoundError(prefix + numberString) 136 | elif (result == -1): 137 | return "LABEL-" + (prefix + numberString) 138 | if (result > 65535): 139 | integerError(lineNumber) 140 | if (result < 0): 141 | integerError(lineNumber) 142 | return result 143 | 144 | def encode(line, lineNumber): 145 | #convert to list of integers 146 | opcode = line[0] 147 | operand0 = 0 148 | operand1 = 0 149 | twoWord = False 150 | #check instruction format: 151 | if (opcode == "JMP"): 152 | if (not(len(line) == 2)): 153 | syntaxError(lineNumber) 154 | twoWord = True 155 | operand0 = int("0000", 16) 156 | operand1 = parseNumber(line[1], lineNumber) 157 | elif (opcode == "JEQ"): 158 | if (not(len(line) == 2)): 159 | syntaxError(lineNumber) 160 | twoWord = True 161 | operand0 = int("0100", 16) 162 | operand1 = parseNumber(line[1], lineNumber) 163 | elif (opcode == "JLT"): 164 | if (not(len(line) == 2)): 165 | syntaxError(lineNumber) 166 | twoWord = True 167 | operand0 = int("0200", 16) 168 | operand1 = parseNumber(line[1], lineNumber) 169 | elif (opcode == "JGE"): 170 | if (not(len(line) == 2)): 171 | syntaxError(lineNumber) 172 | twoWord = True 173 | operand0 = int("0300", 16) 174 | operand1 = parseNumber(line[1], lineNumber) 175 | elif (opcode == "LOAD"): 176 | if (not(len(line) == 3)): 177 | syntaxError(lineNumber) 178 | varVal = getVarIndex(line[2]) #check if references variable 179 | if (line[1][0] == "R" and not(str(varVal)[0] == "-")): 180 | twoWord = True 181 | operand0 = int("0400", 16) + (parseNumber(line[1], lineNumber) * 16) 182 | operand1 = varVal 183 | elif (line[1][0] == line[2][0] and line[1][0] == "R"): 184 | operand0 = int("1900", 16) + (parseNumber(line[1], lineNumber) * 16) + parseNumber(line[2], lineNumber) 185 | elif (line[1][0] == "R" and line[2][0] == "@"): 186 | twoWord = True 187 | operand0 = int("0400", 16) + (parseNumber(line[1], lineNumber) * 16) 188 | operand1 = parseNumber(line[2], lineNumber) 189 | elif (line[1][0] == "R" and (line[2][0] == "$" or line[2][0] == "#")): 190 | twoWord = True 191 | operand0 = int("0500", 16) + (parseNumber(line[1], lineNumber) * 16) 192 | operand1 = parseNumber(line[2], lineNumber) 193 | else: 194 | syntaxError(lineNumber) 195 | elif (opcode == "STORE"): 196 | if (not(len(line) == 3)): 197 | syntaxError(lineNumber) 198 | if (line[1][0] == line[2][0] and line[1][0] == "R"): 199 | operand0 = int("0700", 16) + (parseNumber(line[1], lineNumber) * 16) + parseNumber(line[2], lineNumber) 200 | elif (line[1][0] == "R" and line[2][0] == "@"): 201 | twoWord = True 202 | operand0 = int("0600", 16) + (parseNumber(line[1], lineNumber) * 16) 203 | operand1 = parseNumber(line[2], lineNumber) 204 | else: 205 | syntaxError(lineNumber) 206 | elif (opcode == "TRAN"): 207 | if (not(len(line) == 3)): 208 | syntaxError(lineNumber) 209 | if (line[1][0] == line[2][0] and line[1][0] == "R"): 210 | operand0 = int("0800", 16) + (parseNumber(line[1], lineNumber) * 16) + parseNumber(line[2], lineNumber) 211 | else: 212 | syntaxError(lineNumber) 213 | elif (opcode == "ADD"): 214 | if (not(len(line) == 3)): 215 | syntaxError(lineNumber) 216 | if (line[1][0] == line[2][0] and line[1][0] == "R"): 217 | operand0 = int("0900", 16) + (parseNumber(line[1], lineNumber) * 16) + parseNumber(line[2], lineNumber) 218 | else: 219 | syntaxError(lineNumber) 220 | elif (opcode == "SUB"): 221 | if (not(len(line) == 3)): 222 | syntaxError(lineNumber) 223 | if (line[1][0] == line[2][0] and line[1][0] == "R"): 224 | operand0 = int("0A00", 16) + (parseNumber(line[1], lineNumber) * 16) + parseNumber(line[2], lineNumber) 225 | else: 226 | syntaxError(lineNumber) 227 | elif (opcode == "MULT"): 228 | if (not(len(line) == 3)): 229 | syntaxError(lineNumber) 230 | if (line[1][0] == line[2][0] and line[1][0] == "R"): 231 | operand0 = int("0B00", 16) + (parseNumber(line[1], lineNumber) * 16) + parseNumber(line[2], lineNumber) 232 | else: 233 | syntaxError(lineNumber) 234 | elif (opcode == "DIV"): 235 | if (not(len(line) == 3)): 236 | syntaxError(lineNumber) 237 | if (line[1][0] == line[2][0] and line[1][0] == "R"): 238 | operand0 = int("0C00", 16) + (parseNumber(line[1], lineNumber) * 16) + parseNumber(line[2], lineNumber) 239 | else: 240 | syntaxError(lineNumber) 241 | elif (opcode == "INC"): 242 | if (not(len(line) == 2)): 243 | syntaxError(lineNumber) 244 | if (line[1][0] == "R"): 245 | operand0 = int("0D00", 16) + (parseNumber(line[1], lineNumber) * 16) 246 | else: 247 | syntaxError(lineNumber) 248 | elif (opcode == "DEC"): 249 | if (not(len(line) == 2)): 250 | syntaxError(lineNumber) 251 | if (line[1][0] == "R"): 252 | operand0 = int("0E00", 16) + (parseNumber(line[1], lineNumber) * 16) 253 | else: 254 | syntaxError(lineNumber) 255 | elif (opcode == "AND"): 256 | if (not(len(line) == 3)): 257 | syntaxError(lineNumber) 258 | if (line[1][0] == line[2][0] and line[1][0] == "R"): 259 | operand0 = int("0F00", 16) + (parseNumber(line[1], lineNumber) * 16) + parseNumber(line[2], lineNumber) 260 | else: 261 | syntaxError(lineNumber) 262 | elif (opcode == "OR"): 263 | if (not(len(line) == 3)): 264 | syntaxError(lineNumber) 265 | if (line[1][0] == line[2][0] and line[1][0] == "R"): 266 | operand0 = int("1000", 16) + (parseNumber(line[1], lineNumber) * 16) + parseNumber(line[2], lineNumber) 267 | else: 268 | syntaxError(lineNumber) 269 | elif (opcode == "XOR"): 270 | if (not(len(line) == 3)): 271 | syntaxError(lineNumber) 272 | if (line[1][0] == line[2][0] and line[1][0] == "R"): 273 | operand0 = int("1100", 16) + (parseNumber(line[1], lineNumber) * 16) + parseNumber(line[2], lineNumber) 274 | else: 275 | syntaxError(lineNumber) 276 | elif (opcode == "NOT"): 277 | if (not(len(line) == 2)): 278 | syntaxError(lineNumber) 279 | if (line[1][0] == "R"): 280 | operand0 = int("1200", 16) + (parseNumber(line[1], lineNumber) * 16) 281 | else: 282 | syntaxError(lineNumber) 283 | elif (opcode == "ROL"): 284 | if (not(len(line) == 3)): 285 | syntaxError(lineNumber) 286 | if (not(line[1][0]) == line[2][0] and line[1][0] == "R"): 287 | operand0 = int("1300", 16) + (parseNumber(line[1], lineNumber) * 16) + parseNumber(line[2], lineNumber) 288 | else: 289 | syntaxError(lineNumber) 290 | elif (opcode == "ROR"): 291 | if (not(len(line) == 3)): 292 | syntaxError(lineNumber) 293 | if (not(line[1][0] == line[2][0]) and line[1][0] == "R"): 294 | operand0 = int("1400", 16) + (parseNumber(line[1], lineNumber) * 16) + parseNumber(line[2], lineNumber) 295 | else: 296 | syntaxError(lineNumber) 297 | elif (opcode == "CMP"): 298 | if (not(len(line) == 3)): 299 | syntaxError(lineNumber) 300 | if (line[1][0] == line[2][0] and line[1][0] == "R"): 301 | operand0 = int("1500", 16) + (parseNumber(line[1], lineNumber) * 16) + parseNumber(line[2], lineNumber) 302 | else: 303 | syntaxError(lineNumber) 304 | elif (opcode == "CLC"): 305 | if (not(len(line) == 1)): 306 | syntaxError(lineNumber) 307 | operand0 = int("1600", 16) 308 | elif (opcode == "STC"): 309 | if (not(len(line) == 1)): 310 | syntaxError(lineNumber) 311 | operand0 = int("1700", 16) 312 | elif (opcode == "NOP"): 313 | if (not(len(line) == 1)): 314 | syntaxError(lineNumber) 315 | operand0 = int("1800", 16) 316 | elif (len(line) == 3 and line[1] == "="): #variables 317 | if (len(program) > 0): 318 | varSequenceError(lineNumber) 319 | data.append(createLine(line[0], line[2])) 320 | return None 321 | elif (len(line) == 1 and line[0] == ".DATA"): 322 | return None 323 | elif (len(line) == 1 and line[0] == ".CODE"): 324 | #ADD JUMP TO START OF PROGRAM 325 | data.insert(0, createLine("", len(data) + 2)) #number of words in data section 326 | data.insert(0, createLine("", int("0000", 16))) #JMP 327 | return None 328 | elif (opcode == "ORG"): 329 | if (not(len(line) == 2)): 330 | syntaxError(lineNumber) 331 | targetAddress = parseNumber(line[1], lineNumber) 332 | currentAddress = getCurrentAddress() 333 | if (currentAddress > targetAddress): 334 | orgError(lineNumber) 335 | while(currentAddress < targetAddress): 336 | program.append(createLine("", [0])) 337 | currentAddress = currentAddress + 1 338 | return None 339 | elif (opcode == ".INC"): 340 | if (not(len(line) == 2)): 341 | syntaxError(lineNumber) 342 | line[1] = line[1].replace("\"", "") 343 | line[1] = line[1].replace("\'", "") 344 | if (not(os.path.isfile(line[1]))): 345 | incResourceError(line[1], lineNumber) 346 | includeBIN(line[1]) 347 | return None 348 | else: 349 | unrecognizedError(lineNumber) 350 | 351 | if (not twoWord): 352 | #print(str(hex(operand0)).upper()) 353 | return [operand0] 354 | else: 355 | #print(str(hex(operand0)).upper() + ", " + str(hex(operand1)).upper()) 356 | return [operand0, operand1] 357 | 358 | def parseProgram(): 359 | global output 360 | global compiled 361 | global data 362 | for value in data: 363 | if (len(value[0]) > 0): 364 | value[1] = parseNumber(value[1], 0) 365 | output.append(value[1]) 366 | for operations in program: 367 | for value in operations[1]: 368 | output.append(value) 369 | if (len(output) > 65536): 370 | lengthError(len(output) - 65536) 371 | compiled = True 372 | return 373 | 374 | def parseUnmarkedLabels(): 375 | pLine = 0 376 | for operations in program: 377 | valLine = 0 378 | for val in operations[1]: 379 | if ("LABEL" in str(val)): 380 | program[pLine][1][valLine] = parseNumber(val[6:], -1) 381 | valLine = valLine + 1 382 | pLine = pLine + 1 383 | return 384 | 385 | def compileASM(filepath): 386 | file = open(filepath, "r") 387 | lineNumber = 1 #file line number for specifying errors 388 | for line in file: 389 | #print(line) 390 | line = line.upper() 391 | line = line.split(";") #getting rid of comments 392 | line[0] = line[0].replace("\n", "") #removing return line 393 | line[0] = line[0].replace("\r", "") 394 | line[0] = line[0].strip() 395 | #print(line) 396 | if (len(line[0]) > 0): 397 | parseLine(line[0], lineNumber) 398 | lineNumber = lineNumber + 1 399 | parseUnmarkedLabels() 400 | parseProgram() 401 | compileResults() 402 | 403 | def parseLine(line, lineNumber): 404 | global labelOpen 405 | global labelToUse 406 | labelLine = line.split(":"); 407 | label = labelLine[0] 408 | if (":" in line and len(labelLine[1]) <= 1): 409 | if (labelOpen): 410 | labelError(lineNumber) 411 | labelToUse = label #add a label with no operations to program 412 | labelOpen = True 413 | return 414 | elif (":" not in line): 415 | if (labelOpen): 416 | label = labelToUse 417 | labelOpen = False 418 | else: 419 | label = "" 420 | else: 421 | if (labelOpen): 422 | labelError(lineNumber) 423 | line = labelLine[1].strip() 424 | line = line.split(" ") 425 | #print(str(lineNumber) + "\t" + str(label) + "\t" + str(line)) 426 | operations = encode(line, lineNumber) 427 | if (not(operations == None)): 428 | program.append(createLine(label, operations)) 429 | return 430 | 431 | def sendToSpreadsheet(): 432 | #load excel file 433 | workbook = load_workbook(filename = spreadsheet) 434 | #open workbook 435 | sheet = workbook.active 436 | try: 437 | i = 0 438 | while (i < pow(2, 16)): 439 | if (i < len(output)): 440 | sheet.cell(row = math.floor(i / 256) + 1, column = (i % 256) + 1, value = output[i]) 441 | else: 442 | sheet.cell(row = math.floor(i / 256) + 1, column = (i % 256) + 1, value = 0) 443 | i = i + 1 444 | #save the file 445 | workbook.save(filename = spreadsheet) 446 | except: 447 | ROMbookError() 448 | return 449 | 450 | def compileResults(): 451 | if (not(compiled)): 452 | print(RED + "\tProgram could not be compiled" + ENDCOLOR) 453 | else: 454 | print("\tProgram compiled Successfully") 455 | #print(output) 456 | print("\tProgram length in words: " + str(getCurrentAddress())) 457 | print("\tWriting to spreadsheet ROM...") 458 | sendToSpreadsheet() 459 | print("\tFinished in " + str(time.time()-startTime)[:6] + "s") 460 | exit() 461 | 462 | if __name__ == "__main__": 463 | startTime = time.time() 464 | os.system('color') 465 | print("\tStarting operation...") 466 | 467 | if (len(sys.argv) == 3): 468 | filePath = sys.argv[1] 469 | spreadsheet = sys.argv[2] 470 | elif (len(sys.argv) == 1): 471 | print(RED + "\tInsufficent arguments, no ASM file specified" + ENDCOLOR) 472 | compileResults() 473 | elif (len(sys.argv) == 2): 474 | print(RED + "\tInsufficent arguments, no target spreadsheet specified" + ENDCOLOR) 475 | compileResults() 476 | else: 477 | print(RED + "\tArguments too many" + ENDCOLOR) 478 | compileResults() 479 | 480 | if (not(os.path.isfile(filePath))): 481 | print(RED + "\tFile " + filePath + " not found" + ENDCOLOR) 482 | compileResults() 483 | compileASM(filePath) --------------------------------------------------------------------------------