├── .gitignore ├── .idea ├── bbc c.iml ├── encodings.xml ├── inspectionProfiles │ └── Project_Default.xml ├── misc.xml ├── modules.xml └── vcs.xml ├── README.md ├── bbcasm ├── __init__.py ├── asm.py ├── insts.py ├── lexer.py ├── parser.py └── tokens.py ├── bbcc ├── __init__.py ├── asm.py ├── ast.py ├── ctypes.py ├── decl_tree.py ├── il.py ├── interpreter.py ├── lexer.py ├── optimiser.py ├── parser.py ├── preproc.py ├── spots.py ├── symbols.py └── tokens.py ├── bbcdisk ├── __init__.py └── tools │ ├── __init__.py │ ├── diskutils.py │ └── makedfs.py ├── bbcld ├── __init__.py ├── linker.py └── parser.py ├── bbcnix ├── build.sh ├── io.h ├── io.o ├── io.s ├── main.c ├── main.o ├── main.s ├── out └── out.ssd ├── bbcout.o ├── bbcpython ├── build.sh ├── chunk.c ├── chunk.h ├── chunk.o ├── chunk.s ├── common.h ├── compiler.c ├── compiler.h ├── compiler.o ├── compiler.s ├── debug.c ├── debug.h ├── debug.o ├── debug.s ├── main.c ├── main.o ├── main.s ├── memory.c ├── memory.h ├── memory.o ├── memory.s ├── object.c ├── object.h ├── object.o ├── object.s ├── out ├── out.o ├── out.ssd ├── python.o ├── scanner.c ├── scanner.h ├── scanner.o ├── scanner.s ├── value.c ├── value.h ├── value.o ├── value.s ├── vm.c ├── vm.h ├── vm.o └── vm.s ├── bbctape └── __init__.py ├── bbcvm ├── instruction_set.md ├── loader ├── loader.o ├── loader.s ├── loader.ssd ├── loader.wav ├── vm ├── vm.o ├── vm.s ├── vm.ssd └── vm.wav ├── bbcvmasm ├── __init__.py ├── assembler.py ├── ast.py ├── lexer.py ├── parser.py └── tokens.py ├── bbcvmem ├── gui.fbp ├── gui.py ├── main.py └── requirements.txt ├── bbcvmld ├── __init__.py ├── linker.py └── parser.py ├── brot.s ├── build-lib.sh ├── lib ├── ctype.h ├── libc.o ├── start.o ├── stdbool.h ├── stddef.h ├── stdint.h ├── stdio.h ├── stdlib.h └── string.h ├── lib_src ├── ctype.c ├── ctype.o ├── ctype.s ├── start.o ├── start.s ├── stdio.c ├── stdio.o ├── stdio.s ├── stdioasm.o ├── stdioasm.s ├── stdlib.c ├── stdlib.o ├── stdlib.s ├── string.c ├── string.o └── string.s ├── main.c ├── main.o ├── main.py ├── main.s ├── memory-expansion ├── memory-expansion-cache.lib ├── memory-expansion.bak ├── memory-expansion.kicad_pcb ├── memory-expansion.net ├── memory-expansion.pro ├── memory-expansion.sch ├── memory-expansion.xml └── sym-lib-table ├── out ├── out.o ├── out.ssd ├── out.wav └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Python template 3 | # Byte-compiled / optimized / DLL files 4 | __pycache__/ 5 | *.py[cod] 6 | *$py.class 7 | 8 | # C extensions 9 | *.so 10 | 11 | # Distribution / packaging 12 | .Python 13 | env/ 14 | build/ 15 | develop-eggs/ 16 | dist/ 17 | downloads/ 18 | eggs/ 19 | .eggs/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *,cover 47 | .hypothesis/ 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | local_settings.py 56 | 57 | # Flask stuff: 58 | instance/ 59 | .webassets-cache 60 | 61 | # Scrapy stuff: 62 | .scrapy 63 | 64 | # Sphinx documentation 65 | docs/_build/ 66 | 67 | # PyBuilder 68 | target/ 69 | 70 | # Jupyter Notebook 71 | .ipynb_checkpoints 72 | 73 | # pyenv 74 | .python-version 75 | 76 | # celery beat schedule file 77 | celerybeat-schedule 78 | 79 | # SageMath parsed files 80 | *.sage.py 81 | 82 | # dotenv 83 | .env 84 | 85 | # virtualenv 86 | .venv 87 | venv/ 88 | ENV/ 89 | 90 | # Spyder project settings 91 | .spyderproject 92 | 93 | # Rope project settings 94 | .ropeproject 95 | ### JetBrains template 96 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 97 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 98 | 99 | # User-specific stuff: 100 | .idea/**/workspace.xml 101 | .idea/**/tasks.xml 102 | .idea/dictionaries 103 | 104 | # Sensitive or high-churn files: 105 | .idea/**/dataSources/ 106 | .idea/**/dataSources.ids 107 | .idea/**/dataSources.xml 108 | .idea/**/dataSources.local.xml 109 | .idea/**/sqlDataSources.xml 110 | .idea/**/dynamic.xml 111 | .idea/**/uiDesigner.xml 112 | 113 | # Gradle: 114 | .idea/**/gradle.xml 115 | .idea/**/libraries 116 | 117 | # Mongo Explorer plugin: 118 | .idea/**/mongoSettings.xml 119 | 120 | ## File-based project format: 121 | *.iws 122 | 123 | ## Plugin-specific files: 124 | 125 | # IntelliJ 126 | /out/ 127 | 128 | # mpeltonen/sbt-idea plugin 129 | .idea_modules/ 130 | 131 | # JIRA plugin 132 | atlassian-ide-plugin.xml 133 | 134 | # Crashlytics plugin (for Android Studio and IntelliJ) 135 | com_crashlytics_export_strings.xml 136 | crashlytics.properties 137 | crashlytics-build.properties 138 | fabric.properties 139 | 140 | -------------------------------------------------------------------------------- /.idea/bbc c.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # bbc-c 2 | C compiler for the BBC Micro series of micros 3 | 4 | The repository is aranged as such 5 | 6 | | Folder | Contents | 7 | |:------:|:--------:| 8 | | `bbcasm` | 6502 assembler outputting a with a custom object file header | 9 | | `bbcc` | the C compiler proper | 10 | | `bbcdisk` | utilities for BBC Micro FDD disk images | 11 | | `bbcld` | linker for said custom 6502 object files | 12 | | `bbcnix` | the begginings of *nix for the Beeb (should probably have it's own repo) | 13 | | `bbcpython` | a simple python interpreter for the Beeb (should also be in its own repo) | 14 | | `bbctape` | utilities for BBC Micro tape images | 15 | | `bbcvm` | a custom 32-bit x86-like virtual machine for the 6502 | 16 | | `bbcvmasm` | an assembler for the custom VM | 17 | | `bbcvmem` | a python GUI emulator implementing the VM's instruction set | 18 | | `bbcvmld` | linker for the VM object files | 19 | | `lib` | compiled C standard library | 20 | | `lib_src`| source of the C standard library | 21 | | `memory-expansion` | kicad files for a 24-bit bus memory expansion module for the Beeb | 22 | 23 | ## Usage 24 | 25 | This project requires python 3. 26 | 27 | `main.py` is the entrypoint for everything. 28 | ``` 29 | usage: main.py [-h] [-o OUTPUT] [-Wa [WA [WA ...]]] [-Wl [WL [WL ...]]] [-S] 30 | [-c] [-shared] [-static] [-strip] [-6502] 31 | files [files ...] 32 | 33 | Compiler suite for the BBC Microcomputer 34 | 35 | positional arguments: 36 | files Input files 37 | 38 | optional arguments: 39 | -h, --help show this help message and exit 40 | -o OUTPUT, --output OUTPUT 41 | Output file name 42 | -Wa [WA [WA ...]] Assembler options 43 | -Wl [WL [WL ...]] Linker options 44 | -S Compile only 45 | -c Compile and assemble but do not link 46 | -shared Create a shared library 47 | -static Create a statically linked executable 48 | -strip Strip names of internal symbols from executable header 49 | -6502 Assemble and link (unable to compile) for 6502 instead of VM 50 | 51 | ``` 52 | 53 | If no output is specified the generated `.s` `.o` and `out` files be put in the current directory. 54 | Otherwise the `out` (output executable) file will be the specified output file, and the `.s` and `.o` files 55 | will go in the same directory as the output file. 56 | 57 | All input files must have the same extension (of the same type). 58 | If they are `.c` then they will be compiled (VM output only) assembled and linked (unless specified otherwise). 59 | If they are `.s` then they will be assembled and linked (unless otherwise specified). 60 | If they are `.o` then they will be linked. 61 | 62 | ## The VM 63 | 64 | The VM is documented in greater detail in it's `bbcvm/instruction_set.md` file. 65 | The 6502 implementation of the VM is in the `bbcvm/vm.s` file. 66 | 67 | The `bbcvm/loader.s` file reads files from the BBC Micro filing system into the memory on the extender module, 68 | connected to the system's expansion bus. 69 | 70 | There is a python emulator for the VM that reads in files linked with both `-static` and `-shared` set. 71 | The emulator requires WXPython. 72 | 73 | ## C standard library 74 | 75 | The source of the current standard library is in `lib_src`. Compiled versions are in `lib`, 76 | however if you wish to compile them again the `build-lib.sh` script exists. -------------------------------------------------------------------------------- /bbcasm/__init__.py: -------------------------------------------------------------------------------- 1 | from .lexer import Lexer 2 | from .parser import Parser 3 | from .asm import Assemble 4 | 5 | 6 | def asm_to_object(asm): 7 | lexer_inst = Lexer(asm) 8 | token_list = lexer_inst.tokenize() 9 | 10 | parser = Parser(token_list) 11 | prog = parser.parse() 12 | 13 | assembler = Assemble(prog) 14 | assembler.fill_labels() 15 | 16 | out = assembler.assemble() 17 | return bytes(out) 18 | -------------------------------------------------------------------------------- /bbcasm/asm.py: -------------------------------------------------------------------------------- 1 | from . import insts 2 | import struct 3 | 4 | 5 | class Symbol: 6 | INTERNAL = 0 7 | EXPORT = 1 8 | IMPORT = 2 9 | IMPORT_ADDR = 3 10 | INTERNAL_ADDR = 4 11 | 12 | def __init__(self, name, addr, type, extra=0): 13 | self.name = name 14 | self.addr = addr 15 | self.type = type 16 | self.extra = extra 17 | 18 | def make_bin(self): 19 | return list(struct.pack("".format(self.name, self.addr, self.type, self.extra) 23 | 24 | 25 | class Assemble: 26 | def __init__(self, prog): 27 | self.prog = prog 28 | self.labels = {} 29 | self.symbols = [] 30 | 31 | def fill_labels(self): 32 | addr = 0 33 | for i in self.prog.insts: 34 | if len(i.labels) > 0: 35 | for l in i.labels: 36 | self.labels[l] = addr 37 | if l in self.prog.exports: 38 | self.symbols.append(Symbol(l, addr, Symbol.EXPORT)) 39 | addr += len(i) 40 | for l in self.prog.end_labels: 41 | self.labels[l] = addr 42 | if l in self.prog.exports: 43 | self.symbols.append(Symbol(l, addr, Symbol.EXPORT)) 44 | addr = 0 45 | for n, i in enumerate(self.prog.insts): 46 | inst_len = 1 47 | if isinstance(i, insts.Byte): 48 | inst_len = 0 49 | if isinstance(getattr(i.value, "loc", None), insts.LabelVal): 50 | if i.value.loc.label in self.prog.imports: 51 | loc = i.value.loc.offset 52 | self.symbols.append(Symbol(i.value.loc.label, addr + inst_len, Symbol.IMPORT)) 53 | else: 54 | loc = self.labels[i.value.loc.label] + i.value.loc.offset 55 | if not i.is_relative(): 56 | self.symbols.append(Symbol(i.value.loc.label, addr + inst_len, Symbol.INTERNAL)) 57 | self.prog.insts[n].value.loc = loc 58 | if isinstance(i.value, insts.LabelAddrVal): 59 | if i.value.label in self.prog.imports: 60 | val = insts.LiteralVal(i.value.offset) 61 | self.symbols.append(Symbol(i.value.label, addr + inst_len, Symbol.IMPORT_ADDR)) 62 | else: 63 | val = insts.LiteralVal(i.value.offset) 64 | self.symbols.append(Symbol(i.value.label, addr + inst_len, Symbol.INTERNAL_ADDR, 65 | self.labels[i.value.label]+i.value.loc_offset)) 66 | self.prog.insts[n].value = val 67 | addr += len(i) 68 | 69 | def _make_header(self): 70 | header = [] 71 | for s in self.symbols: 72 | header.extend(s.make_bin()) 73 | 74 | return header 75 | 76 | def assemble(self): 77 | out = [0xB, 0xB, 0xC, 0x42, 0x42, 0x43] 78 | 79 | header = self._make_header() 80 | out.extend(list(struct.pack(" len(self.text) - 1: 17 | return None 18 | else: 19 | return self.text[peek_pos] 20 | 21 | def advance(self): 22 | """Advance the 'pos' pointer and set the 'current_char' variable.""" 23 | self.pos += 1 24 | if self.pos > len(self.text) - 1: 25 | self.current_char = None # Indicates end of input 26 | else: 27 | self.current_char = self.text[self.pos] 28 | 29 | def skip_whitespace(self): 30 | while self.current_char is not None and self.current_char.isspace(): 31 | self.advance() 32 | 33 | def skip_line_comment(self): 34 | while self.current_char != '\n' and self.current_char is not None: 35 | self.advance() 36 | self.advance() 37 | 38 | def integer(self): 39 | result = '' 40 | while self.current_char is not None and self.current_char.isdigit(): 41 | result += self.current_char 42 | self.advance() 43 | return int(result) 44 | 45 | def integer_hex(self): 46 | """Return a integer consumed from the input.""" 47 | result = '' 48 | while self.current_char is not None and \ 49 | (self.current_char.isdigit() or self.current_char.lower() in ["a", "b", "c", "d", "e", "f"]): 50 | result += self.current_char 51 | self.advance() 52 | return int(result, 16) 53 | 54 | def string(self): 55 | result = '' 56 | while self.current_char is not None and self.current_char != '"' and self.current_char != '\n': 57 | result += self.current_char 58 | self.advance() 59 | return result 60 | 61 | def id(self): 62 | result = '' 63 | while self.current_char is not None and self.current_char in string.ascii_letters+string.digits+"_": 64 | result += self.current_char 65 | self.advance() 66 | return result 67 | 68 | def tokenize(self): 69 | tokens = [] 70 | while self.current_char is not None: 71 | if self.current_char.isspace() and self.current_char is not "\n": 72 | self.skip_whitespace() 73 | continue 74 | elif self.current_char == "\n": 75 | self.advance() 76 | 77 | elif self.current_char == "/" and self.peek() == "/": 78 | self.advance() 79 | self.advance() 80 | self.skip_line_comment() 81 | 82 | elif self.current_char == "#": 83 | self.advance() 84 | tokens.append(Token(HASH, "#")) 85 | elif self.current_char == ".": 86 | self.advance() 87 | tokens.append(Token(PERIOD, ".")) 88 | 89 | elif self.current_char == "$": 90 | self.advance() 91 | tokens.append(Token(INTEGER, self.integer_hex())) 92 | elif self.current_char.isdigit(): 93 | tokens.append(Token(INTEGER, self.integer())) 94 | 95 | elif self.current_char == "(": 96 | self.advance() 97 | tokens.append(Token(LPAREM, "(")) 98 | elif self.current_char == ")": 99 | self.advance() 100 | tokens.append(Token(RPAREM, ")")) 101 | elif self.current_char == "<": 102 | self.advance() 103 | tokens.append(Token(LT, "<")) 104 | elif self.current_char == ">": 105 | self.advance() 106 | tokens.append(Token(GT, ">")) 107 | elif self.current_char == "+": 108 | self.advance() 109 | tokens.append(Token(PLUS, "+")) 110 | elif self.current_char == "-": 111 | self.advance() 112 | tokens.append(Token(MINUS, "-")) 113 | elif self.current_char == ",": 114 | self.advance() 115 | tokens.append(Token(COMMA, ",")) 116 | 117 | elif self.current_char == '"': 118 | self.advance() 119 | tokens.append(Token(STRING, self.string())) 120 | self.advance() 121 | 122 | elif not self.current_char.isspace(): 123 | string = self.id() 124 | if self.current_char == ":": 125 | self.advance() 126 | tokens.append(Token(LABEL, string)) 127 | else: 128 | tokens.append(Token(ID, string)) 129 | 130 | else: 131 | self.advance() 132 | 133 | tokens.append(Token(EOF, None)) 134 | 135 | return tokens 136 | -------------------------------------------------------------------------------- /bbcasm/parser.py: -------------------------------------------------------------------------------- 1 | from . import insts 2 | from .tokens import * 3 | import sys 4 | import inspect 5 | 6 | 7 | class Prog: 8 | insts = [] 9 | imports = [] 10 | exports = [] 11 | end_labels = [] 12 | 13 | def __int__(self, insts=None, imports=None, exports=None): 14 | if exports is None: 15 | exports = [] 16 | if imports is None: 17 | imports = [] 18 | if insts is None: 19 | insts = [] 20 | 21 | self.insts = insts 22 | self.imports = imports 23 | self.exports = exports 24 | 25 | 26 | class Parser: 27 | def __init__(self, tokens): 28 | self.tokens = tokens 29 | self.cur_labels = [] 30 | 31 | self.ops = {} 32 | clsmembers = inspect.getmembers(sys.modules[insts.__name__], inspect.isclass) 33 | for c in clsmembers: 34 | if issubclass(c[1], insts.Inst): 35 | self.ops[c[0]] = c[1] 36 | 37 | self.prog = Prog() 38 | 39 | def error(self, index): 40 | raise SyntaxError(f"Invalid syntax: {self.tokens[index]}") 41 | 42 | def eat(self, index, token_type): 43 | if self.tokens[index].type == token_type: 44 | return index + 1 45 | else: 46 | self.error(index) 47 | 48 | def token_is(self, index, token_type): 49 | if self.tokens[index].type == token_type: 50 | return True 51 | else: 52 | return False 53 | 54 | def parse_LiteralVal(self, index): 55 | index = self.eat(index, HASH) 56 | if self.tokens[index].type == INTEGER: 57 | offset = self.tokens[index].value 58 | if self.tokens[index+1].type == LPAREM: 59 | index = self.eat(index+1, LPAREM) 60 | if self.tokens[index].type == ID: 61 | name = self.tokens[index].value 62 | loc_offset = 0 63 | index += 1 64 | if self.tokens[index].type in [PLUS, MINUS]: 65 | index += 1 66 | if not self.tokens[index].type == INTEGER: 67 | self.error(index) 68 | loc_offset = self.tokens[index].value 69 | if self.tokens[index-1].type == MINUS: 70 | loc_offset = -loc_offset 71 | index += 1 72 | index = self.eat(index, RPAREM) 73 | return insts.LabelAddrVal(name, offset=offset, loc_offset=loc_offset), index 74 | else: 75 | self.error(index) 76 | else: 77 | if offset > 255: 78 | self.error(index) 79 | return insts.LiteralVal(offset), index + 1 80 | self.error(index) 81 | 82 | def parse_AccumulatorVal(self, index): 83 | if self.token_is(index, ID): 84 | if self.tokens[index].value.upper() == "A": 85 | return insts.AccumulatorVal(), index + 1 86 | self.error(index) 87 | 88 | def parse_ZpVal(self, index): 89 | if self.tokens[index].type != INTEGER: 90 | self.error(index) 91 | if self.tokens[index].value > 255: 92 | self.error(index) 93 | if self.tokens[index+1].type == LPAREM: 94 | self.error(index) 95 | return insts.ZpVal(self.tokens[index].value), index + 1 96 | 97 | def parse_MemVal(self, index): 98 | if self.tokens[index].type == ID: 99 | return insts.MemVal(insts.LabelVal(self.tokens[index].value)), index + 1 100 | if self.tokens[index].type == INTEGER: 101 | offset = self.tokens[index].value 102 | if self.tokens[index+1].type == LPAREM: 103 | index = self.eat(index+1, LPAREM) 104 | if self.tokens[index].type == ID: 105 | name = self.tokens[index].value 106 | index = self.eat(index+1, RPAREM) 107 | return insts.MemVal(insts.LabelVal(name, offset=offset)), index 108 | else: 109 | self.error(index) 110 | else: 111 | if self.tokens[index].value > 65535: 112 | self.error(index) 113 | return insts.MemVal(self.tokens[index].value), index + 1 114 | self.error(index) 115 | 116 | def parse_ZpXVal(self, index): 117 | value, index = self.parse_ZpVal(index) 118 | index = self.eat(index, COMMA) 119 | if self.token_is(index, ID): 120 | if self.tokens[index].value.upper() == "X": 121 | return insts.ZpXVal(value.loc), index + 1 122 | self.error(index) 123 | 124 | def parse_ZpYVal(self, index): 125 | value, index = self.parse_ZpVal(index) 126 | index = self.eat(index, COMMA) 127 | if self.token_is(index, ID): 128 | if self.tokens[index].value.upper() == "Y": 129 | return insts.ZpYVal(value.loc), index + 1 130 | self.error() 131 | 132 | def parse_MemXVal(self, index): 133 | value, index = self.parse_MemVal(index) 134 | index = self.eat(index, COMMA) 135 | if self.token_is(index, ID): 136 | if self.tokens[index].value.upper() == "X": 137 | return insts.MemXVal(value.loc), index + 1 138 | self.error(index) 139 | 140 | def parse_MemYVal(self, index): 141 | value, index = self.parse_MemVal(index) 142 | index = self.eat(index, COMMA) 143 | if self.token_is(index, ID): 144 | if self.tokens[index].value.upper() == "Y": 145 | return insts.MemYVal(value.loc), index + 1 146 | self.error(index) 147 | 148 | def parse_IndirectVal(self, index): 149 | index = self.eat(index, LPAREM) 150 | value, index = self.parse_MemVal(index) 151 | index = self.eat(index, RPAREM) 152 | return insts.IndirectVal(value.loc), index 153 | 154 | def parse_IndirectYVal(self, index): 155 | index = self.eat(index, LPAREM) 156 | value, index = self.parse_ZpVal(index) 157 | index = self.eat(index, RPAREM) 158 | index = self.eat(index, COMMA) 159 | if self.token_is(index, ID): 160 | if self.tokens[index].value.upper() == "Y": 161 | return insts.IndirectYVal(value.loc), index + 1 162 | self.error(index) 163 | 164 | def parse_IndirectXVal(self, index): 165 | index = self.eat(index, LPAREM) 166 | value, index = self.parse_ZpVal(index) 167 | index = self.eat(index, COMMA) 168 | if self.token_is(index, ID): 169 | if self.tokens[index].value.upper() == "X": 170 | return insts.IndirectXVal(value.loc), self.eat(index + 1, RPAREM) 171 | self.error(index) 172 | 173 | def parse_default(self, index): 174 | self.error(index) 175 | 176 | def parse_inst(self, index): 177 | inst = self.tokens[index].value 178 | op = self.ops.get(inst.upper()) 179 | 180 | if op is None: 181 | raise SyntaxError("Unknown instruction: {}".format(inst)) 182 | if len(op.modes) > 0: 183 | value = None 184 | for m in op.modes: 185 | try: 186 | parser = getattr(self, "parse_{}".format(m[0].__name__), self.parse_default) 187 | value, index = parser(index+1) 188 | break 189 | except SyntaxError: 190 | pass 191 | 192 | if value is None: 193 | self.error(index) 194 | 195 | return op(value), index 196 | 197 | return op(), index+1 198 | 199 | def parse_cmd(self, index): 200 | cmd = self.tokens[index].value 201 | 202 | parser = getattr(self, "parse_cmd_{}".format(cmd), self.parse_default) 203 | index = parser(index + 1) 204 | 205 | return index 206 | 207 | def parse_cmd_export(self, index): 208 | if self.token_is(index, ID): 209 | self.prog.exports.append(self.tokens[index].value) 210 | return index + 1 211 | self.error(index) 212 | 213 | def parse_cmd_import(self, index): 214 | if self.token_is(index, ID): 215 | self.prog.imports.append(self.tokens[index].value) 216 | return index + 1 217 | self.error(index) 218 | 219 | def parse_cmd_byte(self, index): 220 | nums = [] 221 | while True: 222 | if self.tokens[index].type == STRING: 223 | for c in self.tokens[index].value: 224 | nums.append(insts.Byte(insts.LiteralVal(ord(c)))) 225 | index += 1 226 | else: 227 | num, index = self.parse_LiteralVal(index) 228 | inst = insts.Byte(num) 229 | nums.append(inst) 230 | if self.token_is(index, COMMA): 231 | index += 1 232 | else: 233 | break 234 | if len(self.cur_labels) > 0: 235 | nums[0].labels = self.cur_labels 236 | self.cur_labels = [] 237 | self.prog.insts.extend(nums) 238 | return index 239 | 240 | def parse(self): 241 | index = 0 242 | while self.tokens[index].type != EOF: 243 | t = self.tokens[index] 244 | if t.type == ID: 245 | inst, index = self.parse_inst(index) 246 | if len(self.cur_labels) > 0: 247 | inst.labels = self.cur_labels 248 | self.cur_labels = [] 249 | self.prog.insts.append(inst) 250 | 251 | elif t.type == LABEL: 252 | self.cur_labels.append(t.value) 253 | index += 1 254 | 255 | elif t.type == PERIOD: 256 | index = self.parse_cmd(index+1) 257 | 258 | else: 259 | self.error(index) 260 | 261 | if len(self.cur_labels) > 0: 262 | self.prog.end_labels = self.cur_labels 263 | 264 | return self.prog 265 | -------------------------------------------------------------------------------- /bbcasm/tokens.py: -------------------------------------------------------------------------------- 1 | LABEL, ID, EOF, INTEGER, LPAREM, RPAREM, COMMA, HASH, PERIOD, LT, GT, PLUS, MINUS, STRING \ 2 | = "LABEL", "ID", "EOF", "INTEGER", "LPAREM", "RPAREM", "COMMA", "HASH", "PERIOD", "LT", "GT", "PLUS", "MINUS", \ 3 | "STRING" 4 | 5 | 6 | class Token: 7 | def __init__(self, kind, value, ): 8 | self.type = kind 9 | self.value = value 10 | 11 | def __repr__(self): 12 | return 'Token({type}, {value})'.format( 13 | type=self.type, 14 | value=repr(self.value) 15 | ) 16 | -------------------------------------------------------------------------------- /bbcc/__init__.py: -------------------------------------------------------------------------------- 1 | from .lexer import Lexer 2 | from .parser import Parser 3 | from .asm import ASM 4 | from .interpreter import Interpreter 5 | from .symbols import SymbolTableBuilder 6 | from .il import IL 7 | from .preproc import Preproc 8 | 9 | 10 | def main(text: str): 11 | lexer_inst = Lexer(text) 12 | token_list = lexer_inst.tokenize() 13 | 14 | pre = Preproc(token_list) 15 | token_list = pre.process() 16 | 17 | p = Parser(token_list) 18 | ast_out = p.parse() 19 | 20 | symbol_table_builder = SymbolTableBuilder() 21 | symbol_table_builder.visit(ast_out) 22 | symbol_table = symbol_table_builder.scope_out 23 | 24 | asm_code = ASM() 25 | il = IL(symbol_table, asm_code) 26 | interp = Interpreter(symbol_table, il) 27 | 28 | il_out = interp.interpret(ast_out) 29 | 30 | il_out.gen_asm() 31 | 32 | return asm_code.get_asm() 33 | -------------------------------------------------------------------------------- /bbcc/asm.py: -------------------------------------------------------------------------------- 1 | from . import spots 2 | 3 | 4 | class TempAsm: 5 | def __init__(self): 6 | self.asm = [] 7 | 8 | def add_inst(self, inst): 9 | self.asm.append(inst) 10 | 11 | 12 | class ASM: 13 | def __init__(self): 14 | self.asm = [] 15 | self.imports = [] 16 | self.exports = [] 17 | 18 | @classmethod 19 | def to_hex(cls, num, length=2): 20 | return (('%0' + str(length) + 'x') % ((num + (1 << length*4)) % (1 << length*4))).upper() 21 | 22 | def add_inst(self, inst): 23 | self.asm.append(inst) 24 | 25 | def merge_temp_asm(self, asm: TempAsm, ret_label): 26 | for inst in asm.asm: 27 | if isinstance(inst, JmpRet): 28 | self.asm.append(Jmp(spots.MemorySpot(ret_label))) 29 | else: 30 | self.asm.append(inst) 31 | 32 | def add_import(self, name): 33 | self.imports.append(name) 34 | 35 | def add_export(self, name): 36 | self.exports.append(name) 37 | 38 | def get_asm(self): 39 | out = "" 40 | for name in self.imports: 41 | out += f".import {name}\n" 42 | for name in self.exports: 43 | out += f".export {name}\n" 44 | 45 | return out + "\n".join([str(inst) for inst in self.asm]) 46 | 47 | 48 | class _Inst: 49 | name = None 50 | 51 | def __init__(self, source=None, dest=None, size=None): 52 | self.dest = dest.asm(size) if dest else None 53 | self.source = source.asm(size) if source else None 54 | self.size = size 55 | 56 | def __str__(self): 57 | s = "\t" + self.name 58 | if self.source: 59 | s += " " + self.source 60 | if self.dest: 61 | s += ", " + self.dest 62 | return s 63 | 64 | 65 | class Label: 66 | def __init__(self, label): 67 | self.label = label 68 | 69 | def __str__(self): 70 | return self.label + ":" 71 | 72 | 73 | class Comment: 74 | def __init__(self, comment): 75 | self.comment = comment 76 | 77 | def __str__(self): 78 | return "// " + self.comment 79 | 80 | 81 | class Bytes: 82 | def __init__(self, data): 83 | self.data = data 84 | 85 | def __str__(self): 86 | return ".byte " + ",".join(["#{}".format(b) for b in self.data]) 87 | 88 | 89 | class JmpRet: 90 | def __init__(self): 91 | pass 92 | 93 | 94 | class Inline: 95 | def __init__(self, asm): 96 | self.asm = asm 97 | 98 | def __str__(self): 99 | return f"\t{self.asm}" 100 | 101 | class Mov(_Inst): 102 | name = "mov" 103 | 104 | 105 | class Lea(_Inst): 106 | name = "lea" 107 | 108 | 109 | class Call(_Inst): 110 | name = "call" 111 | 112 | 113 | class Jmp(_Inst): 114 | name = "jmp" 115 | 116 | 117 | class Jze(_Inst): 118 | name = "jze" 119 | 120 | 121 | class Jnz(_Inst): 122 | name = "jnz" 123 | 124 | 125 | class Jl(_Inst): 126 | name = "jl" 127 | 128 | 129 | class Jle(_Inst): 130 | name = "jle" 131 | 132 | 133 | class Jg(_Inst): 134 | name = "jg" 135 | 136 | 137 | class Jge(_Inst): 138 | name = "jge" 139 | 140 | 141 | class Ja(_Inst): 142 | name = "ja" 143 | 144 | 145 | class Jae(_Inst): 146 | name = "jae" 147 | 148 | 149 | class Jb(_Inst): 150 | name = "jb" 151 | 152 | 153 | class Jbe(_Inst): 154 | name = "jbe" 155 | 156 | class Sze(_Inst): 157 | name = "sze" 158 | 159 | 160 | class Snz(_Inst): 161 | name = "snz" 162 | 163 | 164 | class Sl(_Inst): 165 | name = "sl" 166 | 167 | 168 | class Sle(_Inst): 169 | name = "sle" 170 | 171 | 172 | class Sg(_Inst): 173 | name = "sg" 174 | 175 | 176 | class Sge(_Inst): 177 | name = "sge" 178 | 179 | 180 | class Sa(_Inst): 181 | name = "sa" 182 | 183 | 184 | class Sae(_Inst): 185 | name = "sae" 186 | 187 | 188 | class Sb(_Inst): 189 | name = "sb" 190 | 191 | 192 | class Sbe(_Inst): 193 | name = "sbe" 194 | 195 | 196 | class Sub(_Inst): 197 | name = "sub" 198 | 199 | 200 | class Add(_Inst): 201 | name = "add" 202 | 203 | 204 | class Mul(_Inst): 205 | name = "mul" 206 | 207 | 208 | class Div(_Inst): 209 | name = "div" 210 | 211 | 212 | class Mod(_Inst): 213 | name = "mod" 214 | 215 | 216 | class And(_Inst): 217 | name = "and" 218 | 219 | 220 | class Or(_Inst): 221 | name = "or" 222 | 223 | 224 | class Not(_Inst): 225 | name = "not" 226 | 227 | 228 | class Xor(_Inst): 229 | name = "xor" 230 | 231 | 232 | class Shl(_Inst): 233 | name = "shl" 234 | 235 | 236 | class Shr(_Inst): 237 | name = "shr" 238 | 239 | 240 | class Neg(_Inst): 241 | name = "neg" 242 | 243 | 244 | class Inc(_Inst): 245 | name = "inc" 246 | 247 | 248 | class Dec(_Inst): 249 | name = "dec" 250 | 251 | 252 | class Cmp(_Inst): 253 | name = "cmp" 254 | 255 | 256 | class Push(_Inst): 257 | name = "push" 258 | 259 | 260 | class Pop(_Inst): 261 | name = "pop" 262 | 263 | 264 | class Ret(_Inst): 265 | name = "ret" 266 | -------------------------------------------------------------------------------- /bbcc/ctypes.py: -------------------------------------------------------------------------------- 1 | import copy 2 | from . import tokens 3 | 4 | 5 | class CType: 6 | def __init__(self, size, const=False): 7 | self.size = size 8 | self.const = const 9 | 10 | def is_complete(self): 11 | return False 12 | 13 | def is_object(self): 14 | return False 15 | 16 | def is_arith(self): 17 | return False 18 | 19 | def is_pointer(self): 20 | return False 21 | 22 | def is_void(self): 23 | return False 24 | 25 | def is_array(self): 26 | return False 27 | 28 | def is_struct_union(self): 29 | return False 30 | 31 | def is_scalar(self): 32 | return self.is_arith() or self.is_pointer() 33 | 34 | def is_const(self): 35 | return self.const 36 | 37 | def is_function(self): 38 | return False 39 | 40 | def is_signed(self): 41 | return False 42 | 43 | def make_unsigned(self): 44 | raise NotImplementedError 45 | 46 | def make_const(self): 47 | const_self = copy.copy(self) 48 | const_self.const = True 49 | return const_self 50 | 51 | 52 | class IntegerCType(CType): 53 | def __init__(self, size, signed): 54 | self.signed = signed 55 | super().__init__(size) 56 | 57 | def is_complete(self): 58 | return True 59 | 60 | def is_object(self): 61 | return True 62 | 63 | def is_arith(self): 64 | return True 65 | 66 | def is_signed(self): 67 | return self.signed 68 | 69 | def make_unsigned(self): 70 | unsign_self = copy.copy(self) 71 | unsign_self.signed = False 72 | return unsign_self 73 | 74 | def __repr__(self): 75 | return ''.format(size=self.size, signed=self.signed, const=self.const) 76 | 77 | def __eq__(self, other): 78 | return type(other) == type(self) and other.size == self.size and other.signed == self.signed 79 | 80 | 81 | class VoidCType(CType): 82 | def __init__(self): 83 | super().__init__(0) 84 | 85 | def is_complete(self): 86 | return False 87 | 88 | def is_void(self): 89 | return True 90 | 91 | def __repr__(self): 92 | return '' 93 | 94 | 95 | class PointerCType(CType): 96 | def __init__(self, arg, const=False): 97 | self.arg = arg 98 | super().__init__(4, const) 99 | 100 | def is_complete(self): 101 | return True 102 | 103 | def is_object(self): 104 | return True 105 | 106 | def is_pointer(self): 107 | return True 108 | 109 | def __repr__(self): 110 | return ''.format(arg=self.arg, const=self.const) 111 | 112 | 113 | class ArrayCType(CType): 114 | def __init__(self, el, n): 115 | self.el = el 116 | self.n = n 117 | super().__init__((n or 1) * self.el.size) 118 | 119 | def is_complete(self): 120 | return self.n is not None 121 | 122 | def is_object(self): 123 | return True 124 | 125 | def is_array(self): 126 | return True 127 | 128 | def __repr__(self): 129 | return ''.format(el=self.el, n=self.n, const=self.const) 130 | 131 | 132 | class FunctionCType(CType): 133 | def __init__(self, args, ret, is_varargs=False): 134 | """Initialize type.""" 135 | self.args = args 136 | self.ret = ret 137 | self.is_varargs = is_varargs 138 | super().__init__(0) 139 | 140 | def is_complete(self): 141 | return False 142 | 143 | def is_function(self): 144 | return True 145 | 146 | def __repr__(self): 147 | return ''.format(args=self.args, ret=self.ret, varargs=self.is_varargs) 148 | 149 | 150 | class _UnionStructCType(CType): 151 | def __init__(self, tag, members=None): 152 | self.tag = tag 153 | self.members = members 154 | self.offsets = {} 155 | super().__init__(1) 156 | 157 | def is_complete(self): 158 | return self.members is not None 159 | 160 | def is_object(self): 161 | return True 162 | 163 | def is_struct_union(self): 164 | return True 165 | 166 | def get_offset(self, member): 167 | return self.offsets.get(member, (None, None)) 168 | 169 | def set_members(self, members): 170 | raise NotImplementedError 171 | 172 | 173 | class StructCType(_UnionStructCType): 174 | def set_members(self, members): 175 | self.members = members 176 | 177 | cur_offset = 0 178 | for member, ctype in members: 179 | self.offsets[member] = cur_offset, ctype 180 | cur_offset += ctype.size 181 | 182 | self.size = cur_offset 183 | 184 | def __repr__(self): 185 | return ''.format(self.members) 186 | 187 | 188 | class UnionCType(_UnionStructCType): 189 | def set_members(self, members): 190 | self.members = members 191 | self.size = max([ctype.size for _, ctype in members], default=0) 192 | for member, ctype in members: 193 | self.offsets[member] = 0, ctype 194 | 195 | def __repr__(self): 196 | return ''.format(self.members) 197 | 198 | 199 | void = VoidCType() 200 | 201 | bool_t = IntegerCType(1, False) 202 | 203 | char = IntegerCType(1, True) 204 | unsig_char = IntegerCType(1, False) 205 | 206 | integer = IntegerCType(4, True) 207 | unsig_int = IntegerCType(4, False) 208 | int_max = 2147483647 209 | int_min = -2147483648 210 | 211 | longint = IntegerCType(8, True) 212 | unsig_longint = IntegerCType(8, False) 213 | longint_max = 9223372036854775807 214 | longint_min = -9223372036854775808 215 | 216 | simple_types = {tokens.VOID: void, 217 | tokens.BOOL: bool_t, 218 | tokens.CHAR: char, 219 | tokens.INT: integer} 220 | -------------------------------------------------------------------------------- /bbcc/decl_tree.py: -------------------------------------------------------------------------------- 1 | from . import tokens 2 | 3 | 4 | class Node: 5 | """Base class for all decl_tree nodes.""" 6 | pass 7 | 8 | 9 | class Root(Node): 10 | """Represents a list of declaration specifiers. 11 | specs (List(Tokens)) - list of the declaration specifiers, as tokens 12 | child (Node) - child declaration node 13 | """ 14 | 15 | def __init__(self, specs, decls, inits=None): 16 | """Generate root node.""" 17 | self.specs = specs 18 | self.decls = decls 19 | 20 | if inits: 21 | self.inits = inits 22 | else: 23 | self.inits = [None] * len(self.decls) 24 | 25 | def __repr__(self): 26 | return str(self.specs) + str(self.decls) 27 | 28 | 29 | class Pointer(Node): 30 | """Represents a pointer to a type.""" 31 | 32 | def __init__(self, child, const=False): 33 | """Generate pointer node.""" 34 | self.child = child 35 | self.const = const 36 | 37 | 38 | class Array(Node): 39 | """Represents an array of a type. 40 | n (int) - size of the array 41 | """ 42 | 43 | def __init__(self, n, child): 44 | """Generate array node.""" 45 | self.n = n 46 | self.child = child 47 | 48 | 49 | class Function(Node): 50 | """Represents an function with given arguments and returning given type. 51 | args (List(Node)) - arguments of the functions 52 | """ 53 | 54 | def __init__(self, args, child): 55 | """Generate array node.""" 56 | self.args = args 57 | self.child = child 58 | 59 | 60 | class Identifier(Node): 61 | """Represents an identifier. 62 | If this is a type name and has no identifier, `identifier` is None. 63 | """ 64 | 65 | def __init__(self, identifier): 66 | """Generate identifier node from an identifier token.""" 67 | self.identifier = identifier 68 | 69 | def __repr__(self): 70 | return str(self.identifier) 71 | 72 | 73 | class _StructUnion(Node): 74 | def __init__(self, tag, members): 75 | self.tag = tag 76 | self.members = members 77 | 78 | super().__init__() 79 | 80 | 81 | class Struct(_StructUnion): 82 | """Represents a struct C type.""" 83 | 84 | def __init__(self, tag, members): 85 | self.type = tokens.STRUCT 86 | super().__init__(tag, members) 87 | 88 | 89 | class Union(_StructUnion): 90 | """Represents a union C type.""" 91 | 92 | def __init__(self, tag, members): 93 | self.type = tokens.UNION 94 | super().__init__(tag, members) 95 | -------------------------------------------------------------------------------- /bbcc/optimiser.py: -------------------------------------------------------------------------------- 1 | from . import il 2 | from . import spots 3 | 4 | 5 | class Optimiser: 6 | def optimise_Add(self, commands, index, spotmap): 7 | c = commands[index] 8 | l = spotmap[c.left] 9 | r = spotmap[c.right] 10 | if isinstance(l, spots.LiteralSpot) and isinstance(r, spots.LiteralSpot): 11 | spotmap[c.output] = spots.LiteralSpot(l.value+r.value, c.output.type) 12 | del commands[index] 13 | index -= 1 14 | 15 | return commands, index+1, spotmap 16 | 17 | def optimise_Not(self, commands, index, spotmap): 18 | c = commands[index] 19 | e = spotmap[c.expr] 20 | if isinstance(e, spots.LiteralSpot): 21 | spotmap[c.output] = spots.LiteralSpot(~e.value, c.output.type) 22 | del commands[index] 23 | index -= 1 24 | 25 | return commands, index+1, spotmap 26 | 27 | def optimise_Set(self, commands, index, spotmap): 28 | c = commands[index] 29 | c2 = commands[index - 1] 30 | if isinstance(c2, il.Set) and spotmap[c2.output] == spotmap[c.value]: 31 | del commands[index] 32 | index -= 1 33 | spotmap[c2.output] = spotmap[c.output] 34 | 35 | if isinstance(spotmap[c.value], spots.LiteralSpot) or isinstance(spotmap[c.value], spots.LabelMemorySpot): 36 | if c.output.type.is_const() and c.output.type == c.value.type: 37 | spotmap[c.output] = spotmap[c.value] 38 | del commands[index] 39 | index -= 1 40 | 41 | return commands, index+1, spotmap 42 | 43 | def optimise(self, commands, spotmap): 44 | i = 0 45 | while i < len(commands): 46 | if (i + 1) == len(commands): 47 | break 48 | type_name = type(commands[i]).__name__ 49 | o = getattr(self, "optimise_{}".format(type_name), None) 50 | if o is not None: 51 | commands, i, spotmap = o(commands, i, spotmap) 52 | else: 53 | i += 1 54 | 55 | return commands, spotmap -------------------------------------------------------------------------------- /bbcc/preproc.py: -------------------------------------------------------------------------------- 1 | from .tokens import * 2 | from . import lexer 3 | import os 4 | 5 | 6 | class Preproc: 7 | def __init__(self, tokens): 8 | self.tokens = tokens 9 | self.if_depth = 0 10 | self.macros = {} 11 | 12 | def error(self): 13 | raise SyntaxError("Invalid syntax") 14 | 15 | def eat(self, index): 16 | self.tokens = self.tokens[:index] + self.tokens[index + 1:] 17 | 18 | def token_is(self, index, token_type): 19 | if index < 0: 20 | index = 0 21 | try: 22 | if self.tokens[index].type == token_type: 23 | return True 24 | else: 25 | return False 26 | except IndexError: 27 | return False 28 | 29 | def parse_stmt(self, index): 30 | if not self.token_is(index, ID): 31 | self.error() 32 | 33 | try: 34 | index = self.parse_include(index) 35 | return index 36 | except SyntaxError: 37 | pass 38 | try: 39 | index = self.parse_define(index) 40 | return index 41 | except SyntaxError: 42 | pass 43 | try: 44 | index = self.parse_undef(index) 45 | return index 46 | except SyntaxError: 47 | pass 48 | try: 49 | index = self.parse_ifdef(index) 50 | return index 51 | except SyntaxError: 52 | pass 53 | try: 54 | index = self.parse_ifndef(index) 55 | return index 56 | except SyntaxError: 57 | pass 58 | index = self.parse_endif(index) 59 | 60 | return index 61 | 62 | def parse_include(self, index): 63 | if self.tokens[index].value != "include": 64 | self.error() 65 | 66 | if not self.token_is(index+1, STRING): 67 | self.error() 68 | file = self.tokens[index+1].value 69 | tokens = self.read_file(file) 70 | self.tokens = self.tokens[:index] + tokens + self.tokens[index + 2:] 71 | return index 72 | 73 | def parse_define(self, index): 74 | if self.tokens[index].value != "define": 75 | self.error() 76 | self.eat(index) 77 | 78 | if not self.token_is(index, ID): 79 | self.error() 80 | 81 | macro_name = self.tokens[index].value 82 | self.eat(index) 83 | 84 | macro_tokens = [] 85 | while not self.token_is(index, NEWLINE): 86 | macro_tokens.append(self.tokens[index]) 87 | self.eat(index) 88 | 89 | self.macros[macro_name] = macro_tokens 90 | 91 | return index 92 | 93 | def parse_undef(self, index): 94 | if self.tokens[index].value != "undef": 95 | self.error() 96 | self.eat(index) 97 | 98 | if not self.token_is(index, ID): 99 | self.error() 100 | macro_name = self.tokens[index].value 101 | self.eat(index) 102 | 103 | del self.macros[macro_name] 104 | return index 105 | 106 | def parse_ifdef(self, index): 107 | if self.tokens[index].value != "ifdef": 108 | self.error() 109 | self.eat(index) 110 | 111 | if not self.token_is(index, ID): 112 | self.error() 113 | macro_name = self.tokens[index].value 114 | self.eat(index) 115 | 116 | if self.macros.get(macro_name) is None: 117 | old_depth = self.if_depth 118 | self.if_depth += 1 119 | while not self.token_is(index, EOF): 120 | self.eat(index) 121 | if self.token_is(index, HASH) and (self.token_is(index-1, NEWLINE) or index == 0): 122 | self.eat(index) 123 | if self.tokens[index].value.startswith("if"): 124 | self.eat(index) 125 | self.if_depth += 1 126 | if self.tokens[index].value == "endif": 127 | self.eat(index) 128 | self.if_depth -= 1 129 | 130 | if self.if_depth == old_depth: 131 | break 132 | else: 133 | self.if_depth += 1 134 | 135 | return index 136 | 137 | def parse_ifndef(self, index): 138 | if self.tokens[index].value != "ifndef": 139 | self.error() 140 | self.eat(index) 141 | 142 | if not self.token_is(index, ID): 143 | self.error() 144 | macro_name = self.tokens[index].value 145 | self.eat(index) 146 | 147 | if self.macros.get(macro_name) is not None: 148 | old_depth = self.if_depth 149 | self.if_depth += 1 150 | while not self.token_is(index, EOF): 151 | self.eat(index) 152 | if self.token_is(index, HASH) and (self.token_is(index-1, NEWLINE) or index == 0): 153 | self.eat(index) 154 | if self.tokens[index].value.startswith("if"): 155 | self.eat(index) 156 | self.if_depth += 1 157 | if self.tokens[index].value == "endif": 158 | self.eat(index) 159 | self.if_depth -= 1 160 | 161 | if self.if_depth == old_depth: 162 | break 163 | else: 164 | self.if_depth += 1 165 | 166 | return index 167 | 168 | def parse_endif(self, index): 169 | if self.tokens[index].value != "endif": 170 | self.error() 171 | self.eat(index) 172 | self.if_depth -= 1 173 | 174 | return index 175 | 176 | @staticmethod 177 | def read_file(file): 178 | if os.path.exists(file): 179 | f = open(file.decode()) 180 | else: 181 | lib = os.path.join(os.path.dirname(__file__), "..", "lib", file.decode()) 182 | if os.path.exists(lib): 183 | f = open(lib) 184 | else: 185 | raise FileNotFoundError("Unable to find {} for the preprocessor".format(file)) 186 | lex = lexer.Lexer(f.read()) 187 | tokens = lex.tokenize() 188 | return tokens[:-1] 189 | 190 | def process(self): 191 | index = 0 192 | while not self.token_is(index, EOF): 193 | if self.token_is(index, HASH) and (self.token_is(index-1, NEWLINE) or index == 0): 194 | self.tokens = self.tokens[:index] + self.tokens[index+1:] 195 | index = self.parse_stmt(index) 196 | elif self.token_is(index, ID): 197 | if self.tokens[index].value in self.macros: 198 | self.tokens = self.tokens[:index] + self.macros[self.tokens[index].value] + self.tokens[index + 1:] 199 | else: 200 | index += 1 201 | else: 202 | index += 1 203 | 204 | if self.if_depth != 0: 205 | self.error() 206 | 207 | index = 0 208 | while not self.token_is(index, EOF): 209 | if self.token_is(index, NEWLINE): 210 | self.tokens = self.tokens[:index] + self.tokens[index+1:] 211 | else: 212 | index += 1 213 | 214 | return self.tokens 215 | -------------------------------------------------------------------------------- /bbcc/spots.py: -------------------------------------------------------------------------------- 1 | class Spot: 2 | def __init__(self, detail): 3 | self.detail = detail 4 | 5 | def stack_offset(self): 6 | return 0 7 | 8 | def __eq__(self, other): 9 | if type(self) == type(other): 10 | if other.detail == self.detail: 11 | return True 12 | return False 13 | 14 | def __hash__(self): 15 | return hash((self.__class__.__name__, self.detail)) 16 | 17 | def asm(self, size: int): 18 | raise NotImplementedError 19 | 20 | 21 | class LiteralSpot(Spot): 22 | def __init__(self, value): 23 | super().__init__(value) 24 | self.value = value 25 | 26 | def asm(self, size: int): 27 | return "#{}".format(self.value) 28 | 29 | def __repr__(self): 30 | return ''.format(value=self.value) 31 | 32 | 33 | class RegisterSpot(Spot): 34 | def __init__(self, num): 35 | super().__init__(num) 36 | self.num = num 37 | 38 | def asm(self, size: int): 39 | return "%{}".format(self.num) 40 | 41 | def __repr__(self): 42 | return f'' 43 | 44 | 45 | class MemorySpot(Spot): 46 | size_map = {1: "BYTE ", 47 | 2: "WORD ", 48 | 4: "DWORD "} 49 | 50 | def __init__(self, base, offset=0): 51 | super().__init__((base, offset)) 52 | self.base = base 53 | self.offset = offset 54 | 55 | def asm(self, size: int): 56 | if isinstance(self.base, Spot): 57 | base_str = self.base.asm(0) 58 | else: 59 | base_str = self.base 60 | 61 | if self.offset == 0: 62 | simple = f"[{base_str}]" 63 | elif self.offset > 0: 64 | simple = f"{self.offset}[{base_str}]" 65 | else: 66 | simple = f"-{-self.offset}[{base_str}]" 67 | 68 | if size is not None and size not in self.size_map.keys(): 69 | raise SyntaxError(f"Invalid memory spot length: {size}") 70 | 71 | size_desc = self.size_map.get(size, "") 72 | return f"{size_desc}{simple}" 73 | 74 | def __repr__(self): 75 | return f'' 76 | 77 | 78 | R0 = RegisterSpot("r0") 79 | R1 = RegisterSpot("r1") 80 | R2 = RegisterSpot("r2") 81 | R3 = RegisterSpot("r3") 82 | R4 = RegisterSpot("r4") 83 | R5 = RegisterSpot("r5") 84 | R6 = RegisterSpot("r6") 85 | R7 = RegisterSpot("r7") 86 | R8 = RegisterSpot("r8") 87 | R9 = RegisterSpot("r9") 88 | R10 = RegisterSpot("r10") 89 | R11 = RegisterSpot("r11") 90 | 91 | registers = [R0, R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11] 92 | 93 | RBP = RegisterSpot("r12") 94 | RSP = RegisterSpot("r14") 95 | -------------------------------------------------------------------------------- /bbcc/tokens.py: -------------------------------------------------------------------------------- 1 | INTEGER, CHARACTER, STRING, \ 2 | PLUS, MINUS, DIVIDE, MOD, LPAREM, RPAREM, SEMI, COLON, \ 3 | EQUALS, PLUSEQUALS, MINUSEQUALS, STAREQUALS, DIVEQUALS, MODEQUALS, \ 4 | BOOLOR, BOOLAND, BOOLEQUALS, BOOLNOT, LESSTHAN, MORETHAN, LESSEQUAL, MOREEQUAL, \ 5 | ID, INT, CHAR, VOID, BOOL, RETURN, LBRACE, RBRACE, LBRACK, RBRACK, COMMA, BREAK, CONTINUE, \ 6 | SIGNED, UNSIGNED, STATIC, AUTO, CONST, EXTERN, \ 7 | INCR, DECR, AMP, STAR, NOT, ELLIPSIS, TILDA, QMARK, VLINE, HAT, \ 8 | IF, ELSE, WHILE, DO, FOR, SIZEOF, STRUCT, UNION, TYPEDEF, EOF, \ 9 | HASH, NEWLINE, ARROW, DOT, SHIFTLEFT, SHIFTRIGHT = \ 10 | "INTEGER", "CHAR", "STRING", \ 11 | "PLUS", "MINUS", "DIVIDE", "MOD", "LRAPEM", "RPAREM", "SEMI", "COLON", \ 12 | "EQUALS", "PLUSEQUALS", "MINUSEQUALS", "STAREQUALS", "DIVEQUALS", "MODEQUALS", \ 13 | "BOOLOR", "BOOLAND", "BOOLEQUALS", "BOOLNOT", "LESSTHAN", "MORETHAN", "LESSEQUAL", "MOREEQUAL", \ 14 | "ID", "int", "char", "void", "_Bool", "RETURN", "LBRACE", "RBRACE", "LBRACK", "RBRACK", "COMMA", "BREAK", "CONTINUE", \ 15 | "signed", "unsigned", "static", "auto", "const", "extern", \ 16 | "INCR", "DECR", "AMP", "STAR", "NOT", "ELLIPSIS", "TILDA", "QMARK", "VLINE", "HAT", \ 17 | "IF", "ELSE", "WHILE", "DO", "FOR", "SIZEOF", "STRUCT", "UNION", "TYPEDEF", "EOF", \ 18 | "HASH", "NEWLINE", "ARROW", "DOT", "SHIFTLEFT", "SHIFTRIGHT" 19 | 20 | 21 | class Token: 22 | def __init__(self, kind, value, ): 23 | self.type = kind 24 | self.value = value 25 | 26 | def __str__(self): 27 | """String representation of the class instance. 28 | 29 | Examples: 30 | Token(INTEGER, 3) 31 | Token(PLUS '+') 32 | """ 33 | return 'Token({type}, {value})'.format( 34 | type=self.type, 35 | value=repr(self.value) 36 | ) 37 | 38 | def __repr__(self): 39 | return self.__str__() 40 | 41 | 42 | TYPES = { 43 | 'int': Token(INT, 'int'), 44 | 'char': Token(CHAR, 'char'), 45 | 'void': Token(VOID, 'void'), 46 | '_Bool': Token(BOOL, '_Bool'), 47 | } 48 | 49 | STORAGE = { 50 | 'auto': Token(AUTO, 'auto'), 51 | 'static': Token(STATIC, 'static'), 52 | 'extern': Token(EXTERN, 'extern'), 53 | 'typedef': Token(TYPEDEF, 'typedef'), 54 | } 55 | 56 | QUALIFIERS = { 57 | 'const': Token(CONST, 'const'), 58 | } 59 | 60 | TYPE_MODS = { 61 | 'signed': Token(SIGNED, 'signed'), 62 | 'unsigned': Token(UNSIGNED, 'unsigned'), 63 | } 64 | 65 | MODIFIERS = {**STORAGE, **QUALIFIERS, **TYPE_MODS} 66 | 67 | RESERVED_KEYWORDS = {**TYPES, **{**MODIFIERS, **{ 68 | 'return': Token(RETURN, 'return'), 69 | 'break': Token(BREAK, 'break'), 70 | 'continue': Token(CONTINUE, 'continue'), 71 | 'if': Token(IF, 'if'), 72 | 'else': Token(ELSE, 'else'), 73 | 'while': Token(WHILE, 'while'), 74 | 'do': Token(DO, 'do'), 75 | 'for': Token(FOR, 'for'), 76 | 'sizeof': Token(SIZEOF, 'sizeof'), 77 | 'struct': Token(STRUCT, 'struct'), 78 | 'union': Token(UNION, 'union'), 79 | 'typedef': Token(TYPEDEF, 'typedef'), 80 | }}} 81 | -------------------------------------------------------------------------------- /bbcdisk/__init__.py: -------------------------------------------------------------------------------- 1 | from .tools import makedfs 2 | 3 | 4 | File = makedfs.File 5 | 6 | 7 | def files_to_disk(files): 8 | disk = makedfs.Disk() 9 | disk.new() 10 | 11 | cat = disk.catalogue() 12 | cat.write("", files) 13 | 14 | disk.file.seek(0, 0) 15 | return disk.file.read() 16 | -------------------------------------------------------------------------------- /bbcdisk/tools/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheEnbyperor/bbc-c/be7a2d43d7908218be2b788f7bc51d8e16624a63/bbcdisk/tools/__init__.py -------------------------------------------------------------------------------- /bbcdisk/tools/diskutils.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright (C) 2014 David Boddie 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program. If not, see . 16 | """ 17 | 18 | import struct 19 | import time 20 | 21 | # Find the number of centiseconds between 1900 and 1970. 22 | between_epochs = ((365 * 70) + 17) * 24 * 360000 23 | 24 | 25 | class DiskError(Exception): 26 | pass 27 | 28 | 29 | class Utilities: 30 | # Little endian reading 31 | 32 | def _read_signed_word(self, s): 33 | return struct.unpack("> 8 82 | i += 1 83 | 84 | return s 85 | 86 | def _binary(self, size, n): 87 | new = "" 88 | while (n != 0) & (size > 0): 89 | 90 | if (n & 1) == 1: 91 | new = "1" + new 92 | else: 93 | new = "0" + new 94 | 95 | n = n >> 1 96 | size = size - 1 97 | 98 | if size > 0: 99 | new = ("0" * size) + new 100 | 101 | return new 102 | 103 | def _safe(self, s, with_space=0): 104 | new = "" 105 | if with_space == 1: 106 | lower = 31 107 | else: 108 | lower = 32 109 | 110 | for c in s: 111 | 112 | if ord(c) >= 128: 113 | i = ord(c) ^ 128 114 | c = chr(i) 115 | 116 | if ord(c) <= lower: 117 | break 118 | 119 | new = new + c 120 | 121 | return new 122 | 123 | def _pad(self, s, length, ch): 124 | s = s[:length] 125 | if len(s) < length: 126 | s += (length - len(s)) * ch 127 | 128 | return s 129 | 130 | 131 | class Directory: 132 | """directory = Directory(name, address) 133 | 134 | The directory created contains name and files attributes containing the 135 | directory name and the objects it contains. 136 | """ 137 | 138 | def __init__(self, name, files): 139 | self.name = name 140 | self.files = files 141 | 142 | def __repr__(self): 143 | return '<%s instance, "%s", at %x>' % (self.__class__, self.name, id(self)) 144 | 145 | 146 | class File: 147 | """file = File(name, data, load_address, execution_address, length) 148 | """ 149 | 150 | def __init__(self, name, data, load_address, execution_address, 151 | locked=False, disk_address=0): 152 | self.name = name 153 | self.data = data 154 | self.load_address = load_address 155 | self.execution_address = execution_address 156 | self.length = len(data) 157 | self.locked = locked 158 | self.disk_address = disk_address 159 | 160 | def __repr__(self): 161 | return '<%s instance, "%s", at %x>' % (self.__class__, self.name, id(self)) 162 | 163 | def has_filetype(self): 164 | """Returns True if the file's meta-data contains filetype information.""" 165 | return self.load_address & 0xfff00000 == 0xfff00000 166 | 167 | def filetype(self): 168 | """Returns the meta-data containing the filetype information. 169 | 170 | Note that a filetype can be obtained for all files, though it may not 171 | necessarily be valid. Use has_filetype() to determine whether the file 172 | is likely to have a valid filetype.""" 173 | 174 | return "%03x" % ((self.load_address >> 8) & 0xfff) 175 | 176 | def time_stamp(self): 177 | """Returns the time stamp for the file as a tuple of values containing 178 | the local time, or an empty tuple if the file does not have a time stamp.""" 179 | 180 | # RISC OS time is given as a five byte block containing the 181 | # number of centiseconds since 1900 (presumably 1st January 1900). 182 | 183 | # Convert the time to the time elapsed since the Epoch (assuming 184 | # 1970 for this value). 185 | date_num = struct.unpack(" 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program. If not, see . 16 | """ 17 | 18 | __author__ = "David Boddie " 19 | __date__ = "2014-04-22" 20 | __version__ = "0.1" 21 | __license__ = "GNU General Public License (version 3 or later)" 22 | 23 | import io 24 | from .diskutils import DiskError, File, Utilities 25 | 26 | 27 | class Catalogue(Utilities): 28 | def __init__(self, file): 29 | self.file = file 30 | self.sector_size = 256 31 | self.track_size = 10 * self.sector_size 32 | self.interleaved = False 33 | 34 | # The free space map initially contains all the space after the 35 | # catalogue. 36 | self.free_space = [(2, 798)] 37 | self.sectors = 800 38 | self.tracks = self.sectors / 10 39 | self.disk_cycle = 0 40 | self.boot_option = 0 41 | 42 | def read_free_space(self): 43 | # Using notes from http://mdfs.net/Docs/Comp/Disk/Format/DFS 44 | 45 | self.free_space = [] 46 | 47 | def read(self): 48 | disk_title, files = self.read_catalogue(0) 49 | 50 | if self.interleaved: 51 | other_title, more_files = self.read_catalogue(self.track_size) 52 | files += more_files 53 | 54 | return disk_title, files 55 | 56 | def read_catalogue(self, offset): 57 | disk_title = self._read(offset, 8) + self._read(offset + 0x100, 4) 58 | self.disk_cycle = self._read_unsigned_byte(self._read(offset + 0x104, 1)) 59 | last_entry = self._read_unsigned_byte(self._read(offset + 0x105, 1)) 60 | extra = self._read_unsigned_byte(self._read(offset + 0x106, 1)) 61 | sectors = self._read_unsigned_byte(self._read(offset + 0x107, 1)) 62 | self.sectors = sectors | ((extra & 0x03) << 8) 63 | self.boot_option = (extra & 0x30) >> 4 64 | 65 | files = [] 66 | p = 8 67 | 68 | while p <= last_entry: 69 | name = self._read(offset + p, 7) 70 | if name[0] == "\x00": 71 | break 72 | 73 | name = name.strip() 74 | extra = self._read_unsigned_byte(self._read(offset + p + 7)) 75 | prefix = chr(extra & 0x7f) 76 | locked = (extra & 0x80) != 0 77 | 78 | load = self._read_unsigned_half_word(self._read(offset + 0x100 + p, 2)) 79 | exec_ = self._read_unsigned_half_word(self._read(offset + 0x100 + p + 2, 2)) 80 | length = self._read_unsigned_half_word(self._read(offset + 0x100 + p + 4, 2)) 81 | 82 | extra = self._read_unsigned_byte(self._read(offset + 0x100 + p + 6)) 83 | load = load | ((extra & 0x0c) << 14) 84 | length = length | ((extra & 0x30) << 12) 85 | exec_ = exec_ | ((extra & 0xc0) << 10) 86 | 87 | if load & 0x30000 == 0x30000: 88 | load = load | 0xfc0000 89 | if exec_ & 0x30000 == 0x30000: 90 | exec_ = exec_ | 0xfc0000 91 | 92 | file_start_sector = self._read_unsigned_byte(self._read(offset + 0x100 + p + 7)) 93 | file_start_sector = file_start_sector | ((extra & 0x03) << 8) 94 | 95 | if not self.interleaved: 96 | data = self._read(file_start_sector * self.sector_size, length) 97 | disk_address = file_start_sector * self.sector_size 98 | else: 99 | data = "" 100 | sector = file_start_sector 101 | disk_address = self._disk_address(sector) 102 | 103 | while len(data) < length: 104 | addr = offset + self._disk_address(sector) 105 | data += self._read(addr, min(self.sector_size, length - len(data))) 106 | sector += 1 107 | 108 | files.append(File(prefix + "." + name, data, load, exec_, length, locked, 109 | disk_address)) 110 | 111 | p += 8 112 | 113 | return disk_title, files 114 | 115 | def write(self, disk_title, files): 116 | if len(files) > 31: 117 | raise DiskError("Too many entries to write.") 118 | 119 | disk_name = self._pad(self._safe(disk_title), 12, " ") 120 | self._write(0, disk_title[:8].encode()) 121 | self._write(0x100, disk_title[8:12].encode()) 122 | 123 | # Write the number of files and the disk cycle. 124 | self.disk_cycle += 1 125 | self._write(0x104, self._write_unsigned_byte(self.disk_cycle)) 126 | self._write(0x105, self._write_unsigned_byte(len(files) * 8)) 127 | 128 | extra = (self.sectors >> 8) & 0x03 129 | extra = extra | (self.boot_option << 4) 130 | self._write(0x106, self._write_unsigned_byte(extra)) 131 | self._write(0x107, self._write_unsigned_byte(self.sectors & 0xff)) 132 | 133 | p = 8 134 | for file in files: 135 | prefix, name = file.name.split(".", 1) 136 | name = self._pad(name, 7, " ") 137 | self._write(p, name.encode()) 138 | 139 | extra = ord(prefix) 140 | if file.locked: 141 | extra = extra | 128 142 | 143 | self._write(p + 7, self._write_unsigned_byte(extra)) 144 | 145 | load = file.load_address 146 | exec_ = file.execution_address 147 | length = file.length 148 | 149 | self._write(0x100 + p, self._write_unsigned_half_word(load & 0xffff)) 150 | self._write(0x100 + p + 2, self._write_unsigned_half_word(exec_ & 0xffff)) 151 | self._write(0x100 + p + 4, self._write_unsigned_half_word(length & 0xffff)) 152 | 153 | disk_address = self._find_space(file) 154 | file_start_sector = int(disk_address / self.sector_size) 155 | self._write(disk_address, file.data) 156 | 157 | extra = ((file_start_sector >> 8) & 0x03) 158 | extra = extra | ((load >> 14) & 0x0c) 159 | extra = extra | ((exec_ >> 12) & 0x30) 160 | extra = extra | ((length >> 10) & 0xc0) 161 | 162 | self._write(0x100 + p + 6, self._write_unsigned_byte(extra)) 163 | self._write(0x100 + p + 7, self._write_unsigned_byte(file_start_sector & 0xff)) 164 | 165 | p += 8 166 | 167 | def _find_space(self, file): 168 | for i in range(len(self.free_space)): 169 | sector, length = self.free_space[i] 170 | file_length = file.length // self.sector_size 171 | 172 | if file.length % self.sector_size != 0: 173 | file_length += 1 174 | 175 | if length >= file_length: 176 | if length > file_length: 177 | # Update the free space entry to contain the remaining space. 178 | self.free_space[i] = (sector + file_length, length - file_length) 179 | else: 180 | # Remove the free space entry. 181 | del self.free_space[i] 182 | 183 | return sector * self.sector_size 184 | 185 | raise DiskError("Failed to find space for file: %s" % file.name) 186 | 187 | def _disk_address(self, sector): 188 | track = sector / 10 189 | addr = 0 190 | 191 | # Handle some .dsd files with interleaved tracks. 192 | if track >= self.tracks: 193 | track -= self.tracks 194 | addr += self.track_size 195 | 196 | addr += (track * self.track_size * 2) + ((sector % 10) * self.sector_size) 197 | 198 | return addr 199 | 200 | 201 | class Disk: 202 | DiskSizes = {None: 200 * 1024} 203 | SectorSizes = {None: 256} 204 | Catalogues = {None: Catalogue} 205 | 206 | def __init__(self, format=None): 207 | self.format = format 208 | self.size = 0 209 | self.data = "" 210 | self.file = None 211 | 212 | def new(self): 213 | self.size = self.DiskSizes[self.format] 214 | self.data = bytearray("\x00" * self.size, "ASCII") 215 | self.file = io.BytesIO(self.data) 216 | 217 | def open(self, file_object): 218 | self.size = self.DiskSizes[self.format] 219 | self.file = file_object 220 | 221 | def catalogue(self): 222 | return self.Catalogues[self.format](self.file) 223 | -------------------------------------------------------------------------------- /bbcld/__init__.py: -------------------------------------------------------------------------------- 1 | from . import linker 2 | 3 | 4 | def link_object_files_static(objects, sta): 5 | link = linker.Linker(objects, sta) 6 | out, exa = link.link_static() 7 | return bytes(out), exa 8 | 9 | 10 | def link_object_files_shared(objects, strip=False, staic=False): 11 | link = linker.Linker(objects, 0) 12 | out = link.link_shared(strip, staic) 13 | return bytes(out) 14 | -------------------------------------------------------------------------------- /bbcld/linker.py: -------------------------------------------------------------------------------- 1 | from . import parser 2 | import glob 3 | import struct 4 | import os 5 | 6 | 7 | class Linker: 8 | def __init__(self, objects, sta): 9 | self.objects = objects 10 | self.defined_symbols = {} 11 | self.executables = [] 12 | self.sta = sta 13 | 14 | def _find_lib(self, symbol_name): 15 | lib_dir = os.path.join(os.path.dirname(__file__), "..", "lib_6502") 16 | libs = glob.glob(os.path.join(lib_dir, "*.o")) 17 | for l in libs: 18 | lib = open(l, "rb") 19 | lib_source = lib.read() 20 | lib.close() 21 | parse = parser.Parser(list(lib_source)) 22 | obj = parse.parse() 23 | for s in obj.symbols: 24 | if s.type == parser.Symbol.EXPORT and s.name == symbol_name: 25 | return obj 26 | 27 | def _get_symbol(self, s, static): 28 | if s not in self.defined_symbols and static: 29 | lib = self._find_lib(s) 30 | if lib is None: 31 | raise LookupError("Symbol {} not exported anywhere".format(s)) 32 | lib = self._get_symbols(lib, static) 33 | self.executables.append(lib) 34 | 35 | def _get_symbols(self, e, static): 36 | symbols = [] 37 | for s in filter(lambda symbol: symbol.type == parser.Symbol.EXPORT, e.symbols): 38 | self.defined_symbols[s.name] = (s, -1) 39 | symbols.append(s.name) 40 | 41 | for s in filter(lambda symbol: symbol.type == parser.Symbol.IMPORT or 42 | symbol.type == parser.Symbol.IMPORT_ADDR, e.symbols): 43 | self._get_symbol(s.name, static) 44 | 45 | for symbol in symbols: 46 | s = self.defined_symbols[symbol] 47 | if s[1] == -1: 48 | self.defined_symbols[symbol] = (s[0], s[0].addr + self.sta) 49 | e.pos = self.sta 50 | self.sta += len(e.code) 51 | return e 52 | 53 | def _parse_obj(self, obj, static): 54 | p = parser.Parser(list(obj)) 55 | e = p.parse() 56 | e = self._get_symbols(e, static) 57 | self.executables.append(e) 58 | 59 | def link_static(self): 60 | for o in self.objects: 61 | self._parse_obj(o, True) 62 | self._get_symbol("_start", True) 63 | 64 | out = [] 65 | for e in self.executables: 66 | for s in e.symbols: 67 | if s.type == parser.Symbol.IMPORT: 68 | d_symbol, d_addr = self.defined_symbols[s.name] 69 | cur_val = struct.unpack("> (cur_val*8)) & 0xff)) +\ 75 | e.code[s.addr+1:] 76 | 77 | elif s.type == parser.Symbol.INTERNAL: 78 | cur_val = struct.unpack("> (cur_val*8)) & 0xff)) + e.code[s.addr+1:] 83 | out.extend(e.code) 84 | 85 | start_symbol = self.defined_symbols.get("_start") 86 | if start_symbol is None: 87 | raise LookupError("No _start symbol, don't know where to start execution") 88 | 89 | return out, start_symbol[1] 90 | 91 | def link_shared(self, strip): 92 | for o in self.objects: 93 | self._parse_obj(o, False) 94 | 95 | out = [0xB, 0xB, 0xC, 0x42, 0x42, 0x43] 96 | symbols = [] 97 | code = [] 98 | for e in self.executables: 99 | for s in e.symbols: 100 | if s.type == parser.Symbol.IMPORT: 101 | sym = self.defined_symbols.get(s.name) 102 | if sym is not None: 103 | d_symbol, d_addr = sym 104 | cur_val = struct.unpack("> (cur_val*8)) & 0xff)) + e.code[s.addr+1:] 128 | if strip: 129 | name = "" 130 | else: 131 | name = s.name 132 | symbols.append(parser.Symbol(name, e.pos+s.addr, parser.Symbol.INTERNAL_ADDR, s.extra)) 133 | code.extend(e.code) 134 | 135 | header = [] 136 | for s in symbols: 137 | header.extend(s.make_bin()) 138 | out.extend(list(struct.pack("".format(self.name, self.addr, self.type, self.extra) 22 | 23 | 24 | class Executable: 25 | def __init__(self, symbols, code, pos=0): 26 | self.symbols = symbols 27 | self.code = code 28 | self.pos = pos 29 | 30 | def __repr__(self): 31 | return "".format(self.symbols, self.pos, self.code) 32 | 33 | 34 | class Parser: 35 | HEADER = [0xB, 0xB, 0xC, 0x42, 0x42, 0x43] 36 | 37 | def __init__(self, obj): 38 | self.obj = obj 39 | 40 | def _parse_header(self, header): 41 | symbols = [] 42 | while len(header) > 0: 43 | s_type, s_addr, s_extra = struct.unpack("> 4) - 0x74) ^ 0x20); 8 | __asm__("mov6502 %0 $FE00" : : "g"(12)); 9 | } 10 | 11 | -------------------------------------------------------------------------------- /bbcnix/main.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheEnbyperor/bbc-c/be7a2d43d7908218be2b788f7bc51d8e16624a63/bbcnix/main.o -------------------------------------------------------------------------------- /bbcnix/main.s: -------------------------------------------------------------------------------- 1 | .import writeio 2 | .export main 3 | // Function: main 4 | main: 5 | push %r12 6 | mov %r14, %r12 7 | push %r1 8 | // Set 9 | mov #65024, %r1 10 | // Set 11 | mov #12, %r0 12 | // CallFunction 13 | push %r0 14 | push %r1 15 | call [writeio] 16 | add #8, %r14 17 | // Set 18 | mov #65025, %r1 19 | // ShiftLeft 20 | mov #31744, %r0 21 | shl #4, %r0 22 | // Sub 23 | sub #116, %r0 24 | // ExcOr 25 | xor #32, %r0 26 | // Set 27 | // CallFunction 28 | push %r0 29 | push %r1 30 | call [writeio] 31 | add #8, %r14 32 | // Asm 33 | mov6502 %0 $FE00 34 | // Return 35 | mov #0, %r0 36 | __bbcc_00000000: 37 | pop %r1 38 | mov %r12, %r14 39 | pop %r12 40 | ret -------------------------------------------------------------------------------- /bbcnix/out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheEnbyperor/bbc-c/be7a2d43d7908218be2b788f7bc51d8e16624a63/bbcnix/out -------------------------------------------------------------------------------- /bbcnix/out.ssd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheEnbyperor/bbc-c/be7a2d43d7908218be2b788f7bc51d8e16624a63/bbcnix/out.ssd -------------------------------------------------------------------------------- /bbcout.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheEnbyperor/bbc-c/be7a2d43d7908218be2b788f7bc51d8e16624a63/bbcout.o -------------------------------------------------------------------------------- /bbcpython/build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | python3 ../main.py chunk.c compiler.c debug.c main.c memory.c object.c scanner.c value.c vm.c -static -------------------------------------------------------------------------------- /bbcpython/chunk.c: -------------------------------------------------------------------------------- 1 | #include "chunk.h" 2 | 3 | void initChunk(Chunk* chunk) { 4 | initArray(chunk); 5 | initArray(&chunk->constants); 6 | initArray(&chunk->lineInfo); 7 | } 8 | 9 | void writeChunk(Chunk* chunk, uint8_t byte, unsigned int lineNum) { 10 | if (getLastLine(&chunk->lineInfo) != lineNum) { 11 | writeLine(&chunk->lineInfo, chunk->meta.count, lineNum); 12 | } 13 | 14 | writeArray(chunk, sizeof(uint8_t), &byte); 15 | } 16 | 17 | int addConstant(Chunk* chunk, struct Value *value) { 18 | writeValueArray(&chunk->constants, value); 19 | return chunk->constants.meta.count - 1; 20 | } 21 | 22 | void freeChunk(Chunk* chunk) { 23 | reallocate(chunk->code, 0); 24 | freeArray(&chunk->constants); 25 | freeArray(&chunk->lineInfo); 26 | initChunk(chunk); 27 | } 28 | -------------------------------------------------------------------------------- /bbcpython/chunk.h: -------------------------------------------------------------------------------- 1 | #ifndef bbc_python_chunk_h 2 | #define bbc_python_chunk_h 3 | 4 | #include "common.h" 5 | #include "memory.h" 6 | #include "debug.h" 7 | #include "value.h" 8 | 9 | typedef uint8_t OpCode; 10 | #define OP_CONSTANT 0 11 | #define OP_CONSTANT_LONG 1 12 | #define OP_NEGATE 2 13 | #define OP_ADD 3 14 | #define OP_SUBTRACT 4 15 | #define OP_MULTIPLY 5 16 | #define OP_DIVIDE 6 17 | #define OP_MODULUS 7 18 | #define OP_RETURN 8 19 | #define OP_NOT 9 20 | #define OP_EQUAL 10 21 | #define OP_GREATER 11 22 | #define OP_LESS 12 23 | 24 | struct sChunk { 25 | ArrayMeta meta; 26 | uint8_t* code; 27 | ValueArray constants; 28 | LineInfoArray lineInfo; 29 | }; 30 | typedef struct sChunk Chunk; 31 | 32 | void initChunk(Chunk* chunk); 33 | void freeChunk(Chunk* chunk); 34 | void writeChunk(Chunk* chunk, uint8_t byte, unsigned int lineNum); 35 | int addConstant(Chunk* chunk, struct Value *value); 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /bbcpython/chunk.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheEnbyperor/bbc-c/be7a2d43d7908218be2b788f7bc51d8e16624a63/bbcpython/chunk.o -------------------------------------------------------------------------------- /bbcpython/chunk.s: -------------------------------------------------------------------------------- 1 | .import initArray 2 | .import writeArray 3 | .import freeArray 4 | .import reallocate 5 | .import getLastLine 6 | .import writeLine 7 | .import writeValueArray 8 | .export initChunk 9 | .export freeChunk 10 | .export writeChunk 11 | .export addConstant 12 | // Function: initChunk 13 | initChunk: 14 | push %r12 15 | mov %r14, %r12 16 | // Set 17 | mov DWORD 8[%r12], %r0 18 | // CallFunction 19 | push %r0 20 | call [initArray] 21 | add #4, %r14 22 | // Add 23 | mov DWORD 8[%r12], %r0 24 | add #12, %r0 25 | // Set 26 | // CallFunction 27 | push %r0 28 | call [initArray] 29 | add #4, %r14 30 | // Add 31 | mov DWORD 8[%r12], %r0 32 | add #24, %r0 33 | // Set 34 | // CallFunction 35 | push %r0 36 | call [initArray] 37 | add #4, %r14 38 | // Return 39 | mov #0, %r0 40 | __bbcc_00000001: 41 | mov %r12, %r14 42 | pop %r12 43 | ret 44 | // Function: writeChunk 45 | writeChunk: 46 | push %r12 47 | mov %r14, %r12 48 | push %r1 49 | push %r2 50 | // Add 51 | mov DWORD 8[%r12], %r0 52 | add #24, %r0 53 | // Set 54 | // CallFunction 55 | push %r0 56 | call [getLastLine] 57 | add #4, %r14 58 | // EqualJmp 59 | cmp 16[%r12], %r0 60 | jze [__bbcc_00000000] 61 | // Add 62 | mov DWORD 8[%r12], %r0 63 | add #24, %r0 64 | // Set 65 | // Add 66 | mov DWORD 8[%r12], %r1 67 | // ReadAt 68 | mov DWORD [%r1], %r1 69 | // CallFunction 70 | mov DWORD 16[%r12], %r2 71 | push %r2 72 | push %r1 73 | push %r0 74 | call [writeLine] 75 | add #12, %r14 76 | // Label 77 | __bbcc_00000000: 78 | // Set 79 | mov DWORD 8[%r12], %r1 80 | // AddrOf 81 | lea DWORD 12[%r12], %r0 82 | // Set 83 | // CallFunction 84 | push %r0 85 | mov #1, %r2 86 | push %r2 87 | push %r1 88 | call [writeArray] 89 | add #12, %r14 90 | // Return 91 | mov #0, %r0 92 | __bbcc_00000002: 93 | pop %r2 94 | pop %r1 95 | mov %r12, %r14 96 | pop %r12 97 | ret 98 | // Function: addConstant 99 | addConstant: 100 | push %r12 101 | mov %r14, %r12 102 | push %r1 103 | // Add 104 | mov DWORD 8[%r12], %r0 105 | add #12, %r0 106 | // Set 107 | // Set 108 | mov DWORD 12[%r12], %r1 109 | // CallFunction 110 | push %r1 111 | push %r0 112 | call [writeValueArray] 113 | add #8, %r14 114 | // Add 115 | mov DWORD 8[%r12], %r0 116 | add #12, %r0 117 | // Add 118 | // ReadAt 119 | mov DWORD [%r0], %r0 120 | // Set 121 | mov #1, %r1 122 | // Sub 123 | sub %r1, %r0 124 | // Return 125 | __bbcc_00000003: 126 | pop %r1 127 | mov %r12, %r14 128 | pop %r12 129 | ret 130 | // Function: freeChunk 131 | freeChunk: 132 | push %r12 133 | mov %r14, %r12 134 | push %r1 135 | // ReadAt 136 | mov DWORD 8[%r12], %r0 137 | mov DWORD 8[%r0], %r0 138 | // Set 139 | // Set 140 | mov #0, %r1 141 | // CallFunction 142 | push %r1 143 | push %r0 144 | call [reallocate] 145 | add #8, %r14 146 | // Add 147 | mov DWORD 8[%r12], %r0 148 | add #12, %r0 149 | // Set 150 | // CallFunction 151 | push %r0 152 | call [freeArray] 153 | add #4, %r14 154 | // Add 155 | mov DWORD 8[%r12], %r0 156 | add #24, %r0 157 | // Set 158 | // CallFunction 159 | push %r0 160 | call [freeArray] 161 | add #4, %r14 162 | // Set 163 | mov DWORD 8[%r12], %r0 164 | // CallFunction 165 | push %r0 166 | call [initChunk] 167 | add #4, %r14 168 | // Return 169 | mov #0, %r0 170 | __bbcc_00000004: 171 | pop %r1 172 | mov %r12, %r14 173 | pop %r12 174 | ret -------------------------------------------------------------------------------- /bbcpython/common.h: -------------------------------------------------------------------------------- 1 | #ifndef bbc_python_common_h 2 | #define bbc_python_common_h 3 | 4 | #include "stdio.h" 5 | #include "stdbool.h" 6 | #include "stddef.h" 7 | #include "stdint.h" 8 | 9 | #define DEBUG_PRINT_TOKENS 10 | #define DEBUG_PRINT_CODE 11 | #define DEBUG_TRACE_EXECUTION 12 | 13 | #endif -------------------------------------------------------------------------------- /bbcpython/compiler.c: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include "compiler.h" 3 | #include "scanner.h" 4 | #include "stdlib.h" 5 | 6 | #ifdef DEBUG_PRINT_CODE 7 | 8 | #include "debug.h" 9 | 10 | #endif 11 | 12 | typedef uint8_t Precedence; 13 | #define PREC_NONE 0 14 | #define PREC_NEWLINE 1 15 | #define PREC_ASSIGNMENT 2 16 | #define PREC_LAMBDA 3 17 | #define PREC_CONDITIONAL 4 18 | #define PREC_OR 5 19 | #define PREC_AND 6 20 | #define PREC_NOT 7 21 | #define PREC_COMPARISON 8 22 | #define PREC_BITWISE_OR 9 23 | #define PREC_BITWISE_XOR 10 24 | #define PREC_BITWISE_AND 11 25 | #define PREC_SHIFT 12 26 | #define PREC_ADDITION 13 27 | #define PREC_MULTIPLICATION 14 28 | #define PREC_UNARY 15 29 | #define PREC_EXPONENT 16 30 | #define PREC_AWAIT 17 31 | #define PREC_CALL 18 32 | #define PREC_GROUP 19 33 | 34 | typedef struct { 35 | Token current; 36 | Token previous; 37 | bool hadError; 38 | bool panicMode; 39 | Chunk *currentChunk; 40 | Scanner *scanner; 41 | VM *vm; 42 | } Parser; 43 | 44 | typedef void (*ParseFunc)(Parser *parser); 45 | 46 | typedef struct { 47 | ParseFunc prefix; 48 | ParseFunc suffix; 49 | Precedence precedence; 50 | } ParseRule; 51 | 52 | static Chunk *currentChunk(Parser *parser) { 53 | return parser->currentChunk; 54 | } 55 | 56 | static void errorAt(Parser *parser, Token *token, const char *message) { 57 | if (parser->panicMode) return; 58 | parser->panicMode = true; 59 | 60 | printf("[line %d] Error", token->line); 61 | 62 | if (token->type == TOKEN_EOF) { 63 | printf(" at end"); 64 | } else if (token->type == TOKEN_ERROR) { 65 | // Nothing. 66 | } else { 67 | printf(" at '"); 68 | for (unsigned int i = 0; i < token->length; ++i) { 69 | putchar(token->start[i]); 70 | } 71 | printf("'"); 72 | } 73 | 74 | printf(": %s\n", message); 75 | parser->hadError = true; 76 | } 77 | 78 | static void errorAtCurrent(Parser *parser, const char *message) { 79 | errorAt(parser, &parser->current, message); 80 | } 81 | 82 | static void error(Parser *parser, const char *message) { 83 | errorAt(parser, &parser->previous, message); 84 | } 85 | 86 | static void advance(Parser *parser) { 87 | parser->previous = parser->current; 88 | 89 | for (;;) { 90 | Token token; 91 | 92 | scanToken(parser->scanner, &token); 93 | 94 | #ifdef DEBUG_PRINT_TOKENS 95 | printf("%04u %02u ", token.line, token.type); 96 | for (unsigned i = 0; i < token.length; ++i) { 97 | printf("%c", token.start[i]); 98 | } 99 | printf("\n"); 100 | #endif 101 | 102 | parser->current = token; 103 | if (parser->current.type != TOKEN_ERROR) break; 104 | 105 | errorAtCurrent(parser, parser->current.start); 106 | } 107 | } 108 | 109 | static void consume(Parser *parser, TokenType type, const char *message) { 110 | if (parser->current.type == type) { 111 | advance(parser); 112 | return; 113 | } 114 | 115 | errorAtCurrent(parser, message); 116 | } 117 | 118 | static void emitByte(Parser *parser, uint8_t byte) { 119 | writeChunk(currentChunk(parser), byte, parser->previous.line); 120 | } 121 | 122 | static void emitBytes(Parser *parser, uint8_t byte1, uint8_t byte2) { 123 | emitByte(parser, byte1); 124 | emitByte(parser, byte2); 125 | } 126 | 127 | static void emitReturn(Parser *parser) { 128 | emitByte(parser, OP_RETURN); 129 | } 130 | 131 | static uint8_t makeConstant(Parser *parser, Value *value) { 132 | unsigned int constant = addConstant(currentChunk(parser), value); 133 | if (constant > 256) { 134 | error(parser, "Too many constants in one chunk."); 135 | return 0; 136 | } 137 | 138 | return (uint8_t) constant; 139 | } 140 | 141 | static void emitConstant(Parser *parser, Value *value) { 142 | emitBytes(parser, OP_CONSTANT, makeConstant(parser, value)); 143 | } 144 | 145 | static void endCompiler(Parser *parser) { 146 | emitReturn(parser); 147 | 148 | #ifdef DEBUG_PRINT_CODE 149 | if (!parser->hadError) { 150 | printf("\n"); 151 | disassembleChunk(currentChunk(parser), "code"); 152 | } 153 | #endif 154 | } 155 | 156 | static void expression(); 157 | 158 | static ParseRule *getRule(TokenType type); 159 | 160 | static void parsePrecedence(Parser *parser, Precedence precedence); 161 | 162 | void number(Parser *parser) { 163 | Value value; 164 | int number; 165 | number = atoi(parser->previous.start); 166 | intVal(number, &value); 167 | emitConstant(parser, &value); 168 | } 169 | 170 | static void string(Parser *parser) { 171 | Value value; 172 | objVal(copyString(parser->previous.start + 1, parser->previous.length - 2, parser->vm), &value); 173 | emitConstant(parser, &value); 174 | } 175 | 176 | static void newline(Parser *parser) { 177 | } 178 | 179 | static void grouping(Parser *parser) { 180 | expression(parser); 181 | consume(parser, TOKEN_RIGHT_PAREN, "Expect ')' after expression."); 182 | } 183 | 184 | static void binary(Parser *parser) { 185 | TokenType operatorType = parser->previous.type; 186 | 187 | ParseRule *rule = getRule(operatorType); 188 | parsePrecedence(parser, (Precedence) (rule->precedence + 1)); 189 | 190 | if (operatorType == TOKEN_PLUS) { 191 | emitByte(parser, OP_ADD); 192 | } else if (operatorType == TOKEN_MINUS) { 193 | emitByte(parser, OP_SUBTRACT); 194 | } else if (operatorType == TOKEN_STAR) { 195 | emitByte(parser, OP_MULTIPLY); 196 | } else if (operatorType == TOKEN_SLASH) { 197 | emitByte(parser, OP_DIVIDE); 198 | } else if (operatorType == TOKEN_EQUAL_EQUAL) { 199 | emitByte(parser, OP_EQUAL); 200 | } else if (operatorType == TOKEN_NOT_EQUAL) { 201 | emitBytes(parser, OP_EQUAL, OP_NOT); 202 | } else if (operatorType == TOKEN_GREATER) { 203 | emitByte(parser, OP_GREATER); 204 | } else if (operatorType == TOKEN_GREATER_EQUAL) { 205 | emitBytes(parser, OP_LESS, OP_NOT); 206 | } else if (operatorType == TOKEN_LESS) { 207 | emitByte(parser, OP_LESS); 208 | } else if (operatorType == TOKEN_LESS_EQUAL) { 209 | emitBytes(parser, OP_GREATER, OP_NOT); 210 | } 211 | } 212 | 213 | static void unary(Parser *parser) { 214 | TokenType operatorType = parser->previous.type; 215 | 216 | parsePrecedence(parser, PREC_UNARY); 217 | 218 | if (operatorType == TOKEN_MINUS) { 219 | emitByte(parser, OP_NEGATE); 220 | } else if (operatorType == TOKEN_NOT) { 221 | emitByte(parser, OP_NOT); 222 | } 223 | } 224 | 225 | static void literal(Parser *parser) { 226 | TokenType operatorType = parser->previous.type; 227 | Value value; 228 | 229 | if (operatorType == TOKEN_NONE) { 230 | noneVal(&value); 231 | emitConstant(parser, &value); 232 | } else if (operatorType == TOKEN_TRUE) { 233 | boolVal(true, &value); 234 | emitConstant(parser, &value); 235 | } else if (operatorType == TOKEN_FALSE) { 236 | boolVal(false, &value); 237 | emitConstant(parser, &value); 238 | } 239 | } 240 | 241 | ParseRule rules[61]; 242 | 243 | static ParseRule *getRule(TokenType type) { 244 | return &rules[type]; 245 | } 246 | 247 | void initRule(ParseRule *rule, ParseFunc prefix, ParseFunc suffix, Precedence precedence) { 248 | rule->prefix = prefix; 249 | rule->suffix = suffix; 250 | rule->precedence = precedence; 251 | } 252 | 253 | static void expression(Parser *parser) { 254 | parsePrecedence(parser, PREC_NEWLINE); 255 | } 256 | 257 | void parsePrecedence(Parser *parser, Precedence precedence) { 258 | advance(parser); 259 | ParseFunc prefixRule = (getRule(parser->previous.type))->prefix; 260 | if (prefixRule == NULL) { 261 | error(parser, "Expect expression."); 262 | return; 263 | } 264 | 265 | prefixRule(parser); 266 | 267 | while (precedence <= (getRule(parser->current.type))->precedence) { 268 | advance(parser); 269 | ParseFunc suffixRule = (getRule(parser->previous.type))->suffix; 270 | suffixRule(parser); 271 | } 272 | } 273 | 274 | bool compile(const char *source, Chunk *chunk, VM *vm) { 275 | initRule(&rules[TOKEN_NUMBER], number, NULL, PREC_NONE); 276 | initRule(&rules[TOKEN_STRING], string, NULL, PREC_NONE); 277 | initRule(&rules[TOKEN_NONE], literal, NULL, PREC_NONE); 278 | initRule(&rules[TOKEN_TRUE], literal, NULL, PREC_NONE); 279 | initRule(&rules[TOKEN_FALSE], literal, NULL, PREC_NONE); 280 | 281 | initRule(&rules[TOKEN_PLUS], unary, binary, PREC_ADDITION); 282 | initRule(&rules[TOKEN_MINUS], unary, binary, PREC_ADDITION); 283 | initRule(&rules[TOKEN_STAR], NULL, binary, PREC_MULTIPLICATION); 284 | initRule(&rules[TOKEN_SLASH], NULL, binary, PREC_MULTIPLICATION); 285 | 286 | initRule(&rules[TOKEN_NOT], unary, NULL, PREC_NONE); 287 | initRule(&rules[TOKEN_EQUAL_EQUAL], NULL, binary, PREC_COMPARISON); 288 | initRule(&rules[TOKEN_NOT_EQUAL], NULL, binary, PREC_COMPARISON); 289 | initRule(&rules[TOKEN_GREATER], NULL, binary, PREC_COMPARISON); 290 | initRule(&rules[TOKEN_GREATER_EQUAL], NULL, binary, PREC_COMPARISON); 291 | initRule(&rules[TOKEN_LESS], NULL, binary, PREC_COMPARISON); 292 | initRule(&rules[TOKEN_LESS_EQUAL], NULL, binary, PREC_COMPARISON); 293 | 294 | initRule(&rules[TOKEN_LEFT_PAREN], grouping, NULL, PREC_CALL); 295 | initRule(&rules[TOKEN_NEWLINE], NULL, newline, PREC_NEWLINE); 296 | 297 | Scanner scanner; 298 | Parser parser; 299 | initScanner(&scanner, source); 300 | 301 | parser.currentChunk = chunk; 302 | parser.scanner = &scanner; 303 | parser.vm = vm; 304 | parser.hadError = false; 305 | parser.panicMode = false; 306 | 307 | #ifdef DEBUG_PRINT_TOKENS 308 | printf("START PARSE\n"); 309 | #endif 310 | 311 | advance(&parser); 312 | expression(&parser); 313 | consume(&parser, TOKEN_EOF, "Expected end of expression."); 314 | endCompiler(&parser); 315 | 316 | #ifdef DEBUG_PRINT_TOKENS 317 | printf("END PARSE\n"); 318 | #endif 319 | 320 | return !parser.hadError; 321 | } -------------------------------------------------------------------------------- /bbcpython/compiler.h: -------------------------------------------------------------------------------- 1 | #ifndef bbc_python_compiler_h 2 | #define bbc_python_compiler_h 3 | 4 | #include "chunk.h" 5 | #include "vm.h" 6 | 7 | bool compile(const char* source, Chunk *chunk, VM *vm); 8 | 9 | #endif -------------------------------------------------------------------------------- /bbcpython/compiler.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheEnbyperor/bbc-c/be7a2d43d7908218be2b788f7bc51d8e16624a63/bbcpython/compiler.o -------------------------------------------------------------------------------- /bbcpython/debug.c: -------------------------------------------------------------------------------- 1 | #include "debug.h" 2 | #include "common.h" 3 | 4 | void writeLineInfoArray(LineInfoArray* array, LineInfo* value) { 5 | writeArray(array, sizeof(LineInfo), value); 6 | } 7 | 8 | unsigned int getLastLine(LineInfoArray* array) { 9 | if (array->meta.count == 0) 10 | return 0; 11 | return array->lineInfo[array->meta.count - 1].lineNum; 12 | } 13 | 14 | unsigned int getLine(LineInfoArray* array, unsigned int offset) { 15 | unsigned int lastLine = 0; 16 | for (unsigned int i = 0; i < array->meta.count; i++) { 17 | LineInfo info; 18 | info = array->lineInfo[i]; 19 | if (info.startByte <= offset) { 20 | lastLine = info.lineNum; 21 | } else { 22 | break; 23 | } 24 | } 25 | return lastLine; 26 | } 27 | 28 | void writeLine(LineInfoArray* array, unsigned int offset, unsigned int lineNum) { 29 | LineInfo info; 30 | info.startByte = offset; 31 | info.lineNum = lineNum; 32 | 33 | writeLineInfoArray(array, &info); 34 | } 35 | 36 | static int simpleInstruction(const char* name, int offset); 37 | static int constantInstruction(const char* name, struct Chunk* chunk, int offset); 38 | static int longConstantInstruction(const char* name, struct Chunk* chunk, int offset); 39 | 40 | void disassembleChunk(Chunk* chunk, const char* name) { 41 | printf("== %s ==\n", name); 42 | 43 | for (unsigned int i = 0; i < chunk->meta.count;) { 44 | i = disassembleInstruction(chunk, i); 45 | } 46 | } 47 | 48 | unsigned int disassembleInstruction(Chunk* chunk, unsigned int offset) { 49 | printf("%04u ", offset); 50 | 51 | unsigned int line = getLine(&chunk->lineInfo, offset); 52 | if (offset > 0 && getLine(&chunk->lineInfo, offset - 1) == line) { 53 | printf(" | "); 54 | } else { 55 | printf("%04u ", line); 56 | } 57 | 58 | uint8_t instruction = chunk->code[offset]; 59 | if (instruction == OP_CONSTANT) { 60 | return constantInstruction("OP_CONSTANT", chunk, offset); 61 | } else if (instruction == OP_CONSTANT_LONG) { 62 | return longConstantInstruction("OP_CONSTANT_LONG", chunk, offset); 63 | } else if (instruction == OP_NEGATE) { 64 | return simpleInstruction("OP_NEGATE", offset); 65 | } else if (instruction == OP_ADD) { 66 | return simpleInstruction("OP_ADD", offset); 67 | } else if (instruction == OP_SUBTRACT) { 68 | return simpleInstruction("OP_SUBTRACT", offset); 69 | } else if (instruction == OP_MULTIPLY) { 70 | return simpleInstruction("OP_MULTIPLY", offset); 71 | } else if (instruction == OP_DIVIDE) { 72 | return simpleInstruction("OP_DIVIDE", offset); 73 | } else if (instruction == OP_MODULUS) { 74 | return simpleInstruction("OP_MODULUS", offset); 75 | } else if (instruction == OP_RETURN) { 76 | return simpleInstruction("OP_RETURN", offset); 77 | } else if (instruction == OP_NOT) { 78 | return simpleInstruction("OP_NOT", offset); 79 | } else if (instruction == OP_EQUAL) { 80 | return simpleInstruction("OP_EQUAL", offset); 81 | } else if (instruction == OP_GREATER) { 82 | return simpleInstruction("OP_GREATER", offset); 83 | } else if (instruction == OP_LESS) { 84 | return simpleInstruction("OP_LESS", offset); 85 | } else { 86 | printf("Unknown opcode %d\n", instruction); 87 | return offset + 1; 88 | } 89 | } 90 | 91 | static unsigned int simpleInstruction(const char* name, unsigned int offset) { 92 | printf("%s\n", name); 93 | return offset + 1; 94 | } 95 | 96 | static unsigned int constantInstruction(const char* name, Chunk* chunk, unsigned int offset) { 97 | uint8_t constant = chunk->code[offset + 1]; 98 | printf("%s %04u '", name, constant); 99 | printValue(&chunk->constants.values[constant]); 100 | printf("'\n"); 101 | return offset + 2; 102 | } 103 | 104 | static unsigned int longConstantInstruction(const char* name, Chunk* chunk, unsigned int offset) { 105 | uint16_t constant = *(uint16_t *)(&chunk->code[offset + 1]); 106 | printf("%s %04u '", name, constant); 107 | printValue(&chunk->constants.values[constant]); 108 | printf("'\n"); 109 | return offset + 3; 110 | } -------------------------------------------------------------------------------- /bbcpython/debug.h: -------------------------------------------------------------------------------- 1 | #ifndef bbc_python_debug_h 2 | #define bbc_python_debug_h 3 | 4 | #include "memory.h" 5 | 6 | typedef struct { 7 | unsigned int startByte; 8 | unsigned int lineNum; 9 | } LineInfo; 10 | 11 | typedef struct { 12 | ArrayMeta meta; 13 | LineInfo* lineInfo; 14 | } LineInfoArray; 15 | 16 | #include "chunk.h" 17 | 18 | void writeLineInfoArray(LineInfoArray* array, LineInfo* value); 19 | 20 | unsigned int getLastLine(LineInfoArray* array); 21 | unsigned int getLine(LineInfoArray* array, unsigned int offset); 22 | void writeLine(LineInfoArray* array, unsigned int offset, unsigned int lineNum); 23 | 24 | void disassembleChunk(struct sChunk* chunk, const char* name); 25 | unsigned int disassembleInstruction(struct sChunk* chunk, unsigned int i); 26 | 27 | #endif -------------------------------------------------------------------------------- /bbcpython/debug.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheEnbyperor/bbc-c/be7a2d43d7908218be2b788f7bc51d8e16624a63/bbcpython/debug.o -------------------------------------------------------------------------------- /bbcpython/main.c: -------------------------------------------------------------------------------- 1 | #include "chunk.h" 2 | #include "debug.h" 3 | #include "vm.h" 4 | 5 | static void repl(struct VM *vm) { 6 | char line[255]; 7 | for (;;) { 8 | printf(">>> "); 9 | 10 | if (!gets(line, sizeof(line))) { 11 | printf("\nBye!\n"); 12 | break; 13 | } 14 | 15 | interpret(vm, line); 16 | } 17 | } 18 | 19 | int main() { 20 | VM vm; 21 | initVM(&vm); 22 | 23 | repl(&vm); 24 | 25 | freeVM(&vm); 26 | return 0; 27 | } -------------------------------------------------------------------------------- /bbcpython/main.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheEnbyperor/bbc-c/be7a2d43d7908218be2b788f7bc51d8e16624a63/bbcpython/main.o -------------------------------------------------------------------------------- /bbcpython/main.s: -------------------------------------------------------------------------------- 1 | .import printf 2 | .import gets 3 | .import initVM 4 | .import interpret 5 | .import freeVM 6 | .export main 7 | __bbcc_00000004: 8 | .byte #62,#62,#62,#32,#0 9 | __bbcc_00000005: 10 | .byte #10,#66,#121,#101,#33,#10,#0 11 | // Function: repl 12 | repl: 13 | push %r12 14 | mov %r14, %r12 15 | sub #255, %r14 16 | push %r1 17 | // Label 18 | __bbcc_00000000: 19 | // AddrOf 20 | lea DWORD [__bbcc_00000004], %r0 21 | // Set 22 | // CallFunction 23 | push %r0 24 | call [printf] 25 | add #4, %r14 26 | // AddrOf 27 | lea DWORD -255[%r12], %r0 28 | // Set 29 | // Set 30 | mov #255, %r1 31 | // CallFunction 32 | push %r1 33 | push %r0 34 | call [gets] 35 | add #8, %r14 36 | // JmpNotZero 37 | cmp #0, %r0 38 | jnz [__bbcc_00000003] 39 | // AddrOf 40 | lea DWORD [__bbcc_00000005], %r0 41 | // Set 42 | // CallFunction 43 | push %r0 44 | call [printf] 45 | add #4, %r14 46 | // Jmp 47 | jmp [__bbcc_00000002] 48 | // Label 49 | __bbcc_00000003: 50 | // Set 51 | mov DWORD 8[%r12], %r1 52 | // AddrOf 53 | lea DWORD -255[%r12], %r0 54 | // Set 55 | // CallFunction 56 | push %r0 57 | push %r1 58 | call [interpret] 59 | add #8, %r14 60 | // Label 61 | __bbcc_00000001: 62 | // Jmp 63 | jmp [__bbcc_00000000] 64 | // Label 65 | __bbcc_00000002: 66 | // Return 67 | mov #0, %r0 68 | __bbcc_00000006: 69 | pop %r1 70 | mov %r12, %r14 71 | pop %r12 72 | ret 73 | // Function: main 74 | main: 75 | push %r12 76 | mov %r14, %r12 77 | sub #24, %r14 78 | // AddrOf 79 | lea DWORD -24[%r12], %r0 80 | // Set 81 | // CallFunction 82 | push %r0 83 | call [initVM] 84 | add #4, %r14 85 | // AddrOf 86 | lea DWORD -24[%r12], %r0 87 | // Set 88 | // CallFunction 89 | push %r0 90 | call [repl] 91 | add #4, %r14 92 | // AddrOf 93 | lea DWORD -24[%r12], %r0 94 | // Set 95 | // CallFunction 96 | push %r0 97 | call [freeVM] 98 | add #4, %r14 99 | // Return 100 | mov #0, %r0 101 | __bbcc_00000007: 102 | mov %r12, %r14 103 | pop %r12 104 | ret -------------------------------------------------------------------------------- /bbcpython/memory.c: -------------------------------------------------------------------------------- 1 | #include "memory.h" 2 | #include "object.h" 3 | #include "stdlib.h" 4 | 5 | typedef struct sObj Obj; 6 | 7 | void initArray(DynamicArray* array) { 8 | array->data = NULL; 9 | array->meta.capacity = 0; 10 | array->meta.count = 0; 11 | } 12 | 13 | void writeArray(DynamicArray* array, size_t elmSize, void *value) { 14 | if (array->meta.capacity < array->meta.count + 1) { 15 | int oldCapacity = array->meta.capacity; 16 | array->meta.capacity = growCapacity(oldCapacity); 17 | array->data = reallocate(array->data, array->meta.capacity * elmSize); 18 | } 19 | 20 | size_t offset = array->meta.count * elmSize; 21 | for (unsigned int i = 0; i < elmSize; ++i) 22 | array->data[offset+i] = *(((uint8_t *)value)+i); 23 | array->meta.count++; 24 | } 25 | 26 | 27 | void popArray(DynamicArray* array, size_t elmSize, void *value) { 28 | array->meta.count--; 29 | size_t offset = array->meta.count * elmSize; 30 | for (unsigned int i = 0; i < elmSize; ++i) { 31 | *(((uint8_t *)value)+i) = array->data[offset+i]; 32 | } 33 | 34 | int oldCapacity = array->meta.capacity; 35 | array->meta.capacity = shrinkCapacity(oldCapacity, array->meta.count); 36 | if (array->meta.capacity != oldCapacity) { 37 | array->data = reallocate(array->data, array->meta.capacity * elmSize); 38 | } 39 | } 40 | 41 | void freeArray(DynamicArray* array) { 42 | reallocate(array->data, 0); 43 | initArray(array); 44 | } 45 | 46 | int growCapacity(int oldCapacity) { 47 | return ((oldCapacity) < 8 ? 8 : (oldCapacity) + 8); 48 | } 49 | 50 | int shrinkCapacity(int oldCapacity, int count) { 51 | return ((oldCapacity - 8) < count ? oldCapacity : oldCapacity - 8); 52 | } 53 | 54 | void* reallocate(void* previous, size_t newSize) { 55 | if (newSize == 0) { 56 | free(previous); 57 | return NULL; 58 | } 59 | 60 | return realloc(previous, newSize); 61 | } -------------------------------------------------------------------------------- /bbcpython/memory.h: -------------------------------------------------------------------------------- 1 | #ifndef bbc_python_memory_h 2 | #define bbc_python_memory_h 3 | 4 | #include "common.h" 5 | 6 | typedef struct { 7 | unsigned int count; 8 | unsigned int capacity; 9 | } ArrayMeta; 10 | 11 | typedef struct { 12 | ArrayMeta meta; 13 | uint8_t* data; 14 | } DynamicArray; 15 | 16 | void initArray(DynamicArray *array); 17 | void writeArray(DynamicArray *array, size_t elmSize, void *value); 18 | void popArray(DynamicArray *array, size_t elmSize, void *value); 19 | void freeArray(DynamicArray *array); 20 | 21 | int growCapacity(int oldCapacity); 22 | int shrinkCapacity(int oldCapacity, int count); 23 | void* reallocate(void* previous, size_t newSize); 24 | 25 | #endif -------------------------------------------------------------------------------- /bbcpython/memory.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheEnbyperor/bbc-c/be7a2d43d7908218be2b788f7bc51d8e16624a63/bbcpython/memory.o -------------------------------------------------------------------------------- /bbcpython/memory.s: -------------------------------------------------------------------------------- 1 | .import free 2 | .import realloc 3 | .export initArray 4 | .export writeArray 5 | .export popArray 6 | .export freeArray 7 | .export growCapacity 8 | .export shrinkCapacity 9 | .export reallocate 10 | // Function: initArray 11 | initArray: 12 | push %r12 13 | mov %r14, %r12 14 | push %r1 15 | // Set 16 | mov #0, %r0 17 | // SetAt 18 | mov 8[%r12], %r1 19 | mov %r0, DWORD 8[%r1] 20 | // Add 21 | mov DWORD 8[%r12], %r1 22 | // Set 23 | mov #0, %r0 24 | // SetAt 25 | mov %r0, DWORD 4[%r1] 26 | // Add 27 | mov DWORD 8[%r12], %r1 28 | // Set 29 | mov #0, %r0 30 | // SetAt 31 | mov %r0, DWORD [%r1] 32 | // Return 33 | mov #0, %r0 34 | __bbcc_0000000d: 35 | pop %r1 36 | mov %r12, %r14 37 | pop %r12 38 | ret 39 | // Function: writeArray 40 | writeArray: 41 | push %r12 42 | mov %r14, %r12 43 | push %r1 44 | push %r2 45 | push %r3 46 | push %r4 47 | push %r5 48 | // Add 49 | mov DWORD 8[%r12], %r2 50 | // Add 51 | mov DWORD 8[%r12], %r0 52 | // ReadAt 53 | mov DWORD [%r0], %r0 54 | // Set 55 | mov #1, %r1 56 | // Add 57 | add %r1, %r0 58 | // ReadAt 59 | mov DWORD 4[%r2], %r1 60 | // MoreEqualJmp 61 | cmp %r1, %r0 62 | jae [__bbcc_00000000] 63 | // Add 64 | mov DWORD 8[%r12], %r0 65 | // ReadAt 66 | mov DWORD 4[%r0], %r0 67 | // Set 68 | // Set 69 | // CallFunction 70 | push %r0 71 | call [growCapacity] 72 | add #4, %r14 73 | // Add 74 | mov DWORD 8[%r12], %r1 75 | // Set 76 | // SetAt 77 | mov %r0, DWORD 4[%r1] 78 | // ReadAt 79 | mov DWORD 8[%r12], %r0 80 | mov DWORD 8[%r0], %r1 81 | // Set 82 | // Add 83 | mov DWORD 8[%r12], %r0 84 | // ReadAt 85 | mov DWORD 4[%r0], %r0 86 | // Mult 87 | mul DWORD 12[%r12], %r0 88 | // CallFunction 89 | push %r0 90 | push %r1 91 | call [reallocate] 92 | add #8, %r14 93 | // Set 94 | // SetAt 95 | mov 8[%r12], %r1 96 | mov %r0, DWORD 8[%r1] 97 | // Label 98 | __bbcc_00000000: 99 | // Add 100 | mov DWORD 8[%r12], %r0 101 | // ReadAt 102 | mov DWORD [%r0], %r1 103 | // Mult 104 | mul DWORD 12[%r12], %r1 105 | // Set 106 | // Set 107 | mov #0, %r2 108 | // Set 109 | // Label 110 | __bbcc_00000001: 111 | // MoreEqualJmp 112 | mov 12[%r12], %r0 113 | cmp %r2, %r0 114 | jae [__bbcc_00000003] 115 | // Set 116 | mov DWORD 16[%r12], %r3 117 | // Set 118 | mov %r2, %r0 119 | // Add 120 | add %r0, %r3 121 | // ReadAt 122 | mov BYTE [%r3], %r4 123 | // ReadAt 124 | mov DWORD 8[%r12], %r0 125 | mov DWORD 8[%r0], %r3 126 | // Add 127 | mov %r1, %r0 128 | add %r2, %r0 129 | // SetAt 130 | mov %r3, %r5 131 | add %r0, %r5 132 | mov %r4, BYTE [%r5] 133 | // Label 134 | __bbcc_00000002: 135 | // Inc 136 | inc %r2 137 | // Jmp 138 | jmp [__bbcc_00000001] 139 | // Label 140 | __bbcc_00000003: 141 | // Add 142 | mov DWORD 8[%r12], %r2 143 | // ReadAt 144 | mov DWORD [%r2], %r1 145 | // Set 146 | mov %r1, %r0 147 | // Add 148 | add #1, %r1 149 | // SetAt 150 | mov %r1, DWORD [%r2] 151 | // Return 152 | mov #0, %r0 153 | __bbcc_0000000e: 154 | pop %r5 155 | pop %r4 156 | pop %r3 157 | pop %r2 158 | pop %r1 159 | mov %r12, %r14 160 | pop %r12 161 | ret 162 | // Function: popArray 163 | popArray: 164 | push %r12 165 | mov %r14, %r12 166 | push %r1 167 | push %r2 168 | push %r3 169 | push %r4 170 | // Add 171 | mov DWORD 8[%r12], %r2 172 | // ReadAt 173 | mov DWORD [%r2], %r1 174 | // Set 175 | mov %r1, %r0 176 | // Sub 177 | sub #1, %r1 178 | // SetAt 179 | mov %r1, DWORD [%r2] 180 | // Add 181 | mov DWORD 8[%r12], %r0 182 | // ReadAt 183 | mov DWORD [%r0], %r2 184 | // Mult 185 | mul DWORD 12[%r12], %r2 186 | // Set 187 | // Set 188 | mov #0, %r1 189 | // Set 190 | // Label 191 | __bbcc_00000004: 192 | // MoreEqualJmp 193 | mov 12[%r12], %r0 194 | cmp %r1, %r0 195 | jae [__bbcc_00000006] 196 | // ReadAt 197 | mov DWORD 8[%r12], %r0 198 | mov DWORD 8[%r0], %r3 199 | // Add 200 | mov %r2, %r0 201 | add %r1, %r0 202 | // ReadAt 203 | add %r0, %r3 204 | mov BYTE [%r3], %r4 205 | // Set 206 | mov DWORD 16[%r12], %r3 207 | // Set 208 | mov %r1, %r0 209 | // Add 210 | add %r0, %r3 211 | // SetAt 212 | mov %r4, BYTE [%r3] 213 | // Label 214 | __bbcc_00000005: 215 | // Inc 216 | inc %r1 217 | // Jmp 218 | jmp [__bbcc_00000004] 219 | // Label 220 | __bbcc_00000006: 221 | // Add 222 | mov DWORD 8[%r12], %r0 223 | // ReadAt 224 | mov DWORD 4[%r0], %r1 225 | // Set 226 | // Set 227 | // Add 228 | mov DWORD 8[%r12], %r0 229 | // ReadAt 230 | mov DWORD [%r0], %r0 231 | // Set 232 | // CallFunction 233 | push %r0 234 | push %r1 235 | call [shrinkCapacity] 236 | add #8, %r14 237 | // Add 238 | mov DWORD 8[%r12], %r2 239 | // Set 240 | // SetAt 241 | mov %r0, DWORD 4[%r2] 242 | // Add 243 | mov DWORD 8[%r12], %r0 244 | // ReadAt 245 | mov DWORD 4[%r0], %r0 246 | // EqualJmp 247 | cmp %r0, %r1 248 | jze [__bbcc_00000007] 249 | // ReadAt 250 | mov DWORD 8[%r12], %r0 251 | mov DWORD 8[%r0], %r1 252 | // Set 253 | // Add 254 | mov DWORD 8[%r12], %r0 255 | // ReadAt 256 | mov DWORD 4[%r0], %r0 257 | // Mult 258 | mul DWORD 12[%r12], %r0 259 | // CallFunction 260 | push %r0 261 | push %r1 262 | call [reallocate] 263 | add #8, %r14 264 | // Set 265 | // SetAt 266 | mov 8[%r12], %r1 267 | mov %r0, DWORD 8[%r1] 268 | // Label 269 | __bbcc_00000007: 270 | // Return 271 | mov #0, %r0 272 | __bbcc_0000000f: 273 | pop %r4 274 | pop %r3 275 | pop %r2 276 | pop %r1 277 | mov %r12, %r14 278 | pop %r12 279 | ret 280 | // Function: freeArray 281 | freeArray: 282 | push %r12 283 | mov %r14, %r12 284 | push %r1 285 | // ReadAt 286 | mov DWORD 8[%r12], %r0 287 | mov DWORD 8[%r0], %r0 288 | // Set 289 | // Set 290 | mov #0, %r1 291 | // CallFunction 292 | push %r1 293 | push %r0 294 | call [reallocate] 295 | add #8, %r14 296 | // Set 297 | mov DWORD 8[%r12], %r0 298 | // CallFunction 299 | push %r0 300 | call [initArray] 301 | add #4, %r14 302 | // Return 303 | mov #0, %r0 304 | __bbcc_00000010: 305 | pop %r1 306 | mov %r12, %r14 307 | pop %r12 308 | ret 309 | // Function: growCapacity 310 | growCapacity: 311 | push %r12 312 | mov %r14, %r12 313 | // MoreEqualJmp 314 | mov #8, %r0 315 | cmp 8[%r12], %r0 316 | jge [__bbcc_00000008] 317 | // Set 318 | mov #8, %r0 319 | // Jmp 320 | jmp [__bbcc_00000009] 321 | // Label 322 | __bbcc_00000008: 323 | // Add 324 | mov DWORD 8[%r12], %r0 325 | add #8, %r0 326 | // Set 327 | // Label 328 | __bbcc_00000009: 329 | // Return 330 | __bbcc_00000011: 331 | mov %r12, %r14 332 | pop %r12 333 | ret 334 | // Function: shrinkCapacity 335 | shrinkCapacity: 336 | push %r12 337 | mov %r14, %r12 338 | push %r1 339 | // Sub 340 | mov DWORD 8[%r12], %r0 341 | sub #8, %r0 342 | // MoreEqualJmp 343 | mov 12[%r12], %r1 344 | cmp %r0, %r1 345 | jge [__bbcc_0000000a] 346 | // Set 347 | mov DWORD 8[%r12], %r0 348 | // Jmp 349 | jmp [__bbcc_0000000b] 350 | // Label 351 | __bbcc_0000000a: 352 | // Sub 353 | mov DWORD 8[%r12], %r0 354 | sub #8, %r0 355 | // Set 356 | // Label 357 | __bbcc_0000000b: 358 | // Return 359 | __bbcc_00000012: 360 | pop %r1 361 | mov %r12, %r14 362 | pop %r12 363 | ret 364 | // Function: reallocate 365 | reallocate: 366 | push %r12 367 | mov %r14, %r12 368 | push %r1 369 | // NotEqualJmp 370 | mov #0, %r0 371 | cmp 12[%r12], %r0 372 | jnz [__bbcc_0000000c] 373 | // Set 374 | mov DWORD 8[%r12], %r0 375 | // CallFunction 376 | push %r0 377 | call [free] 378 | add #4, %r14 379 | // Return 380 | mov #0, %r0 381 | jmp [__bbcc_00000013] 382 | // Label 383 | __bbcc_0000000c: 384 | // Set 385 | mov DWORD 8[%r12], %r0 386 | // CallFunction 387 | mov DWORD 12[%r12], %r1 388 | push %r1 389 | push %r0 390 | call [realloc] 391 | add #8, %r14 392 | // Return 393 | __bbcc_00000013: 394 | pop %r1 395 | mov %r12, %r14 396 | pop %r12 397 | ret -------------------------------------------------------------------------------- /bbcpython/object.c: -------------------------------------------------------------------------------- 1 | #include "object.h" 2 | #include "memory.h" 3 | #include "vm.h" 4 | #include "value.h" 5 | #include "string.h" 6 | 7 | static Obj *allocateObject(size_t size, ObjType type, VM *vm) { 8 | Obj *object = (Obj *) reallocate(NULL, size); 9 | object->type = type; 10 | object->next = vm->objects; 11 | vm->objects = object; 12 | return object; 13 | } 14 | 15 | static bool isObjType(Value *value, ObjType type) { 16 | return isObj(value) && (asObj(value))->type == type; 17 | } 18 | 19 | ObjType objType(Value *value) { 20 | return (asObj(value))->type; 21 | } 22 | 23 | bool isString(Value *value) { 24 | return isObjType(value, OBJ_STRING); 25 | } 26 | 27 | struct ObjString *asString(Value *value) { 28 | return (ObjString *) asObj(value); 29 | } 30 | 31 | char *asCString(Value *value) { 32 | return ((ObjString *) asObj(value))->chars; 33 | } 34 | 35 | static ObjString *allocateString(char *chars, int length, struct VM *vm) { 36 | ObjString *string; 37 | string = (ObjString *) allocateObject(sizeof(ObjString), OBJ_STRING, vm); 38 | string->length = length; 39 | string->chars = chars; 40 | return string; 41 | } 42 | 43 | ObjString *takeString(char *chars, int length, struct VM *vm) { 44 | return allocateString(chars, length, vm); 45 | } 46 | 47 | ObjString* copyString(const char* chars, int length, struct VM *vm) { 48 | char* heapChars = reallocate(NULL, length + 1); 49 | memcpy(heapChars, chars, length); 50 | heapChars[length] = '\0'; 51 | 52 | return allocateString(heapChars, length, vm); 53 | } 54 | 55 | static void freeObject(Obj* object) { 56 | ObjType type = object->type; 57 | if (type == OBJ_STRING) { 58 | ObjString *string = (ObjString *) object; 59 | reallocate(string->chars, 0); 60 | reallocate(object, 0); 61 | } 62 | } 63 | 64 | void freeObjects(VM *vm) { 65 | Obj *object = vm->objects; 66 | while (object != NULL) { 67 | Obj *next = object->next; 68 | freeObject(object); 69 | object = next; 70 | } 71 | } -------------------------------------------------------------------------------- /bbcpython/object.h: -------------------------------------------------------------------------------- 1 | #ifndef bbc_python_object_h 2 | #define bbc_python_object_h 3 | 4 | #include "common.h" 5 | #include "value.h" 6 | #include "vm.h" 7 | 8 | typedef uint8_t ObjType; 9 | #define OBJ_STRING 0 10 | 11 | struct sObj { 12 | ObjType type; 13 | Obj* next; 14 | }; 15 | 16 | typedef struct { 17 | struct sObj object; 18 | int length; 19 | char *chars; 20 | } ObjString; 21 | 22 | ObjType objType(Value *value); 23 | 24 | bool isString(Value *value); 25 | 26 | ObjString *asString(Value *value); 27 | 28 | char *asCString(Value *value); 29 | 30 | ObjString *takeString(char *chars, int length, VM *vm); 31 | 32 | ObjString *copyString(const char *chars, int length, VM *vm); 33 | 34 | void freeObjects(VM *vm); 35 | 36 | #endif -------------------------------------------------------------------------------- /bbcpython/object.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheEnbyperor/bbc-c/be7a2d43d7908218be2b788f7bc51d8e16624a63/bbcpython/object.o -------------------------------------------------------------------------------- /bbcpython/object.s: -------------------------------------------------------------------------------- 1 | .import reallocate 2 | .import isObj 3 | .import asObj 4 | .import memcpy 5 | .export objType 6 | .export isString 7 | .export asString 8 | .export asCString 9 | .export takeString 10 | .export copyString 11 | .export freeObjects 12 | // Function: allocateObject 13 | allocateObject: 14 | push %r12 15 | mov %r14, %r12 16 | push %r1 17 | push %r2 18 | // Set 19 | mov #0, %r0 20 | // CallFunction 21 | mov DWORD 8[%r12], %r1 22 | push %r1 23 | push %r0 24 | call [reallocate] 25 | add #8, %r14 26 | // Set 27 | // Set 28 | // Set 29 | // SetAt 30 | mov 12[%r12], %r1 31 | mov %r1, BYTE [%r0] 32 | // ReadAt 33 | mov DWORD 16[%r12], %r1 34 | mov DWORD 20[%r1], %r1 35 | // Set 36 | // SetAt 37 | mov %r1, DWORD 1[%r0] 38 | // Set 39 | mov %r0, %r1 40 | // SetAt 41 | mov 16[%r12], %r2 42 | mov %r1, DWORD 20[%r2] 43 | // Return 44 | __bbcc_00000004: 45 | pop %r2 46 | pop %r1 47 | mov %r12, %r14 48 | pop %r12 49 | ret 50 | // Function: isObjType 51 | isObjType: 52 | push %r12 53 | mov %r14, %r12 54 | push %r1 55 | // Set 56 | mov #0, %r1 57 | // Set 58 | mov DWORD 8[%r12], %r0 59 | // CallFunction 60 | push %r0 61 | call [isObj] 62 | add #4, %r14 63 | // JmpZero 64 | cmp #0, %r0 65 | jze [__bbcc_00000000] 66 | // Set 67 | mov DWORD 8[%r12], %r0 68 | // CallFunction 69 | push %r0 70 | call [asObj] 71 | add #4, %r14 72 | // ReadAt 73 | mov BYTE [%r0], %r0 74 | // NotEqualJmp 75 | cmp 12[%r12], %r0 76 | jnz [__bbcc_00000000] 77 | // Set 78 | mov #1, %r1 79 | // Label 80 | __bbcc_00000000: 81 | // Return 82 | mov %r1, %r0 83 | __bbcc_00000005: 84 | pop %r1 85 | mov %r12, %r14 86 | pop %r12 87 | ret 88 | // Function: objType 89 | objType: 90 | push %r12 91 | mov %r14, %r12 92 | // Set 93 | mov DWORD 8[%r12], %r0 94 | // CallFunction 95 | push %r0 96 | call [asObj] 97 | add #4, %r14 98 | // ReadAt 99 | mov BYTE [%r0], %r0 100 | // Return 101 | __bbcc_00000006: 102 | mov %r12, %r14 103 | pop %r12 104 | ret 105 | // Function: isString 106 | isString: 107 | push %r12 108 | mov %r14, %r12 109 | push %r1 110 | // Set 111 | mov DWORD 8[%r12], %r1 112 | // Set 113 | mov #0, %r0 114 | // CallFunction 115 | push %r0 116 | push %r1 117 | call [isObjType] 118 | add #8, %r14 119 | // Return 120 | __bbcc_00000007: 121 | pop %r1 122 | mov %r12, %r14 123 | pop %r12 124 | ret 125 | // Function: asString 126 | asString: 127 | push %r12 128 | mov %r14, %r12 129 | // Set 130 | mov DWORD 8[%r12], %r0 131 | // CallFunction 132 | push %r0 133 | call [asObj] 134 | add #4, %r14 135 | // Set 136 | // Return 137 | __bbcc_00000008: 138 | mov %r12, %r14 139 | pop %r12 140 | ret 141 | // Function: asCString 142 | asCString: 143 | push %r12 144 | mov %r14, %r12 145 | // Set 146 | mov DWORD 8[%r12], %r0 147 | // CallFunction 148 | push %r0 149 | call [asObj] 150 | add #4, %r14 151 | // Set 152 | // ReadAt 153 | mov DWORD 9[%r0], %r0 154 | // Return 155 | __bbcc_00000009: 156 | mov %r12, %r14 157 | pop %r12 158 | ret 159 | // Function: allocateString 160 | allocateString: 161 | push %r12 162 | mov %r14, %r12 163 | push %r1 164 | push %r2 165 | // Set 166 | mov #0, %r1 167 | // Set 168 | mov DWORD 16[%r12], %r0 169 | // CallFunction 170 | push %r0 171 | push %r1 172 | mov #13, %r2 173 | push %r2 174 | call [allocateObject] 175 | add #12, %r14 176 | // Set 177 | // Set 178 | // Set 179 | // SetAt 180 | mov 12[%r12], %r1 181 | mov %r1, DWORD 5[%r0] 182 | // Set 183 | mov DWORD 8[%r12], %r1 184 | // SetAt 185 | mov %r1, DWORD 9[%r0] 186 | // Return 187 | __bbcc_0000000a: 188 | pop %r2 189 | pop %r1 190 | mov %r12, %r14 191 | pop %r12 192 | ret 193 | // Function: takeString 194 | takeString: 195 | push %r12 196 | mov %r14, %r12 197 | push %r1 198 | push %r2 199 | // Set 200 | mov DWORD 8[%r12], %r1 201 | // Set 202 | mov DWORD 16[%r12], %r0 203 | // CallFunction 204 | push %r0 205 | mov DWORD 12[%r12], %r2 206 | push %r2 207 | push %r1 208 | call [allocateString] 209 | add #12, %r14 210 | // Return 211 | __bbcc_0000000b: 212 | pop %r2 213 | pop %r1 214 | mov %r12, %r14 215 | pop %r12 216 | ret 217 | // Function: copyString 218 | copyString: 219 | push %r12 220 | mov %r14, %r12 221 | push %r1 222 | push %r2 223 | push %r3 224 | // Set 225 | mov #0, %r1 226 | // Add 227 | mov DWORD 12[%r12], %r0 228 | add #1, %r0 229 | // Set 230 | // CallFunction 231 | push %r0 232 | push %r1 233 | call [reallocate] 234 | add #8, %r14 235 | // Set 236 | mov %r0, %r1 237 | // Set 238 | // Set 239 | mov %r1, %r0 240 | // Set 241 | mov DWORD 8[%r12], %r3 242 | // Set 243 | mov DWORD 12[%r12], %r2 244 | // CallFunction 245 | push %r2 246 | push %r3 247 | push %r0 248 | call [memcpy] 249 | add #12, %r14 250 | // Set 251 | mov #0, %r0 252 | // SetAt 253 | mov %r1, %r2 254 | add DWORD 12[%r12], %r2 255 | mov %r0, BYTE [%r2] 256 | // Set 257 | // Set 258 | mov DWORD 16[%r12], %r0 259 | // CallFunction 260 | push %r0 261 | mov DWORD 12[%r12], %r2 262 | push %r2 263 | push %r1 264 | call [allocateString] 265 | add #12, %r14 266 | // Return 267 | __bbcc_0000000c: 268 | pop %r3 269 | pop %r2 270 | pop %r1 271 | mov %r12, %r14 272 | pop %r12 273 | ret 274 | // Function: freeObject 275 | freeObject: 276 | push %r12 277 | mov %r14, %r12 278 | push %r1 279 | // ReadAt 280 | mov DWORD 8[%r12], %r0 281 | mov BYTE [%r0], %r0 282 | // Set 283 | // NotEqualJmp 284 | cmp #0, %r0 285 | jnz [__bbcc_00000001] 286 | // Set 287 | mov DWORD 8[%r12], %r0 288 | // Set 289 | // Set 290 | // ReadAt 291 | mov DWORD 9[%r0], %r0 292 | // Set 293 | // Set 294 | mov #0, %r1 295 | // CallFunction 296 | push %r1 297 | push %r0 298 | call [reallocate] 299 | add #8, %r14 300 | // Set 301 | mov DWORD 8[%r12], %r1 302 | // Set 303 | mov #0, %r0 304 | // CallFunction 305 | push %r0 306 | push %r1 307 | call [reallocate] 308 | add #8, %r14 309 | // Label 310 | __bbcc_00000001: 311 | // Return 312 | mov #0, %r0 313 | __bbcc_0000000d: 314 | pop %r1 315 | mov %r12, %r14 316 | pop %r12 317 | ret 318 | // Function: freeObjects 319 | freeObjects: 320 | push %r12 321 | mov %r14, %r12 322 | push %r1 323 | // ReadAt 324 | mov DWORD 8[%r12], %r0 325 | mov DWORD 20[%r0], %r1 326 | // Set 327 | // Set 328 | // Label 329 | __bbcc_00000002: 330 | // EqualJmp 331 | cmp #0, %r1 332 | jze [__bbcc_00000003] 333 | // ReadAt 334 | mov DWORD 1[%r1], %r0 335 | // Set 336 | // Set 337 | // Set 338 | // CallFunction 339 | push %r1 340 | call [freeObject] 341 | add #4, %r14 342 | // Set 343 | mov %r0, %r1 344 | // Set 345 | // Jmp 346 | jmp [__bbcc_00000002] 347 | // Label 348 | __bbcc_00000003: 349 | // Return 350 | mov #0, %r0 351 | __bbcc_0000000e: 352 | pop %r1 353 | mov %r12, %r14 354 | pop %r12 355 | ret -------------------------------------------------------------------------------- /bbcpython/out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheEnbyperor/bbc-c/be7a2d43d7908218be2b788f7bc51d8e16624a63/bbcpython/out -------------------------------------------------------------------------------- /bbcpython/out.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheEnbyperor/bbc-c/be7a2d43d7908218be2b788f7bc51d8e16624a63/bbcpython/out.o -------------------------------------------------------------------------------- /bbcpython/out.ssd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheEnbyperor/bbc-c/be7a2d43d7908218be2b788f7bc51d8e16624a63/bbcpython/out.ssd -------------------------------------------------------------------------------- /bbcpython/python.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheEnbyperor/bbc-c/be7a2d43d7908218be2b788f7bc51d8e16624a63/bbcpython/python.o -------------------------------------------------------------------------------- /bbcpython/scanner.h: -------------------------------------------------------------------------------- 1 | #ifndef bbc_python_scanner_h 2 | #define bbc_python_scanner_h 3 | 4 | typedef struct { 5 | const char* start; 6 | const char* current; 7 | int line; 8 | bool isStartOfLine; 9 | int indent[20]; 10 | int *curIndent; 11 | } Scanner; 12 | 13 | typedef unsigned int TokenType; 14 | #define TOKEN_EOF 56 15 | #define TOKEN_ERROR 57 16 | #define TOKEN_NEWLINE 58 17 | #define TOKEN_INDENT 59 18 | #define TOKEN_DEDENT 60 19 | 20 | #define TOKEN_NUMBER 0 21 | #define TOKEN_STRING 1 22 | #define TOKEN_IDENTIFIER 2 23 | 24 | #define TOKEN_PLUS 3 25 | #define TOKEN_MINUS 4 26 | #define TOKEN_SLASH 5 27 | #define TOKEN_STAR 6 28 | 29 | #define TOKEN_COMMA 7 30 | #define TOKEN_DOT 8 31 | #define TOKEN_COLON 9 32 | 33 | #define TOKEN_LEFT_PAREN 10 34 | #define TOKEN_RIGHT_PAREN 11 35 | #define TOKEN_LEFT_BRACE 12 36 | #define TOKEN_RIGHT_BRACE 13 37 | 38 | #define TOKEN_FALSE 14 39 | #define TOKEN_AWAIT 15 40 | #define TOKEN_ELSE 16 41 | #define TOKEN_IMPORT 17 42 | #define TOKEN_PASS 18 43 | #define TOKEN_NONE 19 44 | #define TOKEN_BREAK 20 45 | #define TOKEN_EXCEPT 21 46 | #define TOKEN_IN 22 47 | #define TOKEN_RAISE 23 48 | #define TOKEN_TRUE 24 49 | #define TOKEN_CLASS 25 50 | #define TOKEN_FINALLY 26 51 | #define TOKEN_IS 27 52 | #define TOKEN_RETURN 28 53 | #define TOKEN_AND 29 54 | #define TOKEN_CONTINUE 30 55 | #define TOKEN_FOR 31 56 | #define TOKEN_LAMBDA 32 57 | #define TOKEN_TRY 33 58 | #define TOKEN_AS 34 59 | #define TOKEN_DEF 35 60 | #define TOKEN_FROM 36 61 | #define TOKEN_NONLOCAL 37 62 | #define TOKEN_WHILE 38 63 | #define TOKEN_ASSERT 39 64 | #define TOKEN_DEL 40 65 | #define TOKEN_GLOBAL 41 66 | #define TOKEN_NOT 42 67 | #define TOKEN_WITH 43 68 | #define TOKEN_ASYNC 44 69 | #define TOKEN_ELIF 45 70 | #define TOKEN_IF 46 71 | #define TOKEN_OR 47 72 | #define TOKEN_YIELD 48 73 | 74 | #define TOKEN_EQUAL 49 75 | #define TOKEN_EQUAL_EQUAL 50 76 | #define TOKEN_NOT_EQUAL 51 77 | #define TOKEN_GREATER 52 78 | #define TOKEN_GREATER_EQUAL 53 79 | #define TOKEN_LESS 54 80 | #define TOKEN_LESS_EQUAL 55 81 | 82 | typedef struct { 83 | TokenType type; 84 | const char* start; 85 | int length; 86 | int line; 87 | } Token; 88 | 89 | void initScanner(Scanner* scanner, const char* source); 90 | void scanToken(Scanner *scanner, Token *token); 91 | 92 | #endif -------------------------------------------------------------------------------- /bbcpython/scanner.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheEnbyperor/bbc-c/be7a2d43d7908218be2b788f7bc51d8e16624a63/bbcpython/scanner.o -------------------------------------------------------------------------------- /bbcpython/value.c: -------------------------------------------------------------------------------- 1 | #include "memory.h" 2 | #include "object.h" 3 | #include "value.h" 4 | 5 | void writeValueArray(ValueArray *array, Value *value) { 6 | writeArray(array, sizeof(Value), value); 7 | } 8 | 9 | static void printObject(Value *value) { 10 | ObjType type = objType(value); 11 | if (type == OBJ_STRING) printf("%s", asCString(value)); 12 | } 13 | 14 | void printValue(Value *value) { 15 | ValueType type = value->type; 16 | if (type == VAL_INT) printf("%d", asInt(value)); 17 | else if (type == VAL_NONE) printf("None"); 18 | else if (type == VAL_BOOL) printf(asBool(value) ? "True" : "False"); 19 | else if (type == VAL_OBJ) printObject(value); 20 | } 21 | 22 | void boolVal(bool val, Value *value) { 23 | value->type = VAL_BOOL; 24 | value->as.boolean = val; 25 | } 26 | 27 | void noneVal(Value *value) { 28 | value->type = VAL_NONE; 29 | value->as.number = 0; 30 | } 31 | 32 | void intVal(int val, Value *value) { 33 | value->type = VAL_INT; 34 | value->as.number = val; 35 | } 36 | 37 | void objVal(Obj *val, Value *value) { 38 | value->type = VAL_OBJ; 39 | value->as.obj = val; 40 | } 41 | 42 | bool isBool(Value *value) { 43 | return value->type == VAL_BOOL; 44 | } 45 | 46 | bool isNone(Value *value) { 47 | return value->type == VAL_NONE; 48 | } 49 | 50 | bool isInt(Value *value) { 51 | return value->type == VAL_INT; 52 | } 53 | 54 | bool isObj(Value *value) { 55 | return value->type == VAL_OBJ; 56 | } 57 | 58 | bool asBool(Value *value) { 59 | return value->as.boolean; 60 | } 61 | 62 | int asInt(Value *value) { 63 | return value->as.number; 64 | } 65 | 66 | Obj *asObj(Value *value) { 67 | return value->as.obj; 68 | } -------------------------------------------------------------------------------- /bbcpython/value.h: -------------------------------------------------------------------------------- 1 | #ifndef bbc_python_value_h 2 | #define bbc_python_value_h 3 | 4 | #include "common.h" 5 | #include "memory.h" 6 | 7 | typedef uint8_t ValueType; 8 | #define VAL_NONE 0 9 | #define VAL_INT 1 10 | #define VAL_BOOL 2 11 | #define VAL_OBJ 3 12 | 13 | typedef struct sObj Obj; 14 | 15 | typedef struct { 16 | ValueType type; 17 | union { 18 | bool boolean; 19 | int number; 20 | struct Obj *obj; 21 | } as; 22 | } Value; 23 | 24 | typedef struct { 25 | ArrayMeta meta; 26 | Value *values; 27 | } ValueArray; 28 | 29 | 30 | void writeValueArray(ValueArray *array, Value *value); 31 | 32 | void boolVal(bool val, Value *value); 33 | 34 | void noneVal(Value *value); 35 | 36 | void intVal(int val, Value *value); 37 | 38 | void objVal(Obj *val, Value *value); 39 | 40 | bool isBool(Value *value); 41 | 42 | bool isNone(Value *value); 43 | 44 | bool isInt(Value *value); 45 | 46 | bool isObj(Value *value); 47 | 48 | bool asBool(Value *value); 49 | 50 | int asInt(Value *value); 51 | 52 | Obj *asObj(Value *value); 53 | 54 | void printValue(Value *value); 55 | 56 | #endif -------------------------------------------------------------------------------- /bbcpython/value.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheEnbyperor/bbc-c/be7a2d43d7908218be2b788f7bc51d8e16624a63/bbcpython/value.o -------------------------------------------------------------------------------- /bbcpython/value.s: -------------------------------------------------------------------------------- 1 | .import printf 2 | .import writeArray 3 | .import objType 4 | .import asCString 5 | .export writeValueArray 6 | .export boolVal 7 | .export noneVal 8 | .export intVal 9 | .export objVal 10 | .export isBool 11 | .export isNone 12 | .export isInt 13 | .export isObj 14 | .export asBool 15 | .export asInt 16 | .export asObj 17 | .export printValue 18 | __bbcc_0000000a: 19 | .byte #37,#115,#0 20 | __bbcc_0000000b: 21 | .byte #37,#100,#0 22 | __bbcc_0000000c: 23 | .byte #78,#111,#110,#101,#0 24 | __bbcc_0000000d: 25 | .byte #84,#114,#117,#101,#0 26 | __bbcc_0000000e: 27 | .byte #70,#97,#108,#115,#101,#0 28 | // Function: writeValueArray 29 | writeValueArray: 30 | push %r12 31 | mov %r14, %r12 32 | push %r1 33 | push %r2 34 | // Set 35 | mov DWORD 8[%r12], %r1 36 | // Set 37 | mov DWORD 12[%r12], %r0 38 | // CallFunction 39 | push %r0 40 | mov #5, %r2 41 | push %r2 42 | push %r1 43 | call [writeArray] 44 | add #12, %r14 45 | // Return 46 | mov #0, %r0 47 | __bbcc_0000000f: 48 | pop %r2 49 | pop %r1 50 | mov %r12, %r14 51 | pop %r12 52 | ret 53 | // Function: printObject 54 | printObject: 55 | push %r12 56 | mov %r14, %r12 57 | push %r1 58 | // Set 59 | mov DWORD 8[%r12], %r0 60 | // CallFunction 61 | push %r0 62 | call [objType] 63 | add #4, %r14 64 | // Set 65 | // NotEqualJmp 66 | cmp #0, %r0 67 | jnz [__bbcc_00000000] 68 | // AddrOf 69 | lea DWORD [__bbcc_0000000a], %r1 70 | // Set 71 | // Set 72 | mov DWORD 8[%r12], %r0 73 | // CallFunction 74 | push %r0 75 | call [asCString] 76 | add #4, %r14 77 | // CallFunction 78 | push %r0 79 | push %r1 80 | call [printf] 81 | add #8, %r14 82 | // Label 83 | __bbcc_00000000: 84 | // Return 85 | mov #0, %r0 86 | __bbcc_00000010: 87 | pop %r1 88 | mov %r12, %r14 89 | pop %r12 90 | ret 91 | // Function: printValue 92 | printValue: 93 | push %r12 94 | mov %r14, %r12 95 | push %r1 96 | push %r2 97 | // ReadAt 98 | mov DWORD 8[%r12], %r0 99 | mov BYTE [%r0], %r2 100 | // Set 101 | // NotEqualJmp 102 | cmp #1, %r2 103 | jnz [__bbcc_00000001] 104 | // AddrOf 105 | lea DWORD [__bbcc_0000000b], %r1 106 | // Set 107 | // Set 108 | mov DWORD 8[%r12], %r0 109 | // CallFunction 110 | push %r0 111 | call [asInt] 112 | add #4, %r14 113 | // CallFunction 114 | push %r0 115 | push %r1 116 | call [printf] 117 | add #8, %r14 118 | // Jmp 119 | jmp [__bbcc_00000002] 120 | // Label 121 | __bbcc_00000001: 122 | // NotEqualJmp 123 | cmp #0, %r2 124 | jnz [__bbcc_00000003] 125 | // AddrOf 126 | lea DWORD [__bbcc_0000000c], %r0 127 | // Set 128 | // CallFunction 129 | push %r0 130 | call [printf] 131 | add #4, %r14 132 | // Jmp 133 | jmp [__bbcc_00000004] 134 | // Label 135 | __bbcc_00000003: 136 | // NotEqualJmp 137 | cmp #2, %r2 138 | jnz [__bbcc_00000005] 139 | // Set 140 | mov DWORD 8[%r12], %r0 141 | // CallFunction 142 | push %r0 143 | call [asBool] 144 | add #4, %r14 145 | // JmpZero 146 | cmp #0, %r0 147 | jze [__bbcc_00000006] 148 | // AddrOf 149 | lea DWORD [__bbcc_0000000d], %r0 150 | // Set 151 | // Jmp 152 | jmp [__bbcc_00000007] 153 | // Label 154 | __bbcc_00000006: 155 | // AddrOf 156 | lea DWORD [__bbcc_0000000e], %r0 157 | // Set 158 | // Label 159 | __bbcc_00000007: 160 | // Set 161 | // CallFunction 162 | push %r0 163 | call [printf] 164 | add #4, %r14 165 | // Jmp 166 | jmp [__bbcc_00000008] 167 | // Label 168 | __bbcc_00000005: 169 | // NotEqualJmp 170 | cmp #3, %r2 171 | jnz [__bbcc_00000009] 172 | // Set 173 | mov DWORD 8[%r12], %r0 174 | // CallFunction 175 | push %r0 176 | call [printObject] 177 | add #4, %r14 178 | // Label 179 | __bbcc_00000009: 180 | // Label 181 | __bbcc_00000008: 182 | // Label 183 | __bbcc_00000004: 184 | // Label 185 | __bbcc_00000002: 186 | // Return 187 | mov #0, %r0 188 | __bbcc_00000011: 189 | pop %r2 190 | pop %r1 191 | mov %r12, %r14 192 | pop %r12 193 | ret 194 | // Function: boolVal 195 | boolVal: 196 | push %r12 197 | mov %r14, %r12 198 | push %r1 199 | // Set 200 | mov #2, %r0 201 | // SetAt 202 | mov 12[%r12], %r1 203 | mov %r0, BYTE [%r1] 204 | // Add 205 | mov DWORD 12[%r12], %r0 206 | add #1, %r0 207 | // SetAt 208 | mov 8[%r12], %r1 209 | mov %r1, BYTE [%r0] 210 | // Return 211 | mov #0, %r0 212 | __bbcc_00000012: 213 | pop %r1 214 | mov %r12, %r14 215 | pop %r12 216 | ret 217 | // Function: noneVal 218 | noneVal: 219 | push %r12 220 | mov %r14, %r12 221 | push %r1 222 | // Set 223 | mov #0, %r0 224 | // SetAt 225 | mov 8[%r12], %r1 226 | mov %r0, BYTE [%r1] 227 | // Add 228 | mov DWORD 8[%r12], %r0 229 | add #1, %r0 230 | // SetAt 231 | mov #0, %r1 232 | mov %r1, DWORD [%r0] 233 | // Return 234 | mov #0, %r0 235 | __bbcc_00000013: 236 | pop %r1 237 | mov %r12, %r14 238 | pop %r12 239 | ret 240 | // Function: intVal 241 | intVal: 242 | push %r12 243 | mov %r14, %r12 244 | push %r1 245 | // Set 246 | mov #1, %r0 247 | // SetAt 248 | mov 12[%r12], %r1 249 | mov %r0, BYTE [%r1] 250 | // Add 251 | mov DWORD 12[%r12], %r0 252 | add #1, %r0 253 | // SetAt 254 | mov 8[%r12], %r1 255 | mov %r1, DWORD [%r0] 256 | // Return 257 | mov #0, %r0 258 | __bbcc_00000014: 259 | pop %r1 260 | mov %r12, %r14 261 | pop %r12 262 | ret 263 | // Function: objVal 264 | objVal: 265 | push %r12 266 | mov %r14, %r12 267 | push %r1 268 | // Set 269 | mov #3, %r0 270 | // SetAt 271 | mov 12[%r12], %r1 272 | mov %r0, BYTE [%r1] 273 | // Add 274 | mov DWORD 12[%r12], %r1 275 | add #1, %r1 276 | // Set 277 | mov DWORD 8[%r12], %r0 278 | // SetAt 279 | mov %r0, DWORD [%r1] 280 | // Return 281 | mov #0, %r0 282 | __bbcc_00000015: 283 | pop %r1 284 | mov %r12, %r14 285 | pop %r12 286 | ret 287 | // Function: isBool 288 | isBool: 289 | push %r12 290 | mov %r14, %r12 291 | push %r1 292 | // ReadAt 293 | mov DWORD 8[%r12], %r0 294 | mov BYTE [%r0], %r1 295 | // EqualCmp 296 | cmp #2, %r1 297 | sze %r0 298 | // Return 299 | __bbcc_00000016: 300 | pop %r1 301 | mov %r12, %r14 302 | pop %r12 303 | ret 304 | // Function: isNone 305 | isNone: 306 | push %r12 307 | mov %r14, %r12 308 | push %r1 309 | // ReadAt 310 | mov DWORD 8[%r12], %r0 311 | mov BYTE [%r0], %r1 312 | // EqualCmp 313 | cmp #0, %r1 314 | sze %r0 315 | // Return 316 | __bbcc_00000017: 317 | pop %r1 318 | mov %r12, %r14 319 | pop %r12 320 | ret 321 | // Function: isInt 322 | isInt: 323 | push %r12 324 | mov %r14, %r12 325 | push %r1 326 | // ReadAt 327 | mov DWORD 8[%r12], %r0 328 | mov BYTE [%r0], %r1 329 | // EqualCmp 330 | cmp #1, %r1 331 | sze %r0 332 | // Return 333 | __bbcc_00000018: 334 | pop %r1 335 | mov %r12, %r14 336 | pop %r12 337 | ret 338 | // Function: isObj 339 | isObj: 340 | push %r12 341 | mov %r14, %r12 342 | push %r1 343 | // ReadAt 344 | mov DWORD 8[%r12], %r0 345 | mov BYTE [%r0], %r1 346 | // EqualCmp 347 | cmp #3, %r1 348 | sze %r0 349 | // Return 350 | __bbcc_00000019: 351 | pop %r1 352 | mov %r12, %r14 353 | pop %r12 354 | ret 355 | // Function: asBool 356 | asBool: 357 | push %r12 358 | mov %r14, %r12 359 | // Add 360 | mov DWORD 8[%r12], %r0 361 | add #1, %r0 362 | // ReadAt 363 | mov BYTE [%r0], %r0 364 | // Return 365 | __bbcc_0000001a: 366 | mov %r12, %r14 367 | pop %r12 368 | ret 369 | // Function: asInt 370 | asInt: 371 | push %r12 372 | mov %r14, %r12 373 | // Add 374 | mov DWORD 8[%r12], %r0 375 | add #1, %r0 376 | // ReadAt 377 | mov DWORD [%r0], %r0 378 | // Return 379 | __bbcc_0000001b: 380 | mov %r12, %r14 381 | pop %r12 382 | ret 383 | // Function: asObj 384 | asObj: 385 | push %r12 386 | mov %r14, %r12 387 | // Add 388 | mov DWORD 8[%r12], %r0 389 | add #1, %r0 390 | // ReadAt 391 | mov DWORD [%r0], %r0 392 | // Return 393 | __bbcc_0000001c: 394 | mov %r12, %r14 395 | pop %r12 396 | ret -------------------------------------------------------------------------------- /bbcpython/vm.c: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include "debug.h" 3 | #include "memory.h" 4 | #include "compiler.h" 5 | #include "string.h" 6 | #include "vm.h" 7 | 8 | void pushStack(Stack *stack, Value *value) { 9 | writeArray(stack, sizeof(Value), value); 10 | } 11 | 12 | void popStack(Stack *stack, Value *value) { 13 | popArray(stack, sizeof(Value), value); 14 | } 15 | 16 | void initVM(VM *vm) { 17 | initArray(&vm->stack); 18 | vm->objects = NULL; 19 | } 20 | 21 | void freeVM(VM *vm) { 22 | freeArray(&vm->stack); 23 | freeObjects(vm); 24 | } 25 | 26 | static uint8_t readByte(VM *vm) { 27 | return *vm->ip++; 28 | } 29 | 30 | static Value *readConstant(VM *vm) { 31 | return &vm->chunk->constants.values[readByte(vm)]; 32 | } 33 | 34 | static Value *peek(VM *vm, int distance) { 35 | return &vm->stack.data[vm->stack.meta.count - 1 - distance]; 36 | } 37 | 38 | static void runtimeError(VM *vm, const char *format) { 39 | unsigned int instruction = vm->ip - vm->chunk->code - 1; 40 | printf("[line %d] in script: %s\n", getLine(&vm->chunk->lineInfo, instruction), format); 41 | freeArray(&vm->stack); 42 | } 43 | 44 | static bool checkArithType(VM *vm, int *a, int *b, Value **value) { 45 | *value = peek(vm, 1); 46 | if (!isInt(peek(vm, 0)) || !isInt(*value)) { 47 | runtimeError(vm, "Operands must be a number."); 48 | return false; 49 | } 50 | Value aVal, bVal; 51 | popStack(&vm->stack, &bVal); 52 | *a = asInt(*value); 53 | *b = asInt(&bVal); 54 | return true; 55 | } 56 | 57 | static bool isFalsey(VM *vm, Value **value) { 58 | *value = peek(vm, 0); 59 | if (isNone(*value)) return true; 60 | else if (isBool(*value)) return !asBool(*value); 61 | else if (isInt(*value)) return asInt(*value) == 0; 62 | } 63 | 64 | static bool valuesEqual(VM *vm, Value **a) { 65 | Value b; 66 | popStack(&vm->stack, &b); 67 | *a = peek(vm, 0); 68 | 69 | if (b.type != (*a)->type) return false; 70 | 71 | ValueType type = (*a)->type; 72 | if (type == VAL_BOOL) return asBool(*a) == asBool(&b); 73 | if (type == VAL_NONE) return true; 74 | if (type == VAL_INT) return asInt(*a) == asInt(&b); 75 | } 76 | 77 | void concatenate(VM *vm) { 78 | Value bVal; 79 | Value *aVal; 80 | ObjString *bStr, *aStr; 81 | popStack(&vm->stack, &bVal); 82 | aVal = peek(vm, 0); 83 | bStr = asString(&bVal); 84 | aStr = asString(aVal); 85 | 86 | int length = aStr->length + bStr->length; 87 | char *chars = reallocate(NULL, length + 1); 88 | memcpy(chars, aStr->chars, aStr->length); 89 | memcpy(chars + aStr->length, bStr->chars, bStr->length); 90 | chars[length] = '\0'; 91 | 92 | aStr = takeString(chars, length, vm); 93 | objVal(aStr, aVal); 94 | } 95 | 96 | static InterpretResult run(VM *vm) { 97 | Value value; 98 | Value *valuePtr; 99 | int a, b; 100 | 101 | for (;;) { 102 | #ifdef DEBUG_TRACE_EXECUTION 103 | printf("["); 104 | for (int slot = 0; slot < vm->stack.meta.count; slot++) { 105 | if (slot != 0) { 106 | printf(", "); 107 | } 108 | printValue(&vm->stack.data[slot]); 109 | } 110 | printf("]\n"); 111 | disassembleInstruction(vm->chunk, (int) (vm->ip - vm->chunk->code)); 112 | #endif 113 | 114 | uint8_t instruction = readByte(vm); 115 | 116 | if (instruction == OP_CONSTANT) { 117 | struct Value *constant = readConstant(vm); 118 | pushStack(&vm->stack, constant); 119 | } else if (instruction == OP_NEGATE) { 120 | valuePtr = peek(vm, 0); 121 | if (!isInt(valuePtr)) { 122 | runtimeError(vm, "Operand must be a number."); 123 | return INTERPRET_RUNTIME_ERROR; 124 | } 125 | intVal(-asInt(valuePtr), valuePtr); 126 | } else if (instruction == OP_ADD) { 127 | if (isString(peek(vm, 0)) && isString(peek(vm, 1))) { 128 | concatenate(vm); 129 | } else if (isInt(peek(vm, 0)) && isInt(peek(vm, 1))) { 130 | valuePtr = peek(vm, 1); 131 | popStack(&vm->stack, &value); 132 | a = asInt(valuePtr); 133 | b = asInt(&value); 134 | intVal(a + b, valuePtr); 135 | } else { 136 | runtimeError(vm, "Operands must be two numbers or two strings."); 137 | return INTERPRET_RUNTIME_ERROR; 138 | } 139 | } else if (instruction == OP_SUBTRACT) { 140 | if (!checkArithType(vm, &a, &b, &valuePtr)) return INTERPRET_RUNTIME_ERROR; 141 | intVal(a - b, valuePtr); 142 | } else if (instruction == OP_MULTIPLY) { 143 | if (!checkArithType(vm, &a, &b, &valuePtr)) return INTERPRET_RUNTIME_ERROR; 144 | intVal(a * b, valuePtr); 145 | } else if (instruction == OP_DIVIDE) { 146 | if (!checkArithType(vm, &a, &b, &valuePtr)) return INTERPRET_RUNTIME_ERROR; 147 | intVal(a / b, valuePtr); 148 | } else if (instruction == OP_MODULUS) { 149 | if (!checkArithType(vm, &a, &b, &valuePtr)) return INTERPRET_RUNTIME_ERROR; 150 | intVal(a % b, valuePtr); 151 | 152 | } else if (instruction == OP_NOT) { 153 | boolVal(isFalsey(vm, &valuePtr), valuePtr); 154 | } else if (instruction == OP_EQUAL) { 155 | boolVal(valuesEqual(vm, &valuePtr), valuePtr); 156 | } else if (instruction == OP_GREATER) { 157 | if (!checkArithType(vm, &a, &b, &valuePtr)) return INTERPRET_RUNTIME_ERROR; 158 | boolVal(a > b, valuePtr); 159 | } else if (instruction == OP_LESS) { 160 | if (!checkArithType(vm, &a, &b, &valuePtr)) return INTERPRET_RUNTIME_ERROR; 161 | boolVal(a < b, valuePtr); 162 | 163 | } else if (instruction == OP_RETURN) { 164 | popStack(&vm->stack, &value); 165 | printValue(&value); 166 | printf("\n"); 167 | return INTERPRET_OK; 168 | } 169 | } 170 | 171 | } 172 | 173 | InterpretResult interpret(VM *vm, const char *source) { 174 | Chunk chunk; 175 | initChunk(&chunk); 176 | 177 | if (!compile(source, &chunk, vm)) { 178 | freeChunk(&chunk); 179 | return INTERPRET_COMPILE_ERROR; 180 | } 181 | 182 | vm->chunk = &chunk; 183 | vm->ip = vm->chunk->code; 184 | 185 | #ifdef DEBUG_TRACE_EXECUTION 186 | printf("\nSTART VM\n"); 187 | #endif 188 | InterpretResult result = run(vm); 189 | #ifdef DEBUG_TRACE_EXECUTION 190 | printf("END VM\n\n"); 191 | #endif 192 | 193 | freeChunk(&chunk); 194 | return result; 195 | } -------------------------------------------------------------------------------- /bbcpython/vm.h: -------------------------------------------------------------------------------- 1 | #ifndef bbc_python_vm_h 2 | #define bbc_python_vm_h 3 | 4 | #include "chunk.h" 5 | #include "memory.h" 6 | #include "value.h" 7 | 8 | typedef uint8_t InterpretResult; 9 | #define INTERPRET_OK 0 10 | #define INTERPRET_COMPILE_ERROR 1 11 | #define INTERPRET_RUNTIME_ERROR 2 12 | 13 | typedef struct { 14 | ArrayMeta meta; 15 | Value* data; 16 | } Stack; 17 | 18 | typedef struct { 19 | struct sChunk* chunk; 20 | uint8_t* ip; 21 | Stack stack; 22 | Obj* objects; 23 | } VM; 24 | 25 | #include "object.h" 26 | 27 | void pushStack(Stack* stack, Value *value); 28 | void popStack(Stack* stack, Value *value); 29 | 30 | void initVM(VM* vm); 31 | InterpretResult interpret(VM *vm, const char* source); 32 | void freeVM(VM* vm); 33 | 34 | #endif -------------------------------------------------------------------------------- /bbcpython/vm.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheEnbyperor/bbc-c/be7a2d43d7908218be2b788f7bc51d8e16624a63/bbcpython/vm.o -------------------------------------------------------------------------------- /bbctape/__init__.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import wave 3 | import struct 4 | import math 5 | from PyCRC.CRCCCITT import CRCCCITT 6 | 7 | SAMPLE_RATE = 44100 8 | BAUD_RATE = 1500 9 | 10 | 11 | def gen_sine(freq, cycles): 12 | x = np.arange((SAMPLE_RATE / freq) * cycles) 13 | s = np.sin(2 * np.pi * freq * x / SAMPLE_RATE) 14 | return s 15 | 16 | 17 | def make_len(data, secs): 18 | return np.tile(data, int(SAMPLE_RATE / len(data) * secs)) 19 | 20 | 21 | def make_block(filename: str, lda, exa, blkn, prot, empt, lblk, data): 22 | data_len = len(data) 23 | out = bytearray() 24 | out.extend(filename.encode()) 25 | out.append(0) 26 | out.extend(struct.pack("H", crc)) 35 | 36 | data_crc = CRCCCITT().calculate(bytes(data)) 37 | out.extend(data) 38 | out.extend(struct.pack(">H", data_crc)) 39 | 40 | return bytearray([0x2A]) + out 41 | 42 | 43 | bit0 = gen_sine(BAUD_RATE, 1) 44 | bit1 = gen_sine(BAUD_RATE * 2, 2) 45 | stop_bit = gen_sine(BAUD_RATE * 2, 1.5) 46 | start = make_len(bit1, 1.1) 47 | block_end = make_len(bit1, 0.9) 48 | 49 | 50 | def bytes_to_sample(byte): 51 | sample = [] 52 | for b in byte: 53 | sample.extend(bit0) 54 | for i in range(8): 55 | bit = (b >> i) & 1 56 | if bit: 57 | sample.extend(bit1) 58 | else: 59 | sample.extend(bit0) 60 | sample.extend(stop_bit) 61 | return np.array(sample) 62 | 63 | 64 | def make_file(w, file, lda, exa, prot, data): 65 | BLOCK_LEN = 65535 66 | num_blocks = int(math.ceil(len(data) / BLOCK_LEN)) 67 | blocks = [start] 68 | for i in range(num_blocks): 69 | bdata = data[i*BLOCK_LEN:BLOCK_LEN+(i*BLOCK_LEN)] 70 | block = make_block(file, lda, exa, i, prot, 0, (i == num_blocks-1), bdata) 71 | block = bytes_to_sample(block) 72 | blocks.append(block) 73 | blocks.append(block_end) 74 | blocks.append(start) 75 | sample = np.asarray(np.concatenate(blocks) * 0x7fff, dtype=np.int16) 76 | w.setframerate(SAMPLE_RATE) 77 | w.setnchannels(1) 78 | w.setsampwidth(2) 79 | w.setnframes(len(sample)) 80 | w.writeframes(sample) 81 | -------------------------------------------------------------------------------- /bbcvm/loader: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheEnbyperor/bbc-c/be7a2d43d7908218be2b788f7bc51d8e16624a63/bbcvm/loader -------------------------------------------------------------------------------- /bbcvm/loader.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheEnbyperor/bbc-c/be7a2d43d7908218be2b788f7bc51d8e16624a63/bbcvm/loader.o -------------------------------------------------------------------------------- /bbcvm/loader.s: -------------------------------------------------------------------------------- 1 | .export _start 2 | 3 | _start: 4 | jmp _start_loader 5 | 6 | file_not_found: .byte "Unable to open file",#$0D,#0 7 | file_loaded: .byte "File loaded into extended memory",#$0D,#0 8 | file_pointer: .byte #0, #0, #0, #0 9 | 10 | print_string: 11 | ldy #0 12 | print_string_start: 13 | lda ($70),y 14 | beq print_string_end 15 | jsr $FFE3 16 | iny 17 | jmp print_string_start 18 | print_string_end: 19 | rts 20 | 21 | _start_loader: 22 | 23 | // Get command line parameters 24 | ldx #$70 25 | ldy #0 26 | lda #1 27 | jsr $FFDA 28 | 29 | // Set termination byte for OSFIND 30 | ldy #0 31 | set_return_byte: 32 | lda ($70),y 33 | cmp #$0D 34 | beq set_return_byte_end 35 | cmp #$20 36 | beq set_return_byte_space 37 | iny 38 | cpy #$ff 39 | beq set_return_byte_end 40 | bne set_return_byte 41 | set_return_byte_space: 42 | lda #$0D 43 | sta ($70),y 44 | set_return_byte_end: 45 | 46 | // Attempt to open file 47 | ldx $70 48 | ldy $71 49 | lda #$40 50 | jsr $FFCE 51 | 52 | // Print error message and exit if fail 53 | bne open_file_success 54 | lda #0(file_not_found) 55 | sta $70 56 | lda #1(file_not_found) 57 | sta $71 58 | jsr print_string 59 | rts 60 | 61 | // Save file handle on success 62 | open_file_success: 63 | tay 64 | 65 | // Setup indirect for writing to memory 66 | lda #$FD 67 | sta $71 68 | ldx #0 69 | 70 | // Setup memory banks 71 | stx $FC00 72 | stx $FC01 73 | stx $FC02 74 | stx $70 75 | 76 | transfer_loop: 77 | // Read byte from file into extended memory 78 | jsr $FFD7 79 | bcs transfer_end 80 | sta ($70,x) 81 | 82 | // Increment file pointer and update memory banks if nescacary 83 | inc 0(file_pointer) 84 | lda 0(file_pointer) 85 | sta $70 86 | bne inc_fp 87 | inc 1(file_pointer) 88 | lda 1(file_pointer) 89 | sta $FC00 90 | bne inc_fp 91 | inc 2(file_pointer) 92 | lda 3(file_pointer) 93 | sta $FC01 94 | bne inc_fp 95 | inc 3(file_pointer) 96 | lda 3(file_pointer) 97 | sta $FC02 98 | inc_fp: 99 | 100 | jmp transfer_loop 101 | 102 | transfer_end: 103 | // Close file 104 | lda #0 105 | jsr $FFCE 106 | 107 | // Print success message 108 | lda #0(file_loaded) 109 | sta $70 110 | lda #1(file_loaded) 111 | sta $71 112 | jsr print_string 113 | 114 | rts -------------------------------------------------------------------------------- /bbcvm/loader.ssd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheEnbyperor/bbc-c/be7a2d43d7908218be2b788f7bc51d8e16624a63/bbcvm/loader.ssd -------------------------------------------------------------------------------- /bbcvm/loader.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheEnbyperor/bbc-c/be7a2d43d7908218be2b788f7bc51d8e16624a63/bbcvm/loader.wav -------------------------------------------------------------------------------- /bbcvm/vm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheEnbyperor/bbc-c/be7a2d43d7908218be2b788f7bc51d8e16624a63/bbcvm/vm -------------------------------------------------------------------------------- /bbcvm/vm.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheEnbyperor/bbc-c/be7a2d43d7908218be2b788f7bc51d8e16624a63/bbcvm/vm.o -------------------------------------------------------------------------------- /bbcvm/vm.ssd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheEnbyperor/bbc-c/be7a2d43d7908218be2b788f7bc51d8e16624a63/bbcvm/vm.ssd -------------------------------------------------------------------------------- /bbcvm/vm.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheEnbyperor/bbc-c/be7a2d43d7908218be2b788f7bc51d8e16624a63/bbcvm/vm.wav -------------------------------------------------------------------------------- /bbcvmasm/__init__.py: -------------------------------------------------------------------------------- 1 | from . import lexer 2 | from . import parser 3 | from . import assembler 4 | 5 | 6 | def asm_to_object(asm): 7 | lex = lexer.Lexer(asm) 8 | tokens = lex.tokenize() 9 | 10 | parse = parser.Parser(tokens) 11 | ast = parse.parse() 12 | 13 | assemble = assembler.Assembler(ast) 14 | 15 | out = assemble.assemble() 16 | 17 | return out 18 | -------------------------------------------------------------------------------- /bbcvmasm/ast.py: -------------------------------------------------------------------------------- 1 | class ASTNode: 2 | pass 3 | 4 | 5 | class RegisterValue(ASTNode): 6 | def __init__(self, reg_num): 7 | self.reg_num = reg_num 8 | 9 | 10 | class LiteralValue(ASTNode): 11 | def __init__(self, val): 12 | self.val = val 13 | 14 | 15 | class MemoryValue(ASTNode): 16 | def __init__(self, const_loc, reg_indirect, length): 17 | self.const_loc = const_loc 18 | self.reg_indirect = reg_indirect 19 | self.length = length 20 | 21 | 22 | class LabelValue(ASTNode): 23 | def __init__(self, label): 24 | self.label = label 25 | 26 | 27 | class TranslationUnit(ASTNode): 28 | def __init__(self, items): 29 | self.items = items 30 | 31 | 32 | class Label(ASTNode): 33 | def __init__(self, label): 34 | self.label = label 35 | 36 | 37 | class ExportCommand(ASTNode): 38 | def __init__(self, label): 39 | self.label = label 40 | 41 | 42 | class ImportCommand(ASTNode): 43 | def __init__(self, label): 44 | self.label = label 45 | 46 | 47 | class Bytes(ASTNode): 48 | def __init__(self, bytes): 49 | self.bytes = bytes 50 | 51 | 52 | class _ZeroValueNode(ASTNode): 53 | num_values = 0 54 | 55 | def __init__(self): 56 | pass 57 | 58 | 59 | class _OneValueNode(ASTNode): 60 | num_values = 1 61 | 62 | def __init__(self, value): 63 | self.value = value 64 | 65 | 66 | class _TwoValueNode(ASTNode): 67 | num_values = 2 68 | 69 | def __init__(self, left, right): 70 | self.left = left 71 | self.right = right 72 | 73 | 74 | class Call(_OneValueNode): 75 | pass 76 | 77 | 78 | class Jmp(_OneValueNode): 79 | pass 80 | 81 | 82 | class Jze(_OneValueNode): 83 | pass 84 | 85 | 86 | class Jnz(_OneValueNode): 87 | pass 88 | 89 | 90 | class Jl(_OneValueNode): 91 | pass 92 | 93 | 94 | class Jle(_OneValueNode): 95 | pass 96 | 97 | 98 | class Jg(_OneValueNode): 99 | pass 100 | 101 | 102 | class Jge(_OneValueNode): 103 | pass 104 | 105 | 106 | class Ja(_OneValueNode): 107 | pass 108 | 109 | 110 | class Jae(_OneValueNode): 111 | pass 112 | 113 | 114 | class Jb(_OneValueNode): 115 | pass 116 | 117 | 118 | class Jbe(_OneValueNode): 119 | pass 120 | 121 | 122 | class Sze(_OneValueNode): 123 | pass 124 | 125 | 126 | class Snz(_OneValueNode): 127 | pass 128 | 129 | 130 | class Sl(_OneValueNode): 131 | pass 132 | 133 | 134 | class Sle(_OneValueNode): 135 | pass 136 | 137 | 138 | class Sg(_OneValueNode): 139 | pass 140 | 141 | 142 | class Sge(_OneValueNode): 143 | pass 144 | 145 | 146 | class Sa(_OneValueNode): 147 | pass 148 | 149 | 150 | class Sae(_OneValueNode): 151 | pass 152 | 153 | 154 | class Sb(_OneValueNode): 155 | pass 156 | 157 | 158 | class Sbe(_OneValueNode): 159 | pass 160 | 161 | 162 | class Calln(_TwoValueNode): 163 | pass 164 | 165 | 166 | class Push(_OneValueNode): 167 | pass 168 | 169 | 170 | class Pop(_OneValueNode): 171 | pass 172 | 173 | 174 | class Inc(_OneValueNode): 175 | pass 176 | 177 | 178 | class Dec(_OneValueNode): 179 | pass 180 | 181 | 182 | class Neg(_OneValueNode): 183 | pass 184 | 185 | 186 | class Mov(_TwoValueNode): 187 | pass 188 | 189 | 190 | class Mov6502(_TwoValueNode): 191 | pass 192 | 193 | 194 | class Lea(_TwoValueNode): 195 | pass 196 | 197 | 198 | class Cmp(_TwoValueNode): 199 | pass 200 | 201 | 202 | class Add(_TwoValueNode): 203 | pass 204 | 205 | 206 | class Sub(_TwoValueNode): 207 | pass 208 | 209 | 210 | class Mul(_TwoValueNode): 211 | pass 212 | 213 | 214 | class Div(_TwoValueNode): 215 | pass 216 | 217 | 218 | class Mod(_TwoValueNode): 219 | pass 220 | 221 | 222 | class And(_TwoValueNode): 223 | pass 224 | 225 | 226 | class Or(_TwoValueNode): 227 | pass 228 | 229 | 230 | class Xor(_TwoValueNode): 231 | pass 232 | 233 | 234 | class Shl(_TwoValueNode): 235 | pass 236 | 237 | 238 | class Shr(_TwoValueNode): 239 | pass 240 | 241 | 242 | class Ret(_ZeroValueNode): 243 | pass 244 | 245 | 246 | class Exit(_ZeroValueNode): 247 | pass 248 | -------------------------------------------------------------------------------- /bbcvmasm/lexer.py: -------------------------------------------------------------------------------- 1 | import string 2 | from .tokens import * 3 | 4 | 5 | class Lexer: 6 | def __init__(self, text): 7 | self.text = text 8 | self.pos = 0 9 | if len(text) == 0: 10 | self.text = " " 11 | self.current_char = self.text[self.pos] 12 | 13 | def error(self): 14 | raise Exception("Invalid character: " + self.current_char) 15 | 16 | def peek(self, i=1): 17 | peek_pos = self.pos + i 18 | if peek_pos > len(self.text) - 1: 19 | return None 20 | else: 21 | return self.text[peek_pos] 22 | 23 | def advance(self): 24 | """Advance the 'pos' pointer and set the 'current_char' variable.""" 25 | self.pos += 1 26 | if self.pos > len(self.text) - 1: 27 | self.current_char = None # Indicates end of input 28 | else: 29 | self.current_char = self.text[self.pos] 30 | 31 | def skip_whitespace(self): 32 | while self.current_char is not None and self.current_char.isspace(): 33 | self.advance() 34 | 35 | def skip_line_comment(self): 36 | while self.current_char != '\n' and self.current_char is not None: 37 | self.advance() 38 | self.advance() 39 | 40 | def integer(self): 41 | result = '' 42 | while self.current_char is not None and self.current_char.isdigit(): 43 | result += self.current_char 44 | self.advance() 45 | return int(result) 46 | 47 | def integer_hex(self): 48 | """Return a integer consumed from the input.""" 49 | result = '' 50 | while self.current_char is not None and (self.current_char.isdigit() or 51 | self.current_char.lower() in ["a", "b", "c", "d", "e", "f"]): 52 | result += self.current_char 53 | self.advance() 54 | return int(result, 16) 55 | 56 | def id(self): 57 | result = '' 58 | while self.current_char is not None and self.current_char in string.ascii_letters+string.digits+"_": 59 | result += self.current_char 60 | self.advance() 61 | 62 | token_type = RESERVED.get(result, ID) 63 | token = Token(token_type, result) 64 | return token 65 | 66 | def tokenize(self): 67 | tokens = [] 68 | while self.current_char is not None: 69 | if self.current_char.isspace() and self.current_char is not "\n": 70 | self.skip_whitespace() 71 | elif self.current_char == "\n": 72 | self.advance() 73 | 74 | elif self.current_char == "/" and self.peek() == "/": 75 | self.advance() 76 | self.skip_line_comment() 77 | 78 | elif self.current_char == "$": 79 | self.advance() 80 | tokens.append(Token(INTEGER, self.integer_hex())) 81 | elif self.current_char.isdigit(): 82 | tokens.append(Token(INTEGER, self.integer())) 83 | elif self.current_char == '-': 84 | self.advance() 85 | tokens.append(Token(INTEGER, -self.integer())) 86 | 87 | elif self.current_char in string.ascii_letters+string.digits+"_": 88 | tokens.append(self.id()) 89 | 90 | elif self.current_char == ":": 91 | self.advance() 92 | tokens.append(Token(COLON, ":")) 93 | elif self.current_char == ".": 94 | self.advance() 95 | tokens.append(Token(PERIOD, ".")) 96 | elif self.current_char == "%": 97 | self.advance() 98 | tokens.append(Token(PERCENT, "%")) 99 | elif self.current_char == ",": 100 | self.advance() 101 | tokens.append(Token(COMMA, ",")) 102 | elif self.current_char == "#": 103 | self.advance() 104 | tokens.append(Token(HASH, "#")) 105 | elif self.current_char == "[": 106 | self.advance() 107 | tokens.append(Token(LBRACE, "[")) 108 | elif self.current_char == "]": 109 | self.advance() 110 | tokens.append(Token(RBRACE, "]")) 111 | elif self.current_char == "+": 112 | self.advance() 113 | tokens.append(Token(PLUS, "+")) 114 | 115 | else: 116 | self.error() 117 | 118 | tokens.append(Token(EOF, None)) 119 | 120 | return tokens 121 | -------------------------------------------------------------------------------- /bbcvmasm/parser.py: -------------------------------------------------------------------------------- 1 | from .tokens import * 2 | from . import ast 3 | 4 | 5 | class Parser: 6 | def __init__(self, tokens): 7 | self.tokens = tokens 8 | 9 | def error(self): 10 | raise SyntaxError("Invalid syntax") 11 | 12 | def eat(self, index, token_type): 13 | if self.tokens[index].type == token_type: 14 | return index+1 15 | else: 16 | self.error() 17 | 18 | def eat_id(self, index, value): 19 | if self.tokens[index].type == ID and self.tokens[index].value == value: 20 | return index+1 21 | else: 22 | self.error() 23 | 24 | def token_is(self, index, token_type): 25 | if self.tokens[index].type == token_type: 26 | return True 27 | else: 28 | return False 29 | 30 | def parse(self): 31 | index = 0 32 | items = [] 33 | while True: 34 | try: 35 | item, index = self.parse_line(index) 36 | items.append(item) 37 | except SyntaxError: 38 | break 39 | 40 | if self.tokens[index:][0].type == EOF: 41 | return ast.TranslationUnit(items) 42 | else: 43 | self.error() 44 | 45 | def parse_line(self, index): 46 | try: 47 | return self.parse_command(index) 48 | except SyntaxError: 49 | try: 50 | return self.parse_label(index) 51 | except SyntaxError: 52 | return self.parse_inst(index) 53 | 54 | def parse_command(self, index): 55 | index = self.eat(index, PERIOD) 56 | values = [self.parse_export_command, self.parse_import_command, self.parse_bytes_command] 57 | 58 | for val in values: 59 | try: 60 | return val(index) 61 | except SyntaxError: 62 | continue 63 | self.error() 64 | 65 | def parse_export_command(self, index): 66 | index = self.eat_id(index, "export") 67 | if not self.token_is(index, ID): 68 | self.error() 69 | return ast.ExportCommand(self.tokens[index].value), index + 1 70 | 71 | def parse_import_command(self, index): 72 | index = self.eat_id(index, "import") 73 | if not self.token_is(index, ID): 74 | self.error() 75 | return ast.ImportCommand(self.tokens[index].value), index + 1 76 | 77 | def parse_bytes_command(self, index): 78 | index = self.eat_id(index, "byte") 79 | vals = [] 80 | while True: 81 | try: 82 | val, index = self.parse_value(index) 83 | vals.append(val) 84 | index = self.eat(index, COMMA) 85 | except SyntaxError: 86 | break 87 | return ast.Bytes(vals), index 88 | 89 | def parse_label(self, index): 90 | if not self.token_is(index, ID): 91 | self.error() 92 | index = self.eat(index + 1, COLON) 93 | return ast.Label(self.tokens[index-2].value), index 94 | 95 | def parse_inst(self, index): 96 | insts = [("push", ast.Push), ("pop", ast.Pop), ("ret", ast.Ret), ("call", ast.Call), ("exit", ast.Exit), 97 | ("calln", ast.Calln), ("mov", ast.Mov), ("add", ast.Add), ("sub", ast.Sub), ("mul", ast.Mul), 98 | ("div", ast.Div), ("mod", ast.Mod), ("cmp", ast.Cmp), ("jze", ast.Jze), ("je", ast.Jze), 99 | ("jnz", ast.Jnz), ("jne", ast.Jnz), ("jl", ast.Jl), ("jle", ast.Jle), ("jg", ast.Jg), ("jge", ast.Jge), 100 | ("ja", ast.Ja), ("jae", ast.Jae), ("jb", ast.Jb), ("jbe", ast.Jbe), ("sze", ast.Sze), ("se", ast.Sze), 101 | ("snz", ast.Snz), ("sne", ast.Snz), ("sl", ast.Sl), ("sle", ast.Sle), ("sg", ast.Sg), ("sge", ast.Sge), 102 | ("sa", ast.Sa), ("sae", ast.Sae), ("sb", ast.Sb), ("sbe", ast.Sbe), ("jmp", ast.Jmp), ("lea", ast.Lea), 103 | ("inc", ast.Inc), ("dec", ast.Dec), ("and", ast.And), ("or", ast.Or), ("xor", ast.Xor), ("neg", ast.Neg), 104 | ("mov6502", ast.Mov6502), ("shl", ast.Shl), ("shr", ast.Shr)] 105 | 106 | def parse(inst, index): 107 | index = self.eat_id(index, inst[0]) 108 | args = [] 109 | for i in range(inst[1].num_values): 110 | if i != 0: 111 | index = self.eat(index, COMMA) 112 | value, index = self.parse_value(index) 113 | args.append(value) 114 | return inst[1](*args), index 115 | 116 | for inst in insts: 117 | try: 118 | return parse(inst, index) 119 | except SyntaxError: 120 | continue 121 | self.error() 122 | 123 | def parse_value(self, index): 124 | values = [self.parse_register_value, self.parse_literal_value, self.parse_mem_value] 125 | 126 | for val in values: 127 | try: 128 | return val(index) 129 | except SyntaxError: 130 | continue 131 | self.error() 132 | 133 | def parse_literal_value(self, index): 134 | index = self.eat(index, HASH) 135 | if not self.token_is(index, INTEGER): 136 | self.error() 137 | num = self.tokens[index].value 138 | return ast.LiteralValue(num), index + 1 139 | 140 | def parse_literal_mem_value(self, index): 141 | if not self.token_is(index, INTEGER): 142 | self.error() 143 | num = self.tokens[index].value 144 | return ast.LiteralValue(num), index + 1 145 | 146 | def parse_label_value(self, index): 147 | if not self.token_is(index, ID): 148 | self.error() 149 | label = self.tokens[index].value 150 | return ast.LabelValue(label), index + 1 151 | 152 | def parse_register_value(self, index): 153 | index = self.eat(index, PERCENT) 154 | if not self.token_is(index, ID): 155 | self.error() 156 | reg = self.tokens[index].value 157 | if reg[0] != "r": 158 | self.error() 159 | reg_num = int(reg[1:]) 160 | return ast.RegisterValue(reg_num), index + 1 161 | 162 | def parse_mem_value(self, index): 163 | length = 4 164 | if self.token_is(index, BYTE): 165 | length = 1 166 | index += 1 167 | elif self.token_is(index, WORD): 168 | length = 2 169 | index += 1 170 | elif self.token_is(index, DWORD): 171 | index += 1 172 | 173 | def get_const_loc(index): 174 | try: 175 | return self.parse_label_value(index) 176 | except SyntaxError: 177 | try: 178 | return self.parse_literal_mem_value(index) 179 | except SyntaxError: 180 | return None, index 181 | 182 | const_loc, index = get_const_loc(index) 183 | reg_indirect = None 184 | if self.token_is(index, LBRACE): 185 | index += 1 186 | seen_const = False 187 | if const_loc is None: 188 | const_loc, index = get_const_loc(index) 189 | seen_const = True 190 | seen_plus = False 191 | try: 192 | if seen_const: 193 | index = self.eat(index, PLUS) 194 | seen_plus = True 195 | except SyntaxError: 196 | pass 197 | try: 198 | reg_indirect, index = self.parse_value(index) 199 | except SyntaxError: 200 | pass 201 | if seen_plus and reg_indirect is None: 202 | self.error() 203 | index = self.eat(index, RBRACE) 204 | if const_loc is None and reg_indirect is None: 205 | self.error() 206 | return ast.MemoryValue(const_loc, reg_indirect, length), index 207 | -------------------------------------------------------------------------------- /bbcvmasm/tokens.py: -------------------------------------------------------------------------------- 1 | ID, EOF, INTEGER, COLON, PERIOD, PERCENT, COMMA, HASH, LBRACE, RBRACE, DWORD, WORD, BYTE, PLUS \ 2 | = "ID", "EOF", "INTEGER", "COLON", "PERIOD", "PERCENT", "COMMA", "HASH", "LBRACE", "RBRACE", "DWORD", "WORD",\ 3 | "BYTE", "PLUS" 4 | 5 | 6 | class Token: 7 | def __init__(self, kind, value, ): 8 | self.type = kind 9 | self.value = value 10 | 11 | def __repr__(self): 12 | return 'Token({type}, {value})'.format( 13 | type=self.type, 14 | value=repr(self.value) 15 | ) 16 | 17 | 18 | RESERVED = { 19 | "BYTE": BYTE, 20 | "WORD": WORD, 21 | "DWORD": DWORD, 22 | } 23 | -------------------------------------------------------------------------------- /bbcvmem/requirements.txt: -------------------------------------------------------------------------------- 1 | wxPython -------------------------------------------------------------------------------- /bbcvmld/__init__.py: -------------------------------------------------------------------------------- 1 | from . import linker 2 | from . import parser 3 | 4 | 5 | def link_object_files_static(obj): 6 | link = linker.Linker(obj) 7 | exec = link.link_static() 8 | return exec 9 | 10 | 11 | def link_object_files_shared(objects, strip=False, static=False): 12 | link = linker.Linker(objects) 13 | out = link.link_shared(strip, static) 14 | return bytes(out) 15 | 16 | -------------------------------------------------------------------------------- /bbcvmld/linker.py: -------------------------------------------------------------------------------- 1 | from . import parser 2 | import glob 3 | import struct 4 | import os 5 | 6 | 7 | class Linker: 8 | def __init__(self, objects): 9 | self.objects = objects 10 | self.defined_symbols = {} 11 | self.imports = {} 12 | self.executables = [] 13 | self.pos = 0 14 | 15 | def _find_lib(self, symbol_name): 16 | lib_dir = os.path.join(os.path.dirname(__file__), "..", "lib") 17 | libs = glob.glob(os.path.join(lib_dir, "*.o")) 18 | for l in libs: 19 | lib = open(l, "rb") 20 | lib_source = lib.read() 21 | lib.close() 22 | parse = parser.Parser(lib_source) 23 | try: 24 | obj = parse.parse() 25 | if symbol_name in obj.exports: 26 | return obj 27 | except SyntaxError: 28 | pass 29 | 30 | def _get_symbol(self, s, static): 31 | if s not in self.defined_symbols and s != "_HIMEM" and static: 32 | lib = self._find_lib(s) 33 | if lib is None: 34 | raise LookupError("Symbol {} not exported anywhere".format(s)) 35 | lib = self._load_exports(lib) 36 | self.executables.append(lib) 37 | 38 | def _load_exports(self, e): 39 | for n, l in e.exports.items(): 40 | self.defined_symbols[n] = l + self.pos 41 | e.pos = self.pos 42 | self.pos += len(e.code) 43 | return e 44 | 45 | def _get_symbols(self, static): 46 | for e in self.executables: 47 | for i in e.imports: 48 | self._get_symbol(i[0], static) 49 | 50 | def _parse_obj(self, obj): 51 | p = parser.Parser(obj) 52 | e = p.parse() 53 | e = self._load_exports(e) 54 | self.executables.append(e) 55 | 56 | def link_static(self): 57 | for o in self.objects: 58 | self._parse_obj(o) 59 | self._get_symbols(static=True) 60 | self._get_symbol("_start", static=True) 61 | 62 | himem = 0 63 | for e in self.executables: 64 | if e.pos + len(e.code) > himem: 65 | himem = e.pos + len(e.code) 66 | 67 | self.defined_symbols["_HIMEM"] = himem 68 | 69 | out = bytearray() 70 | for e in self.executables: 71 | code = bytearray(e.code) 72 | for i in e.imports: 73 | s_addr = self.defined_symbols[i[0]] 74 | pos = i[4] + e.pos 75 | if s_addr < pos: 76 | am = 0x1 77 | mv = pos - s_addr 78 | else: 79 | am = 0x2 80 | mv = s_addr - pos 81 | 82 | lal = i[2] 83 | if i[3] == 0: 84 | code[lal] = (code[lal] & 0x0F) | ((am << 4) & 0xF0) 85 | else: 86 | code[lal] = (code[lal] & 0xF0) | (am & 0x0F) 87 | 88 | code[i[4]:i[4]+4] = struct.pack(" himem: 112 | himem = e.pos + len(e.code) 113 | 114 | self.defined_symbols["_HIMEM"] = himem 115 | 116 | out = bytearray([0xB, 0xB, 0xC, ord('V'), ord('M'), 0x0]) 117 | code_out = bytearray() 118 | exports = [] 119 | imports = [] 120 | 121 | for e in self.executables: 122 | code = bytearray(e.code) 123 | for i, l in e.exports.items(): 124 | pos = l + e.pos 125 | exports.append((i, pos)) 126 | for i in e.imports: 127 | s_addr = self.defined_symbols.get(i[0]) 128 | if s_addr is not None: 129 | pos = i[4] + e.pos 130 | if s_addr < pos: 131 | am = 0x1 132 | mv = pos - s_addr 133 | else: 134 | am = 0x2 135 | mv = s_addr - pos 136 | 137 | lal = i[2] 138 | if i[3] == 0: 139 | code[lal] = (code[lal] & 0x0F) | ((am << 4) & 0xF0) 140 | else: 141 | code[lal] = (code[lal] & 0xF0) | (am & 0x0F) 142 | 143 | code[i[4]:i[4]+2] = struct.pack("" 13 | 14 | 15 | class Parser: 16 | HEADER = bytes([0xB, 0xB, 0xC, ord('V'), ord('M'), 0x0]) 17 | 18 | def __init__(self, obj): 19 | self.obj = obj 20 | 21 | def parse(self): 22 | obj = self.obj 23 | if obj[:len(self.HEADER)] != self.HEADER: 24 | raise SyntaxError("File is not a BBC VM executable") 25 | obj = obj[len(self.HEADER):] 26 | 27 | header_len = struct.unpack("= 'A' && c <= 'Z'; 5 | } 6 | 7 | bool islower(int c) { 8 | return c >= 'a' && c <= 'z'; 9 | } 10 | 11 | bool isalpha(int c) { 12 | return islower(c) || isupper(c); 13 | } 14 | 15 | bool isdigit(int c) { 16 | return c >= '0' && c <= '9'; 17 | } 18 | 19 | bool isalnum(int c) { 20 | return isalpha(c) || isdigit(c); 21 | } 22 | 23 | bool isascii(int c) { 24 | return c <= 127; 25 | } 26 | 27 | bool isblank(int c) { 28 | return (c == '\t') || (c == ' '); 29 | } 30 | 31 | bool iscntrl(int c) { 32 | return c < 32; 33 | } 34 | 35 | bool isspace(int c) { 36 | return c == ' ' || c == '\n' || c == '\t' || c == '\r'; 37 | } 38 | 39 | bool isxdigit(int c) { 40 | return isdigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); 41 | } 42 | 43 | bool toupper(int c) { 44 | return islower(c) ? (c & ~32) : c; 45 | } 46 | 47 | bool tolower(int c) { 48 | return isupper(c) ? (c | 32) : c; 49 | } -------------------------------------------------------------------------------- /lib_src/ctype.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheEnbyperor/bbc-c/be7a2d43d7908218be2b788f7bc51d8e16624a63/lib_src/ctype.o -------------------------------------------------------------------------------- /lib_src/ctype.s: -------------------------------------------------------------------------------- 1 | .export isupper 2 | .export islower 3 | .export isalpha 4 | .export isdigit 5 | .export isalnum 6 | .export isascii 7 | .export isblank 8 | .export iscntrl 9 | .export isspace 10 | .export isxdigit 11 | .export toupper 12 | .export tolower 13 | // Function: isupper 14 | isupper: 15 | push %r12 16 | mov %r14, %r12 17 | push %r1 18 | // Set 19 | mov #0, %r0 20 | // LessThanJmp 21 | mov #65, %r1 22 | cmp 8[%r12], %r1 23 | jl [__bbcc_00000000] 24 | // MoreThanJmp 25 | mov #90, %r1 26 | cmp 8[%r12], %r1 27 | jg [__bbcc_00000000] 28 | // Set 29 | mov #1, %r0 30 | // Label 31 | __bbcc_00000000: 32 | // Return 33 | __bbcc_0000000e: 34 | pop %r1 35 | mov %r12, %r14 36 | pop %r12 37 | ret 38 | // Function: islower 39 | islower: 40 | push %r12 41 | mov %r14, %r12 42 | push %r1 43 | // Set 44 | mov #0, %r0 45 | // LessThanJmp 46 | mov #97, %r1 47 | cmp 8[%r12], %r1 48 | jl [__bbcc_00000001] 49 | // MoreThanJmp 50 | mov #122, %r1 51 | cmp 8[%r12], %r1 52 | jg [__bbcc_00000001] 53 | // Set 54 | mov #1, %r0 55 | // Label 56 | __bbcc_00000001: 57 | // Return 58 | __bbcc_0000000f: 59 | pop %r1 60 | mov %r12, %r14 61 | pop %r12 62 | ret 63 | // Function: isalpha 64 | isalpha: 65 | push %r12 66 | mov %r14, %r12 67 | push %r1 68 | // Set 69 | mov #1, %r1 70 | // CallFunction 71 | mov DWORD 8[%r12], %r0 72 | push %r0 73 | call [islower] 74 | add #4, %r14 75 | // JmpNotZero 76 | cmp #0, %r0 77 | jnz [__bbcc_00000002] 78 | // CallFunction 79 | mov DWORD 8[%r12], %r0 80 | push %r0 81 | call [isupper] 82 | add #4, %r14 83 | // JmpNotZero 84 | cmp #0, %r0 85 | jnz [__bbcc_00000002] 86 | // Set 87 | mov #0, %r1 88 | // Label 89 | __bbcc_00000002: 90 | // Return 91 | mov %r1, %r0 92 | __bbcc_00000010: 93 | pop %r1 94 | mov %r12, %r14 95 | pop %r12 96 | ret 97 | // Function: isdigit 98 | isdigit: 99 | push %r12 100 | mov %r14, %r12 101 | push %r1 102 | // Set 103 | mov #0, %r0 104 | // LessThanJmp 105 | mov #48, %r1 106 | cmp 8[%r12], %r1 107 | jl [__bbcc_00000003] 108 | // MoreThanJmp 109 | mov #57, %r1 110 | cmp 8[%r12], %r1 111 | jg [__bbcc_00000003] 112 | // Set 113 | mov #1, %r0 114 | // Label 115 | __bbcc_00000003: 116 | // Return 117 | __bbcc_00000011: 118 | pop %r1 119 | mov %r12, %r14 120 | pop %r12 121 | ret 122 | // Function: isalnum 123 | isalnum: 124 | push %r12 125 | mov %r14, %r12 126 | push %r1 127 | // Set 128 | mov #1, %r1 129 | // CallFunction 130 | mov DWORD 8[%r12], %r0 131 | push %r0 132 | call [isalpha] 133 | add #4, %r14 134 | // JmpNotZero 135 | cmp #0, %r0 136 | jnz [__bbcc_00000004] 137 | // CallFunction 138 | mov DWORD 8[%r12], %r0 139 | push %r0 140 | call [isdigit] 141 | add #4, %r14 142 | // JmpNotZero 143 | cmp #0, %r0 144 | jnz [__bbcc_00000004] 145 | // Set 146 | mov #0, %r1 147 | // Label 148 | __bbcc_00000004: 149 | // Return 150 | mov %r1, %r0 151 | __bbcc_00000012: 152 | pop %r1 153 | mov %r12, %r14 154 | pop %r12 155 | ret 156 | // Function: isascii 157 | isascii: 158 | push %r12 159 | mov %r14, %r12 160 | push %r1 161 | // LessEqualCmp 162 | mov #127, %r1 163 | cmp 8[%r12], %r1 164 | sle %r0 165 | // Return 166 | __bbcc_00000013: 167 | pop %r1 168 | mov %r12, %r14 169 | pop %r12 170 | ret 171 | // Function: isblank 172 | isblank: 173 | push %r12 174 | mov %r14, %r12 175 | push %r1 176 | // Set 177 | mov #1, %r0 178 | // EqualJmp 179 | mov #9, %r1 180 | cmp 8[%r12], %r1 181 | jze [__bbcc_00000005] 182 | // EqualJmp 183 | mov #32, %r1 184 | cmp 8[%r12], %r1 185 | jze [__bbcc_00000005] 186 | // Set 187 | mov #0, %r0 188 | // Label 189 | __bbcc_00000005: 190 | // Return 191 | __bbcc_00000014: 192 | pop %r1 193 | mov %r12, %r14 194 | pop %r12 195 | ret 196 | // Function: iscntrl 197 | iscntrl: 198 | push %r12 199 | mov %r14, %r12 200 | push %r1 201 | // LessThanCmp 202 | mov #32, %r1 203 | cmp 8[%r12], %r1 204 | sl %r0 205 | // Return 206 | __bbcc_00000015: 207 | pop %r1 208 | mov %r12, %r14 209 | pop %r12 210 | ret 211 | // Function: isspace 212 | isspace: 213 | push %r12 214 | mov %r14, %r12 215 | push %r1 216 | // Set 217 | mov #1, %r0 218 | // EqualJmp 219 | mov #32, %r1 220 | cmp 8[%r12], %r1 221 | jze [__bbcc_00000006] 222 | // EqualJmp 223 | mov #10, %r1 224 | cmp 8[%r12], %r1 225 | jze [__bbcc_00000006] 226 | // EqualJmp 227 | mov #9, %r1 228 | cmp 8[%r12], %r1 229 | jze [__bbcc_00000006] 230 | // EqualJmp 231 | mov #13, %r1 232 | cmp 8[%r12], %r1 233 | jze [__bbcc_00000006] 234 | // Set 235 | mov #0, %r0 236 | // Label 237 | __bbcc_00000006: 238 | // Return 239 | __bbcc_00000016: 240 | pop %r1 241 | mov %r12, %r14 242 | pop %r12 243 | ret 244 | // Function: isxdigit 245 | isxdigit: 246 | push %r12 247 | mov %r14, %r12 248 | push %r1 249 | push %r2 250 | // Set 251 | mov #1, %r1 252 | // CallFunction 253 | mov DWORD 8[%r12], %r0 254 | push %r0 255 | call [isdigit] 256 | add #4, %r14 257 | // JmpNotZero 258 | cmp #0, %r0 259 | jnz [__bbcc_00000007] 260 | // Set 261 | mov #0, %r0 262 | // LessThanJmp 263 | mov #97, %r2 264 | cmp 8[%r12], %r2 265 | jl [__bbcc_00000008] 266 | // MoreThanJmp 267 | mov #102, %r2 268 | cmp 8[%r12], %r2 269 | jg [__bbcc_00000008] 270 | // Set 271 | mov #1, %r0 272 | // Label 273 | __bbcc_00000008: 274 | // JmpNotZero 275 | cmp #0, %r0 276 | jnz [__bbcc_00000007] 277 | // Set 278 | mov #0, %r0 279 | // LessThanJmp 280 | mov #65, %r2 281 | cmp 8[%r12], %r2 282 | jl [__bbcc_00000009] 283 | // MoreThanJmp 284 | mov #70, %r2 285 | cmp 8[%r12], %r2 286 | jg [__bbcc_00000009] 287 | // Set 288 | mov #1, %r0 289 | // Label 290 | __bbcc_00000009: 291 | // JmpNotZero 292 | cmp #0, %r0 293 | jnz [__bbcc_00000007] 294 | // Set 295 | mov #0, %r1 296 | // Label 297 | __bbcc_00000007: 298 | // Return 299 | mov %r1, %r0 300 | __bbcc_00000017: 301 | pop %r2 302 | pop %r1 303 | mov %r12, %r14 304 | pop %r12 305 | ret 306 | // Function: toupper 307 | toupper: 308 | push %r12 309 | mov %r14, %r12 310 | push %r1 311 | // CallFunction 312 | mov DWORD 8[%r12], %r0 313 | push %r0 314 | call [islower] 315 | add #4, %r14 316 | // JmpZero 317 | cmp #0, %r0 318 | jze [__bbcc_0000000a] 319 | // Neg 320 | mov #-32, %r1 321 | // And 322 | mov 8[%r12], %r0 323 | and %r1, %r0 324 | // Set 325 | // Jmp 326 | jmp [__bbcc_0000000b] 327 | // Label 328 | __bbcc_0000000a: 329 | // Set 330 | mov DWORD 8[%r12], %r0 331 | // Label 332 | __bbcc_0000000b: 333 | // Return 334 | __bbcc_00000018: 335 | pop %r1 336 | mov %r12, %r14 337 | pop %r12 338 | ret 339 | // Function: tolower 340 | tolower: 341 | push %r12 342 | mov %r14, %r12 343 | // CallFunction 344 | mov DWORD 8[%r12], %r0 345 | push %r0 346 | call [isupper] 347 | add #4, %r14 348 | // JmpZero 349 | cmp #0, %r0 350 | jze [__bbcc_0000000c] 351 | // IncOr 352 | mov 8[%r12], %r0 353 | or #32, %r0 354 | // Set 355 | // Jmp 356 | jmp [__bbcc_0000000d] 357 | // Label 358 | __bbcc_0000000c: 359 | // Set 360 | mov DWORD 8[%r12], %r0 361 | // Label 362 | __bbcc_0000000d: 363 | // Return 364 | __bbcc_00000019: 365 | mov %r12, %r14 366 | pop %r12 367 | ret -------------------------------------------------------------------------------- /lib_src/start.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheEnbyperor/bbc-c/be7a2d43d7908218be2b788f7bc51d8e16624a63/lib_src/start.o -------------------------------------------------------------------------------- /lib_src/start.s: -------------------------------------------------------------------------------- 1 | .import main 2 | .export _start 3 | 4 | _start: 5 | mov #$40000, %r14 6 | call main 7 | exit -------------------------------------------------------------------------------- /lib_src/stdio.c: -------------------------------------------------------------------------------- 1 | #include "stddef.h" 2 | #include "stdio.h" 3 | #include "stdbool.h" 4 | #include "string.h" 5 | #include "ctype.h" 6 | #include "stdlib.h" 7 | 8 | int fputs(const char *s) { 9 | char c; 10 | 11 | for (; c = *s; ++s) { 12 | putchar(c); 13 | } 14 | return 0; 15 | } 16 | 17 | int puts(const char *s) { 18 | fputs(s); 19 | putchar('\n'); 20 | return 0; 21 | } 22 | 23 | char *gets(char *s, int n) { 24 | char *cs; 25 | char c; 26 | 27 | cs = s; 28 | while(--n > 0) { 29 | c = getchar(); 30 | if (c == 3) { 31 | return NULL; 32 | } 33 | putchar(c); 34 | if (c == 127) { 35 | --cs; 36 | continue; 37 | } 38 | if ((*cs++ = c) == '\n') { 39 | --cs; 40 | break; 41 | } 42 | } 43 | *cs = '\0'; 44 | return s; 45 | } 46 | 47 | 48 | void itoa(int n, char *s, bool unsig, unsigned int zero_pad) { 49 | int i; 50 | bool negative = false; 51 | 52 | if (!unsig && n < 0) { 53 | negative = true; 54 | n = -n; 55 | } 56 | 57 | i = 0; 58 | do { /* generate digits in reverse order */ 59 | s[i++] = n % 10 + '0'; /* get next digit */ 60 | } while ((n /= 10) > 0); /* delete it */ 61 | 62 | 63 | while (i < zero_pad) s[i++] = '0'; 64 | 65 | if (negative) s[i++] = '-'; 66 | 67 | s[i] = '\0'; 68 | strrev(s); 69 | } 70 | 71 | 72 | int printf(const char *format, ...) { 73 | void *ap; 74 | char c; 75 | char bf[24]; 76 | int i; 77 | 78 | ap = &format+1; 79 | 80 | for (; c = *format; ++format) { 81 | if (c == '%') { 82 | int zero_pad = 0; 83 | c = *(++format); 84 | 85 | if (c == '0') { 86 | c = *(++format); 87 | if (c == '\0') 88 | break; 89 | while (isdigit(c)) { 90 | zero_pad *= 10; 91 | zero_pad += (int) (c - '0'); 92 | c = *(++format); 93 | } 94 | } 95 | 96 | if (c == '\0') { 97 | break; 98 | } else if (c == 'c') { 99 | char c = *((char *)ap); 100 | ap = (char*)ap + sizeof(int); 101 | putchar(c); 102 | ++i; 103 | continue; 104 | } else if (c == 's') { 105 | char* s = *((char **)ap); 106 | ap = (char*)ap + sizeof(char *); 107 | fputs(s); 108 | i += strlen(s); 109 | continue; 110 | } else if (c == 'u') { 111 | int i = *((int *)ap); 112 | ap = (char*)ap + sizeof(int); 113 | itoa(i, bf, true, zero_pad); 114 | fputs(bf); 115 | i += strlen(bf); 116 | continue; 117 | } else if (c == 'd') { 118 | int i = *((int *)ap); 119 | ap = (char*)ap + sizeof(int); 120 | itoa(i, bf, false, zero_pad); 121 | fputs(bf); 122 | i += strlen(bf); 123 | continue; 124 | } 125 | putchar(c); 126 | } 127 | putchar(c); 128 | ++i; 129 | } 130 | } -------------------------------------------------------------------------------- /lib_src/stdio.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheEnbyperor/bbc-c/be7a2d43d7908218be2b788f7bc51d8e16624a63/lib_src/stdio.o -------------------------------------------------------------------------------- /lib_src/stdioasm.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheEnbyperor/bbc-c/be7a2d43d7908218be2b788f7bc51d8e16624a63/lib_src/stdioasm.o -------------------------------------------------------------------------------- /lib_src/stdioasm.s: -------------------------------------------------------------------------------- 1 | .export putchar 2 | .export getchar 3 | 4 | putchar: 5 | mov 4[%r14], %r0 6 | calln $FFEE, %r0 7 | cmp #10, %r0 8 | jne _putchar 9 | mov #13, %r0 10 | calln $FFEE, %r0 11 | _putchar: 12 | ret 13 | 14 | getchar: 15 | calln $FFE0, %r0 16 | cmp #27, %r0 17 | jne _getchar_1 18 | mov #$7E, %r1 19 | calln $FFF4, %r1 20 | jmp _getchar_2 21 | _getchar_1: 22 | cmp #13, %r0 23 | jne _getchar_2 24 | mov #10, %r0 25 | _getchar_2: 26 | ret 27 | -------------------------------------------------------------------------------- /lib_src/stdlib.c: -------------------------------------------------------------------------------- 1 | #include "string.h" 2 | #include "stddef.h" 3 | #include "stdbool.h" 4 | #include "ctype.h" 5 | 6 | extern void *_HIMEM; 7 | #define MEM_TOP 262143 8 | 9 | struct s_block_meta { 10 | unsigned int size; 11 | struct s_block_meta *next; 12 | struct s_block_meta *prev; 13 | bool free; 14 | }; 15 | 16 | typedef struct s_block_meta block_meta; 17 | 18 | static void *global_base = NULL; 19 | static void *mem_top = NULL; 20 | 21 | #define META_SIZE sizeof(block_meta) 22 | #define MIN_BLOCK_SIZE 4 23 | 24 | static block_meta *get_block_ptr(void *ptr) { 25 | return (char *) ptr - META_SIZE; 26 | } 27 | 28 | static void *get_data_ptr(block_meta *ptr) { 29 | return (char *) ptr + META_SIZE; 30 | } 31 | 32 | static block_meta *find_free_block(block_meta **last, unsigned int size) { 33 | block_meta *current = global_base; 34 | while (current && !(current->free && current->size >= size)) { 35 | *last = current; 36 | current = current->next; 37 | } 38 | return current; 39 | } 40 | 41 | static block_meta *request_space(block_meta *last, unsigned int size) { 42 | block_meta *block; 43 | 44 | if (!mem_top) 45 | mem_top = &_HIMEM; 46 | block = mem_top; 47 | 48 | if (((char *) mem_top + size + META_SIZE) >= MEM_TOP) 49 | return NULL; 50 | mem_top = (char *) mem_top + size + META_SIZE; 51 | 52 | if (last) 53 | last->next = block; 54 | block->prev = last; 55 | block->size = size; 56 | block->next = NULL; 57 | block->free = 0; 58 | return block; 59 | } 60 | 61 | static void split_block(block_meta *block, unsigned int size) { 62 | block_meta *new_block; 63 | 64 | new_block = (char *) block + META_SIZE + size; 65 | new_block->size = block->size - size - META_SIZE; 66 | new_block->next = block->next; 67 | new_block->prev = block; 68 | new_block->free = 1; 69 | block->size = size; 70 | block->next = new_block; 71 | if (new_block->next) 72 | new_block->next->prev = new_block; 73 | } 74 | 75 | static block_meta *fusion(block_meta *block) { 76 | if (block->next && block->next->free) { 77 | block->size += META_SIZE + block->next->size; 78 | block->next = block->next->next; 79 | if (block->next) 80 | block->next->prev = block; 81 | } 82 | return block; 83 | } 84 | 85 | void *malloc(unsigned int size) { 86 | block_meta *block; 87 | if (size <= 0) return NULL; 88 | 89 | if (!global_base) { 90 | block = request_space(NULL, size); 91 | if (!block) { 92 | return NULL; 93 | } 94 | global_base = block; 95 | } else { 96 | block_meta *last = global_base; 97 | block = find_free_block(&last, size); 98 | if (!block) { // Failed to find free block. 99 | block = request_space(last, size); 100 | if (!block) { 101 | return NULL; 102 | } 103 | } else { 104 | if ((block->size - size) >= (META_SIZE + MIN_BLOCK_SIZE)) 105 | split_block(block, size); 106 | block->free = 0; 107 | } 108 | } 109 | 110 | return ((char *) block) + META_SIZE; 111 | } 112 | 113 | void free(void *ptr) { 114 | if (!ptr) { 115 | return; 116 | } 117 | 118 | block_meta *block_ptr = get_block_ptr(ptr); 119 | block_ptr->free = 1; 120 | 121 | if (block_ptr->prev && block_ptr->prev->free) 122 | block_ptr = fusion(block_ptr->prev); 123 | 124 | if (block_ptr->next) 125 | fusion(block_ptr); 126 | else { 127 | if (block_ptr->prev) 128 | block_ptr->prev->next = NULL; 129 | else 130 | global_base = NULL; 131 | mem_top = block_ptr; 132 | } 133 | } 134 | 135 | static void copy_block(block_meta *old_block, block_meta *new_block) { 136 | char *old_data, *new_data; 137 | old_data = get_data_ptr(old_block); 138 | new_data = get_data_ptr(new_block); 139 | for (unsigned int i = 0; i < old_block->size && i < new_block->size; ++i) 140 | new_data[i] = old_data[i]; 141 | } 142 | 143 | void *realloc(void *p, unsigned int size) { 144 | if (!p) 145 | return malloc(size); 146 | 147 | block_meta *block, *new_block; 148 | block = get_block_ptr(p); 149 | 150 | if (block->size >= size) { 151 | if ((block->size - size) >= (META_SIZE + MIN_BLOCK_SIZE)) 152 | split_block(block, size); 153 | } else { 154 | if (block->next && block->next->free && (block->size + META_SIZE + block->next->size) >= size) { 155 | fusion(block); 156 | if ((block->size - size) >= (META_SIZE + MIN_BLOCK_SIZE)) 157 | split_block(block, size); 158 | } else { 159 | void *newp = malloc(size); 160 | if (!newp) 161 | return NULL; 162 | new_block = get_block_ptr(newp); 163 | copy_block(block, new_block); 164 | free(p); 165 | return newp; 166 | } 167 | } 168 | return p; 169 | } 170 | 171 | int atoi(const char *s) { 172 | char *str = s; 173 | int res = 0; 174 | bool negative = false; 175 | 176 | if (*str == '-') { 177 | negative = true; 178 | ++str; 179 | } 180 | 181 | while (isdigit(*str)) { 182 | res *= 10; 183 | res += (int) (*str - '0'); 184 | ++str; 185 | } 186 | 187 | if (negative) res = -res; 188 | 189 | return res; 190 | } -------------------------------------------------------------------------------- /lib_src/stdlib.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheEnbyperor/bbc-c/be7a2d43d7908218be2b788f7bc51d8e16624a63/lib_src/stdlib.o -------------------------------------------------------------------------------- /lib_src/string.c: -------------------------------------------------------------------------------- 1 | #include "stddef.h" 2 | 3 | int strlen(char s[]) { 4 | int len = 0; 5 | while (s[len]) ++len; 6 | return len; 7 | } 8 | 9 | void strrev(char *s) { 10 | int i, j; 11 | char c; 12 | 13 | for (i = 0, j = strlen(s) - 1; i < j; ++i, --j) { 14 | c = s[i]; 15 | s[i] = s[j]; 16 | s[j] = c; 17 | } 18 | } 19 | 20 | int strcmp(char s1[], char s2[]) { 21 | int i; 22 | for (i = 0; s1[i] == s2[i]; ++i) { 23 | if (s1[i] == '\0') return 0; 24 | } 25 | return s1[i] - s2[i]; 26 | } 27 | 28 | int memcmp(const void *str1, const void *str2, size_t n) { 29 | char *s1, *s2; 30 | s1 = str1; 31 | s2 = str2; 32 | for (size_t i = 0; s1[i] == s2[i]; ++i) { 33 | if ((i + 1) == n) return 0; 34 | } 35 | return s1[i] - s2[i]; 36 | } 37 | 38 | void *memset(char *s, char c, size_t n) { 39 | for (unsigned int i = 0; i < n; ++i) 40 | s[i] = c; 41 | } 42 | 43 | void *memcpy(void *s, const void *ct, size_t n) { 44 | do { 45 | --n; 46 | ((char *) s)[n] = ((char *) ct)[n]; 47 | } while (n > 0); 48 | return s; 49 | } -------------------------------------------------------------------------------- /lib_src/string.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheEnbyperor/bbc-c/be7a2d43d7908218be2b788f7bc51d8e16624a63/lib_src/string.o -------------------------------------------------------------------------------- /lib_src/string.s: -------------------------------------------------------------------------------- 1 | .export strlen 2 | .export strrev 3 | .export strcmp 4 | .export memcmp 5 | .export memset 6 | .export memcpy 7 | // Function: strlen 8 | strlen: 9 | push %r12 10 | mov %r14, %r12 11 | push %r1 12 | // Set 13 | mov #0, %r0 14 | // Label 15 | __bbcc_00000000: 16 | // ReadAt 17 | mov DWORD 8[%r12], %r1 18 | mov DWORD 8[%r12], %r1 19 | add %r0, %r1 20 | mov BYTE [%r1], %r1 21 | // JmpZero 22 | cmp #0, %r1 23 | jze [__bbcc_00000001] 24 | // Inc 25 | inc %r0 26 | // Jmp 27 | jmp [__bbcc_00000000] 28 | // Label 29 | __bbcc_00000001: 30 | // Return 31 | __bbcc_00000012: 32 | pop %r1 33 | mov %r12, %r14 34 | pop %r12 35 | ret 36 | // Function: strrev 37 | strrev: 38 | push %r12 39 | mov %r14, %r12 40 | push %r1 41 | push %r2 42 | push %r3 43 | push %r4 44 | push %r5 45 | // Set 46 | mov #0, %r3 47 | // Set 48 | mov DWORD 8[%r12], %r0 49 | // CallFunction 50 | push %r0 51 | call [strlen] 52 | add #4, %r14 53 | // Sub 54 | sub #1, %r0 55 | // Set 56 | // Label 57 | __bbcc_00000002: 58 | // MoreEqualJmp 59 | cmp %r3, %r0 60 | jge [__bbcc_00000004] 61 | // ReadAt 62 | mov DWORD 8[%r12], %r1 63 | mov DWORD 8[%r12], %r1 64 | add %r3, %r1 65 | mov BYTE [%r1], %r1 66 | // Set 67 | // ReadAt 68 | mov DWORD 8[%r12], %r2 69 | mov DWORD 8[%r12], %r2 70 | add %r0, %r2 71 | mov BYTE [%r2], %r2 72 | // SetAt 73 | mov 8[%r12], %r4 74 | mov %r4, %r5 75 | add %r3, %r5 76 | mov %r2, BYTE [%r5] 77 | // SetAt 78 | mov 8[%r12], %r2 79 | mov %r2, %r4 80 | add %r0, %r4 81 | mov %r1, BYTE [%r4] 82 | // Label 83 | __bbcc_00000003: 84 | // Inc 85 | inc %r3 86 | // Dec 87 | dec %r0 88 | // Jmp 89 | jmp [__bbcc_00000002] 90 | // Label 91 | __bbcc_00000004: 92 | // Return 93 | mov #0, %r0 94 | __bbcc_00000013: 95 | pop %r5 96 | pop %r4 97 | pop %r3 98 | pop %r2 99 | pop %r1 100 | mov %r12, %r14 101 | pop %r12 102 | ret 103 | // Function: strcmp 104 | strcmp: 105 | push %r12 106 | mov %r14, %r12 107 | push %r1 108 | push %r2 109 | // Set 110 | mov #0, %r2 111 | // Label 112 | __bbcc_00000005: 113 | // ReadAt 114 | mov DWORD 8[%r12], %r0 115 | mov DWORD 8[%r12], %r0 116 | add %r2, %r0 117 | mov BYTE [%r0], %r1 118 | // ReadAt 119 | mov DWORD 12[%r12], %r0 120 | mov DWORD 12[%r12], %r0 121 | add %r2, %r0 122 | mov BYTE [%r0], %r0 123 | // NotEqualJmp 124 | cmp %r1, %r0 125 | jnz [__bbcc_00000007] 126 | // ReadAt 127 | mov DWORD 8[%r12], %r0 128 | mov DWORD 8[%r12], %r0 129 | add %r2, %r0 130 | mov BYTE [%r0], %r0 131 | // NotEqualJmp 132 | cmp #0, %r0 133 | jnz [__bbcc_00000008] 134 | // Return 135 | mov #0, %r0 136 | jmp [__bbcc_00000014] 137 | // Label 138 | __bbcc_00000008: 139 | // Label 140 | __bbcc_00000006: 141 | // Inc 142 | inc %r2 143 | // Jmp 144 | jmp [__bbcc_00000005] 145 | // Label 146 | __bbcc_00000007: 147 | // ReadAt 148 | mov DWORD 8[%r12], %r0 149 | mov DWORD 8[%r12], %r0 150 | add %r2, %r0 151 | mov BYTE [%r0], %r0 152 | // ReadAt 153 | mov DWORD 12[%r12], %r1 154 | mov DWORD 12[%r12], %r1 155 | add %r2, %r1 156 | mov BYTE [%r1], %r1 157 | // Sub 158 | sub %r1, %r0 159 | // Return 160 | __bbcc_00000014: 161 | pop %r2 162 | pop %r1 163 | mov %r12, %r14 164 | pop %r12 165 | ret 166 | // Function: memcmp 167 | memcmp: 168 | push %r12 169 | mov %r14, %r12 170 | push %r1 171 | push %r2 172 | push %r3 173 | push %r4 174 | // Set 175 | mov DWORD 8[%r12], %r3 176 | // Set 177 | // Set 178 | mov DWORD 12[%r12], %r2 179 | // Set 180 | // Set 181 | mov #0, %r1 182 | // Set 183 | // Label 184 | __bbcc_00000009: 185 | // ReadAt 186 | mov %r3, %r0 187 | add %r1, %r0 188 | mov BYTE [%r0], %r4 189 | // ReadAt 190 | mov %r2, %r0 191 | add %r1, %r0 192 | mov BYTE [%r0], %r0 193 | // NotEqualJmp 194 | cmp %r4, %r0 195 | jnz [__bbcc_0000000b] 196 | // Set 197 | mov #1, %r4 198 | // Add 199 | mov %r1, %r0 200 | add %r4, %r0 201 | // NotEqualJmp 202 | cmp 16[%r12], %r0 203 | jnz [__bbcc_0000000c] 204 | // Return 205 | mov #0, %r0 206 | jmp [__bbcc_00000015] 207 | // Label 208 | __bbcc_0000000c: 209 | // Label 210 | __bbcc_0000000a: 211 | // Inc 212 | inc %r1 213 | // Jmp 214 | jmp [__bbcc_00000009] 215 | // Label 216 | __bbcc_0000000b: 217 | // ReadAt 218 | mov %r3, %r0 219 | add %r1, %r0 220 | mov BYTE [%r0], %r0 221 | // ReadAt 222 | add %r1, %r2 223 | mov BYTE [%r2], %r1 224 | // Sub 225 | sub %r1, %r0 226 | // Return 227 | __bbcc_00000015: 228 | pop %r4 229 | pop %r3 230 | pop %r2 231 | pop %r1 232 | mov %r12, %r14 233 | pop %r12 234 | ret 235 | // Function: memset 236 | memset: 237 | push %r12 238 | mov %r14, %r12 239 | push %r1 240 | push %r2 241 | // Set 242 | mov #0, %r0 243 | // Set 244 | // Label 245 | __bbcc_0000000d: 246 | // MoreEqualJmp 247 | mov 16[%r12], %r1 248 | cmp %r0, %r1 249 | jae [__bbcc_0000000f] 250 | // SetAt 251 | mov 8[%r12], %r1 252 | mov %r1, %r2 253 | add %r0, %r2 254 | mov 12[%r12], %r1 255 | mov %r1, BYTE [%r2] 256 | // Label 257 | __bbcc_0000000e: 258 | // Inc 259 | inc %r0 260 | // Jmp 261 | jmp [__bbcc_0000000d] 262 | // Label 263 | __bbcc_0000000f: 264 | // Return 265 | mov #0, %r0 266 | __bbcc_00000016: 267 | pop %r2 268 | pop %r1 269 | mov %r12, %r14 270 | pop %r12 271 | ret 272 | // Function: memcpy 273 | memcpy: 274 | push %r12 275 | mov %r14, %r12 276 | push %r1 277 | push %r2 278 | // Label 279 | __bbcc_00000010: 280 | // Dec 281 | dec 16[%r12] 282 | // Set 283 | mov DWORD 12[%r12], %r0 284 | // ReadAt 285 | add DWORD 16[%r12], %r0 286 | mov BYTE [%r0], %r1 287 | // Set 288 | mov DWORD 8[%r12], %r0 289 | // SetAt 290 | mov %r0, %r2 291 | add DWORD 16[%r12], %r2 292 | mov %r1, BYTE [%r2] 293 | // LessEqualJmp 294 | mov #0, %r0 295 | cmp 16[%r12], %r0 296 | jle [__bbcc_00000011] 297 | // Jmp 298 | jmp [__bbcc_00000010] 299 | // Label 300 | __bbcc_00000011: 301 | // Return 302 | mov DWORD 8[%r12], %r0 303 | __bbcc_00000017: 304 | pop %r2 305 | pop %r1 306 | mov %r12, %r14 307 | pop %r12 308 | ret -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include "stdio.h" 2 | 3 | 4 | int main() { 5 | printf("Hello, world!\n"); 6 | } 7 | -------------------------------------------------------------------------------- /main.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheEnbyperor/bbc-c/be7a2d43d7908218be2b788f7bc51d8e16624a63/main.o -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import bbcc 2 | import bbcasm 3 | import bbcvmasm 4 | import bbcld 5 | import bbcvmld 6 | import bbcdisk 7 | import bbctape 8 | import sys 9 | import os 10 | import argparse 11 | import wave 12 | 13 | 14 | def compile_c(text: str, out: str): 15 | asm = bbcc.main(text) 16 | asm_file = open(out, "w") 17 | asm_file.write(asm) 18 | 19 | 20 | def assemble_s(text: str, name: str, arg): 21 | if getattr(arg, "6502", False): 22 | out = bbcasm.asm_to_object(text) 23 | else: 24 | out = bbcvmasm.asm_to_object(text) 25 | obj_file = open(name, "wb") 26 | obj_file.write(out) 27 | 28 | 29 | def link_o(arg, *args, **kwargs): 30 | if arg.static and arg.shared: 31 | link_o_static_shared(arg, *args, **kwargs) 32 | elif arg.static: 33 | link_o_static(arg, *args, **kwargs) 34 | elif arg.shared: 35 | link_o_shared(arg, *args, **kwargs) 36 | 37 | 38 | def link_o_static(arg, objs, name: str): 39 | exa = 0 40 | if getattr(arg, "6502", False): 41 | out, exa = bbcld.link_object_files_static(objs, 0x1900) 42 | else: 43 | out = bbcvmld.link_object_files_static(objs) 44 | 45 | out_file = open(name, "wb") 46 | out_file.write(out) 47 | 48 | if getattr(arg, "6502", False): 49 | make_tape(name, name, 0x1900, exa, out) 50 | make_disk([("$.{}".format(name.upper()), out, 0x1900, exa)], name) 51 | else: 52 | make_disk_with_vm([("$.{}".format(name.upper()), out)], name) 53 | 54 | 55 | def link_o_static_shared(arg, objs, name: str): 56 | if not getattr(arg, "6502", False): 57 | out = bbcvmld.link_object_files_shared(objs, arg.strip, True) 58 | 59 | out_file = open(name, "wb") 60 | out_file.write(out) 61 | 62 | 63 | def link_o_shared(arg, objs, name: str): 64 | if getattr(arg, "6502", False): 65 | out = bbcld.link_object_files_shared(objs, arg.strip) 66 | else: 67 | out = bbcvmld.link_object_files_shared(objs, arg.strip) 68 | 69 | out_file = open(name, "wb") 70 | out_file.write(out) 71 | 72 | 73 | def make_tape(name, file, lda, exa, data): 74 | w = wave.open("{}.wav".format(name), "w") 75 | bbctape.make_file(w, file, lda, exa, 0, data) 76 | 77 | 78 | def make_disk(files, out): 79 | files = list(map(lambda f: bbcdisk.File(f[0], f[1], f[2], f[3]), files)) 80 | disk = bbcdisk.files_to_disk(files) 81 | disk_file = open("{}.ssd".format(out), "wb") 82 | disk_file.write(disk) 83 | 84 | 85 | def make_disk_with_vm(files, out): 86 | files = list(map(lambda f: (f[0], f[1], 0, 0), files)) 87 | vm_dir = os.path.join(os.path.dirname(__file__), "bbcvm") 88 | loader_file = os.path.join(vm_dir, "loader") 89 | vm_file = os.path.join(vm_dir, "vm") 90 | with open(loader_file, 'rb') as l: 91 | loader = bytes(l.read()) 92 | with open(vm_file, 'rb') as v: 93 | vm = bytes(v.read()) 94 | files += (("$.LOADER", loader, 0x1900, 0x1900),) 95 | files += (("$.VM", vm, 0x1900, 0x1900),) 96 | make_disk(files, out) 97 | 98 | 99 | if __name__ == "__main__": 100 | parser = argparse.ArgumentParser(description='Compiler suite for the BBC Microcomputer') 101 | parser.add_argument("-o", "--output", help="Output file name", type=str) 102 | parser.add_argument("-Wa", nargs="*", help="Assembler options", type=str) 103 | parser.add_argument("-Wl", nargs="*", help="Linker options", type=str) 104 | parser.add_argument("-S", help="Compile only", action="store_true") 105 | parser.add_argument("-c", help="Compile and assemble but do not link", action="store_true") 106 | parser.add_argument("-shared", help="Create a shared library", action="store_true") 107 | parser.add_argument("-static", help="Create a statically linked executable", action="store_true") 108 | parser.add_argument("-strip", help="Strip names of internal symbols from executable header", action="store_true") 109 | parser.add_argument("-6502", help="Assemble and link (not compile) for 6502 instead of VM", action="store_true") 110 | parser.add_argument("files", nargs="+", help="Input files", type=str) 111 | 112 | args = parser.parse_args() 113 | 114 | source_files = [] 115 | 116 | for f in args.files: 117 | try: 118 | sourceFile = open(f, "rb") 119 | except FileNotFoundError as e: 120 | print("Unable to load file {}".format(e)) 121 | sys.exit(1) 122 | 123 | source_files.append((os.path.splitext(f), sourceFile.read())) 124 | sourceFile.close() 125 | 126 | first_e = source_files[0][0][1] 127 | for e in map(lambda name: name[0][1], source_files): 128 | if e != first_e: 129 | raise RuntimeError("All input files must be of same type") 130 | 131 | if first_e == ".c": 132 | for n, s in source_files: 133 | if args.output is None or not args.S: 134 | name = "{}.s".format(n[0]) 135 | else: 136 | name = args.output 137 | compile_c(s.decode(), name) 138 | 139 | if not args.S: 140 | sourceFile = open(name, "r") 141 | if args.output is None or not args.c: 142 | name = "{}.o".format(n[0]) 143 | else: 144 | name = args.output 145 | assemble_s(sourceFile.read(), name, args) 146 | sourceFile.close() 147 | 148 | if not args.c and not args.S: 149 | files = [] 150 | for n, s in source_files: 151 | name = "{}.o".format(n[0]) 152 | sourceFile = open(name, "rb") 153 | files.append(sourceFile.read()) 154 | sourceFile.close() 155 | if args.output is None: 156 | name = "out" 157 | else: 158 | name = args.output 159 | 160 | link_o(args, files, name) 161 | elif first_e == ".s": 162 | if args.S: 163 | raise RuntimeError("Cant just compile assembly") 164 | for n, s in source_files: 165 | if args.output is None or not args.c: 166 | name = "{}.o".format(n[0]) 167 | else: 168 | name = args.output 169 | assemble_s(s.decode(), name, args) 170 | 171 | if not args.c and not args.S: 172 | files = [] 173 | for n, s in source_files: 174 | name = "{}.o".format(n[0]) 175 | sourceFile = open(name, "rb") 176 | files.append(sourceFile.read()) 177 | sourceFile.close() 178 | if args.output is None: 179 | name = "out" 180 | else: 181 | name = args.output 182 | 183 | link_o(args, files, name) 184 | elif first_e == ".o": 185 | if args.S or args.c: 186 | raise RuntimeError("Cant just compile or assemble object file") 187 | 188 | if args.output is None: 189 | name = "out" 190 | else: 191 | name = args.output 192 | 193 | link_o(args, map(lambda s: s[1], source_files), name) 194 | -------------------------------------------------------------------------------- /main.s: -------------------------------------------------------------------------------- 1 | .import memcpy 2 | .export main 3 | __bbcc_00000000: 4 | .byte #104,#101,#108,#108,#111,#0 5 | // Function: main 6 | main: 7 | push %r12 8 | mov %r14, %r12 9 | sub #20, %r14 10 | push %r1 11 | push %r2 12 | // AddrOf 13 | lea DWORD -20[%r12], %r1 14 | // Set 15 | // AddrOf 16 | lea DWORD [__bbcc_00000000], %r0 17 | // Set 18 | // Set 19 | mov #5, %r2 20 | // CallFunction 21 | push %r2 22 | push %r0 23 | push %r1 24 | call [memcpy] 25 | add #12, %r14 26 | // Return 27 | mov #0, %r0 28 | __bbcc_00000001: 29 | pop %r2 30 | pop %r1 31 | mov %r12, %r14 32 | pop %r12 33 | ret -------------------------------------------------------------------------------- /memory-expansion/memory-expansion.kicad_pcb: -------------------------------------------------------------------------------- 1 | (kicad_pcb (version 4) (host kicad "dummy file") ) 2 | -------------------------------------------------------------------------------- /memory-expansion/memory-expansion.pro: -------------------------------------------------------------------------------- 1 | update=Thu 23 Aug 2018 18:53:29 BST 2 | version=1 3 | last_client=kicad 4 | [general] 5 | version=1 6 | RootSch= 7 | BoardNm= 8 | [pcbnew] 9 | version=1 10 | LastNetListRead= 11 | UseCmpFile=1 12 | PadDrill=0.600000000000 13 | PadDrillOvalY=0.600000000000 14 | PadSizeH=1.500000000000 15 | PadSizeV=1.500000000000 16 | PcbTextSizeV=1.500000000000 17 | PcbTextSizeH=1.500000000000 18 | PcbTextThickness=0.300000000000 19 | ModuleTextSizeV=1.000000000000 20 | ModuleTextSizeH=1.000000000000 21 | ModuleTextSizeThickness=0.150000000000 22 | SolderMaskClearance=0.000000000000 23 | SolderMaskMinWidth=0.000000000000 24 | DrawSegmentWidth=0.200000000000 25 | BoardOutlineThickness=0.100000000000 26 | ModuleOutlineThickness=0.150000000000 27 | [cvpcb] 28 | version=1 29 | NetIExt=net 30 | [eeschema] 31 | version=1 32 | LibDir= 33 | [eeschema/libraries] 34 | [schematic_editor] 35 | version=1 36 | PageLayoutDescrFile= 37 | PlotDirectoryName= 38 | SubpartIdSeparator=0 39 | SubpartFirstId=65 40 | NetFmtName=Pcbnew 41 | SpiceAjustPassiveValues=0 42 | LabSize=50 43 | ERC_TestSimilarLabels=1 44 | -------------------------------------------------------------------------------- /memory-expansion/sym-lib-table: -------------------------------------------------------------------------------- 1 | (sym_lib_table 2 | ) 3 | -------------------------------------------------------------------------------- /out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheEnbyperor/bbc-c/be7a2d43d7908218be2b788f7bc51d8e16624a63/out -------------------------------------------------------------------------------- /out.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheEnbyperor/bbc-c/be7a2d43d7908218be2b788f7bc51d8e16624a63/out.o -------------------------------------------------------------------------------- /out.ssd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheEnbyperor/bbc-c/be7a2d43d7908218be2b788f7bc51d8e16624a63/out.ssd -------------------------------------------------------------------------------- /out.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheEnbyperor/bbc-c/be7a2d43d7908218be2b788f7bc51d8e16624a63/out.wav -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | numpy 2 | pycrc --------------------------------------------------------------------------------