├── LICENSE ├── Microsoft.VC90.CRT └── Microsoft.VC90.CRT.manifest ├── README.markdown ├── app ├── __init__.py ├── assembler.py ├── benchmark.py ├── editor.py ├── emulator.py ├── icons.py ├── preprocessor.py └── view.py ├── build_emulator ├── build_emulator.bat ├── build_emulator_linux ├── build_icons ├── build_icons.bat ├── build_installer.bat ├── clean.bat ├── emulator ├── clock.c ├── clock.h ├── common.h ├── emulator.c ├── emulator.h ├── keyboard.c ├── keyboard.h ├── lem.c └── lem.h ├── icons ├── basket_put.png ├── control_end.png ├── control_play.png ├── control_stop.png ├── disk.png ├── folder_page.png ├── icon.ico ├── icon16.png ├── icon24.png ├── icon256.png ├── icon32.png ├── icon48.png └── page.png ├── installer.iss ├── main.py ├── programs ├── atlas.dasm ├── cube.dasm ├── enumerate_hardware.dasm ├── example.dasm ├── keyboard.dasm ├── life.dasm ├── matrix.dasm ├── mem.dmp ├── minecraft.dasm ├── minesweeper.dasm ├── nyan.dasm ├── preprocessor.dasm ├── pretty.dasm ├── test.dasm └── tetris.dasm ├── screenshots ├── debug.png ├── editor.png ├── no_debug.png ├── nyan.gif └── syntax.png ├── setup.py └── util ├── font.png ├── font.py └── icons.py /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2013 Michael Fogleman 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /Microsoft.VC90.CRT/Microsoft.VC90.CRT.manifest: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | KSaO8M0iCtPF6YEr79P1dZsnomY= ojDmTgpYMFRKJYkPcM6ckpYkWUU= tVogb8kezDre2mXShlIqpp8ErIg= 6 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | ## DCPU-16 Emulator v1.7 2 | 3 | ### Download (Windows Installer) 4 | 5 | See the downloads page: 6 | 7 | https://github.com/fogleman/DCPU-16/downloads 8 | 9 | ### Implemented Specifications 10 | 11 | - DCPU-16 1.7: http://pastebin.com/raw.php?i=Q4JvQvnM 12 | - LEM1802: http://dcpu.com/highnerd/rc_1/lem1802.txt 13 | - Generic Keyboard: http://dcpu.com/highnerd/rc_1/keyboard.txt 14 | - Generic Clock: http://dcpu.com/highnerd/rc_1/clock.txt 15 | 16 | ### Screenshots 17 | 18 | ![](https://raw.github.com/fogleman/DCPU-16/master/screenshots/debug.png) 19 | 20 | ![](https://raw.github.com/fogleman/DCPU-16/master/screenshots/syntax.png) 21 | 22 | ![](https://raw.github.com/fogleman/DCPU-16/master/screenshots/nyan.gif) 23 | 24 | ### Assembler Features 25 | 26 | ```dasm 27 | ; Macros (can be nested) 28 | #macro BRK { NOP } ; ignore breakpoints 29 | 30 | #macro push(x) { 31 | SET PUSH x 32 | } 33 | 34 | ; String Literals 35 | DAT "Here is some text.", 0 ; null-terminated string 36 | 37 | ; Reserve block of memory 38 | RESERVE 0xff 39 | 40 | ; Character Literals 41 | SET A 'a' ; A = 0x61 42 | SET B 'A' ; B = 0x41 43 | 44 | ; Negative Numbers 45 | SET A -1 ; A = 0xffff 46 | SET B -0xff ; B = 0xff01 47 | 48 | ; Breakpoints 49 | BRK 50 | 51 | ; Opcode Aliases 52 | SET A POP 53 | SET A [SP++] ; equivalent to POP 54 | 55 | SET PUSH A 56 | SET [--SP] A ; equivalent to PUSH 57 | 58 | SET A PICK 1 59 | SET A [SP + 1] ; equivalent to PICK 1 60 | 61 | SET A PEEK 62 | SET A [SP] ; equivalent to PEEK 63 | 64 | ; Local Labels 65 | :prefix 66 | SET A 10 67 | :.loop ; becomes prefix.loop 68 | SUB A 1 69 | IFE A 0 70 | SET PC .done 71 | SET PC .loop 72 | :.done ; becomes prefix.done 73 | 74 | ; Fixed Labels 75 | :screen @ 0x8000 76 | ``` 77 | 78 | ### Hardware Enumeration 79 | 80 | The following code enumerates available hardware devices and populates the variables lem, keyboard and clock with their respective hardware identifiers. 81 | 82 | ```dasm 83 | #macro match_hardware(a, b, location) { 84 | IFE A a 85 | IFE B b 86 | SET [location] I 87 | } 88 | 89 | JSR enumerate_hardware 90 | 91 | ; now you can use the devices, e.g. 92 | SET A 0 93 | SET B 0x8000 94 | HWI [lem] 95 | 96 | BRK 97 | 98 | :enumerate_hardware 99 | HWN I 100 | :.loop 101 | IFE I 0 102 | SET PC .done 103 | SUB I 1 104 | HWQ I 105 | match_hardware(0xf615, 0x7349, lem) 106 | match_hardware(0x7406, 0x30cf, keyboard) 107 | match_hardware(0xb402, 0x12d0, clock) 108 | SET PC .loop 109 | :.done 110 | SET PC POP 111 | 112 | :lem 113 | DAT -1 114 | :keyboard 115 | DAT -1 116 | :clock 117 | DAT -1 118 | ``` 119 | 120 | ### Benchmarks 121 | 122 | Usage: python benchmark.py 123 | 124 | Run benchmarks on the emulator to test performance. 125 | 126 | MacBook Air (1.7 GHz Intel Core i5) 127 | 128 | Benchmarking "C" emulator using "programs/life.dasm"... 129 | Result: 88728989 cycles per second 130 | 131 | The C implementation could emulate over 800 DCPU-16 processors at their 100 kHz clock rate. 132 | 133 | ### Pretty Print 134 | 135 | Usage: python assembler.py programs/example.dasm > pretty_output.dasm 136 | 137 | Assemble and output in pretty format. Shows machine code in comments. Comments from the input file are not retained. 138 | 139 | ```dasm 140 | SET A, 48 ; 7c01 0030 141 | SET [0x1000], 32 ; 7de1 1000 0020 142 | SUB A, [0x1000] ; 7803 1000 143 | IFN A, 16 ; c00d 144 | SET PC, crash ; 7dc1 001a 145 | SET I, 10 ; a861 146 | SET A, 0x2000 ; 7c01 2000 147 | :loop 148 | SET [I + 0x2000], [A] ; 2161 2000 149 | SUB I, 1 ; 8463 150 | IFN I, 0 ; 806d 151 | SET PC, loop ; 7dc1 000d 152 | SET X, 4 ; 9031 153 | JSR testsub ; 7c10 0018 154 | SET PC, crash ; 7dc1 001a 155 | :testsub 156 | SHL X, 4 ; 9037 157 | SET PC, POP ; 61c1 158 | :crash 159 | SET PC, crash ; 7dc1 001a 160 | ``` 161 | 162 | ### Dependencies 163 | - **Python 2.5+**: http://www.python.org/ 164 | - **PLY**: http://www.dabeaz.com/ply/ 165 | - **wxPython**: http://www.wxpython.org/ 166 | -------------------------------------------------------------------------------- /app/__init__.py: -------------------------------------------------------------------------------- 1 | from emulator import ( 2 | Emulator, 3 | ) 4 | 5 | from view import ( 6 | Frame, 7 | ) 8 | -------------------------------------------------------------------------------- /app/assembler.py: -------------------------------------------------------------------------------- 1 | import ply.lex as lex 2 | import ply.yacc as yacc 3 | 4 | # Constants 5 | SIZE = 0x10000 6 | 7 | # Lookups 8 | BASIC_OPCODES = { 9 | 'SET': 0x01, 10 | 'ADD': 0x02, 11 | 'SUB': 0x03, 12 | 'MUL': 0x04, 13 | 'MLI': 0x05, 14 | 'DIV': 0x06, 15 | 'DVI': 0x07, 16 | 'MOD': 0x08, 17 | 'MDI': 0x09, 18 | 'AND': 0x0a, 19 | 'BOR': 0x0b, 20 | 'XOR': 0x0c, 21 | 'SHR': 0x0d, 22 | 'ASR': 0x0e, 23 | 'SHL': 0x0f, 24 | 'IFB': 0x10, 25 | 'IFC': 0x11, 26 | 'IFE': 0x12, 27 | 'IFN': 0x13, 28 | 'IFG': 0x14, 29 | 'IFA': 0x15, 30 | 'IFL': 0x16, 31 | 'IFU': 0x17, 32 | 'ADX': 0x1a, 33 | 'SUX': 0x1b, 34 | 'STI': 0x1e, 35 | 'STD': 0x1f, 36 | } 37 | 38 | SPECIAL_OPCODES = { 39 | 'JSR': 0x01, 40 | 'INT': 0x08, 41 | 'IAG': 0x09, 42 | 'IAS': 0x0a, 43 | 'RFI': 0x0b, 44 | 'IAQ': 0x0c, 45 | 'HWN': 0x10, 46 | 'HWQ': 0x11, 47 | 'HWI': 0x12, 48 | } 49 | 50 | COMMAND_OPCODES = { 51 | 'NOP': 0x0000, 52 | 'BRK': 0x0040, 53 | 'RFI': 0x0160, 54 | } 55 | 56 | REGISTERS = { 57 | 'A': 0x0, 58 | 'B': 0x1, 59 | 'C': 0x2, 60 | 'X': 0x3, 61 | 'Y': 0x4, 62 | 'Z': 0x5, 63 | 'I': 0x6, 64 | 'J': 0x7, 65 | } 66 | 67 | DST_CODES = { 68 | 'PUSH': 0x18, 69 | 'PEEK': 0x19, 70 | 'SP': 0x1b, 71 | 'PC': 0x1c, 72 | 'EX': 0x1d, 73 | } 74 | 75 | SRC_CODES = { 76 | 'POP': 0x18, 77 | 'PEEK': 0x19, 78 | 'SP': 0x1b, 79 | 'PC': 0x1c, 80 | 'EX': 0x1d, 81 | } 82 | 83 | # Reverse Lookups 84 | REV_BASIC_OPCODES = dict((v, k) for k, v in BASIC_OPCODES.items()) 85 | REV_SPECIAL_OPCODES = dict((v, k) for k, v in SPECIAL_OPCODES.items()) 86 | REV_COMMAND_OPCODES = dict((v, k) for k, v in COMMAND_OPCODES.items()) 87 | REV_REGISTERS = dict((v, k) for k, v in REGISTERS.items()) 88 | REV_DST_CODES = dict((v, k) for k, v in DST_CODES.items()) 89 | REV_SRC_CODES = dict((v, k) for k, v in SRC_CODES.items()) 90 | 91 | # Helper Functions 92 | def pretty_value(x): 93 | return '%d' % x if x <= 0xff else '0x%04x' % x 94 | 95 | def do_lookup(lookup, word): 96 | if isinstance(word, basestring): 97 | try: 98 | word = lookup[word] 99 | except KeyError: 100 | raise Exception('Undefined symbol: "%s"' % word) 101 | return word 102 | 103 | # Classes 104 | class Program(object): 105 | def __init__(self, instructions): 106 | self.instructions = instructions 107 | self.text = None 108 | self.lookup = {} 109 | self.size = 0 110 | for instruction in instructions: 111 | if instruction.offset is None: 112 | instruction.offset = self.size 113 | self.size += instruction.size 114 | if isinstance(instruction, Label): 115 | self.lookup[instruction.name] = instruction.offset 116 | def assemble(self): 117 | result = [] 118 | for instruction in self.instructions: 119 | result.extend(instruction.assemble(self.lookup)) 120 | return result 121 | def pretty(self): 122 | lines = [] 123 | skip = False 124 | for instruction in self.instructions: 125 | line = instruction.pretty().strip() 126 | if isinstance(instruction, Label): 127 | pad = 0 128 | else: 129 | pad = 4 if skip else 2 130 | line = '%s%s' % (' ' * pad, line) 131 | data = instruction.assemble(self.lookup) 132 | if data and not isinstance(instruction, Data): 133 | pad = ' ' * (32 - len(line)) 134 | data = ' '.join('%04x' % x for x in data) 135 | line = '%s%s; %s' % (line, pad, data) 136 | lines.append(line) 137 | skip = instruction.conditional 138 | return '\n'.join(lines) 139 | 140 | class Data(object): 141 | def __init__(self, data): 142 | self.data = data 143 | self.size = len(data) 144 | self.offset = None 145 | self.conditional = False 146 | def assemble(self, lookup): 147 | return [do_lookup(lookup, word) for word in self.data] 148 | def pretty(self): 149 | data = ', '.join('"%s"' % x if isinstance(x, str) else pretty_value(x) 150 | for x in self.data) 151 | return 'DAT %s' % data 152 | 153 | class Reserve(object): 154 | def __init__(self, size): 155 | self.size = size 156 | self.offset = None 157 | self.conditional = False 158 | def assemble(self, lookup): 159 | return [0] * self.size 160 | def pretty(self): 161 | return 'RESERVE %s' % pretty_value(self.size) 162 | 163 | class Label(object): 164 | def __init__(self, name, offset=None): 165 | self.name = name 166 | self.size = 0 167 | self.offset = offset 168 | self.conditional = False 169 | def assemble(self, lookup): 170 | return [] 171 | def pretty(self): 172 | return ':%s' % self.name 173 | 174 | class BasicInstruction(object): 175 | def __init__(self, op, dst, src): 176 | self.op = op 177 | self.dst = dst 178 | self.src = src 179 | value = self.op 180 | value |= (self.dst.value & 0x1f) << 5 181 | value |= (self.src.value & 0x3f) << 10 182 | self.value = value 183 | self.size = 1 + dst.size + src.size 184 | self.offset = None 185 | self.conditional = 0x10 <= self.op <= 0x17 186 | def assemble(self, lookup): 187 | result = [self.value] 188 | result.extend(self.src.assemble(lookup)) 189 | result.extend(self.dst.assemble(lookup)) 190 | return result 191 | def pretty(self): 192 | op = REV_BASIC_OPCODES[self.op] 193 | dst = self.dst.pretty() 194 | src = self.src.pretty() 195 | return '%s %s, %s' % (op, dst, src) 196 | 197 | class SpecialInstruction(object): 198 | def __init__(self, op, src): 199 | self.op = op 200 | self.src = src 201 | value = 0 202 | value |= (self.op & 0x1f) << 5 203 | value |= (self.src.value & 0x3f) << 10 204 | self.value = value 205 | self.size = 1 + src.size 206 | self.offset = None 207 | self.conditional = False 208 | def assemble(self, lookup): 209 | result = [self.value] 210 | result.extend(self.src.assemble(lookup)) 211 | return result 212 | def pretty(self): 213 | op = REV_SPECIAL_OPCODES[self.op] 214 | src = self.src.pretty() 215 | return '%s %s' % (op, src) 216 | 217 | class CommandInstruction(object): 218 | def __init__(self, value): 219 | self.value = value 220 | self.size = 1 221 | self.offset = None 222 | self.conditional = False 223 | def assemble(self, lookup): 224 | result = [self.value] 225 | return result 226 | def pretty(self): 227 | return REV_COMMAND_OPCODES[self.value] 228 | 229 | class Operand(object): 230 | def __init__(self, codes, value, word=None): 231 | self.codes = codes 232 | self.value = value 233 | self.word = word 234 | self.size = int(word is not None) 235 | def assemble(self, lookup): 236 | return [] if self.word is None else [do_lookup(lookup, self.word)] 237 | def pretty(self): 238 | x = self.value 239 | word = self.word 240 | if isinstance(word, int): 241 | word = pretty_value(word) 242 | if x in REV_REGISTERS: 243 | return REV_REGISTERS[x] 244 | elif x - 0x08 in REV_REGISTERS: 245 | return '[%s]' % REV_REGISTERS[x - 0x08] 246 | elif x - 0x10 in REV_REGISTERS: 247 | return '[%s + %s]' % (REV_REGISTERS[x - 0x10], word) 248 | elif x in self.codes: 249 | return self.codes[x] 250 | elif x == 0x1a: 251 | return 'PICK %s' % word 252 | elif x == 0x1e: 253 | return '[%s]' % word 254 | elif x == 0x1f: 255 | return '%s' % word 256 | elif x == 0x20: 257 | return pretty_value(0xffff) 258 | elif x >= 0x21: 259 | return pretty_value(x - 0x21) 260 | 261 | class DstOperand(Operand): 262 | def __init__(self, *args): 263 | super(DstOperand, self).__init__(REV_DST_CODES, *args) 264 | 265 | class SrcOperand(Operand): 266 | def __init__(self, *args): 267 | super(SrcOperand, self).__init__(REV_SRC_CODES, *args) 268 | 269 | # Lexer Rules 270 | reserved = set( 271 | BASIC_OPCODES.keys() + 272 | SPECIAL_OPCODES.keys() + 273 | COMMAND_OPCODES.keys() + 274 | REGISTERS.keys() + 275 | DST_CODES.keys() + 276 | SRC_CODES.keys() + 277 | ['PICK', 'DAT', 'RESERVE'] 278 | ) 279 | 280 | tokens = [ 281 | 'LBRACK', 282 | 'RBRACK', 283 | 'PLUS', 284 | 'LABEL', 285 | 'ID', 286 | 'DECIMAL', 287 | 'HEX', 288 | 'OCT', 289 | 'STRING', 290 | 'CHAR', 291 | 'INC', 292 | 'DEC', 293 | 'AT' 294 | ] + list(reserved) 295 | 296 | t_ignore = ' \t\r,' 297 | t_ignore_COMMENT = r';.*' 298 | 299 | t_INC = r'\+\+' 300 | t_DEC = r'\-\-' 301 | t_LBRACK = r'\[' 302 | t_RBRACK = r'\]' 303 | t_PLUS = r'\+' 304 | t_AT = r'\@' 305 | 306 | def t_newline(t): 307 | r'\n+' 308 | t.lexer.lineno += len(t.value) 309 | 310 | def t_STRING(t): 311 | r'"[^"]*"' 312 | t.value = tuple(ord(x) for x in t.value[1:-1]) 313 | return t 314 | 315 | def t_CHAR(t): 316 | r"'[^']'" 317 | t.value = ord(t.value[1]) 318 | return t 319 | 320 | def t_HEX(t): 321 | r'\-?0x[a-fA-F0-9]+' 322 | t.value = int(t.value, 16) % SIZE 323 | return t 324 | 325 | def t_OCT(t): 326 | r'\-?0\d+' 327 | t.value = int(t.value, 8) % SIZE 328 | return t 329 | 330 | def t_DECIMAL(t): 331 | r'\-?\d+' 332 | t.value = int(t.value) % SIZE 333 | return t 334 | 335 | def t_LABEL(t): 336 | r':\.?[a-zA-Z_][a-zA-Z_0-9]*' 337 | t.value = t.value[1:] 338 | if t.value[0] == '.': 339 | t.value = '%s%s' % (t.lexer.label_prefix, t.value) 340 | else: 341 | t.lexer.label_prefix = t.value 342 | return t 343 | 344 | def t_ID(t): 345 | r'\.?[a-zA-Z_][a-zA-Z_0-9]*' 346 | upper = t.value.upper() 347 | if upper in reserved: 348 | t.type = upper 349 | t.value = upper 350 | else: 351 | t.type = 'ID' 352 | if t.value[0] == '.': 353 | t.value = '%s%s' % (t.lexer.label_prefix, t.value) 354 | return t 355 | 356 | def t_error(t): 357 | raise Exception('Unrecognized token on line %d: %s' % (t.lineno, t.value)) 358 | 359 | # Parser Rules 360 | def p_program(t): 361 | 'program : instructions' 362 | t[0] = Program(t[1]) 363 | 364 | def p_instructions1(t): 365 | 'instructions : instruction instructions' 366 | t[0] = (t[1],) + t[2] 367 | 368 | def p_instructions2(t): 369 | 'instructions : instruction' 370 | t[0] = (t[1],) 371 | 372 | def p_data1(t): 373 | 'data : literal data' 374 | arg = t[1] if isinstance(t[1], tuple) else (t[1],) 375 | t[0] = arg + t[2] 376 | 377 | def p_data2(t): 378 | 'data : literal' 379 | arg = t[1] if isinstance(t[1], tuple) else (t[1],) 380 | t[0] = arg 381 | 382 | def p_instruction_data(t): 383 | 'instruction : DAT data' 384 | t[0] = Data(t[2]) 385 | 386 | def p_instruction_reserve(t): 387 | 'instruction : RESERVE literal' 388 | t[0] = Reserve(t[2]) 389 | 390 | def p_instruction_label1(t): 391 | 'instruction : LABEL' 392 | t[0] = Label(t[1]) 393 | 394 | def p_instruction_label2(t): 395 | 'instruction : LABEL AT literal' 396 | t[0] = Label(t[1], t[3]) 397 | 398 | def p_instruction_basic(t): 399 | 'instruction : basic_opcode dst_operand src_operand' 400 | t[0] = BasicInstruction(t[1], t[2], t[3]) 401 | 402 | def p_instruction_special(t): 403 | 'instruction : special_opcode src_operand' 404 | t[0] = SpecialInstruction(t[1], t[2]) 405 | 406 | def p_instruction_command(t): 407 | 'instruction : command_opcode' 408 | t[0] = CommandInstruction(t[1]) 409 | 410 | def p_dst_operand_register(t): 411 | 'dst_operand : register' 412 | t[0] = DstOperand(REGISTERS[t[1]]) 413 | 414 | def p_dst_operand_register_dereference(t): 415 | 'dst_operand : LBRACK register RBRACK' 416 | t[0] = DstOperand(REGISTERS[t[2]] + 0x08) 417 | 418 | def p_dst_operand_register_literal_dereference1(t): 419 | 'dst_operand : LBRACK register PLUS literal RBRACK' 420 | t[0] = DstOperand(REGISTERS[t[2]] + 0x10, t[4]) 421 | 422 | def p_dst_operand_register_literal_dereference2(t): 423 | 'dst_operand : LBRACK literal PLUS register RBRACK' 424 | t[0] = DstOperand(REGISTERS[t[4]] + 0x10, t[2]) 425 | 426 | def p_dst_operand_pick1(t): 427 | 'dst_operand : LBRACK SP PLUS literal RBRACK' 428 | t[0] = DstOperand(0x1a, t[4]) 429 | 430 | def p_dst_operand_pick2(t): 431 | 'dst_operand : LBRACK literal PLUS SP RBRACK' 432 | t[0] = DstOperand(0x1a, t[2]) 433 | 434 | def p_dst_operand_pick3(t): 435 | 'dst_operand : PICK literal' 436 | t[0] = DstOperand(0x1a, t[2]) 437 | 438 | def p_dst_operand_code(t): 439 | 'dst_operand : dst_code' 440 | t[0] = DstOperand(DST_CODES[t[1]]) 441 | 442 | def p_dst_operand_push(t): 443 | 'dst_operand : LBRACK DEC SP RBRACK' 444 | t[0] = DstOperand(0x18) 445 | 446 | def p_dst_operand_peek(t): 447 | 'dst_operand : LBRACK SP RBRACK' 448 | t[0] = DstOperand(0x19) 449 | 450 | def p_dst_operand_literal_dereference(t): 451 | 'dst_operand : LBRACK literal RBRACK' 452 | t[0] = DstOperand(0x1e, t[2]) 453 | 454 | def p_dst_operand_literal(t): 455 | 'dst_operand : literal' 456 | t[0] = DstOperand(0x1f, t[1]) 457 | 458 | def p_src_operand_register(t): 459 | 'src_operand : register' 460 | t[0] = SrcOperand(REGISTERS[t[1]]) 461 | 462 | def p_src_operand_register_dereference(t): 463 | 'src_operand : LBRACK register RBRACK' 464 | t[0] = SrcOperand(REGISTERS[t[2]] + 0x08) 465 | 466 | def p_src_operand_register_literal_dereference1(t): 467 | 'src_operand : LBRACK register PLUS literal RBRACK' 468 | t[0] = SrcOperand(REGISTERS[t[2]] + 0x10, t[4]) 469 | 470 | def p_src_operand_register_literal_dereference2(t): 471 | 'src_operand : LBRACK literal PLUS register RBRACK' 472 | t[0] = SrcOperand(REGISTERS[t[4]] + 0x10, t[2]) 473 | 474 | def p_src_operand_pick1(t): 475 | 'src_operand : LBRACK SP PLUS literal RBRACK' 476 | t[0] = SrcOperand(0x1a, t[4]) 477 | 478 | def p_src_operand_pick2(t): 479 | 'src_operand : LBRACK literal PLUS SP RBRACK' 480 | t[0] = SrcOperand(0x1a, t[2]) 481 | 482 | def p_src_operand_pick3(t): 483 | 'src_operand : PICK literal' 484 | t[0] = SrcOperand(0x1a, t[2]) 485 | 486 | def p_src_operand_code(t): 487 | 'src_operand : src_code' 488 | t[0] = SrcOperand(SRC_CODES[t[1]]) 489 | 490 | def p_src_operand_pop(t): 491 | 'src_operand : LBRACK SP INC RBRACK' 492 | t[0] = SrcOperand(0x18) 493 | 494 | def p_src_operand_peek(t): 495 | 'src_operand : LBRACK SP RBRACK' 496 | t[0] = SrcOperand(0x19) 497 | 498 | def p_src_operand_literal_dereference(t): 499 | 'src_operand : LBRACK literal RBRACK' 500 | t[0] = SrcOperand(0x1e, t[2]) 501 | 502 | def p_src_operand_literal(t): 503 | 'src_operand : literal' 504 | if t[1] == 0xffff: 505 | t[0] = SrcOperand(0x20) 506 | elif t[1] <= 0x1e: 507 | t[0] = SrcOperand(0x21 + t[1]) 508 | else: 509 | t[0] = SrcOperand(0x1f, t[1]) 510 | 511 | def p_literal(t): 512 | '''literal : DECIMAL 513 | | HEX 514 | | OCT 515 | | ID 516 | | STRING 517 | | CHAR''' 518 | t[0] = t[1] 519 | 520 | def p_basic_opcode(t): 521 | t[0] = BASIC_OPCODES[t[1]] 522 | p_basic_opcode.__doc__ = ('basic_opcode : %s' % 523 | '\n | '.join(sorted(BASIC_OPCODES))) 524 | 525 | def p_special_opcode(t): 526 | t[0] = SPECIAL_OPCODES[t[1]] 527 | p_special_opcode.__doc__ = ('special_opcode : %s' % 528 | '\n | '.join(sorted(SPECIAL_OPCODES))) 529 | 530 | def p_command_opcode(t): 531 | t[0] = COMMAND_OPCODES[t[1]] 532 | p_command_opcode.__doc__ = ('command_opcode : %s' % 533 | '\n | '.join(sorted(COMMAND_OPCODES))) 534 | 535 | def p_register(t): 536 | t[0] = t[1] 537 | p_register.__doc__ = ('register : %s' % 538 | '\n | '.join(sorted(REGISTERS))) 539 | 540 | def p_dst_code(t): 541 | t[0] = t[1] 542 | p_dst_code.__doc__ = ('dst_code : %s' % 543 | '\n | '.join(sorted(DST_CODES))) 544 | 545 | def p_src_code(t): 546 | t[0] = t[1] 547 | p_src_code.__doc__ = ('src_code : %s' % 548 | '\n | '.join(sorted(SRC_CODES))) 549 | 550 | def p_error(t): 551 | raise Exception('Invalid token on line %d: %s' % (t.lineno, t.value)) 552 | 553 | # Assembler Functions 554 | def create_lexer(): 555 | lexer = lex.lex() 556 | lexer.label_prefix = None 557 | return lexer 558 | 559 | def create_parser(): 560 | parser = yacc.yacc(debug=False, write_tables=False) 561 | return parser 562 | 563 | LEXER = create_lexer() 564 | PARSER = create_parser() 565 | 566 | def parse(text): 567 | LEXER.lineno = 1 568 | program = PARSER.parse(text, lexer=LEXER) 569 | program.text = text 570 | return program 571 | 572 | def parse_file(path): 573 | with open(path) as fp: 574 | text = fp.read() 575 | return parse(text) 576 | 577 | def assemble(text): 578 | program = parse(text) 579 | return program.assemble() 580 | 581 | def assemble_file(path): 582 | with open(path) as fp: 583 | text = fp.read() 584 | return assemble(text) 585 | 586 | def pretty(text): 587 | program = parse(text) 588 | return program.pretty() 589 | 590 | def pretty_file(path): 591 | with open(path) as fp: 592 | text = fp.read() 593 | return pretty(text) 594 | 595 | # Disassembler Functions 596 | def disassemble(words): 597 | def next_word(): 598 | return words.pop() if words else 0 599 | instructions = [] 600 | use_next_word = set(range(0x10, 0x18) + [0x1a, 0x1e, 0x1f]) 601 | words = list(reversed(words)) 602 | while words: 603 | word = next_word() 604 | op = word & 0x1f 605 | dst = (word >> 5) & 0x1f 606 | src = (word >> 10) & 0x3f 607 | if op != 0 and op in REV_BASIC_OPCODES: 608 | dst = DstOperand(dst, next_word() 609 | if dst in use_next_word else None) 610 | src = SrcOperand(src, next_word() 611 | if src in use_next_word else None) 612 | instruction = BasicInstruction(op, dst, src) 613 | instructions.append(instruction) 614 | elif op == 0 and dst in REV_SPECIAL_OPCODES: 615 | src = SrcOperand(src, next_word() 616 | if src in use_next_word else None) 617 | instruction = SpecialInstruction(dst, src) 618 | instructions.append(instruction) 619 | else: 620 | instruction = Data([word]) 621 | instructions.append(instruction) 622 | program = Program(instructions) 623 | program.text = program.pretty() 624 | return program 625 | 626 | def disassemble_file(path): 627 | with open(path, 'rb') as fp: 628 | data = fp.read() 629 | words = [(ord(a) << 8) | ord(b) for a, b in zip(data[::2], data[1::2])] 630 | return disassemble(words) 631 | -------------------------------------------------------------------------------- /app/benchmark.py: -------------------------------------------------------------------------------- 1 | import assembler 2 | import emulator 3 | import time 4 | 5 | def benchmark(name, module, program): 6 | print 'Benchmarking "%s" emulator using "%s"...' % (name, program) 7 | emu = module.Emulator() 8 | emu.load(assembler.assemble_file(program)) 9 | duration = 10 10 | cycles = 0 11 | batch = 100000 12 | start = time.time() 13 | while True: 14 | emu.n_cycles(batch) 15 | cycles += batch 16 | elapsed = time.time() - start 17 | if elapsed > duration: 18 | break 19 | cycles_per_second = int(cycles / elapsed) 20 | print 'Result: %d cycles per second' % cycles_per_second 21 | print 22 | 23 | if __name__ == '__main__': 24 | program = 'programs/life.dasm' 25 | benchmark('C', emulator, program) 26 | -------------------------------------------------------------------------------- /app/editor.py: -------------------------------------------------------------------------------- 1 | import assembler 2 | import sys 3 | import wx 4 | import wx.stc as stc 5 | 6 | FONT = 'Courier New' 7 | SIZE = 14 if sys.platform == 'darwin' else 10 8 | 9 | class Style(object): 10 | instances = [] 11 | def __init__(self, color, bold=False): 12 | Style.instances.append(self) 13 | self.number = len(Style.instances) - 1 14 | self.color = color 15 | self.bold = bold 16 | 17 | COMMENT = Style((0, 64, 0)) 18 | OPCODE = Style((0, 64, 128), True) 19 | OPERAND = Style((128, 0, 64)) 20 | LITERAL = Style((255, 0, 0)) 21 | STRING = Style((128, 128, 128)) 22 | SYMBOL = Style((0, 0, 0), True) 23 | LABEL = Style((0, 0, 0)) 24 | ID = Style((0, 0, 0)) 25 | UNKNOWN = Style((255, 0, 110)) 26 | 27 | class Editor(stc.StyledTextCtrl): 28 | def __init__(self, parent): 29 | super(Editor, self).__init__(parent) 30 | self.styles = self.build_styles() 31 | self.StyleSetFontAttr(stc.STC_STYLE_DEFAULT, SIZE, FONT, 0, 0, 0) 32 | self.StyleClearAll() 33 | for style in Style.instances: 34 | self.StyleSetForeground(style.number, wx.Colour(*style.color)) 35 | self.StyleSetBold(style.number, style.bold) 36 | self.SetLexer(stc.STC_LEX_CONTAINER) 37 | self.SetMarginType(0, stc.STC_MARGIN_NUMBER) 38 | self.SetMarginWidth(1, 0) 39 | self.SetTabIndents(True) 40 | self.SetTabWidth(4) 41 | self.SetUseTabs(False) 42 | self.SetBackSpaceUnIndents(True) 43 | self.Bind(stc.EVT_STC_STYLENEEDED, self.on_style_needed) 44 | self.Bind(stc.EVT_STC_UPDATEUI, self.on_update_ui) 45 | self.Bind(stc.EVT_STC_CHARADDED, self.on_charadded) 46 | def build_styles(self): 47 | result = {} 48 | for name in assembler.BASIC_OPCODES: 49 | result[name] = OPCODE 50 | for name in assembler.SPECIAL_OPCODES: 51 | result[name] = OPCODE 52 | for name in assembler.COMMAND_OPCODES: 53 | result[name] = OPCODE 54 | for name in ['DAT', 'RESERVE']: 55 | result[name] = OPCODE 56 | for name in assembler.REGISTERS: 57 | result[name] = OPERAND 58 | for name in assembler.SRC_CODES: 59 | result[name] = OPERAND 60 | for name in assembler.DST_CODES: 61 | result[name] = OPERAND 62 | for name in ['PICK']: 63 | result[name] = OPERAND 64 | for name in ['DECIMAL', 'HEX', 'OCT']: 65 | result[name] = LITERAL 66 | for name in ['STRING', 'CHAR']: 67 | result[name] = STRING 68 | for name in ['INC', 'DEC', 'LBRACK', 'RBRACK', 'PLUS', 'AT']: 69 | result[name] = SYMBOL 70 | for name in ['LABEL']: 71 | result[name] = LABEL 72 | for name in ['ID']: 73 | result[name] = ID 74 | return result 75 | def stylize(self, line): 76 | position = 0 77 | text = self.GetLine(line) 78 | self.StartStyling(self.PositionFromLine(line), 0x1f) 79 | lexer = assembler.LEXER 80 | lexer.input(text) 81 | while True: 82 | try: 83 | token = lexer.token() 84 | except Exception: 85 | break 86 | if token is None: 87 | break 88 | style = self.styles.get(token.type, UNKNOWN) 89 | start = token.lexpos 90 | end = lexer.lexpos 91 | length = start - position 92 | self.SetStyling(length, 0) 93 | position += length 94 | length = end - position 95 | self.SetStyling(length, style.number) 96 | position += length 97 | def on_style_needed(self, event): 98 | start = self.GetFirstVisibleLine() 99 | end = self.LineFromPosition(event.GetPosition()) 100 | for line in xrange(start, end + 1): 101 | self.stylize(line) 102 | def on_charadded(self, event): 103 | code = event.GetKey() 104 | if code == ord('\n'): 105 | line = self.GetCurrentLine() - 1 106 | if line >= 0: 107 | text = self.GetLine(line) 108 | for index, ch in enumerate(text): 109 | if ch not in ' \t': 110 | self.ReplaceSelection(text[:index]) 111 | break 112 | def on_update_ui(self, event): 113 | wx.CallAfter(self.update_line_numbers) 114 | def update_line_numbers(self): 115 | text = ' %d' % self.GetLineCount() 116 | width = self.TextWidth(stc.STC_STYLE_LINENUMBER, text) 117 | self.SetMarginWidth(0, width) 118 | -------------------------------------------------------------------------------- /app/emulator.py: -------------------------------------------------------------------------------- 1 | from ctypes import * 2 | import os 3 | 4 | dll = CDLL(os.path.realpath(os.path.join( 5 | os.path.dirname(__file__), '..', '_emulator'))) 6 | 7 | class cEmulator(Structure): 8 | _fields_ = [ 9 | ('ram', c_ushort * 0x10010), 10 | ('skip', c_ushort), 11 | ('halt', c_ushort), 12 | ('cycle', c_ulonglong), 13 | ('interrupt_buffer', c_ushort * 256), 14 | ('interrupt_index', c_ushort), 15 | ('interrupt_queueing', c_ushort), 16 | ('lem_screen', c_ushort), 17 | ('lem_font', c_ushort), 18 | ('lem_palette', c_ushort), 19 | ('lem_border', c_ushort), 20 | ('keyboard_buffer', c_ubyte * 16), 21 | ('keyboard_pressed', c_ubyte * 256), 22 | ('keyboard_index', c_ushort), 23 | ('keyboard_message', c_ushort), 24 | ('clock_cycle', c_ulonglong), 25 | ('clock_rate', c_ushort), 26 | ('clock_ticks', c_ushort), 27 | ('clock_message', c_ushort), 28 | ] 29 | 30 | FIELDS = set(x[0] for x in cEmulator._fields_) 31 | 32 | class Emulator(object): 33 | def __init__(self): 34 | self.emulator = cEmulator() 35 | self.reset() 36 | def __getattr__(self, name): 37 | if name in FIELDS: 38 | return getattr(self.emulator, name) 39 | return super(Emulator, self).__getattr__(name) 40 | def __setattr__(self, name, value): 41 | if name in FIELDS: 42 | return setattr(self.emulator, name, value) 43 | return super(Emulator, self).__setattr__(name, value) 44 | def reset(self): 45 | dll.reset(byref(self.emulator)) 46 | def load(self, program): 47 | self.reset() 48 | length = len(program) 49 | data = (c_ushort * length)() 50 | for index, value in enumerate(program): 51 | data[index] = value 52 | dll.load(byref(self.emulator), data, length) 53 | def load_raw(self, data): 54 | words = [(ord(a) << 8) | ord(b) for a, b in zip(data[::2], data[1::2])] 55 | self.load(words) 56 | def step(self): 57 | dll.one_step(byref(self.emulator)) 58 | def n_steps(self, steps): 59 | dll.n_steps(byref(self.emulator), steps) 60 | def n_cycles(self, cycles): 61 | dll.n_cycles(byref(self.emulator), cycles) 62 | def on_key_down(self, key): 63 | dll.on_key_down(byref(self.emulator), key) 64 | def on_key_up(self, key): 65 | dll.on_key_up(byref(self.emulator), key) 66 | def on_char(self, key): 67 | dll.on_char(byref(self.emulator), key) 68 | -------------------------------------------------------------------------------- /app/icons.py: -------------------------------------------------------------------------------- 1 | # Automatically generated file! 2 | from wx.lib.embeddedimage import PyEmbeddedImage 3 | 4 | basket_put = PyEmbeddedImage( 5 | "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAAB" 6 | "l0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAJvSURBVDjLhZNdSJNhFMfP" 7 | "M9eW+3BjbGIKuik5toZCgYRIF/axEUbSRQUjiqigi7roIiiCrrwIwusKbybtJuquZLSBWc" 8 | "SaFU0slC0/arbC6PVjn+/Htv7PKKlwduDH/32ec55zzvPxskqlQpvZtUf7K2JJpJXCGuXk" 9 | "fPnBubm6zeJUVMMYUxSv+zwNdl2iYkmqFVY7AalLamIqqjAiheSacep/J25HBlazcoFaTd" 10 | "3V/OUyI4fFTbuH6zOZvCgmb5StWyao2yaZ3OZeajLtoeVMitYKAvXZj2FPOsPT2Ynif7ew" 11 | "8PlW92xunOaFCbLom8lqbKP48nNKrRupd/t968CVu11bJhi0F6+rl4ZoMveEZn6EaEZ4Rq" 12 | "9TyfXG7Cmysjz128sX/jrs6enpO4qi7AMky7IGtHs8HvZwbFwM2c5oVYKTLrcM08t4onCk" 13 | "f299JBLJS5L0SRRFgn5XYaFis9lcSObCxFWNRsOi0eirjh1m7YnVkZOnDTcpkUgcb7Pq6s" 14 | "Ph8Iher9cVi8Uhp9PpQpJ2FovFOhhjH3O5XAsSCMAN0nA28krAge9FaDP0G7QJrGi12rQg" 15 | "CGOqnp6euWw2u44OviDgMBxvUeGgTqebgvoMBsM7qBf6AX6fyWTiesDhcPAtvGD8KYdgGJ" 16 | "yFg3fQCf0KbYXO8w7AAh8DPm/DOQlgEbs/Wr0FVLj3RwdTvEJDQ8MbXtFsNvOOfBaLJY7x" 17 | "Ieh7JPEiQRpMst8/0+joqAyHEUF2frrQTmgS7AQJBDvAEmhC5V3gYiAQ6Nt4iVigBgUE06" 18 | "8r2gCLNuDXjUOnUqn0uPoOav3OW5nf79dwDQaD0k8mKaZoCMdoNAAAAABJRU5ErkJggg==" 19 | ) 20 | 21 | control_end = PyEmbeddedImage( 22 | "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAAB" 23 | "l0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAH/SURBVDjLpZPLiupAEIbn" 24 | "ofJOeQCjoqusJjoLEQVF0I0bRVEXrgSRFnMUQcRbe1m48H6NGi8oKtbpasbIcIY5HE6gSO" 25 | "j0/9VfVd1vAPD2P/HHQq/XE7rdrtzpdEi73dYopVqz2SSNRkOu1WrCjwAmFpmYrlYrOJ1O" 26 | "cL/feRyPR5jP51CtVmmlUhG/BXyK9cvlAvjge7/fg67rcD6f+RpCy+WyXiqVxC+AT9v0KV" 27 | "bVX7DZbEDTNB7r9RrQ1RNCCKG5XE4wAKxe+blhu92C1WqFQqFgiM1mCzidH9wNPv1+H9Lp" 28 | "tGwAWLMIktEqiiwWKwsL5PN5WC6XDGDmgN1ux/uB7uLxODEArVZLw2bhBgRgRgy73Q6JRA" 29 | "IkyQwOh5O7WywWcL1eIRKJaAaAjYkDcAOWgmKbzcYzJ5NJMJkkUBQH/4/TQBfhcPgFqNfr" 30 | "BDuOZWAGFGNWdDSdThnAxAFoHfcNBgMIhUKvEth85fF4DI/Hg2eQJImLJ5MJD0VR4P1d4e" 31 | "XdbjcoFosQCAReTWRzFVRVpSh6TgKFCB2NRsY4UTwcDlFMWQhfDlI2mxUzmYyOY0Mnh8OB" 32 | "u5nNZnx8KEbrTKh7vV7x26OcSqXEWCxGWUm8duwJ1oxzZ4cHmJC63W7xx8sUjUaFYDAo+/" 33 | "1+4vP5NI/HozERcblcMvsW/nob/zV+A0hzMNxKeHjMAAAAAElFTkSuQmCC" 34 | ) 35 | 36 | control_play = PyEmbeddedImage( 37 | "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAAB" 38 | "l0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHiSURBVDjLpZPLquJAEIbP" 39 | "Q+Wd8gAZnTirPIGIgiLoxo2iCxeuBJGgoggq3trLwoX3a9R4QVGxpv/mJCJzOMMwDUVCur" 40 | "+/qv7qfBDRx//EHx/6/b7U6/W0brerdzodgzFmtFotvdlsavV6XfpWgMMyh9l6vabz+UyP" 41 | "x0PE6XSixWJBtVqNVSoV+UuBT9i8Xq+EhefhcCDTNOlyuYhvEC2Xy2apVJLfBD7LZha82+" 42 | "1ou91SPp8nwzBos9kQqrJEdF1n2WxWsgV4v5p1wIIBOp0/KZfLCXi5XIrAGgwGlEqlNFuA" 43 | "m6VDGaUCtLI6HE4RPKOA4QP8QIJEIqHbAu1224BZ+/1ewMi4Wq047BDhcv2iarVKs9lMCN" 44 | "1uN4pGo4YtwMckBFC+BeMgYFV1kaL8EHvT6dSuIhKJvAQajYYOx9GG1SsOqqr6Bk8mEzGZ" 45 | "4XBI4XD41QKfr4bN5/MpwPl8LspVFEXA2BuPxzQajeh+v1OxWKRgMPgykc9VKhQKDB5gIR" 46 | "sCsAUiKxLgncOMh/R2kTKZjJxOp024j4PH49GuBpcJmSHCQdPn88lfXuVkMinH43HGWxIt" 47 | "wBP0jLljlBxkHo9H/vZnisViUigU0gKBgO73+w2v12twSHe73Rp/l/76N/5r/AZGRj/URb" 48 | "dFDAAAAABJRU5ErkJggg==" 49 | ) 50 | 51 | control_stop = PyEmbeddedImage( 52 | "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAABGdBTUEAAK/INwWK6QAAAB" 53 | "l0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAElSURBVCjPfZFNSwJBHMb9" 54 | "UPOd5pR9hUnvIQrFQl26FHXo0CmImEMoQUhl48vBw5qWtY461pKU9PTsrJRsFM9l2N/v/7" 55 | "IzOeT+z/ehI9qqpZvW2Ia+VdciI3Rk20SIsWBeMUTdXMkVgdjNAcwxg8MbTzEuXU0uBTY3" 56 | "CZ5gDMuMEHlFm3PhhZaKMviJAbo4UV5o6phtE7jO5FEkHnKTMY60F+7sAlPiiDDvhUc8UH" 57 | "rHvvVCg8KE+NnjNRQwYJIee6lwo2dcKZlbJCxgg7jP/wmxm46oqz4+WZE0Hnh4jx4+UEWQ" 58 | "LlkTF2bKrX9gyIIeAhOI5UWdyVM34scX38exOkTgKnLlqo/loalzRMzZXWhUTElmHutA7K" 59 | "htvWXLtqQ3VVn8es2/8gUo3nl2LXz6SAAAAABJRU5ErkJggg==" 60 | ) 61 | 62 | disk = PyEmbeddedImage( 63 | "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAAB" 64 | "l0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAH+SURBVBgZBcE9i11VGAbQ" 65 | "tc/sO0OCkqhghEREAwpWAWUg8aMVf4KFaJEqQtAipTZWViKiCGOh2Ap2gmJhlSIWFsFOxU" 66 | "K0EsUM3pl79n4f12qHb3z3Fh7D83gC95GOJsDe0ixLk5Qq/+xv/Lw9Xd+78/HLX3Y8fXTr" 67 | "2nWapy4eCFKxG7Fby97SnDlYtMbxthyfzHO//nl85fNvfvnk8MbX5xa8IHx1518Vkrj54Q" 68 | "+qQms2vVmWZjdiu5ZR2rT01166/NCZg/2PFjwSVMU6yjoC1oq+x6Y3VbHdlXWExPd379nf" 69 | "7Nmejv2Os6OC2O4KLK0RNn3RNCdr2Z5GJSpU4o+/TkhaJ30mEk5HwNuvX7Hpi76wzvjvtI" 70 | "wqVUSkyjqmpHS0mki8+9mPWmuWxqYvGkbFGCUAOH/+QevYI9GFSqmaHr5wkUYTAlGhqiRR" 71 | "iaqiNes6SOkwJwnQEqBRRRJEgkRLJGVdm6R0GLMQENE0EkmkSkQSVVMqopyuIaUTs0J455" 72 | "VLAAAAAODW0U/GiKT0pTWziEj44PZ1AAAAcPPqkTmH3QiJrlEVDXDt0qsAAAAAapa5BqUn" 73 | "yaw0Am7//gUAAAB49tEXzTmtM5KkV/y2G/X4M5fPao03n/sUAAAAwIX7y5yBv9vhjW/fT/" 74 | "IkuSp5gJKElKRISYoUiSRIyD1tufs/IXxui20QsKIAAAAASUVORK5CYII=" 75 | ) 76 | 77 | folder_page = PyEmbeddedImage( 78 | "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAAB" 79 | "l0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAJCSURBVBgZBcFBi1VlGADg" 80 | "5/3Od+/cYWjUTYlRS43Zi1BGuGlVizZB0EJaFf2JNpHgPt1kBf2EXFlEZFFCUJsIsWmhI0" 81 | "7iqOPM3HvPPed7e57ITAAAcO3mw1wOg2Fo4PbOo6NoGfuL4d7du4tv+r29yz9dfXsemQkA" 82 | "AK78cD8/vHDKw4Mm0DKtxqZ2fP3bE7/f2vn2wb2d9yoAAMA4psdH6c7DVEpaDc3+fPDG6X" 83 | "Xnzxy3MS1vXf/u4LMCAACQ6IJZZdqFaRdm0+K/J3NnTnDx3DEb07WPCwAAAEQw6ahB7cKs" 84 | "Ftt74eb20tN5mtSi3r5+9o/Z5tZWRAFASp8KoSsFiNRastaJErquk6iR5ZWXzn85iQgSkg" 85 | "hu3NdACE0XTGsRmVoLESGTasiF1q8tH1wx9h1lU8Rzfrz1souvv6gWShQt6YLSMGW9kpmq" 86 | "VZRsvbGfypYOt3/29O8/XTrO7hcEEoEOHWZoH/xCC1XkrA1z+9t3rPZ2tNXCibPvq1sf2d" 87 | "zoZBZAyqQU/vn8nOVwIFqJalXU9eedvHAJjUypOXrwlf4ZKWQWhBTq5mtgWja1HPpqlZnj" 88 | "Qr97DQloDudFP7BcsRpGi34wX/aOv/BYxbuf/Lp7bGOyXi1ltoFAJhptZXNtxXQpxwXtUB" 89 | "v35fDU7NSb/sWNy6+ehKrPDCOZ5Ej2si1pC5lzOR7J8UAO+3J8hgYAavatDkePtGFCFrKT" 90 | "OaGtybZBrmT2RE8ZjIsFAKi5WP61ffWd0xIBAAAASMT3tLwN8D9pITwp1Smo1gAAAABJRU" 91 | "5ErkJggg==" 92 | ) 93 | 94 | icon16 = PyEmbeddedImage( 95 | "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG" 96 | "9iZSBJbWFnZVJlYWR5ccllPAAAAkpJREFUeNqEU89PE1EQ/vZHWzEqavSAHpCIB7mCB9CL" 97 | "BxNvlhpFlBIjYI0nTeDm0aMGTTzYBYoRf0SNqPXiH0Ak0vWiCfWAF8GTpmX9sbupu/t8M+" 98 | "22tTTxNc3svDffN9/MmwchBMKVz5xgJz/7H1uJo6UQgaIoGEhefKgr3llVoV0FZCiq0ULG" 99 | "B/LDE/qjJ3PGOSYw0wlxY2EnkoOnkF9ehqqpKFP8u4T8BX6Ag11dmHv8DONHCtDp4NClFz" 100 | "0DQxdM2/6NbPYVIpHIBjCppGSe56F9XzvTEU6lw9zdfpNEOrbDYAoO//UEdBaNRuE4DhdF" 101 | "uKqCM8kR03YdzmJMGbAsC59XVrC/s5PBuq5jYnwCqqrCdl1qE+OYIJdOmDcXSIENTdMwOj" 102 | "KKIAgYSADaIwJS4Ps+XBnHCiSurCA13zM4PGbaUhqBZqYNFNctBi8t5fA6m4UmCfgmpMIw" 103 | "jnBVBZNvAVdKo8Cx1GWZyePaI7LmTbGYvBmNfWqaywSoKbj9/GMCbUfnqTkkd2bqDla/ru" 104 | "FwXx/eSQWZTAaFQrHaVEcmohJIAc/Be+OkmFzche1bN2Nt9YvMHkipAcsN66dyiIB607Zn" 105 | "L9Z/2rja+706id1D51NmVFfwwyo2nYEwOxFsa92Bkifw4F66puDW4m74noM/pVJlcCvgJt" 106 | "NIfdH0Flzp/VZTED89fL11S8txegvyAnne6VtTxIY3ISuE9ct58/Lp/Wuoe40dn2bj5HR/" 107 | "mO4XyWMH4mTJD/cbbEdZUt1zpsM6G2vwm1n8FWAANyAm/Bgvx4sAAAAASUVORK5CYII=" 108 | ) 109 | 110 | icon32 = PyEmbeddedImage( 111 | "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG" 112 | "9iZSBJbWFnZVJlYWR5ccllPAAABS1JREFUeNq0l/tvFFUUx78zs48+dqt9ASWEEKuRQoBE" 113 | "2pQo+hvKL22gSET7gBS1or9YYkL8A4i/iCiBWAg0tAspRmxV4C9oi2tsTNqGSlIfTTF9WG" 114 | "yxj+3uzsyO59yZ2U7b2RplvM3Zubsz95wz53POub3A6hH4vqXG4CtL9PNDnswdOpcNn4sD" 115 | "ubn+lLjyRyigezJ36Ew6jUlOZ+qPNnbSD1XsqixJ4m7K4Ln5wH+eGzw3YE1vRdpaa2iqrY" 116 | "xAtpZCVUPtEQwODkBRFFogwYth0J+u69ixYyfar9+oYlskc7YDgZ4LryaCio6PuwE1mcS3" 117 | "39xCIOCHJHnkAL19Mqli67NbkSISP7QcnE3oCva+dzPIDvhpUkrX4tdqG6OapsHv95EDAU" 118 | "8dYGHdPCre6dpDlym2zQ6oJDP8hSOuUahsw146wLpYt0X1kWVTFTlACKYFgh76RVUhy7IQ" 119 | "XsSLH9cZWx/r5kEI7rsjqDsuENjGUgSMk4eHSMp/6QQ/74ymiUDKjIAf1dhL8hbWgpPN72" 120 | "NsbAxt7RHhBP8urWArjLBwtMR9s4S5lP1+K5lJJ+u21rojONNr5oDMfrJyikBxcTHi8Tgu" 121 | "nD+P+fk5knmhcHx8XJRVKJSLhYUYenp6cPjwIbr2ivt5eXkoKMjHqVMfCidYp50DGREcqT" 122 | "8eVZMmAok89vl8GBoawuXLV9DQUEfOrENOKCcdUpm6zOQfkxjoH8BLL+7F8C8/I5QXwiI5" 123 | "xIb6+/vTucTCuteuAlqlaqowLkIpK2iLXIOfSrLjiy9FiA2rffKVI8TPcahvftVpgxcIOO" 124 | "qMzGchYJ2s2yoDFwS+FD5hBJyEtpGUjg9ONmN4eBhdXV8vSypYrZXezzSk+CArsmsSGtaz" 125 | "ZoITgos19xOavBrB6w1vRlVNdywEioqKsGnTRpSVlRHvkMgDdm/DhnXi/tTUQ2RlZeH06Y" 126 | "/S4c5UEaxbIGjqTCOQHQgesWLdgUDxKZQDP+HM2XOkXMHExARKS5+GQTa6u+8iHAojFouh" 127 | "s6sLEr+9ZK5zE9apuyCQ0nsBITh7txDPPLUZP/b1ibfhPsDNg6+2OMuPB2c4t23GkCkC/C" 128 | "z3k+fKyzH86yian/8TrgjeOPpWVFM1SqolRfX19eimEsvNzcGunbsQiURQWVmJ6gPVGOwf" 129 | "REdHh1DORkS52W+9Ys83JO4D2j8hAIVJNzdxzgH+cXYW27Ztx4MHv6OisgJB4l19oAp3bt" 130 | "9BPtX5p+c+w8uv7EfHjQ5irCFlVYJTDEYqS6bujI1IIJBgt2K7fz+ZF0ZhYQHywjlIEO+N" 131 | "JSWIx+Io370bJSXrcamlBe+eOEG9oF9wdsNgRmCpFTurQLL+TVrPCGqPvR0tyg9jdGREKB" 132 | "LsyOsUlaNhbywUypSRWrZJ8ZxDzzUv2rXLYEybt2zBw5k5XL96yUYwuaIRca3q4k2EsAF2" 133 | "xLy19DZWH4DVcOz9YK3NivVoayHIYgTfESdVW7WLeTFYj66aCPoIQdwNQd2xpmiQ9sS/Zq" 134 | "YFAi8Hl/AT+QVIqAauXb2YAYHEVaClG5GXw2xEFAFJcW9E2YwgWgxDS9CulfAs9M5m5A8E" 135 | "IfmCaN4zhUU3BA2NTdHE4oK5y/0Pg6MQzM5Fe6s7AiUem+ulDecFw5Ae/zDiMpckgzcz2n" 136 | "MxbSNwOhgm2U6y717rQa6x/Sz3rng0N3Xus2yE3c6GiySjfHabTwq3f+OPuaTsydzSOUIy" 137 | "YdladTa0R74lM47vXs5nMh1O08dzqyvafLyeLzsd/y3AAJvT5K77heP7AAAAAElFTkSuQm" 138 | "CC" 139 | ) 140 | 141 | page = PyEmbeddedImage( 142 | "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAAB" 143 | "l0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAINSURBVBgZBcG/r55zGAfg" 144 | "6/4+z3va01NHlYgzEfE7MdCIGISFgS4Gk8ViYyM2Mdlsko4GSf8Do0FLRCIkghhYJA3aVB" 145 | "tEz3nP89wf11VJvPDepdd390+8Nso5nESBQoq0pfvXm9fzWf19453LF85vASqJlz748vIn" 146 | "b517dIw6EyYBIIG49u+xi9/c9MdvR//99MPPZ7+4cP4IZhhTPbwzT2d+vGoaVRRp1rRliV" 147 | "vHq+cfvM3TD82+7mun0o/ceO7NT+/4/KOXjwZU1ekk0840bAZzMQ2mooqh0A72d5x/6sB9" 148 | "D5zYnff3PoYBoWBgFKPKqDKqjCpjKr//dcu9p489dra88cydps30KswACfNEKanSaxhlnt" 149 | "jJ8Mv12Paie+vZ+0+oeSwwQ0Iw1xAR1CiFNJkGO4wu3ZMY1AAzBI0qSgmCNJsJUEOtJSMa" 150 | "CTBDLyQ0CknAGOgyTyFFiLI2awMzdEcSQgSAAKVUmAeNkxvWJWCGtVlDmgYQ0GFtgg4pNt" 151 | "OwbBcwQy/Rife/2yrRRVI0qYCEBly8Z+P4qMEMy7JaVw72N568e+iwhrXoECQkfH91kY7j" 152 | "wwXMsBx1L93ZruqrK6uuiAIdSnTIKKPLPFcvay8ww/Hh+ufeznTXu49v95IMoQG3784gYX" 153 | "dTqvRmqn/Wpa/ADFX58MW3L71SVU9ETgEIQQQIOOzub+fhIvwPRDgeVjWDahIAAAAASUVO" 154 | "RK5CYII=" 155 | ) 156 | 157 | -------------------------------------------------------------------------------- /app/preprocessor.py: -------------------------------------------------------------------------------- 1 | import ply.lex as lex 2 | import ply.yacc as yacc 3 | import re 4 | 5 | # Classes 6 | class Program(object): 7 | def __init__(self, items): 8 | self.items = items 9 | def get_lookup(self): 10 | return dict((item.name, item) for item in self.items 11 | if isinstance(item, MacroDefinition)) 12 | def preprocess(self, lookup): 13 | items = [] 14 | count = 0 15 | line = 1 16 | for item in self.items: 17 | newlines = item.line - line 18 | if newlines: 19 | items.append('\n' * (newlines)) 20 | line = item.line 21 | if isinstance(item, MacroCall): 22 | if item.name not in lookup: 23 | raise Exception('Call to undefined macro: %s' 24 | % item.name) 25 | macro = lookup[item.name] 26 | items.extend(macro.invoke(item.arguments)) 27 | count += 1 28 | elif isinstance(item, Token): 29 | if item.name in lookup: 30 | macro = lookup[item.name] 31 | items.extend(macro.invoke(())) 32 | count += 1 33 | else: 34 | items.append(item.name) 35 | lines = ' '.join(items).split('\n') 36 | result = '\n'.join(line.strip() for line in lines) 37 | return count, result 38 | 39 | class MacroDefinition(object): 40 | def __init__(self, line, name, parameters, tokens): 41 | self.line = line 42 | self.name = name 43 | self.parameters = parameters 44 | self.tokens = tokens 45 | def invoke(self, arguments): 46 | if len(arguments) != len(self.parameters): 47 | raise Exception('Incorrect number of arguments for macro: %s' 48 | % self.name) 49 | lookup = dict(zip(self.parameters, arguments)) 50 | tokens = [] 51 | for token in self.tokens: 52 | tokens.extend(lookup.get(token.name, [token])) 53 | result = [token.name for token in tokens] 54 | return result 55 | 56 | class MacroCall(object): 57 | def __init__(self, line, name, arguments): 58 | self.line = line 59 | self.name = name 60 | self.arguments = arguments 61 | 62 | class Token(object): 63 | def __init__(self, line, name): 64 | self.line = line 65 | self.name = name 66 | 67 | # Lexer Rules 68 | tokens = [ 69 | 'MACRO', 70 | 'COMMA', 71 | 'LBRACE', 72 | 'RBRACE', 73 | 'LBRACK', 74 | 'RBRACK', 75 | 'LPAREN', 76 | 'RPAREN', 77 | 'STRING', 78 | 'ID', 79 | 'OTHER', 80 | ] 81 | 82 | t_ignore = ' \t\r' 83 | t_ignore_COMMENT = r';.*' 84 | 85 | t_MACRO = r'\#macro' 86 | t_COMMA = r'\,' 87 | t_LBRACE = r'\{' 88 | t_RBRACE = r'\}' 89 | t_LBRACK = r'\[' 90 | t_RBRACK = r'\]' 91 | t_LPAREN = r'\(' 92 | t_RPAREN = r'\)' 93 | t_STRING = r'"[^"]*"' 94 | t_ID = r'[_a-zA-Z][_a-zA-Z0-9]*' 95 | t_OTHER = r'[^_a-zA-Z\s\;\,\{\}\[\]\(\)\"\#][^\s\;\,\{\}\[\]\(\)\"\#]*' 96 | 97 | def t_newline(t): 98 | r'\n+' 99 | t.lexer.lineno += len(t.value) 100 | 101 | def t_error(t): 102 | raise Exception(t) 103 | 104 | # Parser Rules 105 | def p_program(t): 106 | 'program : items' 107 | t[0] = Program(t[1]) 108 | 109 | def p_items1(t): 110 | 'items : item items' 111 | t[0] = (t[1],) + t[2] 112 | 113 | def p_items2(t): 114 | 'items : item' 115 | t[0] = (t[1],) 116 | 117 | def p_item(t): 118 | '''item : macro_definition 119 | | macro_call 120 | | token''' 121 | t[0] = t[1] 122 | 123 | def p_macro_definition(t): 124 | 'macro_definition : MACRO ID parameter_list LBRACE tokens RBRACE' 125 | t[0] = MacroDefinition(t.lineno(1), t[2], t[3], t[5]) 126 | 127 | def p_parameter_list1(t): 128 | 'parameter_list : LPAREN parameters RPAREN' 129 | t[0] = t[2] 130 | 131 | def p_parameter_list2(t): 132 | 'parameter_list : empty' 133 | t[0] = () 134 | 135 | def p_parameters1(t): 136 | 'parameters : ID COMMA parameters' 137 | t[0] = (t[1],) + t[3] 138 | 139 | def p_parameters2(t): 140 | 'parameters : ID' 141 | t[0] = (t[1],) 142 | 143 | def p_macro_call(t): 144 | 'macro_call : ID argument_list' 145 | t[0] = MacroCall(t.lineno(1), t[1], t[2]) 146 | 147 | def p_argument_list1(t): 148 | 'argument_list : LPAREN arguments RPAREN' 149 | t[0] = t[2] 150 | 151 | def p_argument_list2(t): 152 | 'argument_list : empty' 153 | t[0] = () 154 | 155 | def p_arguments1(t): 156 | 'arguments : argument COMMA arguments' 157 | t[0] = (t[1],) + t[3] 158 | 159 | def p_arguments2(t): 160 | 'arguments : argument' 161 | t[0] = (t[1],) 162 | 163 | def p_argument1(t): 164 | 'argument : argument_token argument' 165 | t[0] = (t[1],) + t[2] 166 | 167 | def p_argument2(t): 168 | 'argument : argument_token' 169 | t[0] = (t[1],) 170 | 171 | def p_argument_token(t): 172 | '''argument_token : LBRACK 173 | | RBRACK 174 | | STRING 175 | | ID 176 | | OTHER''' 177 | t[0] = Token(t.lineno(1), t[1]) 178 | 179 | def p_tokens1(t): 180 | 'tokens : token tokens' 181 | t[0] = (t[1],) + t[2] 182 | 183 | def p_tokens2(t): 184 | 'tokens : token' 185 | t[0] = (t[1],) 186 | 187 | def p_token(t): 188 | '''token : COMMA 189 | | LBRACK 190 | | RBRACK 191 | | LPAREN 192 | | RPAREN 193 | | STRING 194 | | ID 195 | | OTHER''' 196 | t[0] = Token(t.lineno(1), t[1]) 197 | 198 | def p_empty(t): 199 | 'empty :' 200 | pass 201 | 202 | def p_error(t): 203 | raise Exception(t) 204 | 205 | # Preprocessor Functions 206 | def create_lexer(): 207 | lexer = lex.lex() 208 | return lexer 209 | 210 | def create_parser(): 211 | parser = yacc.yacc(debug=False, write_tables=False) 212 | return parser 213 | 214 | LEXER = create_lexer() 215 | PARSER = create_parser() 216 | 217 | def include_files(text): 218 | lines = [] 219 | pattern = re.compile(r'\#include\s+\"([^"]+)\"') 220 | for line in text.split('\n'): 221 | match = pattern.match(line.strip()) 222 | if match is None: 223 | lines.append(line) 224 | else: 225 | path = match.group(1) 226 | with open(path) as fp: 227 | lines.extend(fp.read().split('\n')) 228 | result = '\n'.join(lines) 229 | return result 230 | 231 | def convert_defines(text): 232 | lines = [] 233 | pattern = re.compile(r'\#define\s+([_a-zA-Z][_a-zA-Z0-9]*)\s+(.+)') 234 | for line in text.split('\n'): 235 | match = pattern.match(line.strip()) 236 | if match is None: 237 | lines.append(line) 238 | else: 239 | name = match.group(1) 240 | value = match.group(2) 241 | macro = '#macro %s { %s }' % (name, value) 242 | print macro 243 | lines.append(macro) 244 | result = '\n'.join(lines) 245 | return result 246 | 247 | def preprocess(text): 248 | text = convert_defines(text) 249 | lookup = None 250 | while True: 251 | LEXER.lineno = 1 252 | program = PARSER.parse(text) 253 | if lookup is None: 254 | lookup = program.get_lookup() 255 | count, text = program.preprocess(lookup) 256 | if count == 0: 257 | break 258 | return text 259 | 260 | def preprocess_file(path): 261 | with open(path) as fp: 262 | text = fp.read() 263 | return preprocess(text) 264 | -------------------------------------------------------------------------------- /app/view.py: -------------------------------------------------------------------------------- 1 | import assembler 2 | import editor 3 | import functools 4 | import icons 5 | import preprocessor 6 | import sys 7 | import time 8 | import wx 9 | import wx.stc as stc 10 | 11 | # Constants 12 | SCALE = 3 13 | WIDTH = 128 14 | HEIGHT = 96 15 | BORDER = 10 16 | CYCLES_PER_SECOND = 100000 17 | BLINK_RATE = 50000 18 | 19 | FONT = [ 20 | 0xb79e, 0x388e, 0x722c, 0x75f4, 0x19bb, 0x7f8f, 0x85f9, 0xb158, 21 | 0x242e, 0x2400, 0x082a, 0x0800, 0x0008, 0x0000, 0x0808, 0x0808, 22 | 0x00ff, 0x0000, 0x00f8, 0x0808, 0x08f8, 0x0000, 0x080f, 0x0000, 23 | 0x000f, 0x0808, 0x00ff, 0x0808, 0x08f8, 0x0808, 0x08ff, 0x0000, 24 | 0x080f, 0x0808, 0x08ff, 0x0808, 0x6633, 0x99cc, 0x9933, 0x66cc, 25 | 0xfef8, 0xe080, 0x7f1f, 0x0701, 0x0107, 0x1f7f, 0x80e0, 0xf8fe, 26 | 0x5500, 0xaa00, 0x55aa, 0x55aa, 0xffaa, 0xff55, 0x0f0f, 0x0f0f, 27 | 0xf0f0, 0xf0f0, 0x0000, 0xffff, 0xffff, 0x0000, 0xffff, 0xffff, 28 | 0x0000, 0x0000, 0x005f, 0x0000, 0x0300, 0x0300, 0x3e14, 0x3e00, 29 | 0x266b, 0x3200, 0x611c, 0x4300, 0x3629, 0x7650, 0x0002, 0x0100, 30 | 0x1c22, 0x4100, 0x4122, 0x1c00, 0x1408, 0x1400, 0x081c, 0x0800, 31 | 0x4020, 0x0000, 0x0808, 0x0800, 0x0040, 0x0000, 0x601c, 0x0300, 32 | 0x3e49, 0x3e00, 0x427f, 0x4000, 0x6259, 0x4600, 0x2249, 0x3600, 33 | 0x0f08, 0x7f00, 0x2745, 0x3900, 0x3e49, 0x3200, 0x6119, 0x0700, 34 | 0x3649, 0x3600, 0x2649, 0x3e00, 0x0024, 0x0000, 0x4024, 0x0000, 35 | 0x0814, 0x2200, 0x1414, 0x1400, 0x2214, 0x0800, 0x0259, 0x0600, 36 | 0x3e59, 0x5e00, 0x7e09, 0x7e00, 0x7f49, 0x3600, 0x3e41, 0x2200, 37 | 0x7f41, 0x3e00, 0x7f49, 0x4100, 0x7f09, 0x0100, 0x3e41, 0x7a00, 38 | 0x7f08, 0x7f00, 0x417f, 0x4100, 0x2040, 0x3f00, 0x7f08, 0x7700, 39 | 0x7f40, 0x4000, 0x7f06, 0x7f00, 0x7f01, 0x7e00, 0x3e41, 0x3e00, 40 | 0x7f09, 0x0600, 0x3e61, 0x7e00, 0x7f09, 0x7600, 0x2649, 0x3200, 41 | 0x017f, 0x0100, 0x3f40, 0x7f00, 0x1f60, 0x1f00, 0x7f30, 0x7f00, 42 | 0x7708, 0x7700, 0x0778, 0x0700, 0x7149, 0x4700, 0x007f, 0x4100, 43 | 0x031c, 0x6000, 0x417f, 0x0000, 0x0201, 0x0200, 0x8080, 0x8000, 44 | 0x0001, 0x0200, 0x2454, 0x7800, 0x7f44, 0x3800, 0x3844, 0x2800, 45 | 0x3844, 0x7f00, 0x3854, 0x5800, 0x087e, 0x0900, 0x4854, 0x3c00, 46 | 0x7f04, 0x7800, 0x047d, 0x0000, 0x2040, 0x3d00, 0x7f10, 0x6c00, 47 | 0x017f, 0x0000, 0x7c18, 0x7c00, 0x7c04, 0x7800, 0x3844, 0x3800, 48 | 0x7c14, 0x0800, 0x0814, 0x7c00, 0x7c04, 0x0800, 0x4854, 0x2400, 49 | 0x043e, 0x4400, 0x3c40, 0x7c00, 0x1c60, 0x1c00, 0x7c30, 0x7c00, 50 | 0x6c10, 0x6c00, 0x4c50, 0x3c00, 0x6454, 0x4c00, 0x0836, 0x4100, 51 | 0x0077, 0x0000, 0x4136, 0x0800, 0x0201, 0x0201, 0x0205, 0x0200, 52 | ] 53 | 54 | PALETTE = [ 55 | 0x0000, 0x000a, 0x00a0, 0x00aa, 0x0a00, 0x0a0a, 0x0a50, 0x0aaa, 56 | 0x0555, 0x055f, 0x05f5, 0x05ff, 0x0f55, 0x0f5f, 0x0ff5, 0x0fff, 57 | ] 58 | 59 | KEYS = { 60 | wx.WXK_BACK: 0x10, 61 | wx.WXK_RETURN: 0x11, 62 | wx.WXK_INSERT: 0x12, 63 | wx.WXK_DELETE: 0x13, 64 | wx.WXK_UP: 0x80, 65 | wx.WXK_DOWN: 0x81, 66 | wx.WXK_LEFT: 0x82, 67 | wx.WXK_RIGHT: 0x83, 68 | wx.WXK_SHIFT: 0x90, 69 | wx.WXK_CONTROL: 0x91, 70 | } 71 | 72 | # Helper Functions 73 | def menu_item(window, menu, label, func, kind=wx.ITEM_NORMAL): 74 | item = wx.MenuItem(menu, -1, label, '', kind) 75 | window.Bind(wx.EVT_MENU, func, id=item.GetId()) 76 | menu.AppendItem(item) 77 | return item 78 | 79 | def tool_item(window, toolbar, label, func, icon): 80 | item = toolbar.AddSimpleTool(-1, icon.GetBitmap(), label) 81 | window.Bind(wx.EVT_TOOL, func, id=item.GetId()) 82 | return item 83 | 84 | def make_font(face, size, bold=False, italic=False, underline=False): 85 | if sys.platform == 'darwin': 86 | size = int(size * 1.5) 87 | family = wx.FONTFAMILY_DEFAULT 88 | style = wx.FONTSTYLE_ITALIC if italic else wx.FONTSTYLE_NORMAL 89 | weight = wx.FONTWEIGHT_BOLD if bold else wx.FONTWEIGHT_NORMAL 90 | font = wx.Font(size, family, style, weight, underline, face) 91 | return font 92 | 93 | def set_icon(window): 94 | bundle = wx.IconBundle() 95 | bundle.AddIcon(wx.IconFromBitmap(icons.icon16.GetBitmap())) 96 | bundle.AddIcon(wx.IconFromBitmap(icons.icon32.GetBitmap())) 97 | window.SetIcons(bundle) 98 | 99 | # Controls 100 | class RamList(wx.ListCtrl): 101 | INDEX_ADDR = 0 102 | INDEX_HEX = 1 103 | INDEX_DEC = 2 104 | def __init__(self, parent, emu): 105 | style = wx.LC_REPORT | wx.LC_VIRTUAL | wx.LC_SINGLE_SEL 106 | super(RamList, self).__init__(parent, -1, style=style) 107 | self.emu = emu 108 | self.InsertColumn(RamList.INDEX_ADDR, 'Addr') 109 | self.InsertColumn(RamList.INDEX_HEX, 'Hex') 110 | self.InsertColumn(RamList.INDEX_DEC, 'Dec') 111 | self.SetColumnWidth(RamList.INDEX_ADDR, 55) 112 | self.SetColumnWidth(RamList.INDEX_HEX, 55) 113 | self.SetColumnWidth(RamList.INDEX_DEC, 55) 114 | self.SetItemCount(0x10000) 115 | self.SetFont(make_font('Courier New', 9)) 116 | def OnGetItemText(self, index, column): 117 | if column == RamList.INDEX_ADDR: 118 | return '%04x' % index 119 | if column == RamList.INDEX_HEX: 120 | return '%04x' % self.emu.ram[index] 121 | if column == RamList.INDEX_DEC: 122 | return '%d' % self.emu.ram[index] 123 | return '' 124 | 125 | class ProgramList(wx.ListCtrl): 126 | INDEX_ADDR = 0 127 | INDEX_CODE = 1 128 | def __init__(self, parent): 129 | style = wx.LC_REPORT | wx.LC_VIRTUAL | wx.LC_SINGLE_SEL 130 | super(ProgramList, self).__init__(parent, -1, style=style) 131 | self.instructions = [] 132 | self.lookup = {} 133 | self.InsertColumn(ProgramList.INDEX_ADDR, 'Addr') 134 | self.InsertColumn(ProgramList.INDEX_CODE, 'Code') 135 | self.SetColumnWidth(ProgramList.INDEX_ADDR, 55) 136 | self.SetColumnWidth(ProgramList.INDEX_CODE, 155) 137 | self.SetFont(make_font('Courier New', 9)) 138 | def update(self, instructions): 139 | self.instructions = instructions 140 | self.lookup = {} 141 | for index, instruction in enumerate(instructions): 142 | self.lookup[instruction.offset] = index 143 | self.SetItemCount(len(instructions)) 144 | def focus(self, offset): 145 | if offset not in self.lookup: 146 | return 147 | index = self.lookup[offset] 148 | self.Select(index) 149 | self.EnsureVisible(index) 150 | def OnGetItemText(self, index, column): 151 | instruction = self.instructions[index] 152 | if column == ProgramList.INDEX_ADDR: 153 | return '%04x' % instruction.offset 154 | if column == ProgramList.INDEX_CODE: 155 | return instruction.pretty().strip() 156 | return '' 157 | 158 | class Canvas(wx.Panel): 159 | def __init__(self, parent, emu): 160 | style = wx.WANTS_CHARS | wx.BORDER_STATIC 161 | super(Canvas, self).__init__(parent, style=style) 162 | self.emu = emu 163 | self.scale = 0 164 | self.cache = {} 165 | self.bitmap = wx.EmptyBitmap(1, 1) 166 | self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM) 167 | self.Bind(wx.EVT_SIZE, self.on_size) 168 | self.Bind(wx.EVT_PAINT, self.on_paint) 169 | self.Bind(wx.EVT_KEY_DOWN, self.on_key_down) 170 | self.Bind(wx.EVT_KEY_UP, self.on_key_up) 171 | self.Bind(wx.EVT_CHAR, self.on_char) 172 | self.SetInitialSize(( 173 | WIDTH * SCALE + BORDER * 4, 174 | HEIGHT * SCALE + BORDER * 4 + 24)) 175 | def on_key_down(self, event): 176 | event.Skip() 177 | code = event.GetKeyCode() 178 | code = KEYS.get(code, code) 179 | self.emu.on_key_down(code) 180 | def on_key_up(self, event): 181 | event.Skip() 182 | code = event.GetKeyCode() 183 | code = KEYS.get(code, code) 184 | self.emu.on_key_up(code) 185 | def on_char(self, event): 186 | event.Skip() 187 | code = event.GetKeyCode() 188 | code = KEYS.get(code, code) 189 | self.emu.on_char(code) 190 | def on_size(self, event): 191 | event.Skip() 192 | self.Refresh() 193 | cw, ch = self.GetClientSize() 194 | scale = min((cw - BORDER * 2) / WIDTH, (ch - BORDER * 2) / HEIGHT) 195 | scale = max(scale, 1) 196 | if scale != self.scale: 197 | self.scale = scale 198 | self.cache = {} 199 | self.bitmap = wx.EmptyBitmap(WIDTH * scale, HEIGHT * scale) 200 | dc = wx.MemoryDC(self.bitmap) 201 | dc.SetBackground(wx.BLACK_BRUSH) 202 | dc.Clear() 203 | def on_paint(self, event): 204 | bitmap = self.bitmap 205 | cw, ch = self.GetClientSize() 206 | bw, bh = bitmap.GetWidth(), bitmap.GetHeight() 207 | dx, dy = (cw - bw) / 2, (ch - bh) / 2 208 | mdc = wx.MemoryDC(bitmap) 209 | border_brush = self.draw_screen(mdc) 210 | dc = wx.AutoBufferedPaintDC(self) 211 | dc.SetBackground(border_brush) 212 | dc.Clear() 213 | dc.Blit(dx, dy, bw, bh, mdc, 0, 0) 214 | def get_character(self, address, show_blink=True): 215 | value = self.emu.ram[address] 216 | character = value & 0x7f 217 | blink = bool(value & 0x80) 218 | color = (value >> 8) & 0xff 219 | back = color & 0x0f 220 | fore = (color >> 4) & 0x0f 221 | if blink and not show_blink: 222 | fore = back 223 | return character, back, fore 224 | def draw_screen(self, dc): 225 | address = self.emu.lem_screen 226 | if not address: 227 | self.cache = {} 228 | dc.SetBackground(wx.BLACK_BRUSH) 229 | dc.Clear() 230 | return wx.BLACK_BRUSH 231 | font_address = self.emu.lem_font 232 | if font_address: 233 | font = self.emu.ram[font_address : font_address + 256] 234 | else: 235 | font = FONT 236 | palette_address = self.emu.lem_palette 237 | if palette_address: 238 | palette = self.emu.ram[palette_address : palette_address + 16] 239 | else: 240 | palette = PALETTE 241 | brushes = [] 242 | for x in palette: 243 | r, g, b = (x >> 8) & 0xf, (x >> 4) & 0xf, (x >> 0) & 0xf 244 | r, g, b = (r << 4) | r, (g << 4) | g, (b << 4) | b 245 | brushes.append(wx.Brush(wx.Colour(r, g, b))) 246 | dc.SetPen(wx.TRANSPARENT_PEN) 247 | show_blink = bool((self.emu.cycle / BLINK_RATE) % 2) 248 | for j in xrange(12): 249 | for i in xrange(32): 250 | ch, back, fore = self.get_character(address, show_blink) 251 | a = font[ch * 2] 252 | b = font[ch * 2 + 1] 253 | bitmap = a << 16 | b 254 | key = (palette[back], palette[fore], bitmap) 255 | if self.cache.get((i, j)) != key: 256 | self.cache[(i, j)] = key 257 | x, y = i * 4 * self.scale, j * 8 * self.scale 258 | self.draw_character(dc, x, y, brushes[back], 259 | brushes[fore], bitmap) 260 | address += 1 261 | return brushes[self.emu.lem_border & 0xf] 262 | def draw_character(self, dc, x, y, back, fore, bitmap): 263 | mask = 1 264 | for i in xrange(3, -1, -1): 265 | for j in xrange(8): 266 | dx = i * self.scale 267 | dy = j * self.scale 268 | if bitmap & mask: 269 | dc.SetBrush(fore) 270 | else: 271 | dc.SetBrush(back) 272 | dc.DrawRectangle(x + dx, y + dy, self.scale, self.scale) 273 | mask <<= 1 274 | 275 | class Frame(wx.Frame): 276 | def __init__(self, emu): 277 | super(Frame, self).__init__(None) 278 | self.emu = emu 279 | self.last_time = time.time() 280 | self.last_refresh = time.time() 281 | self._path = None 282 | self._dirty = False 283 | self.program = None 284 | self.running = False 285 | self.step_power = 0 286 | self.refresh_rate = 1 287 | self.cycles_per_second = CYCLES_PER_SECOND 288 | self.show_debug = True 289 | self.debug_controls = [] 290 | set_icon(self) 291 | self.create_menu() 292 | self.create_toolbar() 293 | self.create_statusbar() 294 | self.panel = wx.Panel(self) 295 | sizer = self.create_controls(self.panel) 296 | self.panel.SetSizerAndFit(sizer) 297 | self.Fit() 298 | self.SetTitle('DCPU-16 Emulator') 299 | self.Bind(wx.EVT_CLOSE, self.on_close) 300 | wx.CallAfter(self.on_timer) 301 | args = sys.argv[1:] 302 | if len(args) == 1: 303 | self.open_file(args[0]) 304 | @property 305 | def path(self): 306 | return self._path 307 | @path.setter 308 | def path(self, path): 309 | if path != self._path: 310 | self._path = path 311 | self.update_title() 312 | @property 313 | def dirty(self): 314 | return self._dirty 315 | @dirty.setter 316 | def dirty(self, dirty): 317 | if dirty != self._dirty: 318 | self._dirty = dirty 319 | self.update_title() 320 | def update_title(self): 321 | tokens = [] 322 | if self.dirty: 323 | tokens.append('* ') 324 | tokens.append('DCPU-16 Emulator') 325 | if self.path: 326 | tokens.append(' - %s' % self.path) 327 | title = ''.join(tokens) 328 | self.SetTitle(title) 329 | def check_dirty(self, can_veto=True): 330 | if not self.dirty: 331 | return True 332 | path = self.path or '(Untitled)' 333 | style = wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION 334 | if can_veto: 335 | style |= wx.CANCEL 336 | dialog = wx.MessageDialog(self, 'Save changes to file %s?' % path, 337 | 'Save Changes?', style) 338 | result = dialog.ShowModal() 339 | dialog.Destroy() 340 | if result == wx.ID_YES: 341 | if self.path: 342 | self.on_save(None) 343 | return True 344 | else: 345 | return self.on_save_as(None) 346 | elif result == wx.ID_NO: 347 | return True 348 | else: 349 | return False 350 | def on_close(self, event): 351 | can_veto = event.CanVeto() 352 | veto = not self.check_dirty(can_veto) 353 | if veto and can_veto: 354 | event.Veto() 355 | return 356 | event.Skip() 357 | def create_menu(self): 358 | menubar = wx.MenuBar() 359 | # File 360 | menu = wx.Menu() 361 | menu_item(self, menu, 'New\tCtrl+N', self.on_new) 362 | menu_item(self, menu, 'Open...\tCtrl+O', self.on_open) 363 | menu_item(self, menu, 'Save\tCtrl+S', self.on_save) 364 | menu_item(self, menu, 'Save As...\tCtrl+Shift+S', self.on_save_as) 365 | menu_item(self, menu, 'Save Binary...\tCtrl+D', self.on_save_binary) 366 | menu.AppendSeparator() 367 | menu_item(self, menu, 'Exit\tAlt+F4', self.on_exit) 368 | menubar.Append(menu, '&File') 369 | # Edit 370 | menu = wx.Menu() 371 | menu_item(self, menu, 'Undo\tCtrl+Z', self.on_undo) 372 | menu_item(self, menu, 'Redo\tCtrl+Y', self.on_redo) 373 | menu.AppendSeparator() 374 | menu_item(self, menu, 'Cut\tCtrl+X', self.on_cut) 375 | menu_item(self, menu, 'Copy\tCtrl+C', self.on_copy) 376 | menu_item(self, menu, 'Paste\tCtrl+V', self.on_paste) 377 | menu_item(self, menu, 'Delete\tDel', self.on_delete) 378 | menu_item(self, menu, 'Select All\tCtrl+A', self.on_select_all) 379 | menubar.Append(menu, '&Edit') 380 | # Run 381 | menu = wx.Menu() 382 | menu_item(self, menu, 'Assemble\tF4', self.on_assemble) 383 | menu_item(self, menu, 'Start\tF5', self.on_start) 384 | menu_item(self, menu, 'Stop\tF6', self.on_stop) 385 | menu_item(self, menu, 'Step\tF7', self.on_step) 386 | menu.AppendSeparator() 387 | for power in range(6): 388 | func = functools.partial(self.on_step_power, power=power) 389 | item = menu_item(self, menu, '10^%d Steps' % power, func, 390 | wx.ITEM_RADIO) 391 | if power == 0: 392 | item.Check() 393 | menu.AppendSeparator() 394 | for power in range(-2, 6): 395 | func = functools.partial(self.on_clock_rate, power=power) 396 | item = menu_item(self, menu, '%gx Clock Rate' % (2 ** power), func, 397 | wx.ITEM_RADIO) 398 | if power == 0: 399 | item.Check() 400 | menubar.Append(menu, '&Run') 401 | # View 402 | menu = wx.Menu() 403 | item = menu_item(self, menu, 'Show Debug Controls\tF12', 404 | self.on_toggle_debug, wx.ITEM_CHECK) 405 | item.Check() 406 | menu.AppendSeparator() 407 | data = [ 408 | (-1, 'No Refresh'), 409 | (0, 'Live Refresh'), 410 | (1, 'One Second Refresh'), 411 | ] 412 | for rate, name in data: 413 | func = functools.partial(self.on_refresh_rate, rate=rate) 414 | item = menu_item(self, menu, name, func, wx.ITEM_RADIO) 415 | if rate == self.refresh_rate: 416 | item.Check() 417 | menubar.Append(menu, '&View') 418 | self.SetMenuBar(menubar) 419 | def create_toolbar(self): 420 | style = wx.HORIZONTAL | wx.TB_FLAT | wx.TB_NODIVIDER 421 | toolbar = self.CreateToolBar(style) 422 | toolbar.SetToolBitmapSize((18, 18)) 423 | tool_item(self, toolbar, 'New', self.on_new, icons.page) 424 | tool_item(self, toolbar, 'Open', self.on_open, icons.folder_page) 425 | tool_item(self, toolbar, 'Save', self.on_save, icons.disk) 426 | toolbar.AddSeparator() 427 | tool_item(self, toolbar, 'Assemble', self.on_assemble, icons.basket_put) 428 | tool_item(self, toolbar, 'Start', self.on_start, icons.control_play) 429 | tool_item(self, toolbar, 'Stop', self.on_stop, icons.control_stop) 430 | tool_item(self, toolbar, 'Step', self.on_step, icons.control_end) 431 | toolbar.Realize() 432 | toolbar.Fit() 433 | def create_statusbar(self): 434 | sizes = [0, 100, 140, -1] 435 | styles = [wx.SB_NORMAL] * len(sizes) 436 | styles[0] = wx.SB_FLAT 437 | bar = self.CreateStatusBar() 438 | bar.SetFieldsCount(len(sizes)) 439 | bar.SetStatusWidths(sizes) 440 | bar.SetStatusStyles(styles) 441 | self.update_statusbar() 442 | def update_statusbar(self): 443 | bar = self.GetStatusBar() 444 | running = 'Running' if self.running else 'Not Running' 445 | bar.SetStatusText(running, 1) 446 | cycle = 'Cycle: %d' % self.emu.cycle 447 | bar.SetStatusText(cycle, 2) 448 | def show_debug_controls(self, show): 449 | for item in self.debug_controls: 450 | item.Show(show) 451 | self.panel.Layout() 452 | def reset(self, flags=True): 453 | if flags: 454 | self.path = None 455 | self.dirty = False 456 | self.running = False 457 | self.program = None 458 | self.emu.reset() 459 | self.program_list.update([]) 460 | self.refresh_debug_info() 461 | def on_new(self, event): 462 | if not self.check_dirty(): 463 | return 464 | self.reset() 465 | self.editor.SetText('') 466 | self.editor.SetSavePoint() 467 | self.dirty = False 468 | def open_file(self, path): 469 | try: 470 | self.reset() 471 | self.path = path 472 | extensions = ['.dasm', '.dasm16'] 473 | if any(ext in path for ext in extensions): 474 | with open(path) as fp: 475 | text = fp.read() 476 | wx.CallAfter(self.assemble) 477 | else: 478 | self.program = assembler.disassemble_file(path) 479 | self.emu.load(self.program.assemble()) 480 | self.program_list.update(self.program.instructions) 481 | text = self.program.pretty() 482 | self.editor.SetText(text) 483 | self.editor.SetSavePoint() 484 | self.dirty = False 485 | self.refresh_debug_info() 486 | except Exception as e: 487 | self.reset() 488 | dialog = wx.MessageDialog(self, str(e), 'Error', 489 | wx.ICON_ERROR | wx.OK) 490 | dialog.ShowModal() 491 | dialog.Destroy() 492 | def on_open(self, event): 493 | if not self.check_dirty(): 494 | return 495 | dialog = wx.FileDialog(self, 'Open', 496 | style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST) 497 | if dialog.ShowModal() == wx.ID_OK: 498 | path = dialog.GetPath() 499 | self.open_file(path) 500 | dialog.Destroy() 501 | def on_save(self, event): 502 | if self.path is None: 503 | self.on_save_as(None) 504 | return 505 | with open(self.path, 'wb') as fp: 506 | fp.write(self.editor.GetText()) 507 | self.dirty = False 508 | def on_save_as(self, event): 509 | dialog = wx.FileDialog(self, 'Save As', wildcard='*.dasm', 510 | style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT) 511 | result = dialog.ShowModal() 512 | dialog.Destroy() 513 | if result == wx.ID_OK: 514 | path = dialog.GetPath() 515 | with open(path, 'wb') as fp: 516 | fp.write(self.editor.GetText()) 517 | self.path = path 518 | self.dirty = False 519 | return True 520 | else: 521 | return False 522 | def save_binary(self, path): 523 | words = self.program.assemble() 524 | data = [] 525 | for word in words: 526 | data.append(chr((word >> 8) & 0xff)) 527 | data.append(chr((word >> 0) & 0xff)) 528 | data = ''.join(data) 529 | with open(path, 'wb') as fp: 530 | fp.write(data) 531 | def on_save_binary(self, event): 532 | if self.program is None: 533 | return 534 | dialog = wx.FileDialog(self, 'Save Binary', wildcard='*.obj', 535 | style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT) 536 | if dialog.ShowModal() == wx.ID_OK: 537 | path = dialog.GetPath() 538 | self.save_binary(path) 539 | dialog.Destroy() 540 | def on_undo(self, event): 541 | self.editor.Undo() 542 | self.dirty = self.editor.GetModify() 543 | def on_redo(self, event): 544 | self.editor.Redo() 545 | self.dirty = self.editor.GetModify() 546 | def on_cut(self, event): 547 | self.editor.Cut() 548 | def on_copy(self, event): 549 | self.editor.Copy() 550 | def on_paste(self, event): 551 | self.editor.Paste() 552 | def on_delete(self, event): 553 | self.editor.Clear() 554 | def on_select_all(self, event): 555 | self.editor.SelectAll() 556 | def on_exit(self, event): 557 | self.Close() 558 | def assemble(self): 559 | text = self.editor.GetText() 560 | try: 561 | self.reset(False) 562 | self.program = assembler.parse(preprocessor.preprocess(text)) 563 | self.emu.load(self.program.assemble()) 564 | self.program_list.update(self.program.instructions) 565 | self.refresh_debug_info() 566 | except Exception as e: 567 | self.reset(False) 568 | dialog = wx.MessageDialog(self, str(e), 'Error', 569 | wx.ICON_ERROR | wx.OK) 570 | dialog.ShowModal() 571 | dialog.Destroy() 572 | def on_assemble(self, event): 573 | self.assemble() 574 | def on_start(self, event): 575 | self.running = True 576 | self.refresh_debug_info() 577 | def on_stop(self, event): 578 | self.running = False 579 | self.refresh_debug_info() 580 | def on_step(self, event): 581 | if not self.running: 582 | steps = 10 ** self.step_power 583 | self.emu.n_steps(steps) 584 | self.refresh_debug_info() 585 | def on_step_power(self, event, power): 586 | self.step_power = power 587 | def on_clock_rate(self, event, power): 588 | self.cycles_per_second = CYCLES_PER_SECOND * (2 ** power) 589 | def on_toggle_debug(self, event): 590 | self.show_debug = not self.show_debug 591 | self.show_debug_controls(self.show_debug) 592 | def on_refresh_rate(self, event, rate): 593 | self.refresh_rate = rate 594 | def on_page_changed(self, event): 595 | event.Skip() 596 | index = event.GetEventObject().GetSelection() 597 | if index == 0: 598 | wx.CallAfter(self.editor.SetFocus) 599 | else: 600 | wx.CallAfter(self.canvas.SetFocus) 601 | def on_text(self, event): 602 | event.Skip() 603 | self.dirty = self.editor.GetModify() 604 | def update(self, dt): 605 | if self.running: 606 | cycles = int(dt * self.cycles_per_second) 607 | self.emu.n_cycles(cycles) 608 | if self.emu.halt: 609 | self.running = False 610 | self.emu.halt = 0 611 | self.refresh_debug_info() 612 | def refresh(self): 613 | self.canvas.Refresh() 614 | self.canvas.Update() 615 | if self.running and self.refresh_rate >= 0: 616 | if time.time() - self.last_refresh > self.refresh_rate: 617 | self.refresh_debug_info() 618 | def refresh_debug_info(self): 619 | self.last_refresh = time.time() 620 | self.update_statusbar() 621 | self.program_list.focus(self.emu.ram[0x10009]) 622 | self.ram_list.RefreshItems(0, self.ram_list.GetItemCount() - 1) 623 | for address, widget in self.registers.iteritems(): 624 | widget.SetValue('%04x' % self.emu.ram[address]) 625 | def on_timer(self): 626 | now = time.time() 627 | dt = now - self.last_time 628 | self.last_time = now 629 | self.update(dt) 630 | self.refresh() 631 | wx.CallLater(10, self.on_timer) 632 | def create_controls(self, parent): 633 | body = self.create_body(parent) 634 | sizer = wx.BoxSizer(wx.VERTICAL) 635 | sizer.Add(body, 1, wx.EXPAND | wx.ALL, 10) 636 | return sizer 637 | def create_body(self, parent): 638 | self.program_list = ProgramList(parent) 639 | self.program_list.SetInitialSize((245, -1)) 640 | center = self.create_center(parent) 641 | self.ram_list = RamList(parent, self.emu) 642 | self.ram_list.SetInitialSize((200, -1)) 643 | sizer = wx.BoxSizer(wx.HORIZONTAL) 644 | c1 = sizer.Add(self.program_list, 0, wx.EXPAND) 645 | c2 = sizer.AddSpacer(10) 646 | sizer.Add(center, 1, wx.EXPAND) 647 | c3 = sizer.AddSpacer(10) 648 | c4 = sizer.Add(self.ram_list, 0, wx.EXPAND) 649 | self.debug_controls.extend([c1, c2, c3, c4]) 650 | return sizer 651 | def create_center(self, parent): 652 | notebook = self.create_notebook(parent) 653 | registers = self.create_registers(parent) 654 | sizer = wx.BoxSizer(wx.VERTICAL) 655 | sizer.Add(notebook, 1, wx.EXPAND) 656 | c1 = sizer.AddSpacer(10) 657 | c2 = sizer.Add(registers, 0, wx.EXPAND) 658 | self.debug_controls.extend([c1, c2]) 659 | return sizer 660 | def create_notebook(self, parent): 661 | notebook = wx.Notebook(parent) 662 | notebook.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self.on_page_changed) 663 | editor = self.create_editor(notebook) 664 | canvas = self.create_canvas(notebook) 665 | notebook.AddPage(editor, 'Editor') 666 | notebook.AddPage(canvas, 'Display') 667 | return notebook 668 | def create_editor(self, parent): 669 | panel = wx.Panel(parent) 670 | self.editor = editor.Editor(panel) 671 | self.editor.Bind(stc.EVT_STC_CHANGE, self.on_text) 672 | sizer = wx.BoxSizer(wx.VERTICAL) 673 | sizer.Add(self.editor, 1, wx.EXPAND | wx.ALL, 5) 674 | panel.SetSizer(sizer) 675 | return panel 676 | def create_canvas(self, parent): 677 | panel = wx.Panel(parent) 678 | self.canvas = Canvas(panel, self.emu) 679 | sizer = wx.BoxSizer(wx.VERTICAL) 680 | sizer.Add(self.canvas, 1, wx.EXPAND | wx.ALL, 5) 681 | panel.SetSizer(sizer) 682 | return panel 683 | def create_registers(self, parent): 684 | self.registers = {} 685 | sizer = wx.FlexGridSizer(4, 6, 5, 5) 686 | for col in range(6): 687 | sizer.AddGrowableCol(col, 1) 688 | data1 = [ 689 | ('A', 0), 690 | ('B', 1), 691 | ('C', 2), 692 | ('X', 3), 693 | ('Y', 4), 694 | ('Z', 5), 695 | ] 696 | data2 = [ 697 | ('SP', 8), 698 | ('PC', 9), 699 | ('EX', 10), 700 | ('IA', 11), 701 | ('I', 6), 702 | ('J', 7), 703 | ] 704 | groups = [data1, data2] 705 | for data in groups: 706 | for name, offset in data: 707 | text = wx.StaticText(parent, -1, name) 708 | sizer.Add(text, flag=wx.ALIGN_CENTER) 709 | for name, offset in data: 710 | address = 0x10000 + offset 711 | style = wx.TE_CENTER | wx.TE_READONLY 712 | size = (0, -1) 713 | text = wx.TextCtrl(parent, -1, '0000', size=size, style=style) 714 | text.SetFont(make_font('Courier New', 9)) 715 | sizer.Add(text, 1, wx.EXPAND) 716 | self.registers[address] = text 717 | return sizer 718 | -------------------------------------------------------------------------------- /build_emulator: -------------------------------------------------------------------------------- 1 | gcc -std=c99 -O3 -c emulator/*.c 2 | gcc -shared -o _emulator *.o 3 | rm *.o 4 | -------------------------------------------------------------------------------- /build_emulator.bat: -------------------------------------------------------------------------------- 1 | gcc -std=c99 -O3 -c emulator\*.c 2 | gcc -shared -o _emulator.dll *.o 3 | del *.o 4 | -------------------------------------------------------------------------------- /build_emulator_linux: -------------------------------------------------------------------------------- 1 | gcc -std=c99 -O3 -c emulator/*.c -fPIC 2 | gcc -shared -o _emulator *.o 3 | rm *.o 4 | -------------------------------------------------------------------------------- /build_icons: -------------------------------------------------------------------------------- 1 | python util/icons.py "icons" > "app/icons.py" 2 | -------------------------------------------------------------------------------- /build_icons.bat: -------------------------------------------------------------------------------- 1 | python util\icons.py "icons" > "app\icons.py" 2 | -------------------------------------------------------------------------------- /build_installer.bat: -------------------------------------------------------------------------------- 1 | python setup.py 2 | "C:\Program Files (x86)\Inno Setup 5\Compil32.exe" /cc installer.iss 3 | -------------------------------------------------------------------------------- /clean.bat: -------------------------------------------------------------------------------- 1 | rmdir /S /Q build 2 | rmdir /S /Q dist 3 | rmdir /S /Q installer 4 | -------------------------------------------------------------------------------- /emulator/clock.c: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include "emulator.h" 3 | #include "clock.h" 4 | 5 | void on_clock(Emulator *emulator) { 6 | switch (REG(0)) { 7 | case 0: // SET_RATE 8 | emulator->clock_cycle = REG(1) ? NEXT_TICK : 0; 9 | emulator->clock_rate = REG(1); 10 | emulator->clock_ticks = 0; 11 | break; 12 | case 1: // GET_TICKS 13 | REG(2) = emulator->clock_ticks; 14 | break; 15 | case 2: // ENABLE_INTERRUPTS 16 | emulator->clock_message = REG(1); 17 | break; 18 | } 19 | } 20 | 21 | void on_clock_step(Emulator *emulator) { 22 | if (emulator->clock_rate) { 23 | if (emulator->cycle >= emulator->clock_cycle) { 24 | emulator->clock_ticks++; 25 | emulator->clock_cycle = NEXT_TICK; 26 | if (emulator->clock_message) { 27 | interrupt(emulator, emulator->clock_message); 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /emulator/clock.h: -------------------------------------------------------------------------------- 1 | #ifndef CLOCK_H 2 | #define CLOCK_H 3 | 4 | #include "emulator.h" 5 | 6 | void on_clock(Emulator *emulator); 7 | 8 | void on_clock_step(Emulator *emulator); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /emulator/common.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMON_H 2 | #define COMMON_H 3 | 4 | // Constants 5 | #define SIZE 0x10000 6 | #define MAX_VALUE 0xffff 7 | #define EXT_SIZE 0x10010 8 | #define REG_ADDR 0x10000 9 | #define SP_ADDR 0x10008 10 | #define PC_ADDR 0x10009 11 | #define EX_ADDR 0x1000a 12 | #define IA_ADDR 0x1000b 13 | #define LT_ADDR 0x1000c 14 | 15 | // Helper Macros 16 | #define NEXT_TICK (emulator->cycle + 100000 * emulator->clock_rate / 60) 17 | #define CONDITIONAL(opcode) ((opcode) >= 0x10 && (opcode) <= 0x17) 18 | #define CYCLES(count) (emulator->cycle += (count)) 19 | #define RAM(address) (emulator->ram[(address)]) 20 | #define REG(index) (emulator->ram[REG_ADDR + (index)]) 21 | #define SP (emulator->ram[SP_ADDR]) 22 | #define PC (emulator->ram[PC_ADDR]) 23 | #define EX (emulator->ram[EX_ADDR]) 24 | #define IA (emulator->ram[IA_ADDR]) 25 | #define LT (emulator->ram[LT_ADDR]) 26 | #define SKIP (emulator->skip) 27 | #define HALT (emulator->halt) 28 | #define CYCLE (emulator->cycle) 29 | 30 | // Basic Opcodes 31 | #define SET 0x01 32 | #define ADD 0x02 33 | #define SUB 0x03 34 | #define MUL 0x04 35 | #define MLI 0x05 36 | #define DIV 0x06 37 | #define DVI 0x07 38 | #define MOD 0x08 39 | #define MDI 0x09 40 | #define AND 0x0a 41 | #define BOR 0x0b 42 | #define XOR 0x0c 43 | #define SHR 0x0d 44 | #define ASR 0x0e 45 | #define SHL 0x0f 46 | #define IFB 0x10 47 | #define IFC 0x11 48 | #define IFE 0x12 49 | #define IFN 0x13 50 | #define IFG 0x14 51 | #define IFA 0x15 52 | #define IFL 0x16 53 | #define IFU 0x17 54 | #define ADX 0x1a 55 | #define SUX 0x1b 56 | #define STI 0x1e 57 | #define STD 0x1f 58 | 59 | // Non Basic Opcodes 60 | #define JSR 0x01 61 | #define BRK 0x02 62 | #define INT 0x08 63 | #define IAG 0x09 64 | #define IAS 0x0a 65 | #define RFI 0x0b 66 | #define IAQ 0x0c 67 | #define HWN 0x10 68 | #define HWQ 0x11 69 | #define HWI 0x12 70 | 71 | // Hardware 72 | #define N_DEVICES 3 73 | #define LEM 0 74 | #define KEYBOARD 1 75 | #define CLOCK 2 76 | 77 | #endif 78 | -------------------------------------------------------------------------------- /emulator/emulator.c: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include "emulator.h" 3 | #include "lem.h" 4 | #include "keyboard.h" 5 | #include "clock.h" 6 | 7 | // Emulator Functions 8 | void reset(Emulator *emulator) { 9 | // DCPU-16 10 | for (unsigned int i = 0; i < EXT_SIZE; i++) { 11 | RAM(i) = 0; 12 | } 13 | SKIP = 0; 14 | HALT = 0; 15 | CYCLE = 0; 16 | for (unsigned int i = 0; i < 256; i++) { 17 | emulator->interrupt_buffer[i] = 0; 18 | } 19 | emulator->interrupt_index = 0; 20 | emulator->interrupt_queueing = 0; 21 | // LEM 22 | emulator->lem_screen = 0; 23 | emulator->lem_font = 0; 24 | emulator->lem_palette = 0; 25 | emulator->lem_border = 0; 26 | // KEYBOARD 27 | for (unsigned int i = 0; i < 16; i++) { 28 | emulator->keyboard_buffer[i] = 0; 29 | } 30 | for (unsigned int i = 0; i < 256; i++) { 31 | emulator->keyboard_pressed[i] = 0; 32 | } 33 | emulator->keyboard_index = 0; 34 | emulator->keyboard_message = 0; 35 | // CLOCK 36 | emulator->clock_cycle = 0; 37 | emulator->clock_rate = 0; 38 | emulator->clock_ticks = 0; 39 | emulator->clock_message = 0; 40 | } 41 | 42 | void load(Emulator *emulator, unsigned short *program, unsigned int length) { 43 | if (length > SIZE) { 44 | length = SIZE; 45 | } 46 | for (unsigned int i = 0; i < length; i++) { 47 | RAM(i) = program[i]; 48 | } 49 | } 50 | 51 | void interrupt(Emulator *emulator, unsigned short message) { 52 | if (emulator->interrupt_index < 256) { 53 | emulator->interrupt_buffer[emulator->interrupt_index++] = message; 54 | } 55 | } 56 | 57 | int operand(Emulator *emulator, unsigned char x, unsigned char dereference) { 58 | int result; 59 | unsigned char literal = 0; 60 | if (x < 0x08) { 61 | result = REG_ADDR + x; 62 | } 63 | else if (x >= 0x08 && x <= 0x0f) { 64 | result = REG(x - 0x08); 65 | } 66 | else if (x >= 0x10 && x <= 0x17) { 67 | result = (REG(x - 0x10) + RAM(PC++)) % SIZE; 68 | if (!SKIP) { 69 | CYCLES(1); 70 | } 71 | } 72 | else if (x == 0x18 && dereference) { 73 | result = SP; 74 | if (!SKIP) { 75 | SP++; 76 | } 77 | } 78 | else if (x == 0x18 && !dereference) { 79 | if (!SKIP) { 80 | SP--; 81 | } 82 | result = SP; 83 | } 84 | else if (x == 0x19) { 85 | result = SP; 86 | } 87 | else if (x == 0x1a) { 88 | result = (SP + RAM(PC++)) % SIZE; 89 | if (!SKIP) { 90 | CYCLES(1); 91 | } 92 | } 93 | else if (x == 0x1b) { 94 | result = SP_ADDR; 95 | } 96 | else if (x == 0x1c) { 97 | result = PC_ADDR; 98 | } 99 | else if (x == 0x1d) { 100 | result = EX_ADDR; 101 | } 102 | else if (x == 0x1e) { 103 | result = RAM(PC++); 104 | if (!SKIP) { 105 | CYCLES(1); 106 | } 107 | } 108 | else if (x == 0x1f) { 109 | literal = 1; 110 | result = RAM(PC++); 111 | if (!SKIP) { 112 | CYCLES(1); 113 | } 114 | } 115 | else if (x == 0x20) { 116 | literal = 1; 117 | result = MAX_VALUE; 118 | } 119 | else { 120 | literal = 1; 121 | result = x - 0x21; 122 | } 123 | if (literal && !dereference) { 124 | LT = result; 125 | result = LT_ADDR; 126 | } 127 | if (dereference && !literal) { 128 | result = RAM(result); 129 | } 130 | return result; 131 | } 132 | 133 | int divmod(int x, int *quo) { 134 | int quotient = x / SIZE; 135 | if (x < 0 && x % SIZE) { 136 | quotient--; 137 | } 138 | *quo = quotient; 139 | return x % SIZE; 140 | } 141 | 142 | void basic_instruction(Emulator *emulator, unsigned char opcode, 143 | unsigned char op_dst, unsigned char op_src) { 144 | int src = operand(emulator, op_src, 1); 145 | int dst = operand(emulator, op_dst, 0); 146 | int ram = RAM(dst); 147 | short ssrc = (short)(unsigned short)src; 148 | short sram = (short)(unsigned short)ram; 149 | int quo; 150 | int mod; 151 | if (SKIP) { 152 | if (CONDITIONAL(opcode)) { 153 | CYCLES(1); 154 | } 155 | else { 156 | SKIP = 0; 157 | } 158 | return; 159 | } 160 | switch (opcode) { 161 | case SET: 162 | RAM(dst) = src; 163 | CYCLES(1); 164 | break; 165 | case ADD: 166 | mod = divmod(ram + src, &quo); 167 | EX = quo ? 1 : 0; 168 | RAM(dst) = mod; 169 | CYCLES(2); 170 | break; 171 | case SUB: 172 | mod = divmod(ram - src, &quo); 173 | EX = quo ? MAX_VALUE : 0; 174 | RAM(dst) = mod; 175 | CYCLES(2); 176 | break; 177 | case MUL: 178 | mod = divmod(ram * src, &quo); 179 | EX = quo % SIZE; 180 | RAM(dst) = mod; 181 | CYCLES(2); 182 | break; 183 | case MLI: 184 | mod = divmod(sram * ssrc, &quo); 185 | EX = quo % SIZE; 186 | RAM(dst) = mod; 187 | CYCLES(2); 188 | break; 189 | case DIV: 190 | if (src) { 191 | EX = ((ram << 16) / src) % SIZE; 192 | RAM(dst) = (ram / src) % SIZE; 193 | } 194 | else { 195 | EX = 0; 196 | RAM(dst) = 0; 197 | } 198 | CYCLES(3); 199 | break; 200 | case DVI: 201 | if (src) { 202 | EX = ((sram << 16) / ssrc) % SIZE; 203 | RAM(dst) = (sram / ssrc) % SIZE; 204 | } 205 | else { 206 | EX = 0; 207 | RAM(dst) = 0; 208 | } 209 | CYCLES(3); 210 | break; 211 | case MOD: 212 | if (src) { 213 | RAM(dst) = (ram % src) % SIZE; 214 | } 215 | else { 216 | RAM(dst) = 0; 217 | } 218 | CYCLES(3); 219 | break; 220 | case MDI: 221 | if (src) { 222 | RAM(dst) = (sram % ssrc) % SIZE; 223 | } 224 | else { 225 | RAM(dst) = 0; 226 | } 227 | CYCLES(3); 228 | break; 229 | case AND: 230 | RAM(dst) = (ram & src) % SIZE; 231 | CYCLES(1); 232 | break; 233 | case BOR: 234 | RAM(dst) = (ram | src) % SIZE; 235 | CYCLES(1); 236 | break; 237 | case XOR: 238 | RAM(dst) = (ram ^ src) % SIZE; 239 | CYCLES(1); 240 | break; 241 | case SHR: 242 | EX = ((ram << 16) >> src) % SIZE; 243 | RAM(dst) = (ram >> src) % SIZE; 244 | CYCLES(1); 245 | break; 246 | case ASR: 247 | EX = ((sram << 16) >> src) % SIZE; 248 | RAM(dst) = (sram >> src) % SIZE; 249 | CYCLES(1); 250 | break; 251 | case SHL: 252 | EX = ((ram << src) >> 16) % SIZE; 253 | RAM(dst) = (ram << src) % SIZE; 254 | CYCLES(1); 255 | break; 256 | case IFB: 257 | SKIP = (ram & src) != 0 ? 0 : 1; 258 | CYCLES(2 + SKIP); 259 | break; 260 | case IFC: 261 | SKIP = (ram & src) == 0 ? 0 : 1; 262 | CYCLES(2 + SKIP); 263 | break; 264 | case IFE: 265 | SKIP = (ram == src) ? 0 : 1; 266 | CYCLES(2 + SKIP); 267 | break; 268 | case IFN: 269 | SKIP = (ram != src) ? 0 : 1; 270 | CYCLES(2 + SKIP); 271 | break; 272 | case IFG: 273 | SKIP = (ram > src) ? 0 : 1; 274 | CYCLES(2 + SKIP); 275 | break; 276 | case IFA: 277 | SKIP = (sram > ssrc) ? 0 : 1; 278 | CYCLES(2 + SKIP); 279 | break; 280 | case IFL: 281 | SKIP = (ram < src) ? 0 : 1; 282 | CYCLES(2 + SKIP); 283 | break; 284 | case IFU: 285 | SKIP = (sram < ssrc) ? 0 : 1; 286 | CYCLES(2 + SKIP); 287 | break; 288 | case ADX: 289 | mod = divmod(ram + src + EX, &quo); 290 | EX = quo ? 1 : 0; 291 | RAM(dst) = mod; 292 | CYCLES(3); 293 | break; 294 | case SUX: 295 | mod = divmod(ram - src + EX, &quo); 296 | EX = quo ? MAX_VALUE : 0; 297 | RAM(dst) = mod; 298 | CYCLES(3); 299 | break; 300 | case STI: 301 | RAM(dst) = src; 302 | REG(6)++; 303 | REG(7)++; 304 | CYCLES(2); 305 | break; 306 | case STD: 307 | RAM(dst) = src; 308 | REG(6)--; 309 | REG(7)--; 310 | CYCLES(2); 311 | break; 312 | default: 313 | CYCLES(1); 314 | break; 315 | } 316 | } 317 | 318 | void on_hwq(Emulator *emulator, unsigned short index) { 319 | switch (index) { 320 | case LEM: 321 | REG(0) = 0xf615; 322 | REG(1) = 0x7349; 323 | REG(2) = 0x1802; 324 | REG(3) = 0x8b36; 325 | REG(4) = 0x1c6c; 326 | break; 327 | case KEYBOARD: 328 | REG(0) = 0x7406; 329 | REG(1) = 0x30cf; 330 | REG(2) = 0x0001; 331 | REG(3) = 0x8b36; 332 | REG(4) = 0x1c6c; 333 | break; 334 | case CLOCK: 335 | REG(0) = 0xb402; 336 | REG(1) = 0x12d0; 337 | REG(2) = 0x0001; 338 | REG(3) = 0x8b36; 339 | REG(4) = 0x1c6c; 340 | break; 341 | } 342 | } 343 | 344 | void on_hwi(Emulator *emulator, unsigned short index) { 345 | switch (index) { 346 | case LEM: 347 | on_lem(emulator); 348 | break; 349 | case KEYBOARD: 350 | on_keyboard(emulator); 351 | break; 352 | case CLOCK: 353 | on_clock(emulator); 354 | break; 355 | } 356 | } 357 | 358 | void special_instruction(Emulator *emulator, unsigned char opcode, 359 | unsigned char op_dst) { 360 | int dst = operand(emulator, op_dst, 0); 361 | int ram = RAM(dst); 362 | if (SKIP) { 363 | SKIP = 0; 364 | return; 365 | } 366 | switch (opcode) { 367 | case JSR: 368 | RAM(--SP) = PC; 369 | PC = ram; 370 | CYCLES(3); 371 | break; 372 | case BRK: 373 | HALT = 1; 374 | CYCLES(1); 375 | break; 376 | case INT: 377 | interrupt(emulator, ram); 378 | CYCLES(4); 379 | break; 380 | case IAG: 381 | RAM(dst) = IA; 382 | CYCLES(1); 383 | break; 384 | case IAS: 385 | IA = ram; 386 | CYCLES(1); 387 | break; 388 | case RFI: 389 | emulator->interrupt_queueing = 0; 390 | REG(0) = RAM(SP++); 391 | PC = RAM(SP++); 392 | CYCLES(3); 393 | break; 394 | case IAQ: 395 | emulator->interrupt_queueing = ram; 396 | CYCLES(2); 397 | break; 398 | case HWN: 399 | RAM(dst) = N_DEVICES; 400 | CYCLES(2); 401 | break; 402 | case HWQ: 403 | on_hwq(emulator, ram); 404 | CYCLES(4); 405 | break; 406 | case HWI: 407 | on_hwi(emulator, ram); 408 | CYCLES(4); 409 | break; 410 | default: 411 | CYCLES(1); 412 | break; 413 | } 414 | } 415 | 416 | void do_interrupt(Emulator *emulator) { 417 | if (emulator->interrupt_index) { 418 | unsigned short message = emulator->interrupt_buffer[0]; 419 | for (unsigned int i = 1; i < 256; i++) { 420 | emulator->interrupt_buffer[i - 1] = 421 | emulator->interrupt_buffer[i]; 422 | } 423 | emulator->interrupt_buffer[255] = 0; 424 | emulator->interrupt_index--; 425 | if (IA) { 426 | emulator->interrupt_queueing = 1; 427 | RAM(--SP) = PC; 428 | RAM(--SP) = REG(0); 429 | PC = IA; 430 | REG(0) = message; 431 | } 432 | } 433 | } 434 | 435 | void one_step(Emulator *emulator) { 436 | do { 437 | unsigned short word = RAM(PC++); 438 | unsigned char op = word & 0x1f; 439 | unsigned char dst = (word >> 5) & 0x1f; 440 | unsigned char src = (word >> 10) & 0x3f; 441 | if (op) { 442 | basic_instruction(emulator, op, dst, src); 443 | } 444 | else { 445 | special_instruction(emulator, dst, src); 446 | } 447 | } while (SKIP); 448 | on_clock_step(emulator); 449 | if (!emulator->interrupt_queueing) { 450 | do_interrupt(emulator); 451 | } 452 | } 453 | 454 | void n_steps(Emulator *emulator, unsigned int steps) { 455 | for (unsigned int i = 0; i < steps; i++) { 456 | one_step(emulator); 457 | if (HALT) { 458 | break; 459 | } 460 | } 461 | } 462 | 463 | void n_cycles(Emulator *emulator, unsigned int cycles) { 464 | unsigned long long int cycle = CYCLE + cycles; 465 | while (CYCLE < cycle) { 466 | one_step(emulator); 467 | if (HALT) { 468 | break; 469 | } 470 | } 471 | } 472 | -------------------------------------------------------------------------------- /emulator/emulator.h: -------------------------------------------------------------------------------- 1 | #ifndef EMULATOR_H 2 | #define EMULATOR_H 3 | 4 | // Emulator State 5 | typedef struct { 6 | // DCPU-16 7 | unsigned short ram[EXT_SIZE]; 8 | unsigned short skip; 9 | unsigned short halt; 10 | unsigned long long int cycle; 11 | unsigned short interrupt_buffer[256]; 12 | unsigned short interrupt_index; 13 | unsigned short interrupt_queueing; 14 | // LEM 15 | unsigned short lem_screen; 16 | unsigned short lem_font; 17 | unsigned short lem_palette; 18 | unsigned short lem_border; 19 | // KEYBOARD 20 | unsigned char keyboard_buffer[16]; 21 | unsigned char keyboard_pressed[256]; 22 | unsigned short keyboard_index; 23 | unsigned short keyboard_message; 24 | // CLOCK 25 | unsigned long long int clock_cycle; 26 | unsigned short clock_rate; 27 | unsigned short clock_ticks; 28 | unsigned short clock_message; 29 | } Emulator; 30 | 31 | // Emulator Functions 32 | void reset(Emulator *emulator); 33 | 34 | void load(Emulator *emulator, unsigned short *program, unsigned int length); 35 | 36 | void interrupt(Emulator *emulator, unsigned short message); 37 | 38 | int operand(Emulator *emulator, unsigned char x, unsigned char dereference); 39 | 40 | int divmod(int x, int *quo); 41 | 42 | void basic_instruction(Emulator *emulator, unsigned char opcode, 43 | unsigned char op_dst, unsigned char op_src); 44 | 45 | void on_hwq(Emulator *emulator, unsigned short index); 46 | 47 | void on_hwi(Emulator *emulator, unsigned short index); 48 | 49 | void special_instruction(Emulator *emulator, unsigned char opcode, 50 | unsigned char op_dst); 51 | 52 | void do_interrupt(Emulator *emulator); 53 | 54 | void step(Emulator *emulator); 55 | 56 | void n_steps(Emulator *emulator, unsigned int steps); 57 | 58 | void n_cycles(Emulator *emulator, unsigned int cycles); 59 | 60 | #endif 61 | -------------------------------------------------------------------------------- /emulator/keyboard.c: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include "emulator.h" 3 | #include "keyboard.h" 4 | 5 | void on_key_down(Emulator *emulator, unsigned char key) { 6 | emulator->keyboard_pressed[key] = 1; 7 | if (emulator->keyboard_message) { 8 | interrupt(emulator, emulator->keyboard_message); 9 | } 10 | } 11 | 12 | void on_key_up(Emulator *emulator, unsigned char key) { 13 | emulator->keyboard_pressed[key] = 0; 14 | if (emulator->keyboard_message) { 15 | interrupt(emulator, emulator->keyboard_message); 16 | } 17 | } 18 | 19 | void on_char(Emulator *emulator, unsigned char key) { 20 | if (emulator->keyboard_index < 16) { 21 | emulator->keyboard_buffer[emulator->keyboard_index++] = key; 22 | if (emulator->keyboard_message) { 23 | interrupt(emulator, emulator->keyboard_message); 24 | } 25 | } 26 | } 27 | 28 | void on_keyboard(Emulator *emulator) { 29 | switch (REG(0)) { 30 | case 0: // CLEAR_BUFFER 31 | for (unsigned int i = 0; i < 16; i++) { 32 | emulator->keyboard_buffer[i] = 0; 33 | } 34 | emulator->keyboard_index = 0; 35 | break; 36 | case 1: // GET_CHARACTER 37 | if (emulator->keyboard_index) { 38 | REG(2) = emulator->keyboard_buffer[0]; 39 | for (unsigned int i = 1; i < 16; i++) { 40 | emulator->keyboard_buffer[i - 1] = 41 | emulator->keyboard_buffer[i]; 42 | } 43 | emulator->keyboard_buffer[15] = 0; 44 | emulator->keyboard_index--; 45 | } 46 | else { 47 | REG(2) = 0; 48 | } 49 | break; 50 | case 2: // IS_PRESSED 51 | REG(2) = REG(1) < 256 ? emulator->keyboard_pressed[REG(1)] : 0; 52 | break; 53 | case 3: // ENABLE_INTERRUPTS 54 | emulator->keyboard_message = REG(1); 55 | break; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /emulator/keyboard.h: -------------------------------------------------------------------------------- 1 | #ifndef KEYBOARD_H 2 | #define KEYBOARD_H 3 | 4 | #include "emulator.h" 5 | 6 | void on_key_down(Emulator *emulator, unsigned char key); 7 | 8 | void on_key_up(Emulator *emulator, unsigned char key); 9 | 10 | void on_char(Emulator *emulator, unsigned char key); 11 | 12 | void on_keyboard(Emulator *emulator); 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /emulator/lem.c: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include "emulator.h" 3 | #include "lem.h" 4 | 5 | // Default Font 6 | unsigned short LEM_FONT[] = { 7 | 0xb79e, 0x388e, 0x722c, 0x75f4, 0x19bb, 0x7f8f, 0x85f9, 0xb158, 8 | 0x242e, 0x2400, 0x082a, 0x0800, 0x0008, 0x0000, 0x0808, 0x0808, 9 | 0x00ff, 0x0000, 0x00f8, 0x0808, 0x08f8, 0x0000, 0x080f, 0x0000, 10 | 0x000f, 0x0808, 0x00ff, 0x0808, 0x08f8, 0x0808, 0x08ff, 0x0000, 11 | 0x080f, 0x0808, 0x08ff, 0x0808, 0x6633, 0x99cc, 0x9933, 0x66cc, 12 | 0xfef8, 0xe080, 0x7f1f, 0x0701, 0x0107, 0x1f7f, 0x80e0, 0xf8fe, 13 | 0x5500, 0xaa00, 0x55aa, 0x55aa, 0xffaa, 0xff55, 0x0f0f, 0x0f0f, 14 | 0xf0f0, 0xf0f0, 0x0000, 0xffff, 0xffff, 0x0000, 0xffff, 0xffff, 15 | 0x0000, 0x0000, 0x005f, 0x0000, 0x0300, 0x0300, 0x3e14, 0x3e00, 16 | 0x266b, 0x3200, 0x611c, 0x4300, 0x3629, 0x7650, 0x0002, 0x0100, 17 | 0x1c22, 0x4100, 0x4122, 0x1c00, 0x1408, 0x1400, 0x081c, 0x0800, 18 | 0x4020, 0x0000, 0x0808, 0x0800, 0x0040, 0x0000, 0x601c, 0x0300, 19 | 0x3e49, 0x3e00, 0x427f, 0x4000, 0x6259, 0x4600, 0x2249, 0x3600, 20 | 0x0f08, 0x7f00, 0x2745, 0x3900, 0x3e49, 0x3200, 0x6119, 0x0700, 21 | 0x3649, 0x3600, 0x2649, 0x3e00, 0x0024, 0x0000, 0x4024, 0x0000, 22 | 0x0814, 0x2200, 0x1414, 0x1400, 0x2214, 0x0800, 0x0259, 0x0600, 23 | 0x3e59, 0x5e00, 0x7e09, 0x7e00, 0x7f49, 0x3600, 0x3e41, 0x2200, 24 | 0x7f41, 0x3e00, 0x7f49, 0x4100, 0x7f09, 0x0100, 0x3e41, 0x7a00, 25 | 0x7f08, 0x7f00, 0x417f, 0x4100, 0x2040, 0x3f00, 0x7f08, 0x7700, 26 | 0x7f40, 0x4000, 0x7f06, 0x7f00, 0x7f01, 0x7e00, 0x3e41, 0x3e00, 27 | 0x7f09, 0x0600, 0x3e61, 0x7e00, 0x7f09, 0x7600, 0x2649, 0x3200, 28 | 0x017f, 0x0100, 0x3f40, 0x7f00, 0x1f60, 0x1f00, 0x7f30, 0x7f00, 29 | 0x7708, 0x7700, 0x0778, 0x0700, 0x7149, 0x4700, 0x007f, 0x4100, 30 | 0x031c, 0x6000, 0x417f, 0x0000, 0x0201, 0x0200, 0x8080, 0x8000, 31 | 0x0001, 0x0200, 0x2454, 0x7800, 0x7f44, 0x3800, 0x3844, 0x2800, 32 | 0x3844, 0x7f00, 0x3854, 0x5800, 0x087e, 0x0900, 0x4854, 0x3c00, 33 | 0x7f04, 0x7800, 0x047d, 0x0000, 0x2040, 0x3d00, 0x7f10, 0x6c00, 34 | 0x017f, 0x0000, 0x7c18, 0x7c00, 0x7c04, 0x7800, 0x3844, 0x3800, 35 | 0x7c14, 0x0800, 0x0814, 0x7c00, 0x7c04, 0x0800, 0x4854, 0x2400, 36 | 0x043e, 0x4400, 0x3c40, 0x7c00, 0x1c60, 0x1c00, 0x7c30, 0x7c00, 37 | 0x6c10, 0x6c00, 0x4c50, 0x3c00, 0x6454, 0x4c00, 0x0836, 0x4100, 38 | 0x0077, 0x0000, 0x4136, 0x0800, 0x0201, 0x0201, 0x0205, 0x0200, 39 | }; 40 | 41 | // Default Palette 42 | unsigned short LEM_PALETTE[] = { 43 | 0x0000, 0x000a, 0x00a0, 0x00aa, 0x0a00, 0x0a0a, 0x0a50, 0x0aaa, 44 | 0x0555, 0x055f, 0x05f5, 0x05ff, 0x0f55, 0x0f5f, 0x0ff5, 0x0fff, 45 | }; 46 | 47 | void on_lem(Emulator *emulator) { 48 | unsigned short address; 49 | switch (REG(0)) { 50 | case 0: // MEM_MAP_SCREEN 51 | emulator->lem_screen = REG(1); 52 | break; 53 | case 1: // MEM_MAP_FONT 54 | emulator->lem_font = REG(1); 55 | break; 56 | case 2: // MEM_MAP_PALETTE 57 | emulator->lem_palette = REG(1); 58 | break; 59 | case 3: // SET_BORDER_COLOR 60 | emulator->lem_border = REG(1); 61 | break; 62 | case 4: // DUMP_FONT 63 | address = REG(1); 64 | for (unsigned int i = 0; i < 256; i++) { 65 | RAM(address++) = LEM_FONT[i]; 66 | } 67 | CYCLES(256); 68 | break; 69 | case 5: // DUMP_PALETTE 70 | address = REG(1); 71 | for (unsigned int i = 0; i < 16; i++) { 72 | RAM(address++) = LEM_PALETTE[i]; 73 | } 74 | CYCLES(16); 75 | break; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /emulator/lem.h: -------------------------------------------------------------------------------- 1 | #ifndef LEM_H 2 | #define LEM_H 3 | 4 | #include "emulator.h" 5 | 6 | void on_lem(Emulator *emulator); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /icons/basket_put.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fogleman/DCPU-16/db6f7aa15cdee8f3e285c11ee5dd7d286fe46551/icons/basket_put.png -------------------------------------------------------------------------------- /icons/control_end.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fogleman/DCPU-16/db6f7aa15cdee8f3e285c11ee5dd7d286fe46551/icons/control_end.png -------------------------------------------------------------------------------- /icons/control_play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fogleman/DCPU-16/db6f7aa15cdee8f3e285c11ee5dd7d286fe46551/icons/control_play.png -------------------------------------------------------------------------------- /icons/control_stop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fogleman/DCPU-16/db6f7aa15cdee8f3e285c11ee5dd7d286fe46551/icons/control_stop.png -------------------------------------------------------------------------------- /icons/disk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fogleman/DCPU-16/db6f7aa15cdee8f3e285c11ee5dd7d286fe46551/icons/disk.png -------------------------------------------------------------------------------- /icons/folder_page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fogleman/DCPU-16/db6f7aa15cdee8f3e285c11ee5dd7d286fe46551/icons/folder_page.png -------------------------------------------------------------------------------- /icons/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fogleman/DCPU-16/db6f7aa15cdee8f3e285c11ee5dd7d286fe46551/icons/icon.ico -------------------------------------------------------------------------------- /icons/icon16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fogleman/DCPU-16/db6f7aa15cdee8f3e285c11ee5dd7d286fe46551/icons/icon16.png -------------------------------------------------------------------------------- /icons/icon24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fogleman/DCPU-16/db6f7aa15cdee8f3e285c11ee5dd7d286fe46551/icons/icon24.png -------------------------------------------------------------------------------- /icons/icon256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fogleman/DCPU-16/db6f7aa15cdee8f3e285c11ee5dd7d286fe46551/icons/icon256.png -------------------------------------------------------------------------------- /icons/icon32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fogleman/DCPU-16/db6f7aa15cdee8f3e285c11ee5dd7d286fe46551/icons/icon32.png -------------------------------------------------------------------------------- /icons/icon48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fogleman/DCPU-16/db6f7aa15cdee8f3e285c11ee5dd7d286fe46551/icons/icon48.png -------------------------------------------------------------------------------- /icons/page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fogleman/DCPU-16/db6f7aa15cdee8f3e285c11ee5dd7d286fe46551/icons/page.png -------------------------------------------------------------------------------- /installer.iss: -------------------------------------------------------------------------------- 1 | ; Script generated by the Inno Setup Script Wizard. 2 | ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! 3 | 4 | [Setup] 5 | ; NOTE: The value of AppId uniquely identifies this application. 6 | ; Do not use the same AppId value in installers for other applications. 7 | ; (To generate a new GUID, click Tools | Generate GUID inside the IDE.) 8 | AppId={{212C9D15-1A9D-4126-95F7-5A7340D186B5} 9 | AppName=DCPU-16 Emulator 10 | AppVerName=DCPU-16 Emulator 1.0 11 | AppPublisher=Michael Fogleman 12 | AppPublisherURL=http://www.michaelfogleman.com/ 13 | AppSupportURL=http://www.michaelfogleman.com/ 14 | AppUpdatesURL=http://www.michaelfogleman.com/ 15 | DefaultDirName={pf}\DCPU-16 16 | DefaultGroupName=DCPU-16 17 | AllowNoIcons=yes 18 | OutputDir=installer 19 | OutputBaseFilename=dcpu16-setup 20 | Compression=lzma 21 | SolidCompression=yes 22 | 23 | [Languages] 24 | Name: "english"; MessagesFile: "compiler:Default.isl" 25 | 26 | [Tasks] 27 | Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked 28 | Name: "quicklaunchicon"; Description: "{cm:CreateQuickLaunchIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked 29 | 30 | [Dirs] 31 | Name: "{app}"; Permissions: everyone-modify 32 | 33 | [Files] 34 | Source: "dist\dcpu16.exe"; DestDir: "{app}"; Flags: ignoreversion; Permissions: everyone-readexec 35 | Source: "dist\w9xpopen.exe"; DestDir: "{app}"; Flags: ignoreversion; Permissions: everyone-readexec 36 | ;Source: "dist\library.zip"; DestDir: "{app}"; Flags: ignoreversion; Permissions: everyone-readexec 37 | Source: "dist\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs 38 | 39 | [Icons] 40 | Name: "{group}\DCPU-16"; Filename: "{app}\dcpu16.exe"; WorkingDir: "{app}"; 41 | Name: "{group}\{cm:UninstallProgram,DCPU-16}"; Filename: "{uninstallexe}" 42 | Name: "{userdesktop}\DCPU-16"; Filename: "{app}\dcpu16.exe"; WorkingDir: "{app}"; Tasks: desktopicon 43 | Name: "{userappdata}\Microsoft\Internet Explorer\Quick Launch\DCPU-16"; Filename: "{app}\dcpu16.exe"; WorkingDir: "{app}"; Tasks: quicklaunchicon 44 | 45 | [Registry] 46 | Root: HKCR; Subkey: ".dasm"; ValueType: string; ValueName: ""; ValueData: "dcpu16file"; Flags: uninsdeletevalue 47 | Root: HKCR; Subkey: ".dasm16"; ValueType: string; ValueName: ""; ValueData: "dcpu16file"; Flags: uninsdeletevalue 48 | Root: HKCR; Subkey: "dcpu16file"; ValueType: string; ValueName: ""; ValueData: "DCPU-16 Emulator"; Flags: uninsdeletekey 49 | Root: HKCR; Subkey: "dcpu16file\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\dcpu16.exe,0" 50 | Root: HKCR; Subkey: "dcpu16file\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\dcpu16.exe"" ""%1""" 51 | 52 | [Run] 53 | Filename: "{app}\dcpu16.exe"; Description: "{cm:LaunchProgram,DCPU-16}"; Flags: nowait postinstall 54 | 55 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | from app import Emulator, Frame 2 | import wx 3 | 4 | def main(): 5 | app = wx.App(None) 6 | frame = Frame(Emulator()) 7 | frame.Center() 8 | frame.Show() 9 | app.MainLoop() 10 | 11 | if __name__ == '__main__': 12 | main() 13 | -------------------------------------------------------------------------------- /programs/cube.dasm: -------------------------------------------------------------------------------- 1 | ; sin[x] = (1<<14)*(1+sin(x&255)) where x = 0..255 2 | ; y*sin(t) -> y*((1+sin(t))-1) = y*(1+sin(t))-y = (y*sin[t]>>14) - y 3 | ; set a, y 4 | ; set b, sin[t] 5 | ; mul a, b 6 | ; O is now (y*(1+sin[t])>>2) 7 | ; shl O, 2 8 | ; shr a, 14 9 | ; add a, O 10 | ; sub a, y 11 | ; 12 | 13 | set [0x8280], 1 14 | set pc, mainloop 15 | :scale dat 25 16 | :speed1 dat 5 17 | :speed2 dat 2 18 | :mainloop 19 | ; clear offscreen buffer 20 | set sp, 0x7000 21 | :clrloop 22 | dat 33153,33153,33153,33153,33153,33153,33153,33153 ; set pop,0 x16 23 | dat 33153,33153,33153,33153,33153,33153,33153,33153 ; 1 cycle each 24 | ifn sp, 0x7180 ; 3 cycles 25 | set pc, clrloop ; 2 cycles -> (16+3+2)*32*12/16 = 504 us to clear screen 26 | ; this could be 1 cycle if we had a better assembler... 27 | ; try sub pc, 16+2+1 = sub pc, 19? 28 | set sp, 0 29 | 30 | ; render points to offscreen buffer 31 | set i, vertices 32 | set j, vbuf 33 | :vertexloop 34 | ; rotate b[1], b[2] by angle[0] 35 | set a, [angle] 36 | set x, [i] ; vertex.x 37 | set y, [i+1] ; vertex.y 38 | jsr rotate 39 | set [vertex], a ; vertex.x' 40 | set [vertex1], b ; vertex.y' 41 | ; rotate b[0], b[1] by angle[1] 42 | set x, [i+2] ; vertex.z' 43 | set y, b ; vertex.y' 44 | set a, [angle1] 45 | jsr rotate 46 | ; a,b -> vertex.{z,y} 47 | set z, a 48 | set a, [vertex] 49 | ; a,b are mostly in [-400,400] 50 | ; we want them in [0,32],[0,24] 51 | 52 | add z, 1024 ; z from 768..1280 53 | ; so we want kx/z in -256..256, (kx+cz)/z in 0..32, cz/z=16 54 | ; k(-256/768)=-256 -> k=768 55 | ; but that's totally bonkers; let's do the math from 0..65535 56 | ; for this to work, kx+cz must be < 65536 57 | ; x is -256..256, z is 768..1280, c is 256 58 | ; we need to bring z down by a factor of >10 59 | ; lets just do >>4 on x and z 60 | ; k(-256/48)=-256; k=48 61 | shr z, 4 62 | set c, 256 63 | mul c, z ; c is cz 64 | 65 | mul a, [scale] 66 | add a, c 67 | ;ifg a, 0x7fff 68 | ; set pc, offscreen 69 | div a, z 70 | ;ifg a, 31 71 | ; set pc, offscreen 72 | set c, 192 73 | mul c, z 74 | mul b, [scale] 75 | add b, c 76 | ;ifg b, 0x7fff 77 | ; set pc, offscreen 78 | div b, z 79 | ;ifg b, 23 80 | ; set pc, offscreen 81 | set [j], a 82 | set [j+1], b 83 | set [j+2], z 84 | 85 | add i, 3 86 | add j, 3 87 | ifn i, lastvertex 88 | set pc, vertexloop 89 | 90 | ; now run through edges and draw lines 91 | set i, edges 92 | :edgeloop 93 | 94 | set z, [i] 95 | set a, [vbuf+z] 96 | set b, [vbuf1+z] 97 | set z, [i+1] 98 | set x, [vbuf+z] 99 | set y, [vbuf1+z] 100 | jsr drawline 101 | 102 | add i, 2 103 | ifn i, lastedge 104 | set pc, edgeloop 105 | 106 | ; copy offscreen to on-screen 107 | :redraw 108 | set sp, 0x8000 109 | set a, 0x7000 110 | :cploop 111 | set pop, [a] ; unroll 8x copy to screen (this one is 1 cycle) 112 | set pop, [a+1] ; each of these is 2 cycles 113 | set pop, [a+2] ; loop cost is (7+15)*32*12/8 = 1056 cycles = 1.056ms 114 | set pop, [a+3] ; if we unroll N times, (7+2*N-1)*32*12/N 115 | set pop, [a+4] ; (N must divide 384) 116 | set pop, [a+5] ; 912 cycles to unroll 16x 117 | set pop, [a+6] ; 840 if we unroll 32x 118 | set pop, [a+7] ; guess the asymptote is 768 if we unroll 384x 119 | ; in fact, we might as well have a loop that just writes 'set pop, [x]' 120 | ; instructions 384 times into memory ("speedcode"!) 121 | add a, 8 ; 2 cycles 122 | ifn a, 0x7180 ; 3 cycles 123 | set pc, cploop ; 2 cycles 124 | 125 | add [angle], [speed1] 126 | add [angle1], [speed2] 127 | set pc, mainloop ; mainloop will restore sp to something sane 128 | 129 | ; rotate x,y -> a,b by angle a 130 | ; a = [cos a, -sin a] [x y] 131 | ; b = [sin a, cos a] [x y] 132 | ; x and y are bounded by [-1023, 1023] 133 | ; so let's add 1024 to them to get around the lack of imul without branching or 134 | ; holding onto sign flags -- and it means the result of the mul will be 1<<24 135 | ;(cx+a)(dy+b) = cdxy + cxb + dya + ab 136 | ; xy = ((cx+a)(dy+b) - cxb - dya - ab) / cd 137 | ; = (cx+a)(dy+b)/cd - xb/d - ya/c - ab/cd 138 | ; in this case 139 | ; c=256, x -> coord, a=1024 140 | ; d=256, y -> sin, b=256 141 | ; cx+a = reg, dy+b = sin 142 | ; x=(reg-a)/c = reg/c - a/c 143 | ; y=(sin-b)/d = sin/d - b/d 144 | ; xy = (cx+a)(dy+b)/cd - xb/d - ya/c - ab/cd 145 | ; xy = reg*sin/cd - b(reg-a)/cd - a(sin-b)/cd - ab/cd 146 | ; 147 | ; assume c is 1 so we maintain flexibility, a=16384, b=256, c=1, d=256 148 | ; in that case 149 | ; xy = reg*sin>>8 - (reg-16384) - (sin-256)<<6 - 16384 150 | ; = reg*sin>>8 - reg + 16384 - sin<<6 + 16384 - 16384 151 | ; = reg*sin>>8 - reg - sin<<6 + 16384 152 | ; e.g. 153 | ; x = 0 154 | ; reg = 16384 155 | ; sin = 512 156 | ; 32768 - 16384 - 32768 + 16384 -> 0 157 | ; reg = 50+16384 = 16434 158 | ; sin = -0.5*256+256 = 128 159 | ; 128*16434/256 - 16434 - 128*64 + 16384 160 | ; 8217 - 16434 - 8192 + 16384 = -25! 161 | ; ok, this is sound. 162 | 163 | 164 | :xcos 165 | dat 0 166 | :ycos 167 | dat 0 168 | :xsin 169 | dat 0 170 | :ysin 171 | dat 0 172 | 173 | :rotate 174 | set push, i 175 | set push, j 176 | 177 | add x, 16384 178 | add y, 16384 179 | and a, 255 180 | set i, [sin_tbl+a] ; i -> sin(a) 181 | add a, 64 182 | and a, 255 183 | set j, [sin_tbl+a] ; j -> cos(a) 184 | 185 | set a, x ; x*sin 186 | mul a, i 187 | set b, o 188 | shr a, 8 189 | shl b, 8 190 | add a, b 191 | sub a, x 192 | set b, i 193 | shl b, 6 194 | sub a, b 195 | add a, 16384 196 | set [xsin], a 197 | 198 | set a, y ; y*sin 199 | mul a, i 200 | set b, o 201 | shr a, 8 202 | shl b, 8 203 | add a, b 204 | sub a, y 205 | set b, i 206 | shl b, 6 207 | sub a, b 208 | add a, 16384 209 | set [ysin], a 210 | 211 | set a, x ; x*cos 212 | mul a, j 213 | set b, o 214 | shr a, 8 215 | shl b, 8 216 | add a, b 217 | sub a, x 218 | set b, j 219 | shl b, 6 220 | sub a, b 221 | add a, 16384 222 | set [xcos], a 223 | 224 | set a, y ; y*cos 225 | mul a, j 226 | set b, o 227 | shr a, 8 228 | shl b, 8 229 | add a, b 230 | sub a, y 231 | set b, j 232 | shl b, 6 233 | sub a, b 234 | add a, 16384 235 | set [ycos], a 236 | 237 | set a, [xcos] 238 | sub a, [ysin] 239 | 240 | set b, [xsin] 241 | add b, [ycos] 242 | 243 | set j, pop 244 | set i, pop 245 | set pc, pop 246 | 247 | ; 248 | :sin_tbl ; sin_tbl[a] = 256*sin(pi*a/128) + 256 249 | dat 256, 262, 268, 274, 281, 287, 293, 299, 305, 312, 318, 324, 330, 336 250 | dat 342, 348, 353, 359, 365, 371, 376, 382, 387, 392, 398, 403, 408, 413 251 | dat 418, 423, 427, 432, 437, 441, 445, 449, 453, 457, 461, 465, 468, 472 252 | dat 475, 478, 481, 484, 487, 490, 492, 494, 497, 499, 500, 502, 504, 505 253 | dat 507, 508, 509, 510, 510, 511, 511, 511, 512, 511, 511, 511, 510, 510 254 | dat 509, 508, 507, 505, 504, 502, 500, 499, 497, 494, 492, 490, 487, 484 255 | dat 481, 478, 475, 472, 468, 465, 461, 457, 453, 449, 445, 441, 437, 432 256 | dat 427, 423, 418, 413, 408, 403, 398, 392, 387, 382, 376, 371, 365, 359 257 | dat 353, 348, 342, 336, 330, 324, 318, 312, 305, 299, 293, 287, 281, 274 258 | dat 268, 262, 256, 250, 244, 238, 231, 225, 219, 213, 207, 200, 194, 188 259 | dat 182, 176, 170, 164, 159, 153, 147, 141, 136, 130, 125, 120, 114, 109 260 | dat 104, 99, 94, 89, 85, 80, 75, 71, 67, 63, 59, 55, 51, 47, 44, 40, 37 261 | dat 34, 31, 28, 25, 22, 20, 18, 15, 13, 12, 10, 8, 7, 5, 4, 3, 2, 2, 1, 1 262 | dat 1, 0, 1, 1, 1, 2, 2, 3, 4, 5, 7, 8, 10, 12, 13, 15, 18, 20, 22, 25 263 | dat 28, 31, 34, 37, 40, 44, 47, 51, 55, 59, 63, 67, 71, 75, 80, 85, 89 264 | dat 94, 99, 104, 109, 114, 120, 125, 130, 136, 141, 147, 153, 159, 164 265 | dat 170, 176, 182, 188, 194, 200, 207, 213, 219, 225, 231, 238, 244, 250 266 | 267 | :vertices 268 | ;row1: (0, 256), (243, 79), (150, -207), (-150, -207), (-243, 79) 269 | ;row2: (150, 207), (-150, 207), (-243, -79), (0, -256), (243, -79) 270 | dat 256, 256, 256 271 | dat 256, 256, 65280 272 | dat 256, 65280, 256 273 | dat 256, 65280, 65280 274 | dat 65280, 256, 256 275 | dat 65280, 256, 65280 276 | dat 65280, 65280, 256 277 | dat 65280, 65280, 65280 278 | :lastvertex 279 | 280 | :edges 281 | dat 0, 3 ; 0, 1 282 | dat 0, 6 ; 0, 2 283 | dat 0, 12 ; 0, 4 284 | dat 3, 9 ; 1, 3 285 | dat 3, 15 ; 1, 5 286 | dat 6, 9 ; 2, 3 287 | dat 6, 18 ; 2, 6 288 | dat 9, 21 ; 3, 7 289 | dat 12,15 ; 4, 5 290 | dat 12,18 ; 4, 6 291 | dat 15,21 ; 5, 7 292 | dat 18,21 ; 6, 7 293 | :lastedge 294 | 295 | 296 | :angle 297 | dat 0 298 | :angle1 299 | dat 0 300 | 301 | :vertex 302 | dat 0 303 | :vertex1 304 | dat 0 305 | :vertex2 306 | dat 0 307 | 308 | :drawline 309 | set push, c 310 | set push, z 311 | set push, i 312 | set push, j 313 | jsr unsafe_drawline 314 | set j, pop 315 | set i, pop 316 | set z, pop 317 | set c, pop 318 | set pc, pop 319 | 320 | :unsafe_drawline 321 | ; a: loop counter (x1-x) 322 | ; x: dx (x1-x0) 323 | ; y: dy (y1-y0) 324 | ; i: address being plotted 325 | ; c: character value being plotted 326 | ; j: err*dx; 327 | ; inner loop: err += dy; if(err>=dx) { y++; err-=dx; } 328 | ; if x1|dx| we need to iterate by y, and potentially swap the points again 330 | ; so first, find out if |dy|>|dx| 331 | sub x, a ; x = dx -> x1 = a+x 332 | sub y, b ; y = dy -> y1 = b+y 333 | ; we can safely clobber czij here 334 | set c, x 335 | ifg c, 0x7fff 336 | mul c, 65535 ; c = abs(dx) 337 | set z, y 338 | ifg z, 0x7fff 339 | mul z, 65535 ; z = abs(dy) 340 | ifg z, c ; if abs(dy) > abs(dx) 341 | set pc, drawsteepline 342 | ife c, x ; if abs(dx) == x 343 | set pc, drawline1 344 | ; else, flip x0,y0 with x1,y1 345 | add a, x ; a = x1 346 | set x, c ; dx = abs(dx) 347 | add b, y ; b = y1 348 | mul y, 65535 ; dy = -dy 349 | :drawline1 350 | set j, b ; j = err... 351 | and j, 15 352 | mul j, x 353 | shr j, 4 ; err0 = ((y&15)*dx)>>4 354 | ; -- FIXME: need to fixup err by x&15*dy>>4 as well to account for subpixel 355 | ; horizontal offsets 356 | set i, b 357 | and i, 0xffe0 358 | shr a, 4 359 | add i, a 360 | add i, 0x7000 ; i = addr = (a>>4) + (b>>5)<<5 361 | set c, 0x0f1c ; char = lower half-block with bg set (= pixel on top) 362 | ifb b, 0x10 363 | xor c, 0xff00 ; lower by 1/2 block by swapping fg/bg 364 | set a, x 365 | add a, 15 ; round up to next 16th 366 | shr a, 4 ; a = (dx+15)/16 = loop counter 367 | ifg y, 0x7fff ; if dy<0 368 | set pc, dloop_shallow_negdy 369 | 370 | :dloop_shallow_posdy 371 | bor [i], c ; plot pixel 372 | sub a, 1 373 | ife a, 0 ; if a == 0, return 374 | set pc, pop 375 | add i, 1 376 | add j, y ; err += dy 377 | ifg x, j ; if dx > err 378 | set pc, dloop_shallow_posdy 379 | ; else ++y 380 | sub j, x ; err -= dx 381 | xor c, 0xff00 382 | ife c, 0x0f1c 383 | add i, 32 ; incr addr 384 | set pc, dloop_shallow_posdy 385 | 386 | :dloop_shallow_negdy 387 | bor [i], c ; plot pixel 388 | sub a, 1 389 | ife a, 0 ; if a == 0, return 390 | set pc, pop 391 | add i, 1 392 | add j, y ; err += dy (note: dy is negative) 393 | ifg 0x8000, j ; if err >= 0 394 | set pc, dloop_shallow_negdy 395 | ; else --y 396 | add j, x ; err += dx 397 | xor c, 0xff00 398 | ife c, 0xf01c 399 | sub i, 32 ; decr addr 400 | set pc, dloop_shallow_negdy 401 | 402 | 403 | 404 | :drawsteepline 405 | ife z, y ; if abs(dy) == y 406 | set pc, drawline2 407 | ; else, flip x0,y0 with x1,y1 408 | add a, x ; a = x1 409 | mul x, 65535 ; dx = -dx 410 | add b, y ; b = y1 411 | set y, z ; dy = abs(dy) 412 | :drawline2 413 | set j, a ; j = err... 414 | and j, 15 415 | mul j, y ; (in terms of dy) 416 | shr j, 4 ; err0 = ((x&15)*dy)>>4 417 | ; -- FIXME: need to fixup err by y&15*dx>>4 as well to account for subpixel 418 | ; horizontal offsets 419 | set i, b 420 | and i, 0xffe0 421 | shr a, 4 422 | add i, a 423 | add i, 0x7000 ; i = addr = (a>>4) + (b>>5)<<5 424 | set c, 0x0f1c ; char = lower half-block with bg set (= pixel on top) 425 | ifb b, 0x10 426 | xor c, 0xff00 ; lower by 1/2 block by swapping fg/bg 427 | set a, y 428 | add a, 15 ; round up to next 16th 429 | shr a, 4 ; a = (dy+15)/16 = loop counter 430 | ifg x, 0x7fff ; if dx<0 431 | set pc, dloop_steep_negdx 432 | 433 | :dloop_steep_posdx 434 | bor [i], c ; plot pixel 435 | sub a, 1 436 | ife a, 0 ; if a == 0, return 437 | set pc, pop 438 | xor c, 0xff00 439 | ife c, 0x0f1c 440 | add i, 32 ; incr addr 441 | add j, x ; err += dx 442 | ifg y, j ; if dy > err 443 | set pc, dloop_steep_posdx 444 | ; else ++x 445 | add i, 1 446 | sub j, y ; err -= dy 447 | set pc, dloop_steep_posdx 448 | 449 | :dloop_steep_negdx 450 | bor [i], c ; plot pixel 451 | sub a, 1 452 | ife a, 0 ; if a == 0, return 453 | set pc, pop 454 | xor c, 0xff00 455 | ife c, 0x0f1c 456 | add i, 32 ; incr addr 457 | add j, x ; err += dx 458 | ifg 0x8000, j ; if err >= 0 459 | set pc, dloop_steep_negdx 460 | ; else --x 461 | sub i, 1 462 | add j, y ; err += dy 463 | set pc, dloop_steep_negdx 464 | 465 | ;;; bss section 466 | 467 | :proj_cz 468 | dat 0 469 | :proj_cz1 470 | dat 0 471 | :proj_kx 472 | dat 0 473 | :proj_kx1 474 | dat 0 475 | 476 | :vbuf 477 | dat 0 478 | :vbuf1 479 | -------------------------------------------------------------------------------- /programs/enumerate_hardware.dasm: -------------------------------------------------------------------------------- 1 | #macro match_hardware(a, b, location) { 2 | IFE A a 3 | IFE B b 4 | SET [location] I 5 | } 6 | 7 | JSR enumerate_hardware 8 | BRK 9 | 10 | :enumerate_hardware 11 | HWN I 12 | :.loop 13 | IFE I 0 14 | SET PC .done 15 | SUB I 1 16 | HWQ I 17 | match_hardware(0xf615, 0x7349, lem) 18 | match_hardware(0x7406, 0x30cf, keyboard) 19 | match_hardware(0xb402, 0x12d0, clock) 20 | SET PC .loop 21 | :.done 22 | SET PC POP 23 | 24 | :lem 25 | DAT -1 26 | :keyboard 27 | DAT -1 28 | :clock 29 | DAT -1 30 | -------------------------------------------------------------------------------- /programs/example.dasm: -------------------------------------------------------------------------------- 1 | ; Try some basic stuff 2 | SET A, 0x30 ; 7c01 0030 3 | SET [0x1000], 0x20 ; 7de1 1000 0020 4 | SUB A, [0x1000] ; 7803 1000 5 | IFN A, 0x10 ; c00d 6 | SET PC, crash ; 7dc1 001a [*] 7 | 8 | ; Do a loopy thing 9 | SET I, 10 ; a861 10 | SET A, 0x2000 ; 7c01 2000 11 | :loop SET [0x2000+I], [A] ; 2161 2000 12 | SUB I, 1 ; 8463 13 | IFN I, 0 ; 806d 14 | SET PC, loop ; 7dc1 000d [*] 15 | 16 | ; Call a subroutine 17 | SET X, 0x4 ; 9031 18 | JSR testsub ; 7c10 0018 [*] 19 | SET PC, crash ; 7dc1 001a [*] 20 | 21 | :testsub SHL X, 4 ; 9037 22 | SET PC, POP ; 61c1 23 | 24 | ; Hang forever. X should now be 0x40 if everything went right. 25 | :crash SET PC, crash ; 7dc1 001a [*] 26 | 27 | ; [*]: Note that these can be one word shorter and one cycle faster by using the short form (0x00-0x1f) of literals, 28 | ; but my assembler doesn't support short form labels yet. 29 | -------------------------------------------------------------------------------- /programs/keyboard.dasm: -------------------------------------------------------------------------------- 1 | ; configure interrupt handler 2 | IAS interrupt_handler 3 | 4 | ; subscribe to keyboard interrupts 5 | SET A 3 6 | SET B 1 7 | HWI 1 8 | 9 | ; busy loop 10 | :loop 11 | SET PC loop 12 | 13 | ; our interrupt handler 14 | :interrupt_handler 15 | SET A 1 16 | HWI 1 ; get next character in buffer 17 | IFE C 0 18 | RFI ; buffer is empty 19 | SET PUSH C ; call keyboard handler 20 | JSR keyboard_handler 21 | SET 0 POP ; clean up stack 22 | RFI 23 | 24 | :keyboard_handler 25 | SET Z PICK 1 ; Z is now the character 26 | SET PC POP 27 | -------------------------------------------------------------------------------- /programs/life.dasm: -------------------------------------------------------------------------------- 1 | ; Conway's Game of Life 2 | ; Renders a 64x64 field by writing characters 3 | ; on a 16x8 grid 4 | 5 | ; map screen 6 | SET A 0 7 | SET B 0x8000 8 | HWI 0 9 | ; map font 10 | SET A 1 11 | SET B 0x8180 12 | HWI 0 13 | ; border color 14 | SET A 3 15 | SET B 0x4 16 | HWI 0 17 | 18 | ADD PC, 1 19 | :randseed 20 | dat 0xACE1 ; change to get different initial states 21 | 22 | ; Initialize the screen 23 | SET [0x8280], 0x4 ; red border color 24 | 25 | ; Set screen to the appropriate characters 26 | SET A, 0xf000 ; white fg | black bg | char # 27 | 28 | SET I, 0x8000 29 | :loop_init 30 | SET X, I 31 | AND X, 0xf 32 | SET Y, I 33 | SHR Y, 1 34 | AND Y, 0x70 35 | BOR X, Y 36 | BOR X, A 37 | SET [I], X 38 | ADD I, 1 39 | IFN I, 0x8180 40 | SET PC, loop_init 41 | 42 | ; the internal grid is actually 66x66, to not 43 | ; have to check if an access is out-of-bounds 44 | ; (we set the border to do toroidal wrap-around) 45 | :randomize_grid ; set a random initial state 46 | SET SP, 0x2105 47 | :randomize_loop 48 | JSR rand 49 | AND A, 1 50 | SET PUSH, A 51 | IFN 0x0fff, SP 52 | SET PC, randomize_loop 53 | 54 | ; The core loop iterates over cells in a block pattern 55 | ; it calculates 2x8 groups at a time, since that's 56 | ; the dimensions of one word of a character font 57 | 58 | ; C -- address of current field (since it's double-buffered) 59 | ; A, B -- coordinates inside current group 60 | ; X, Y -- coordinates of the current cell 61 | ; Z -- number of live neighbors 62 | ; SP -- address of last half-character we modified 63 | ; I -- top-left neighbor index 64 | ; J -- current half-character bitmap 65 | ; we modify a character by doing SET PUSH, J 66 | 67 | SET C, 0x1000 ; the live/dead cells are stored at 0x1000 and 0x3000 68 | 69 | :loop_main 70 | ; copy cells to let us do toroidal wrap-around. 71 | ; we have an MxN matrix, and need to copy the 72 | ; rows and columns to the opposite edges, and also 73 | ; do the corners properly 74 | 75 | ; Copy the M-1th row to the 1st row. 76 | SET SP, C ; source 77 | ADD SP, 0x1081 ; 66 * 64 + 1 78 | SET I, C ; target 79 | SET X, I 80 | ADD X, 65 ; the last element we write 81 | :toroid_row_zero 82 | ADD I, 1 83 | SET [I], POP 84 | IFN I, X 85 | SET PC, toroid_row_zero 86 | 87 | ; Copy the 2nd row to the Mth row. 88 | SET SP, C ; source 89 | ADD SP, 67 90 | SET I, C ; target 91 | ADD I, 0x10c2 ; 66 * 65 92 | SET X, I 93 | ADD X, 65 ; the last element we write 94 | :toroid_row_last 95 | ADD I, 1 96 | SET [I], POP 97 | IFN I, X 98 | SET PC, toroid_row_last 99 | 100 | ; Do the columns. 101 | SET I, C ; left 102 | SET J, C ; right 103 | ADD J, 64 104 | SET X, I 105 | ADD X, 0x1080 ; end address (X) (66 * 64) 106 | SET A, 66 ; increment amount 107 | :toroid_columns 108 | ADD I, A 109 | ADD J, A 110 | SET [I], [J] 111 | SET [J+1], [I+1] 112 | IFN I, X 113 | SET PC, toroid_columns 114 | 115 | ; Do the corners. 116 | SET [C], [C+0x10c0] ; (0,0) = (64,64) 117 | SET [C+65], [C+0x1081] ; (65, 0) = (1, 64) 118 | SET [C+0x10c2], [C+0x82] ; (0, 65) = (64, 1) 119 | SET [C+0x1103], [C+67] ; (65, 65) = (1, 1) 120 | 121 | SET X, 62 ; cell coords 122 | SET Y, 56 123 | SET SP, 0x8280 ; half-character address 124 | :loop_group 125 | SET A, 0 126 | SET J, 0 127 | :loop_a 128 | SET B, 8 129 | 130 | SET I, Y ; I = (Y+A)*66 + (X+B) + C (index of top-left neighbor) 131 | BOR I, 7 ; hoisted out of the inner loop 132 | MUL I, 66 133 | ADD I, X 134 | ADD I, A 135 | ADD I, C 136 | 137 | :loop_b 138 | SUB B, 1 139 | 140 | ; count how many neighbors we have 141 | SET Z, [I] ; -1, -1 142 | ADD Z, [I+0x1] ; 0, -1 143 | ADD Z, [I+0x2] ; 1, -1 144 | ADD Z, [I+0x42] ; -1, 0 145 | ADD Z, [I+0x44] ; 1, 0 146 | ADD Z, [I+0x84] ; -1, 1 147 | ADD Z, [I+0x85] ; 0, 1 148 | ADD Z, [I+0x86] ; 1, 1 149 | 150 | ; trick: cell is alive if (neighbors | alive) == 3 151 | BOR Z, [I+0x43] 152 | IFN Z, 3 153 | SET Z, 0 154 | AND Z, 1 155 | 156 | SHL J, 1 157 | IFE Z, 1 158 | XOR J, 1 ; set the font display 159 | 160 | XOR I, 0x4000 ; set the cell in the opposite page 161 | SET [I+0x43], Z 162 | XOR I, 0x4000 163 | 164 | SUB I, 66 165 | 166 | IFN B, 0 167 | SET PC, loop_b 168 | ADD A, 1 169 | IFN A, 2 170 | SET PC, loop_a 171 | SET PUSH, J 172 | SUB X, 2 173 | IFN EX, 0 174 | SET X, 62 175 | IFN EX, 0 176 | SUB Y, 8 177 | IFG SP, 0x8180 ; have we written the last character? 178 | SET PC, loop_group 179 | 180 | XOR C, 0x4000 181 | SET PC, loop_main 182 | 183 | :rand ; simple LFSR RNG -- only use the low bit! 184 | SET A, randseed 185 | SHR [A], 1 186 | IFN EX, 0 187 | XOR [A], 0xB400 188 | SET A, [A] 189 | SET PC, POP 190 | -------------------------------------------------------------------------------- /programs/matrix.dasm: -------------------------------------------------------------------------------- 1 | ; map screen 2 | SET A 0 3 | SET B 0x8000 4 | HWI 0 5 | 6 | set z, 0x1234 ; rand_seed 7 | set y, z ; cur_rand 8 | set pc, main_loop 9 | 10 | :next_rand 11 | mul y, 10061 12 | add y, 1 13 | set pc, pop 14 | 15 | :main_loop 16 | set i, 0 17 | set x, y 18 | set y, z 19 | set a, 0x8000 20 | 21 | :next_row1 22 | set j, 0 23 | :next_char1 24 | jsr next_rand 25 | ife [a], 0 26 | set pc, skip1 27 | ifb y, 0x7000 28 | set pc, skip1 29 | 30 | ; mutate char at [a] (j, i) 31 | set push, y 32 | set y, x 33 | jsr next_rand 34 | and [a], 0xff00 ; reset char 35 | and y, 0x003f 36 | bor [a], [code+y] ; set letter 37 | set x, y 38 | set y, pop 39 | 40 | :skip1 41 | add j, 1 42 | add a, 1 43 | ifg 32, j 44 | set pc, next_char1 45 | add i, 1 46 | ifg 12, i 47 | set pc, next_row1 48 | set y, x 49 | 50 | ; step 2: move all columns down 51 | set i, 12 52 | set a, 0x817f 53 | 54 | :next_row2 55 | set j, 32 56 | :next_char2 57 | ife [a], 0 58 | set pc, empty_char 59 | ifb [a+32], 0xffff 60 | set pc, move_down 61 | 62 | ; add new char at [a+32] (j, i+1) 63 | jsr next_rand 64 | set [a+32], [a] 65 | and [a+32], 0xff00 66 | set x, y 67 | and x, 0x003f 68 | bor [a+32], [code+x] 69 | and [a], 0x7fff 70 | set pc, skip2 71 | 72 | :empty_char 73 | set [a+32], 0 74 | 75 | :move_down 76 | and [a+32], 0x7fff 77 | 78 | :skip2 79 | sub j, 1 80 | sub a, 1 81 | ifg j, 0 82 | set pc, next_char2 83 | sub i, 1 84 | ifg i, 0 85 | set pc, next_row2 86 | set y, x 87 | 88 | ; step 3: update top layer 89 | set a, 0x8000 90 | set j, 0 91 | :next_char3 92 | jsr next_rand 93 | ifb y, 0x0700 94 | set pc, skip3 95 | ifb [a], 0xffff 96 | set pc, empty_char2 97 | 98 | set [a], 0x2000 99 | ifb y, 0x0800 100 | set [a], 0xa000 101 | set x, y 102 | and x, 0x003f 103 | bor [a], [code+x] 104 | set pc, skip3 105 | 106 | :empty_char2 107 | set [a], 0 108 | 109 | :skip3 110 | add j, 1 111 | add a, 1 112 | ifg 32, j 113 | set pc, next_char3 114 | 115 | set PC, main_loop 116 | 117 | sub PC, 1 118 | 119 | :code dat "00112334567889&||!!@==::**##<>>__TYYUDQZJJIX- ~~oiwlrkm//\\'[]^)`" 120 | -------------------------------------------------------------------------------- /programs/mem.dmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fogleman/DCPU-16/db6f7aa15cdee8f3e285c11ee5dd7d286fe46551/programs/mem.dmp -------------------------------------------------------------------------------- /programs/minecraft.dasm: -------------------------------------------------------------------------------- 1 | ;************************************************************ 2 | ; minecraft.dasm16 3 | ; v1.6 4 | ; Created by Pseudox/trevs231 5 | ;************************************************************ 6 | ;This file is stand alone. 7 | 8 | ;It does NOT currently support keyboard ring buffer. (It will soon) 9 | 10 | ;NOTE: If the Keyword RESERVE is not supported, go to 'mc_water_flow_saves' 11 | ; at the end of the variables section, and change it to DAT and copy 12 | ; '0x0,' 64 times 13 | 14 | ;************************************************************ 15 | ;NOTES FOR USING IN AN OS 16 | ;************************************************************ 17 | 18 | ;Copy code after line 47, indicated below. 19 | ;Call 'JSR minecraft_init' to start. 20 | 21 | ;NOTE Atlas OS USERS: 22 | ; You should NOT copy 'video_mem', 23 | ; as it is included in the OS. It is situated directly below 24 | ; the lines indicated previously. 25 | ; You can also delete the pushes and pops and insert pusha and popa, 26 | ; before and after calling 'minecraft_init' 27 | ; If you plan on suspending the program to run background 28 | ; functions, putting the suspend in 'mc_reset_input' is likely 29 | ; optimal. Remember to kill programs using the screen first. 30 | ; The program is designed to be killed after it exits. 31 | 32 | 33 | ; NOTE: for other OS, you may want to check if there are other 34 | ; conflicts with 'video mem', 'video_mem_end", and 'kbrd_in' 35 | 36 | ;************************************************************* 37 | ;************************************************************* 38 | 39 | JSR minecraft_init 40 | 41 | :crash SET PC, crash 42 | ; end of main section----------------- 43 | 44 | 45 | ;******************************************************** 46 | ;COPY CODE BELOW into your OS AND CALL 'JSR minecraft_init' 47 | ;vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv 48 | :video_mem DAT 0x8000 ;first screen location 49 | 50 | 51 | 52 | ;============================================================ 53 | ;MINECRAFT GAME FOR DCPU-16 v1.5 by Pseudox/trevs231 54 | ;============================================================ 55 | 56 | ;==================================================== 57 | ;Variables and constants 58 | 59 | :video_mem_end DAT 0x81FF ;last screen location 60 | :kbrd_in DAT 0x9000 ;where inputs are read from keyboard 61 | 62 | :mc_key_in_reset DAT 0x100 63 | :mc_fall_timer DAT 0x500 64 | :mc_fall_time DAT 0x500 65 | :mc_water_timer DAT 0x500 66 | 67 | :mc_player_pos DAT 0x80F0 68 | :mc_player_char DAT 0x4058 69 | 70 | :mc_current_block DAT 0x862A 71 | 72 | :mc_sky_char DAT 0x0B00 73 | :mc_water_char DAT 0x0100 74 | :mc_block_chars DAT 0x182A, 0x862A, 0x0200, 0x0800, 0x0600 75 | DAT 0x782A, 0xE82A, 0xC82A, 0xB82A, 0 76 | ; water source, dirt, grass, rock, cobblestone, wood, gold deposit 77 | ; redstone, diamond, 78 | 79 | :mc_up_key DAT 0x26 ;up arrow 80 | :mc_left_key DAT 0x25 ;left arrow 81 | :mc_right_key DAT 0x27 ;right arrow 82 | 83 | :mc_do_up_key DAT 0x0077 ;w 84 | :mc_do_down_key DAT 0x0073 ;s 85 | :mc_do_left_key DAT 0x0061 ;a 86 | :mc_do_right_key DAT 0x0064 ;d 87 | 88 | :mc_swap_key DAT 0x0065 ;e 89 | 90 | :mc_reset_key DAT 0x006F ;o 91 | :mc_quit_key DAT 0x0070 ;p 92 | 93 | :mc_water_flow_saves DAT 94 | 0,0,0,0,0,0,0,0, 95 | 0,0,0,0,0,0,0,0, 96 | 0,0,0,0,0,0,0,0, 97 | 0,0,0,0,0,0,0,0, 98 | 0,0,0,0,0,0,0,0, 99 | 0,0,0,0,0,0,0,0, 100 | 0,0,0,0,0,0,0,0, 101 | 0,0,0,0,0,0,0,0 102 | 103 | 104 | 105 | ;===================================================== 106 | 107 | :minecraft_init 108 | ;(Atlas OS only! can delete from here) 109 | SET PUSH, A 110 | SET PUSH, B 111 | SET PUSH, C 112 | SET PUSH, X 113 | SET PUSH, Y 114 | SET PUSH, Z 115 | SET PUSH, I 116 | SET PUSH, J ;(to here. dont forget to add pusha) 117 | 118 | :mc_reset_point 119 | SET z, [kbrd_in] ;initialize input buffer 120 | SET [z], 0 121 | 122 | SET x, [video_mem] 123 | SET [mc_fall_timer], [mc_fall_time] ;for fall speed 124 | SET [mc_water_timer], [mc_fall_time] ;for water flow speed 125 | SET J, x 126 | ADD j, 0x100 127 | :minecraft_init_loop1 128 | SET [x], [mc_sky_char] ;setting terrain 129 | ADD x, 1 130 | IFG j, x 131 | SET PC, minecraft_init_loop1 132 | 133 | ADD j, 0x20 134 | SET i, 2 135 | :minecraft_init_loop2 136 | SET [x], [mc_block_chars+i] 137 | ADD x, 1 138 | IFG j, x 139 | SET PC, minecraft_init_loop2 140 | 141 | ADD j, 0x40 142 | SET i, 1 143 | :minecraft_init_loop3 144 | SET [x], [mc_block_chars+i] 145 | ADD x, 1 146 | IFG j, x 147 | SET PC, minecraft_init_loop3 148 | 149 | SET j, [video_mem_end] 150 | ADD j, 0x1 151 | SET i, 3 152 | :minecraft_init_loop4 153 | SET [x], [mc_block_chars+i] 154 | ADD x, 1 155 | IFG j, x 156 | SET PC, minecraft_init_loop4 157 | 158 | SET i, 1 159 | SET [mc_current_block], [mc_block_chars+i] ;show current block 160 | SET x, [video_mem] 161 | SET [x], [mc_current_block] 162 | 163 | ;initialize player 164 | SET [mc_player_pos], [video_mem] 165 | ADD [mc_player_pos], 0xF0 166 | 167 | JSR mc_print_player 168 | 169 | SET [mc_key_in_reset], 0x100 170 | 171 | 172 | ;=========================================================== 173 | :mc_game_loop 174 | JSR mc_in_air_check 175 | JSR mc_water_flow 176 | 177 | IFE [z], [mc_up_key] 178 | JSR mc_jump 179 | IFE [z], [mc_left_key] 180 | JSR mc_move_left 181 | IFE [z], [mc_right_key] 182 | JSR mc_move_right 183 | 184 | IFE [z], [mc_do_up_key] 185 | JSR mc_do_up 186 | IFE [z], [mc_do_down_key] 187 | JSR mc_do_down 188 | IFE [z], [mc_do_left_key] 189 | JSR mc_do_left 190 | IFE [z], [mc_do_right_key] 191 | JSR mc_do_right 192 | 193 | IFE [z], [mc_swap_key] 194 | JSR mc_swap_item 195 | 196 | IFE [z], [mc_reset_key] 197 | SET PC, mc_reset_game 198 | 199 | IFE [z], [mc_quit_key] 200 | SET PC, mc_game_exit 201 | 202 | SUB [mc_key_in_reset], 1 203 | IFE [mc_key_in_reset], 0 204 | JSR mc_reset_input 205 | 206 | SET PC, mc_game_loop 207 | 208 | ;=================================================== 209 | :mc_game_exit 210 | SET [z], 0 211 | ;(Atlas OS only! delete from here) 212 | SET J, POP 213 | SET I, POP 214 | SET Z, POP 215 | SET Y, POP 216 | SET X, POP 217 | SET C, POP 218 | SET B, POP 219 | SET A, POP ; (to here. don't forget to add popa!) 220 | 221 | SET PC, POP 222 | 223 | ;===================================================== 224 | ;prevents input buffer from getting full 225 | ;while preventing issues with input 226 | :mc_reset_input 227 | SET [z], 0 228 | SET [mc_key_in_reset], 0x100 229 | SET PC, POP 230 | 231 | 232 | 233 | ;===================================================== 234 | :mc_jump 235 | SET [z], 0 236 | :mc_jump_water 237 | SET J, [mc_player_pos] 238 | SET X, [mc_player_pos] 239 | SUB J, 0x20 240 | IFG [video_mem], j ;at the top? 241 | SET PC, POP 242 | IFE [j], [mc_water_char] 243 | SET PC, mc_jump2 244 | IFN [j], [mc_sky_char] ;block above? 245 | SET PC, POP 246 | IFG [mc_fall_time], [mc_fall_timer] ;can't if in the air 247 | SET PC, POP 248 | :mc_jump2 249 | AND [X], 0x0F00 250 | SET [mc_player_pos], j 251 | JSR mc_print_player 252 | SUB [mc_fall_timer], 1 253 | SET PC, POP 254 | 255 | ;===================================================== 256 | :mc_move_left 257 | SET [z], 0 258 | SET j, [mc_player_pos] 259 | SET x, [mc_player_pos] 260 | MOD j, 0x20 261 | IFE j, 0 262 | SET PC, mc_wrap_left ;at left edge? 263 | 264 | SET J, [mc_player_pos] 265 | SUB j, 1 266 | IFE [j], [mc_sky_char] ;block above? 267 | SET PC, mc_move_left2 268 | IFN [j], [mc_water_char] 269 | SET PC, POP 270 | :mc_move_left2 271 | AND [X], 0x0F00 272 | SET [mc_player_pos], j 273 | JSR mc_print_player 274 | SET PC, POP 275 | 276 | :mc_wrap_left 277 | SET J, [mc_player_pos] 278 | ADD J, 0x1F 279 | IFE [j], [mc_sky_char] ;block above? 280 | SET PC, mc_move_leftw2 281 | IFN [j], [mc_water_char] 282 | SET PC, POP 283 | :mc_move_leftw2 284 | AND [X], 0x0F00 285 | SET [mc_player_pos], j 286 | JSR mc_print_player 287 | SET PC, POP 288 | 289 | ;==================================================== 290 | :mc_move_right 291 | SET [z], 0 292 | SET x, [mc_player_pos] 293 | SET j, [mc_player_pos] 294 | MOD j, 0x20 295 | IFE j, 0x1F ;at right edge? 296 | SET PC, mc_wrap_right 297 | 298 | SET J, [mc_player_pos] 299 | ADD j, 1 300 | IFE [j], [mc_sky_char] ;block above? 301 | SET PC, mc_move_right2 302 | IFN [j], [mc_water_char] 303 | SET PC, POP 304 | :mc_move_right2 305 | AND [X], 0x0F00 306 | SET [mc_player_pos], j 307 | JSR mc_print_player 308 | SET PC, POP 309 | 310 | 311 | :mc_wrap_right 312 | SET J, [mc_player_pos] 313 | SUB J, 0x1F 314 | IFE [j], [mc_sky_char] ;block above? 315 | SET PC, mc_move_rightw2 316 | IFN [j], [mc_water_char] 317 | SET PC, POP 318 | :mc_move_rightw2 319 | AND [X], 0x0F00 320 | SET [mc_player_pos], j 321 | JSR mc_print_player 322 | SET PC, POP 323 | 324 | ;====================================================== 325 | :mc_do_up 326 | SET [z], 0 327 | SET J, [mc_player_pos] 328 | SUB J, 0x20 329 | IFG [video_mem], j ;at the top? 330 | SET PC, POP 331 | IFE [video_mem], j ;current block? 332 | SET PC, POP 333 | 334 | IFN [j], [mc_sky_char] ;is it a block? 335 | SET PC, mc_do_is_block 336 | SET [j], [mc_current_block] 337 | 338 | SET PC, POP 339 | 340 | ;====================================================== 341 | :mc_do_down 342 | SET [z], 0 343 | SET J, [mc_player_pos] 344 | ADD J, 0x20 345 | IFG J, [video_mem_end] ;at the bottom? 346 | SET PC, POP 347 | 348 | IFN [j], [mc_sky_char] ;is it a block? 349 | SET PC, mc_do_is_block 350 | SET [j], [mc_current_block] 351 | 352 | SET PC, POP 353 | 354 | ;====================================================== 355 | :mc_do_left 356 | SET [z], 0 357 | SET j, [mc_player_pos] 358 | MOD j, 0x20 359 | IFE j, 0x0 ;at left edge? 360 | SET PC, mc_do_wrap_left 361 | 362 | SET J, [mc_player_pos] 363 | SUB j, 1 364 | IFE j, [video_mem] ;current block? 365 | SET PC, POP 366 | 367 | IFN [j], [mc_sky_char] ;block there? 368 | SET PC, mc_do_is_block 369 | SET [j], [mc_current_block] 370 | 371 | SET PC, POP 372 | 373 | 374 | :mc_do_wrap_left 375 | SET J, [mc_player_pos] 376 | ADD J, 0x1F 377 | 378 | IFN [J], [mc_sky_char] ;block there? 379 | SET PC, mc_do_is_block 380 | SET [j], [mc_current_block] 381 | 382 | SET PC, POP 383 | 384 | ;====================================================== 385 | :mc_do_right 386 | SET [z], 0 387 | SET j, [mc_player_pos] 388 | MOD j, 0x20 389 | IFE j, 0x1F ;at right edge? 390 | SET PC, mc_do_wrap_right 391 | 392 | SET J, [mc_player_pos] 393 | ADD j, 1 394 | IFN [j], [mc_sky_char] ;block there? 395 | SET PC, mc_do_is_block 396 | SET [j], [mc_current_block] 397 | 398 | SET PC, POP 399 | 400 | 401 | :mc_do_wrap_right 402 | SET J, [mc_player_pos] 403 | SUB J, 0x1F 404 | IFE j, [video_mem] ;current block? 405 | SET PC, POP 406 | 407 | IFN [J], [mc_sky_char] ;block there? 408 | SET PC, mc_do_is_block 409 | SET [j], [mc_current_block] 410 | 411 | SET PC, POP 412 | 413 | ;====================================================== 414 | :mc_do_is_block 415 | SET [j], [mc_sky_char] 416 | 417 | SET PC, POP 418 | 419 | ;====================================================== 420 | :mc_swap_item 421 | SET [z], 0 422 | SET j, mc_block_chars 423 | :mc_swap_item_loop 424 | IFE [mc_current_block], [j] 425 | SET PC, mc_swap_item_x 426 | ADD j, 1 427 | SET PC, mc_swap_item_loop 428 | 429 | :mc_swap_item_x 430 | ADD j, 1 431 | IFE [j], 0 432 | SET PC, mc_swap_item_reset 433 | SET [mc_current_block], [j] 434 | SET x, [video_mem] 435 | SET [x], [mc_current_block] 436 | SET PC, POP 437 | :mc_swap_item_reset 438 | SET [mc_current_block], [mc_block_chars] 439 | SET x, [video_mem] 440 | SET [x], [mc_current_block] 441 | SET PC, POP 442 | 443 | ;====================================================== 444 | :mc_reset_game 445 | SET [z], 0 446 | SET PC, mc_reset_point 447 | 448 | ;====================================================== 449 | ;checks if player is in the air 450 | :mc_in_air_check 451 | SET X, [mc_player_pos] 452 | ADD X, 0x20 453 | 454 | IFG x, [video_mem_end] ;at bottom? 455 | SET PC, POP 456 | 457 | IFE [x], [mc_sky_char] ;ground below? 458 | SET PC, mc_in_air 459 | IFE [x], [mc_water_char] 460 | SET PC, mc_in_air 461 | SET [mc_fall_timer], [mc_fall_time] 462 | SET PC, pop 463 | 464 | :mc_in_air 465 | SUB [mc_fall_timer], 1 466 | IFN [mc_fall_timer], 0 ;time up? 467 | SET PC, POP 468 | ;dont put anything here 469 | :mc_fall 470 | SET x, [mc_player_pos] 471 | AND [x], 0x0F00 472 | ADD [mc_player_pos], 0x20 473 | SET [mc_fall_timer], [mc_fall_time] 474 | JSR mc_print_player 475 | SET PC, POP 476 | 477 | :mc_in_air_check_on_ground 478 | SET C, [mc_fall_time] 479 | SET PC, POP 480 | 481 | 482 | ;====================================================== 483 | :mc_print_player 484 | SET X, [mc_player_pos] 485 | BOR [x], [mc_player_char] 486 | SET PC, POP 487 | 488 | ;====================================================== 489 | :mc_water_flow 490 | IFE [mc_water_timer], 0 491 | SET PC, mc_water_check 492 | SUB [mc_water_timer], 1 ;only does this every so often 493 | SET PC, POP 494 | 495 | :mc_water_check 496 | SET j, mc_water_flow_saves ;where it is to be stored 497 | SET i, [video_mem] 498 | ADD i, 1 499 | :mc_water_check_loop 500 | IFE [i], [mc_block_chars] ;are there any sources or water? 501 | JSR mc_flow 502 | SET a, [i] 503 | AND a, 0x0F00 504 | IFE a, [mc_water_char] 505 | JSR mc_flow 506 | ADD i, 1 507 | IFG [video_mem_end], i 508 | SET PC, mc_water_check_loop 509 | 510 | SET [j], 0 511 | SET j, mc_water_flow_saves 512 | :mc_water_print_loop 513 | SET i, [j] 514 | IFE i, 0 515 | SET PC, mc_flow_done 516 | AND [i], 0xF0FF 517 | BOR [i], [mc_water_char] 518 | ADD j, 1 519 | SET PC, mc_water_print_loop 520 | 521 | :mc_flow_done 522 | SET [mc_water_timer], [mc_fall_time] 523 | SET PC, POP 524 | 525 | :mc_flow_save 526 | SET [j], x ;saves location on screen 527 | ADD j, 1 528 | SET PC, POP 529 | 530 | ;===================================================== 531 | :mc_flow 532 | SET x, i 533 | ADD x, 0x20 534 | IFG x, [video_mem_end] ;at bottom? 535 | SET PC, mc_flow_left 536 | SET a, [x] 537 | AND a, 0x0F00 538 | IFE a, [mc_sky_char] ;block below? 539 | JSR mc_flow_save 540 | 541 | 542 | :mc_flow_left 543 | SET x, i 544 | MOD x, 0x20 545 | IFE x, 0x0 ;at left edge? 546 | SET PC, mc_flow_wrap_left 547 | 548 | SET x, i 549 | SUB x, 1 550 | IFE x, [video_mem] ;current block? 551 | SET PC, mc_flow_right 552 | 553 | SET a, [x] 554 | AND a, 0x0F00 555 | IFE a, [mc_sky_char] ;block below? 556 | JSR mc_flow_save 557 | 558 | SET PC, mc_flow_right 559 | 560 | :mc_flow_wrap_left 561 | SET x, i 562 | ADD x, 0x1F 563 | 564 | SET a, [x] 565 | AND a, 0x0F00 566 | IFE a, [mc_sky_char] ;block below? 567 | JSR mc_flow_save 568 | 569 | :mc_flow_right 570 | SET x, i 571 | MOD x, 0x20 572 | IFE x, 0x1F ;at right edge? 573 | SET PC, mc_flow_wrap_right 574 | 575 | SET x, i 576 | ADD x, 1 577 | SET a, [x] 578 | AND a, 0x0F00 579 | IFE a, [mc_sky_char] ;block below? 580 | JSR mc_flow_save 581 | 582 | SET PC, POP 583 | 584 | :mc_flow_wrap_right 585 | SET x, i 586 | SUB x, 0x1F 587 | IFE x, [video_mem] ;current block? 588 | SET PC, POP 589 | 590 | SET a, [x] 591 | AND a, 0x0F00 592 | IFE a, [mc_sky_char] ;block below? 593 | JSR mc_flow_save 594 | 595 | SET PC, POP 596 | ;============================================================== 597 | 598 | 599 | 600 | -------------------------------------------------------------------------------- /programs/minesweeper.dasm: -------------------------------------------------------------------------------- 1 | ; MINESWEEPER by RHY3756547 2 | ; 3 | ; My first DCPU-16 project 4 | ; Feel free to fork and port for operating systems. 5 | ; Heavily commented. :> 6 | ; 7 | ; Twitter: #RHY3756547 8 | 9 | ; ARROW KEYS to move, ENTER to select. 10 | ; F to toggle flag on location. 11 | 12 | ; Upon winning or losing, press enter to start again. 13 | 14 | ; map screen 15 | SET A 0 16 | SET B 0x8000 17 | HWI 0 18 | ; map font 19 | SET A 1 20 | SET B 0x8180 21 | HWI 0 22 | 23 | JSR intro 24 | 25 | SET A, tile0 26 | SET B, 89 27 | SET C, 0x8182 28 | JSR storetiles 29 | :restart 30 | SET [gamestate], 0 31 | SET [gametimer], 0 32 | SET [game_started], 0 33 | SET [old_select_x], 10 34 | SET [old_select_y], 10 35 | JSR clear_board 36 | SET A, boardtiles_l1 37 | SET B, 384 38 | SET C, 0x8000 39 | JSR drawtiles 40 | 41 | SET A, minestext 42 | SET B, 2 43 | SET C, 0x8122 44 | SET Y, 0x8F00 45 | JSR drawtext 46 | JSR draw_timer 47 | 48 | JSR change_selection 49 | SET PC, gameloop 50 | 51 | :gametimer DAT 0 52 | :timer_second_value DAT 575 53 | :youwintext DAT 0x004F, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055 54 | :youlosetext DAT 0x004F, 0x0050, 0x0051, 0x0056, 0x0057, 0x0058, 0x0059 55 | :logosmiley1 DAT 0x0000, 0xF014, 0xF015, 0xF016, 0xF017, 0x0000 56 | :logosmiley2 DAT 0xF033, 0xF034, 0xF035, 0xF036, 0xF037, 0xF038 57 | :logosmiley3 DAT 0x0000, 0xF039, 0xF03A, 0xF03B, 0xF03C, 0x0000 58 | :logosmiley2g DAT 0xF033, 0xF018, 0xF05C, 0xF07C, 0xF01C, 0xF038 59 | 60 | :blklogosmiley1 DAT 0xFF00, 0x0F14, 0x0F15, 0x0F16, 0x0F17, 0xFF00 61 | :blklogosmiley2 DAT 0x0F33, 0x0F18, 0x0F5C, 0x0F7C, 0x0F1C, 0x0F38 62 | :blklogosmiley3 DAT 0xFF00, 0x0F39, 0x0F3A, 0x0F3B, 0x0F3C, 0xFF00 63 | 64 | :spectrum DAT 0x0001, 0x0009, 0x0003, 0x000B, 0x0003, 0x0009 65 | 66 | :glasses1 DAT 0xF019, 0xF01A, 0xF01B, 0xF01C 67 | :glasses2 DAT 0xF01D, 0xF01E, 0xF01F, 0xF03D 68 | :glasses3 DAT 0xF03F, 0xF03F, 0xF03F, 0xF03F 69 | :glasses4 DAT 0x0000, 0xF05F, 0xF07D, 0xF07E 70 | :glasblank DAT 0x0000, 0x0000, 0x0000, 0x0000 71 | 72 | :minewhitetop DAT 0xF000, 0xF001, 0xF002, 0xF003, 0xF004, 0xF005, 0xF006, 0xF007, 0xF008, 0xF009, 0xF00A, 0xF00B, 0xF00C, 0xF00D, 0xF00E, 0xF00F, 0xF010, 0xF011, 0xF012 73 | 74 | :minewhitebtm DAT 0xF020, 0xF021, 0xF022, 0xF023, 0xF024, 0xF025, 0xF026 0xF027, 0xF028, 0xF029, 0xF02A, 0xF02B, 0xF02C, 0xF02D, 0xF02E, 0xF02F, 0xF030, 0xF031, 0xF032, 0xF032 75 | 76 | :sweeperwhitetop DAT 0xF040, 0xF041, 0xF042, 0xF043, 0xF044, 0xF045, 0xF046, 0xF047, 0xF048, 0xF049, 0xF04A, 0xF04B, 0xF04C, 0xF04D, 0xF04E, 0xF04F, 0xF050, 0xF051, 0xF052, 0xF053, 0xF054, 0xF055, 0xF056, 0xF057, 0xF058, 0xF059, 0xF05A, 0xF05B 77 | 78 | :sweeperwhitebtm DAT 0xF060, 0xF061, 0xF062, 0xF063, 0xF064, 0xF065, 0xF066 0xF067, 0xF068, 0xF069, 0xF06A, 0xF06B, 0xF06C, 0xF06D, 0xF06E, 0xF06F, 0xF070, 0xF071, 0xF072, 0xF073, 0xF074, 0xF075, 0xF076, 0xF077, 0xF078, 0xF079, 0xF07A, 0xF07B 79 | 80 | :mines DAT 10 ;change to your liking 81 | :minestext DAT 0x004E, 0x0045, 0x004E ;you should probably change this too 82 | 83 | :intro 84 | SET A, intro0 85 | SET B, 128 86 | SET C, 0x8180 87 | JSR storetiles 88 | SET I, 0 89 | SET A, 0x8000 90 | 91 | SET A, logosmiley1 92 | SET B, 5 93 | SET C, 0x808D 94 | JSR drawtiles 95 | SET A, logosmiley2 96 | SET B, 5 97 | SET C, 0x80AD 98 | JSR drawtiles 99 | SET A, logosmiley3 100 | SET B, 5 101 | SET C, 0x80CD 102 | JSR drawtiles 103 | 104 | SET I, 0 105 | 106 | :glasses_fall_loop ;deal with it, limited memory 107 | 108 | SET J, 0 109 | :slowdown_anim 110 | ADD J, 1 111 | IFN J, 1000 112 | SET PC, slowdown_anim 113 | 114 | SET B, 3 115 | SET X, I 116 | MOD X, 3 ;3 animation frames 117 | SET C, 0x800E 118 | SET Z, I 119 | DIV Z, 3 ;y pos 120 | MUL Z, 32 121 | ADD C, Z 122 | 123 | IFE X, 0 124 | SET A, glasses1 125 | IFE X, 1 126 | SET A, glasses2 127 | IFE X, 2 128 | SET A, glasses3 129 | 130 | JSR drawtiles 131 | 132 | SET A, glasblank 133 | SUB C, 32 134 | JSR drawtiles 135 | 136 | IFE X, 2 137 | JSR glassesbottom 138 | 139 | ADD I, 1 140 | 141 | IFN I, 17 142 | SET PC, glasses_fall_loop 143 | 144 | ;draw white smiley 145 | SET A, logosmiley1 146 | SET B, 5 147 | SET C, 0x808D 148 | JSR drawtiles 149 | SET A, logosmiley2g 150 | SET B, 5 151 | SET C, 0x80AD 152 | JSR drawtiles 153 | SET A, logosmiley3 154 | SET B, 5 155 | SET C, 0x80CD 156 | JSR drawtiles 157 | ;draw mine in white 158 | SET A, minewhitetop 159 | SET B, 18 160 | SET C, 0x8046 161 | JSR drawtiles 162 | SET A, minewhitebtm 163 | SET B, 18 164 | SET C, 0x8066 165 | JSR drawtiles 166 | ;draw sweeper in white 167 | SET A, sweeperwhitetop 168 | SET B, 27 169 | SET C, 0x80E2 170 | JSR drawtiles 171 | SET A, sweeperwhitebtm 172 | SET B, 27 173 | SET C, 0x8102 174 | JSR drawtiles 175 | 176 | SET J, 0 ;wait for a bit 177 | :wait_logo 178 | ADD J, 1 179 | IFN J, 1000 180 | SET PC, wait_logo 181 | 182 | SET J, 0 ;wait for a bit 183 | SET A, 0x8000 184 | :white_screen 185 | SET [A], 0xFF00 186 | ADD J, 1 187 | ADD A, 1 188 | IFN J, 384 189 | SET PC, white_screen 190 | 191 | 192 | ;draw black smiley 193 | SET A, blklogosmiley1 194 | SET B, 5 195 | SET C, 0x808D 196 | JSR drawtiles 197 | SET A, blklogosmiley2 198 | SET B, 5 199 | SET C, 0x80AD 200 | JSR drawtiles 201 | SET A, blklogosmiley3 202 | SET B, 5 203 | SET C, 0x80CD 204 | JSR drawtiles 205 | 206 | SET J, 0 ;cycle for a bit 207 | :cycle_logo 208 | ADD J, 1 209 | 210 | ;draw mine in rainbow 211 | SET A, minewhitetop 212 | SET B, 18 213 | SET C, 0x8046 214 | JSR drawrainbow 215 | SET A, minewhitebtm 216 | SET B, 18 217 | SET C, 0x8066 218 | JSR drawrainbow 219 | ;draw sweeper in rainbow 220 | SET A, sweeperwhitetop 221 | SET B, 27 222 | SET C, 0x80E2 223 | JSR drawrainbow 224 | SET A, sweeperwhitebtm 225 | SET B, 27 226 | SET C, 0x8102 227 | JSR drawrainbow 228 | 229 | IFN J, 66 230 | SET PC, cycle_logo ;just animate a few times as a kind of wait 231 | 232 | SET PC, POP 233 | 234 | :drawrainbow 235 | SET PUSH, I 236 | SET PUSH, C 237 | SET I, 0 238 | SET X, J 239 | MOD X, 6 ;number of colours in spectrum 240 | 241 | :drawrainbow_loop 242 | SET [C], [A] 243 | SHL [C], 8 244 | SHR [C], 8 245 | ADD [C], 0x0F00 246 | SET Y, X 247 | ADD Y, spectrum ;select colour from spectrum 248 | SET Y, [Y] 249 | SHL Y, 12 250 | BOR [C], Y 251 | ADD C, 1 252 | ADD A, 1 253 | ADD I, 1 254 | ADD X, 1 255 | MOD X, 6 256 | IFG I, B 257 | SET PC, drawrainbow_end 258 | SET PC, drawrainbow_loop 259 | 260 | :drawrainbow_end 261 | SET C, POP 262 | SET I, POP 263 | SET PC, POP 264 | 265 | :glassesbottom 266 | ADD C, 64 267 | SET A, glasses4 268 | JSR drawtiles 269 | SET PC, POP 270 | 271 | :clear_board 272 | SET I, 0 273 | SET J, board 274 | SET Y, flag 275 | 276 | :clearboard_loop 277 | SET [J], 0 278 | SET [Y], 0 279 | ADD J, 1 280 | ADD Y, 1 281 | ADD I, 1 282 | IFN I, 81 283 | SET PC, clearboard_loop 284 | 285 | SET A, tiletypes_left ;reset mines too, shouldn't be visible 286 | ADD A, 10 287 | SET [A], 0x8701 288 | 289 | SET A, tiletypes_right 290 | ADD A, 10 291 | SET [A], 0x8702 292 | 293 | SET PC, POP 294 | 295 | :drawtext ; Y is colour 296 | SET PUSH, I 297 | SET I, 0 298 | SET PUSH, X 299 | 300 | :drawtext_loop 301 | SET X, [A] 302 | BOR X, Y 303 | SET [C], X 304 | ADD C, 1 305 | ADD A, 1 306 | ADD I, 1 307 | IFG I, B 308 | SET PC, drawtext_end 309 | SET PC, drawtext_loop 310 | 311 | :drawtext_end 312 | SET X, POP 313 | SET I, POP 314 | SET PC, POP 315 | 316 | :gamestate DAT 0 ;0 = normal, 1 = win, 2 = lose 317 | 318 | :endgame 319 | 320 | IFE [gamestate], 1 321 | SET A, youwintext 322 | IFE [gamestate], 2 323 | SET A, youlosetext 324 | SET B, 6 325 | SET C, 0x816C 326 | IFE [gamestate], 1 327 | SET Y, 0x9700 328 | IFE [gamestate], 2 329 | SET Y, 0x4700 330 | JSR drawtext 331 | 332 | :endgame_loop 333 | 334 | SET C, 0x9000 335 | SET B, 0x0000 336 | :buffloop2 337 | IFN [C], 0 338 | SET B, [C] 339 | SET [C], 0 340 | ADD C, 1 341 | IFN C, 0x9010 342 | SET pc, buffloop2 343 | 344 | IFE B, 0x000a 345 | SET PC, restart 346 | 347 | SET [C], 0 348 | SET C, 0x9000 349 | 350 | SET PC, endgame_loop 351 | 352 | ;-------------------------------------------------------------------- 353 | ;this is the gameloop 354 | ;it loops while the game is running 355 | ; :) 356 | ;-------------------------------------------------------------------- 357 | 358 | :gameloop 359 | IFN [gamestate], 0 ;won or lost 360 | SET PC, endgame 361 | ADD [timer], 1 362 | IFE [game_started], 0 ;timer is non functional 363 | SET PC, skip_timer 364 | 365 | SET A, [timer] 366 | MOD A, [timer_second_value] 367 | IFN A, 0 368 | SET PC, skip_timer 369 | 370 | ADD [gametimer], 1 371 | IFG [gametimer], 999 372 | SET [gametimer], 999 373 | 374 | JSR draw_timer 375 | 376 | :skip_timer 377 | SET C, 0x9000 378 | SET B, 0x0000 379 | :buffloop 380 | IFN [C], 0 381 | SET B, [C] 382 | SET [C], 0 383 | ADD C, 1 384 | IFN C, 0x9010 385 | SET pc, buffloop 386 | 387 | SET A, 0 388 | IFE B, 0x0025 389 | SET A, 2 390 | IFE B, 0x0026 391 | SET A, 4 392 | IFE B, 0x0027 393 | SET A, 1 394 | IFE B, 0x0028 395 | SET A, 3 396 | IFE B, 0x000a 397 | JSR action 398 | IFE B, 0x0066 ;f to flag 399 | JSR flagspace 400 | 401 | IFN A, 0 402 | JSR select_move 403 | 404 | SET [C], 0 405 | 406 | 407 | SET PC, gameloop 408 | 409 | :select_move 410 | IFE A, 1 ;right 411 | ADD [select_x], 1 412 | IFE A, 2 ;left 413 | SUB [select_x], 1 414 | IFE A, 3 ;down 415 | ADD [select_y], 1 416 | IFE A, 4 ;up 417 | SUB [select_y], 1 418 | IFG [select_x], 10 419 | SET [select_x], 0 420 | IFG [select_y], 10 421 | SET [select_y], 0 422 | IFG [select_x], 8 423 | SET [select_x], 8 424 | IFG [select_y], 8 425 | SET [select_y], 8 426 | 427 | JSR change_selection 428 | SET PC, POP 429 | 430 | :draw_timer 431 | SET PUSH, A 432 | SET PUSH, B 433 | SET PUSH, C 434 | SET A, 0x813B ;timer's positon on the screen 435 | 436 | SET C, [gametimer] 437 | DIV C, 100 ;to get NXX (n being this character) 438 | MOD C, 10 ;get lowest int 439 | SET B, 0x8F44 440 | ADD B, C ;get tile pos 441 | IFE C, 0 442 | SET B, 0x8F4E ;0 443 | 444 | SET [A], B 445 | ADD A, 1 446 | 447 | SET C, [gametimer] 448 | DIV C, 10 ;to get XNX (n being this character) 449 | MOD C, 10 ;get lowest int 450 | SET B, 0x8F44 451 | ADD B, C ;get tile pos 452 | IFE C, 0 453 | SET B, 0x8F4E ;0 454 | 455 | SET [A], B 456 | ADD A, 1 457 | 458 | SET C, [gametimer] 459 | MOD C, 10 ;get lowest int 460 | SET B, 0x8F44 461 | ADD B, C ;get tile pos 462 | IFE C, 0 463 | SET B, 0x8F4E ;0 464 | 465 | SET [A], B 466 | 467 | SET C, POP 468 | SET B, POP 469 | SET A, POP 470 | SET PC, POP 471 | 472 | :flagspace 473 | SET PUSH, A 474 | SET A, [select_x] 475 | SET B, [select_y] 476 | MUL B, 9 477 | ADD A, B 478 | ADD A, flag ;move to flag memory 479 | ADD [A], 1 ;change flag value 480 | MOD [A], 2 ;can only be 0 or 1 481 | 482 | JSR redraw_board 483 | 484 | SET A, POP 485 | SET PC, POP 486 | 487 | :action 488 | IFE [game_started], 0 489 | JSR, generate_board 490 | 491 | SET [game_started], 1 492 | 493 | SET PUSH, A 494 | SET PUSH, C 495 | 496 | SET A, [select_x] 497 | SET B, [select_y] 498 | SET X, A ;get normal position for mine seeking 499 | SET Y, B ;^^^ 500 | MUL B, 9 501 | ADD A, B 502 | SET Z, A 503 | ADD Z, flag ;move to flag memory 504 | ADD A, board ;move to board memory 505 | 506 | IFE [Z], 1 ;space is flagged 507 | SET PC, end_action ;yo don't do it bro 508 | 509 | IFE [A], 0 510 | SET PC, mine_seek 511 | IFE [A], 10 512 | JSR lose 513 | 514 | :end_action 515 | 516 | SET C, POP 517 | SET A, POP 518 | ;SET J, POP ;j-pop is shit 519 | SET PC, POP 520 | 521 | :mine_seek 522 | SET Z, 1 523 | SET I, 0 524 | SET J, surrounding_work 525 | IFG [A], 0 526 | ADD I, 1 527 | IFG 10, [A] 528 | ADD I, 1 529 | IFG I, 1 ;within selected tile range 530 | SET PC, POP ;do nothing because the tile's already selected 531 | 532 | SET PUSH, A 533 | SET PUSH, B 534 | SET PUSH, C 535 | 536 | SET A, surprised_top 537 | SET B, 3 538 | SET C, 0x800E 539 | JSR drawtiles 540 | 541 | SET A, surprised_btm 542 | SET B, 3 543 | SET C, 0x802E 544 | JSR drawtiles 545 | 546 | SET C, POP 547 | SET B, POP 548 | SET A, POP 549 | 550 | SET I, 0 551 | JSR check_surroundings ;check the surroundings of the selected tile 552 | IFE I, 0 553 | SET PC, skip_surrounding_loop ;if it's secluded don't do anything (or it'll like crash or something) 554 | 555 | :mine_seek_loop ;oh god 556 | JSR move_surrounding_table 557 | SET J, surrounding_work 558 | SET I, 0 559 | 560 | :surrounding_process_loop 561 | IFE [J], 0 562 | SET PC, skip_surrounding 563 | SET Y, J 564 | SUB Y, surrounding_work 565 | SET A, Y 566 | ADD A, board 567 | DIV Y, 9 ;remove x 568 | SET X, J 569 | SUB X, surrounding_work 570 | MOD X, 9 571 | JSR check_surroundings ;check the surroundings of the selected tile 572 | :skip_surrounding 573 | ADD J, 1 574 | IFN [J], 2 ;end byte 2 575 | SET PC, surrounding_process_loop 576 | 577 | IFN I, 0 ;if nothing was added to surrounding exit the loops 578 | SET PC mine_seek_loop 579 | 580 | :skip_surrounding_loop 581 | 582 | JSR redraw_board 583 | 584 | SET A, norm_top 585 | SET B, 3 586 | SET C, 0x800E 587 | JSR drawtiles 588 | 589 | SET A, norm_btm 590 | SET B, 3 591 | SET C, 0x802E 592 | JSR drawtiles 593 | 594 | JSR wincheck 595 | 596 | SET PC, end_action 597 | 598 | :wincheck 599 | SET A, board 600 | SET I, 0 601 | 602 | :wincheckloop 603 | IFE [A], 0 604 | SET PC, POP 605 | ADD I, 1 606 | ADD A, 1 607 | IFN I, 81 608 | SET PC, wincheckloop 609 | SET [gamestate], 1 ;win is gamestate 1 610 | SET PC, POP 611 | 612 | :move_surrounding_table 613 | SET A, surrounding_next 614 | SET B, surrounding_work 615 | 616 | :move_s_table_loop 617 | SET [B], [A] ;just put it into the read table 618 | SET [A], 0 ;simultaneously clearing the write table 619 | ADD A, 1 620 | ADD B, 1 621 | IFN [A], 2 ;if it hasn't reached the end byte (2, how imaginative) then loop 622 | SET PC, move_s_table_loop 623 | SET PC, POP 624 | 625 | :redraw_board 626 | SET X, 0 627 | SET Y, 0 628 | SET I, 0 629 | SET A, board 630 | SET Z, flag 631 | SET PC, redraw_loop 632 | 633 | 634 | :tiletypes_left DAT 0x8701, 0x7F1E, 0x7F21, 0x7F23, 0x7F25, 0x7F27, 0x7F29, 0x7F2B, 0x7F2D, 0x7F2F, 0x8701, 0x3B31 635 | 636 | 637 | :tiletypes_right DAT 0x8702, 0x7F1F, 0x7F22, 0x7F24, 0x7F26, 0x7F28, 0x7F2A, 0x7F2C, 0x7F2E, 0x7F30, 0x8702, 0x3B32 638 | 639 | :redraw_loop 640 | SET B, X 641 | SET C, Y 642 | MUL B, 2 643 | ADD B, 7 ;change board pos to screen pos 644 | ADD C, 2 645 | MUL C, 32 646 | ADD B, C 647 | ADD B, 0x8000 ;move to video ram 648 | 649 | SET C, [A] 650 | 651 | IFN [Z], 1 ;flagged 652 | SET PC, ingame_continue 653 | 654 | SET J, tiletypes_left 655 | ADD J, [A] 656 | 657 | IFE [J], 0x8701 ;if hidden and flagged then show the flag 658 | SET C, 11 659 | 660 | :ingame_continue 661 | SET J, tiletypes_left 662 | ADD J, C 663 | SET [B], [J] 664 | 665 | ADD B, 1 666 | 667 | SET J, tiletypes_right 668 | ADD J, C 669 | SET [B], [J] 670 | 671 | ADD I, 1 672 | ADD X, 1 673 | ADD A, 1 674 | ADD Z, 1 675 | IFG X, 8 676 | ADD Y, 1 677 | IFG X, 8 678 | SET X, 0 679 | IFG 81, I 680 | SET PC, redraw_loop ; loop until whole thing is done 681 | 682 | JSR redefine_selection ; the selected tile may have changed 683 | JSR change_selection 684 | 685 | ADD [moves], 1 686 | 687 | SET PC, POP 688 | 689 | :moves DAT 0 690 | 691 | :redefine_selection 692 | SET B, [select_x] 693 | SET C, [select_y] 694 | MUL B, 2 695 | ADD B, 7 ;change board pos to screen pos 696 | ADD C, 2 697 | MUL C, 32 698 | ADD B, C 699 | ADD B, 0x8000 ;move to video ram 700 | SET [old_select_tile1], [B] 701 | ADD B, 1 702 | SET [old_select_tile2], [B] 703 | SET PC, POP 704 | 705 | :check_surroundings ;i had to fix endless bugs with this 706 | SET Z, 1 707 | 708 | ADD X, 1 ;check right 709 | ADD A, 1 710 | JSR checkblock 711 | SUB Y, 1 ;check up-right 712 | SUB A, 9 713 | JSR checkblock 714 | SUB X, 1 ;check up 715 | SUB A, 1 716 | JSR checkblock 717 | SUB X, 1 ;check up-left 718 | SUB A, 1 719 | JSR checkblock 720 | ADD Y, 1 ;check left 721 | ADD A, 9 722 | JSR checkblock 723 | ADD Y, 1 ;check down-left 724 | ADD A, 9 725 | JSR checkblock 726 | ADD X, 1 ;check down 727 | ADD A, 1 728 | JSR checkblock 729 | ADD X, 1 ;check down-right 730 | ADD A, 1 731 | JSR checkblock 732 | SUB X, 1 733 | SUB Y, 1 734 | SUB A, 10 ;back to center 735 | SET [A], Z ;set tile type 736 | IFG 2, Z 737 | JSR countsurroundings ;if this isn't a number then every surrounding block becomes visible 738 | SET PC, POP 739 | 740 | :countsurroundings 741 | ADD X, 1 ;check right 742 | ADD A, 1 743 | IFE [A], 0 744 | JSR add_to_surroundings 745 | SUB Y, 1 ;check up-right 746 | SUB A, 9 747 | IFE [A], 0 748 | JSR add_to_surroundings 749 | SUB X, 1 ;check up 750 | SUB A, 1 751 | IFE [A], 0 752 | JSR add_to_surroundings 753 | SUB X, 1 ;check up-left 754 | SUB A, 1 755 | IFE [A], 0 756 | JSR add_to_surroundings 757 | ADD Y, 1 ;check left 758 | ADD A, 9 759 | IFE [A], 0 760 | JSR add_to_surroundings 761 | ADD Y, 1 ;check down-left 762 | ADD A, 9 763 | IFE [A], 0 764 | JSR add_to_surroundings 765 | ADD X, 1 ;check down 766 | ADD A, 1 767 | IFE [A], 0 768 | JSR add_to_surroundings 769 | ADD X, 1 ;check down-right 770 | ADD A, 1 771 | IFE [A], 0 772 | JSR add_to_surroundings 773 | 774 | SET PC, POP 775 | 776 | :add_to_surroundings 777 | ADD I, 1 778 | IFG X, 8 ;illegal block 779 | SET PC, POP 780 | IFG Y, 8 ;illegal block 781 | SET PC, POP 782 | SET B, A 783 | SUB B, board 784 | ADD B, surrounding_next ;change from board id into surroundings id 785 | SET [B], 1 ;1 means active 786 | SET PC, POP 787 | 788 | :checkblock 789 | IFG X, 8 ;illegal block 790 | SET PC, POP 791 | IFG Y, 8 ;illegal block 792 | SET PC, POP 793 | IFE [A], 10 794 | ADD Z, 1 ;increment block number 795 | SET PC, POP 796 | 797 | :lose 798 | SET A, wtf_top 799 | SET B, 3 800 | SET C, 0x800E 801 | JSR drawtiles 802 | 803 | SET A, wtf_btm 804 | SET B, 3 805 | SET C, 0x802E 806 | JSR drawtiles 807 | 808 | SET A, tiletypes_left 809 | ADD A, 10 810 | SET [A], 0x4C33 811 | 812 | SET A, tiletypes_right 813 | ADD A, 10 814 | SET [A], 0x4C34 815 | 816 | JSR redraw_board 817 | 818 | SET [gamestate], 2 ;2 is lose 819 | 820 | SET PC, POP ;???? 821 | 822 | 823 | :generate_board 824 | SET PUSH, A 825 | SET PUSH, B 826 | SET PUSH, C 827 | SET PUSH, X 828 | SET PUSH, I ;iterator 829 | SET I, 0 830 | 831 | :g_board_loop 832 | ADD [timer], 1 833 | SET A, [timer] 834 | MUL A, A ;entropy 835 | MOD A, 9 ;a is x of random mine placement 836 | SET B, [timer] 837 | ADD B, 317 ;b is y of mine placement, needs to be very different from A 838 | MUL B, B 839 | MOD B, 9 840 | 841 | SET C, A ;check if in proximity to cursor, if yes then position again. x 842 | SUB C, [select_x] 843 | ADD C, 10 ;work around underflow 844 | SET X, 0 ;used for checking and condition 845 | IFG C, 8 846 | ADD X, 1 847 | IFG 12, C 848 | ADD X, 1 849 | 850 | SET C, B ;do it for the y placement too 851 | SUB C, [select_y] 852 | ADD C, 10 ;work around underflow 853 | IFG C, 8 854 | ADD X, 1 855 | IFG 12, C 856 | ADD X, 1 857 | 858 | 859 | IFE X, 4 860 | SET PC g_board_loop 861 | 862 | MUL B, 9 863 | ADD A, B 864 | SET C, board 865 | ADD C, A 866 | IFE [C], 10 ;defined at top 867 | SET PC g_board_loop ;already a mine there 868 | SET [C], 10 869 | 870 | ADD I, 1 871 | IFG [mines], I 872 | SET PC g_board_loop ;not enough mines 873 | 874 | SET [timer], 0 875 | 876 | SET I, POP 877 | SET X, POP 878 | SET C, POP 879 | SET B, POP 880 | SET A, POP 881 | SET PC, POP 882 | 883 | :old_select_operation 884 | SET [A], [old_select_tile1] 885 | ADD A, 1 ;and the other side 886 | SET [A], [old_select_tile2] 887 | SET PC, POP 888 | 889 | :change_selection 890 | SET PUSH, A ; x value 891 | SET PUSH, B ; y value 892 | SET A, [old_select_x] 893 | SET B, [old_select_y] 894 | JSR get_selection 895 | 896 | IFN [old_select_x], 10 ;10 is the number which tells it not to do anything 897 | JSR old_select_operation 898 | 899 | SET A, [select_x] 900 | SET B, [select_y] 901 | JSR get_selection 902 | 903 | SET [old_select_tile1], [A] 904 | SHL [A], 4 ;remove highest 2 bits 905 | SHR [A], 4 906 | BOR [A], 0x2A00 907 | ADD A, 1 ;and the other side 908 | SET [old_select_tile2], [A] 909 | SHL [A], 4 ;remove highest 2 bits 910 | SHR [A], 4 911 | BOR [A], 0x2A00 912 | 913 | SET [old_select_x], [select_x] 914 | SET [old_select_y], [select_y] 915 | 916 | SET B, POP 917 | SET A, POP 918 | SET PC, POP 919 | 920 | :get_selection 921 | ADD B, 2 922 | MUL A, 2 923 | ADD A, 7 ;change x and y into screen position instead of game position 924 | MUL B, 32 ;multiply y by screen width to get memory offset 925 | ADD A, B ;the final offset moves into A 926 | ADD A, 0x8000 ;now it's the memory address, wasn't that fun 927 | SET PC, POP 928 | 929 | :old_select_x DAT 10 ;doesn't exist 930 | :old_select_y DAT 10 ;doesn't exist 931 | :old_select_tile1 DAT 10 ;ok that does exist but you get the point 932 | :old_select_tile2 DAT 10 933 | :select_x DAT 4 934 | :select_y DAT 4 935 | 936 | :timer DAT 0 937 | :game_started DAT 0 938 | 939 | :flag DAT 0, 0, 0, 0, 0, 0, 0, 0, 0 940 | DAT 0, 0, 0, 0, 0, 0, 0, 0, 0 941 | DAT 0, 0, 0, 0, 0, 0, 0, 0, 0 942 | DAT 0, 0, 0, 0, 0, 0, 0, 0, 0 943 | DAT 0, 0, 0, 0, 0, 0, 0, 0, 0 944 | DAT 0, 0, 0, 0, 0, 0, 0, 0, 0 945 | DAT 0, 0, 0, 0, 0, 0, 0, 0, 0 946 | DAT 0, 0, 0, 0, 0, 0, 0, 0, 0 947 | DAT 0, 0, 0, 0, 0, 0, 0, 0, 0 948 | 949 | :board DAT 0, 0, 0, 0, 0, 0, 0, 0, 0 950 | DAT 0, 0, 0, 0, 0, 0, 0, 0, 0 951 | DAT 0, 0, 0, 0, 0, 0, 0, 0, 0 952 | DAT 0, 0, 0, 0, 0, 0, 0, 0, 0 953 | DAT 0, 0, 0, 0, 0, 0, 0, 0, 0 954 | DAT 0, 0, 0, 0, 0, 0, 0, 0, 0 955 | DAT 0, 0, 0, 0, 0, 0, 0, 0, 0 956 | DAT 0, 0, 0, 0, 0, 0, 0, 0, 0 957 | DAT 0, 0, 0, 0, 0, 0, 0, 0, 0 ;10 for mines, 0 for undiscovered, 1-9 for selected, 11 for flagged. 958 | 959 | :surrounding_work DAT 0, 0, 0, 0, 0, 0, 0, 0, 0 ;working table, reads from 960 | DAT 0, 0, 0, 0, 0, 0, 0, 0, 0 961 | DAT 0, 0, 0, 0, 0, 0, 0, 0, 0 962 | DAT 0, 0, 0, 0, 0, 0, 0, 0, 0 963 | DAT 0, 0, 0, 0, 0, 0, 0, 0, 0 964 | DAT 0, 0, 0, 0, 0, 0, 0, 0, 0 965 | DAT 0, 0, 0, 0, 0, 0, 0, 0, 0 966 | DAT 0, 0, 0, 0, 0, 0, 0, 0, 0 967 | DAT 0, 0, 0, 0, 0, 0, 0, 0, 0, 2 968 | 969 | :surrounding_next DAT 0, 0, 0, 0, 0, 0, 0, 0, 0 ;next table, writes to 970 | DAT 0, 0, 0, 0, 0, 0, 0, 0, 0 971 | DAT 0, 0, 0, 0, 0, 0, 0, 0, 0 972 | DAT 0, 0, 0, 0, 0, 0, 0, 0, 0 973 | DAT 0, 0, 0, 0, 0, 0, 0, 0, 0 974 | DAT 0, 0, 0, 0, 0, 0, 0, 0, 0 975 | DAT 0, 0, 0, 0, 0, 0, 0, 0, 0 976 | DAT 0, 0, 0, 0, 0, 0, 0, 0, 0 977 | DAT 0, 0, 0, 0, 0, 0, 0, 0, 0, 2 ;board of surrounding tiles because my last idea overflowed all the way into the graphics :@ 978 | 979 | :end 980 | SET PC, end 981 | 982 | :drawtiles 983 | SET PUSH, I 984 | SET PUSH, C 985 | SET I, 0 986 | 987 | :drawtiles_loop 988 | SET [C], [A] 989 | ADD C, 1 990 | ADD A, 1 991 | ADD I, 1 992 | IFG I, B 993 | SET PC, drawtiles_end 994 | SET PC, drawtiles_loop 995 | 996 | :drawtiles_end 997 | SET C, POP 998 | SET I, POP 999 | SET PC, POP 1000 | 1001 | ;tilemap data for the main map 1002 | 1003 | :norm_top DAT 0x6F03, 0x6F04, 0x6F05, 0x6F06 1004 | :norm_btm DAT 0x6F07, 0x6F08, 0x6F09, 0x6F0A 1005 | 1006 | :surprised_top DAT 0x6F35, 0x6F36, 0x6F37, 0x6F38 1007 | :surprised_btm DAT 0x6F39, 0x6F3A, 0x6F3B, 0x6F3C 1008 | 1009 | :wtf_top DAT 0x6F3D, 0x6F3E, 0x6F3F, 0x6F40 1010 | :wtf_btm DAT 0x6F41, 0x6F42, 0x6F43, 0x6F44 1011 | 1012 | :boardtiles_l1 DAT 0x8720, 0x8720, 0x8720, 0x8720, 0x8720, 0x8720, 0x8720, 0x8720, 0x8720, 0x8720, 0x8720, 0x8720, 0x8720, 0x8720, 0x6F03, 0x6F04, 0x6F05, 0x6F06, 0x8720, 0x8720, 0x8720, 0x8720, 0x8720, 0x8720, 0x8720, 0x8720, 0x8720, 0x8720, 0x8720, 0x8720, 0x8720, 0x8720 1013 | 1014 | :boardtiles_l2 DAT 0x8720, 0x8720, 0x8720, 0x8720, 0x8720, 0x8720, 0x70B, 0x711, 0x711, 0x711, 0x711, 0x711, 0x711, 0x711, 0x6F07, 0x6F08, 0x6F09, 0x6F0A, 0x711, 0x711, 0x711, 0x711, 0x711, 0x711, 0x711, 0x70C, 0x8720, 0x8720, 0x8720, 0x8720, 0x8720, 0x8720 1015 | 1016 | :boardtiles_l3 DAT 0x8720, 0x8720, 0x8720, 0x8720, 0x8720, 0x8720, 0x70F, 0x8701, 0x8702, 0x8701, 0x8702, 0x8701, 0x8702, 0x8701, 0x8702, 0x8701, 0x8702, 0x8701, 0x8702, 0x8701, 0x8702, 0x8701, 0x8702, 0x8701, 0x8702, 0x710, 0x8720, 0x8720, 0x8720, 0x8720, 0x8720, 0x8720 1017 | 1018 | :boardtiles_l4 DAT 0x8720, 0x8720, 0x8720, 0x8720, 0x8720, 0x8720, 0x70F, 0x8701, 0x8702, 0x8701, 0x8702, 0x8701, 0x8702, 0x8701, 0x8702, 0x8701, 0x8702, 0x8701, 0x8702, 0x8701, 0x8702, 0x8701, 0x8702, 0x8701, 0x8702, 0x710, 0x8720, 0x8720, 0x8720, 0x8720, 0x8720, 0x8720 1019 | 1020 | :boardtiles_l5 DAT 0x8720, 0x8720, 0x8720, 0x8720, 0x8720, 0x8720, 0x70F, 0x8701, 0x8702, 0x8701, 0x8702, 0x8701, 0x8702, 0x8701, 0x8702, 0x8701, 0x8702, 0x8701, 0x8702, 0x8701, 0x8702, 0x8701, 0x8702, 0x8701, 0x8702, 0x710, 0x8720, 0x8720, 0x8720, 0x8720, 0x8720, 0x8720 1021 | 1022 | :boardtiles_l6 DAT 0x8720, 0x8720, 0x8720, 0x8720, 0x8720, 0x8720, 0x70F, 0x8701, 0x8702, 0x8701, 0x8702, 0x8701, 0x8702, 0x8701, 0x8702, 0x8701, 0x8702, 0x8701, 0x8702, 0x8701, 0x8702, 0x8701, 0x8702, 0x8701, 0x8702, 0x710, 0x8720, 0x8720, 0x8720, 0x8720, 0x8720, 0x8720 1023 | 1024 | :boardtiles_l7 DAT 0x8720, 0x8720, 0x8720, 0x8720, 0x8720, 0x8720, 0x70F, 0x8701, 0x8702, 0x8701, 0x8702, 0x8701, 0x8702, 0x8701, 0x8702, 0x8701, 0x8702, 0x8701, 0x8702, 0x8701, 0x8702, 0x8701, 0x8702, 0x8701, 0x8702, 0x710, 0x8720, 0x8720, 0x8720, 0x8720, 0x8720, 0x8720 1025 | 1026 | :boardtiles_l8 DAT 0x8720, 0x8720, 0x8720, 0x8720, 0x8720, 0x8720, 0x70F, 0x8701, 0x8702, 0x8701, 0x8702, 0x8701, 0x8702, 0x8701, 0x8702, 0x8701, 0x8702, 0x8701, 0x8702, 0x8701, 0x8702, 0x8701, 0x8702, 0x8701, 0x8702, 0x710, 0x8720, 0x8720, 0x8720, 0x8720, 0x8720, 0x8720 1027 | 1028 | :boardtiles_l9 DAT 0x8720, 0x8713, 0x8714, 0x8715, 0x8716, 0x8717, 0x70F, 0x8701, 0x8702, 0x8701, 0x8702, 0x8701, 0x8702, 0x8701, 0x8702, 0x8701, 0x8702, 0x8701, 0x8702, 0x8701, 0x8702, 0x8701, 0x8702, 0x8701, 0x8702, 0x710, 0x8718, 0x8719, 0x871A, 0x871B, 0x871C, 0x8720 1029 | 1030 | :boardtiles_20 DAT 0x8720, 0x871D, 0x8F20, 0x8F20, 0x8F20, 0x8710, 0x70F, 0x8701, 0x8702, 0x8701, 0x8702, 0x8701, 0x8702, 0x8701, 0x8702, 0x8701, 0x8702, 0x8701, 0x8702, 0x8701, 0x8702, 0x8701, 0x8702, 0x8701, 0x8702, 0x710, 0x871D, 0x8F20, 0x8F20, 0x8F20, 0x8710, 0x8720 1031 | 1032 | :boardtiles_21 DAT 0x8720, 0x870D, 0x8712, 0x8712, 0x8712, 0x870E, 0x70F, 0x8701, 0x8702, 0x8701, 0x8702, 0x8701, 0x8702, 0x8701, 0x8702, 0x8701, 0x8702, 0x8701, 0x8702, 0x8701, 0x8702, 0x8701, 0x8702, 0x8701, 0x8702, 0x710, 0x870D, 0x8712, 0x8712, 0x8712, 0x870E, 0x8720 1033 | 1034 | :boardtiles_22 DAT 0x8720, 0x8720, 0x8720, 0x8720, 0x8720, 0x8720, 0x70D, 0x712, 0x712, 0x712, 0x712, 0x712, 0x712, 0x712, 0x712, 0x712, 0x712, 0x712, 0x712, 0x712, 0x712, 0x712, 0x712, 0x712, 0x712, 0x70E, 0x8720, 0x8720, 0x8720, 0x8720, 0x8720, 0x8720 1035 | 1036 | :boardtiles_22 DAT 0x8720, 0x8720, 0x8720, 0x8720, 0x8720, 0x8720, 0x70D, 0x712, 0x712, 0x712, 0x712, 0x712, 0x712, 0x712, 0x712, 0x712, 0x712, 0x712, 0x712, 0x712, 0x712, 0x712, 0x712, 0x712, 0x712, 0x70E, 0x8720, 0x8720, 0x8720, 0x8720, 0x8720, 0x8720 1037 | 1038 | 1039 | ; Tiles. 1040 | 1041 | :tile0 DAT 0xff83, 0x45a9 1042 | :tile1 DAT 0x51b9, 0x7dff 1043 | :tile2 DAT 0xff01, 0x1c1 1044 | :tile3 DAT 0x7151, 0xc9c9 1045 | :tile4 DAT 0x49c9, 0xd171 1046 | :tile5 DAT 0xc101, 0x1ff 1047 | :tile6 DAT 0xff80, 0x8083 1048 | :tile7 DAT 0x8c89, 0x9292 1049 | :tile8 DAT 0x9290, 0x888c 1050 | :tile9 DAT 0x8380, 0x80ff 1051 | :tile10 DAT 0x0, 0x80 1052 | :tile11 DAT 0x8000, 0x0 1053 | :tile12 DAT 0x0, 0x1 1054 | :tile13 DAT 0x100, 0x0 1055 | :tile14 DAT 0x0, 0x55ff 1056 | :tile15 DAT 0xff00, 0x0 1057 | :tile16 DAT 0xc080, 0xc080 1058 | :tile17 DAT 0x101, 0x101 1059 | :tile18 DAT 0x3c, 0x888 1060 | :tile19 DAT 0xbc80, 0xbc80 1061 | :tile20 DAT 0xbc88, 0x90bc 1062 | :tile21 DAT 0x80bc, 0xaca4 1063 | :tile22 DAT 0x8028, 0x2414 1064 | :tile23 DAT 0x0, 0x84 1065 | :tile24 DAT 0xbc84, 0x80bc 1066 | :tile25 DAT 0x80bc, 0x8888 1067 | :tile26 DAT 0xbc80, 0xbcac 1068 | :tile27 DAT 0xa400, 0x0 1069 | :tile28 DAT 0x0, 0xff 1070 | :tile29 DAT 0xff01, 0x101 1071 | :tile30 DAT 0x101, 0x101 1072 | :tile31 DAT 0x0, 0x0 1073 | :tile32 DAT 0xff01, 0x149 1074 | :tile33 DAT 0x7d41, 0x101 1075 | :tile34 DAT 0xff01, 0x149 1076 | :tile35 DAT 0x6559, 0x101 1077 | :tile36 DAT 0xff01, 0x145 1078 | :tile37 DAT 0x5539, 0x101 1079 | :tile38 DAT 0xff01, 0x11d 1080 | :tile39 DAT 0x117d, 0x101 1081 | :tile40 DAT 0xff01, 0x15d 1082 | :tile41 DAT 0x5525, 0x101 1083 | :tile42 DAT 0xff01, 0x139 1084 | :tile43 DAT 0x5525, 0x101 1085 | :tile44 DAT 0xff01, 0x145 1086 | :tile45 DAT 0x350d, 0x101 1087 | :tile46 DAT 0xff01, 0x129 1088 | :tile47 DAT 0x5529, 0x101 1089 | :tile48 DAT 0xff83, 0x10d 1090 | :tile49 DAT 0xd7d, 0x183 1091 | :tile50 DAT 0xff01, 0x5539 1092 | :tile51 DAT 0x6d39, 0x5501 1093 | :tile52 DAT 0xff01, 0x1e1 1094 | :tile53 DAT 0x3131, 0xa969 1095 | :tile54 DAT 0x69a9, 0x7171 1096 | :tile55 DAT 0xa101, 0x1ff 1097 | :tile56 DAT 0xff80, 0x8083 1098 | :tile57 DAT 0x8c88, 0x9096 1099 | :tile58 DAT 0x9690, 0x888c 1100 | :tile59 DAT 0x8380, 0x80ff 1101 | :tile60 DAT 0xff01, 0x1c1 1102 | :tile61 DAT 0x3151, 0xc949 1103 | :tile62 DAT 0x2999, 0x1131 1104 | :tile63 DAT 0xc101, 0x1ff 1105 | :tile64 DAT 0xff80, 0x8083 1106 | :tile65 DAT 0xcc88, 0xd0de 1107 | :tile66 DAT 0x9ed0, 0xc8cc 1108 | :tile67 DAT 0x8380, 0x80ff 1109 | :tile68 DAT 0x48, 0x7c40 1110 | :tile69 DAT 0x48, 0x6458 1111 | :tile70 DAT 0x44, 0x5438 1112 | :tile71 DAT 0x1c, 0x107c 1113 | :tile72 DAT 0x5c, 0x5424 1114 | :tile73 DAT 0x38, 0x5424 1115 | :tile74 DAT 0x44, 0x340c 1116 | :tile75 DAT 0x28, 0x5428 1117 | :tile76 DAT 0x48, 0x5438 1118 | :tile77 DAT 0x38, 0x4438 1119 | :tile78 DAT 0x18, 0xe018 1120 | :tile79 DAT 0x60, 0x9060 1121 | :tile80 DAT 0x70, 0x80f0 1122 | :tile81 DAT 0x0, 0xf8 1123 | :tile82 DAT 0x8040, 0x80f8 1124 | :tile83 DAT 0xe8, 0xf0 1125 | :tile84 DAT 0x20e0, 0x0 1126 | :tile85 DAT 0xf8, 0x8080 1127 | :tile86 DAT 0x60, 0x9060 1128 | :tile87 DAT 0xb0, 0x90d0 1129 | :tile88 DAT 0xf0, 0x90b0 1130 | 1131 | ;1kb worth of tiles used for the intro sequence. worth it! 1132 | 1133 | 1134 | :intro0 DAT 0x0, 0x101 1135 | :intro1 DAT 0xc3eb, 0xebeb 1136 | :intro2 DAT 0xebea, 0xe8e0 1137 | :intro3 DAT 0xc000, 0x80e0 1138 | :intro4 DAT 0xe0e8, 0xeaeb 1139 | :intro5 DAT 0xebeb, 0xb01 1140 | :intro6 DAT 0x101, 0x0 1141 | :intro7 DAT 0x1, 0x1e1 1142 | :intro8 DAT 0xebeb, 0xebeb 1143 | :intro9 DAT 0xeb0b, 0x101 1144 | :intro10 DAT 0x100, 0x101 1145 | :intro11 DAT 0xc1eb, 0xebeb 1146 | :intro12 DAT 0xebea, 0xe8eb 1147 | :intro13 DAT 0xe3eb, 0x2b03 1148 | :intro14 DAT 0x101, 0x0 1149 | :intro15 DAT 0x101, 0xc1eb 1150 | :intro16 DAT 0xebeb, 0xebeb 1151 | :intro17 DAT 0xeb43, 0xc1c1 1152 | :intro18 DAT 0x303, 0xb00 1153 | :intro19 DAT 0x0, 0x0 1154 | :intro20 DAT 0xf0f0, 0x3030 1155 | :intro21 DAT 0xc0c, 0xc0c 1156 | :intro22 DAT 0xc0c, 0xc0c 1157 | :intro23 DAT 0x3030, 0xf0f0 1158 | :intro24 DAT 0x303, 0x3333 1159 | :intro25 DAT 0x303, 0x303 1160 | :intro26 DAT 0xf0f, 0xf0f 1161 | :intro27 DAT 0x303, 0xf0f 1162 | :intro28 DAT 0xf0f, 0x303 1163 | :intro29 DAT 0x1818, 0x1818 1164 | :intro30 DAT 0x7878, 0x7878 1165 | :intro31 DAT 0x1818, 0x7878 1166 | :intro32 DAT 0x4040, 0x6078 1167 | :intro33 DAT 0x7f61, 0x434f 1168 | :intro34 DAT 0x1f7f, 0x3f1f 1169 | :intro35 DAT 0x4f47, 0x417f 1170 | :intro36 DAT 0x7f7f, 0x7f7f 1171 | :intro37 DAT 0x7f6f, 0x4040 1172 | :intro38 DAT 0x0, 0x40 1173 | :intro39 DAT 0x4040, 0x787f 1174 | :intro40 DAT 0x7f7f, 0x7f7f 1175 | :intro41 DAT 0x4740, 0x4040 1176 | :intro42 DAT 0x40, 0x6070 1177 | :intro43 DAT 0x7f71, 0x6347 1178 | :intro44 DAT 0xf1f, 0x3f7f 1179 | :intro45 DAT 0x7f0f, 0x0 1180 | :intro46 DAT 0x0, 0x4040 1181 | :intro47 DAT 0x4060, 0x7f7f 1182 | :intro48 DAT 0x7f7f, 0x7f7f 1183 | :intro49 DAT 0x4340, 0x6073 1184 | :intro50 DAT 0x703c, 0x600 1185 | :intro51 DAT 0x0, 0xffff 1186 | :intro52 DAT 0x0, 0x3030 1187 | :intro53 DAT 0xc3c3, 0xc0c0 1188 | :intro54 DAT 0xc0c0, 0x303 1189 | :intro55 DAT 0x0, 0x0 1190 | :intro56 DAT 0xffff, 0x0 1191 | :intro57 DAT 0xf0f, 0xc0c 1192 | :intro58 DAT 0x3030, 0x3030 1193 | :intro59 DAT 0x3030, 0x3030 1194 | :intro60 DAT 0xc0c, 0xf0f 1195 | :intro61 DAT 0x7878, 0x1818 1196 | :intro62 DAT 0xc0c0, 0xc0c0 1197 | :intro63 DAT 0xc0c0, 0xc0c0 1198 | :intro64 DAT 0xc0d0, 0xd4d4 1199 | :intro65 DAT 0xd2d2, 0xc2c6 1200 | :intro66 DAT 0xc6d6, 0x601 1201 | :intro67 DAT 0x0, 0x216 1202 | :intro68 DAT 0xd6d6, 0xd6d6 1203 | :intro69 DAT 0xd6d2, 0x12d0 1204 | :intro70 DAT 0xd0d0, 0xd4d4 1205 | :intro71 DAT 0xd486, 0x284 1206 | :intro72 DAT 0x4416, 0x602 1207 | :intro73 DAT 0x2, 0x2d6 1208 | :intro74 DAT 0xd6d6, 0xd6d6 1209 | :intro75 DAT 0xd686, 0x8282 1210 | :intro76 DAT 0x616, 0x0 1211 | :intro77 DAT 0x2, 0x2d6 1212 | :intro78 DAT 0xd6d6, 0xd6d6 1213 | :intro79 DAT 0xd686, 0x8282 1214 | :intro80 DAT 0x616, 0x0 1215 | :intro81 DAT 0x2, 0x2d6 1216 | :intro82 DAT 0xd6d6, 0xd6d6 1217 | :intro83 DAT 0xd602, 0x2d6 1218 | :intro84 DAT 0xd400, 0x2 1219 | :intro85 DAT 0x2d6, 0xd6d6 1220 | :intro86 DAT 0xd6d6, 0xd686 1221 | :intro87 DAT 0x8282, 0x616 1222 | :intro88 DAT 0x0, 0x0 1223 | :intro89 DAT 0x202, 0xd6d6 1224 | :intro90 DAT 0xd6d6, 0xd6d6 1225 | :intro91 DAT 0x606, 0x8454 1226 | :intro92 DAT 0xcfcf, 0xcfcf 1227 | :intro93 DAT 0xc0c0, 0xc0c0 1228 | :intro94 DAT 0xc0c0, 0xc0c0 1229 | :intro95 DAT 0x303, 0x303 1230 | :intro96 DAT 0xfb67, 0xcf8f 1231 | :intro97 DAT 0x9f9f, 0x9f7f 1232 | :intro98 DAT 0x7f1f, 0x700 1233 | :intro99 DAT 0x0, 0x0 1234 | :intro100 DAT 0xf7f, 0xffff 1235 | :intro101 DAT 0x3f1f, 0xe03 1236 | :intro102 DAT 0xf3f, 0xff7f 1237 | :intro103 DAT 0x3f0f, 0x601 1238 | :intro104 DAT 0x0, 0x80 1239 | :intro105 DAT 0x8080, 0xf0ff 1240 | :intro106 DAT 0xffff, 0xffff 1241 | :intro107 DAT 0x87c0, 0xc3e7 1242 | :intro108 DAT 0xf00c, 0x80 1243 | :intro109 DAT 0x8080, 0xf0ff 1244 | :intro110 DAT 0xffff, 0xffff 1245 | :intro111 DAT 0x87c0, 0xc3e7 1246 | :intro112 DAT 0xf00c, 0x80 1247 | :intro113 DAT 0x80c0, 0xfeff 1248 | :intro114 DAT 0xffff, 0xffff 1249 | :intro115 DAT 0x8282, 0x301 1250 | :intro116 DAT 0x80, 0x8080 1251 | :intro117 DAT 0xf0ff, 0xffff 1252 | :intro118 DAT 0xffff, 0x87c0 1253 | :intro119 DAT 0xc3e7, 0xf00c 1254 | :intro120 DAT 0x0, 0x8080 1255 | :intro121 DAT 0xc0fe, 0xffff 1256 | :intro122 DAT 0xffff, 0xbf81 1257 | :intro123 DAT 0x83ff, 0xfffe 1258 | :intro124 DAT 0xc3c3, 0xf0f 1259 | :intro125 DAT 0x0, 0x303 1260 | :intro126 DAT 0x303, 0x0 1261 | :intro127 DAT 0x0, 0x0 1262 | 1263 | 1264 | 1265 | :storetiles ;A = Data to Read, B = Length, C = Destination, I = Iterator 1266 | SET PUSH, I 1267 | MUL B, 2 ;2 words to a character 1268 | SET I, 0 1269 | 1270 | :storetiles_loop 1271 | SET [C], [A] 1272 | ADD A, 1 1273 | ADD I, 1 1274 | ADD C, 1 1275 | IFE I, B 1276 | SET PC, storetiles_end 1277 | SET PC, storetiles_loop 1278 | 1279 | :storetiles_end 1280 | SET I, POP 1281 | SET PC, POP 1282 | -------------------------------------------------------------------------------- /programs/nyan.dasm: -------------------------------------------------------------------------------- 1 | HWN Z 2 | :get_monitor 3 | IFE Z, 0 4 | SET PC, not_found 5 | SUB Z, 1 6 | HWQ Z 7 | IFN A, 0xF615 8 | SET PC, get_monitor 9 | SET [monitor], Z 10 | 11 | SET B, font_space 12 | SET A, 1 13 | HWI [monitor] 14 | 15 | SET B, palette_space 16 | SET A, 2 17 | HWI [monitor] 18 | 19 | SET A, 0 20 | SET B, tile_space 21 | HWI [monitor] 22 | 23 | :frame_loop 24 | IFE B, exit 25 | SET B, tile_space 26 | HWI [monitor] 27 | JSR delay 28 | ADD B, 0x0180 29 | SET PC, frame_loop 30 | 31 | :delay 32 | SET X, 0 33 | :loop 34 | ADD X, 1 35 | IFN X, 1500 36 | SET PC, loop 37 | SET PC, POP 38 | :font_space DAT 0x0f0f, 0x0f0f 39 | :palette_space DAT 0x0036, 0x0000, 0x0faf, 0x0aaa, 0x0fda, 0x0f00, 0x0fa0, 0x0ff0, 0x00af, 0x063f, 0x03f0, 0x0fff, 0x0faa, 0x0f3a, 0x0000, 0x0000, 40 | :tile_space DAT 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xb000, 0x0000, 0x0500, 0x0500, 0x0500, 0x0500, 0x0500, 0x0500, 0x0500, 0x0000, 0x0000, 0x0000, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0000, 0x0000, 0x0000, 0x0000, 0xb000, 0x0000, 0x0000, 0x0000, 0x5500, 0x5500, 0x5500, 0x5500, 0x5500, 0x5500, 0x5500, 0x5500, 0x5100, 0x1400, 0x4400, 0x4200, 0x4200, 0x4200, 0x4200, 0x4200, 0x4200, 0x4200, 0x4200, 0x4200, 0x4200, 0x4200, 0x4200, 0x4400, 0x1400, 0x0100, 0x0000, 0x0b00, 0x0000, 0x0000, 0x0000, 0x0000, 0x6600, 0x6600, 0x6600, 0x6600, 0x6600, 0x6600, 0x6600, 0x5600, 0x1100, 0x4400, 0x2200, 0x2200, 0xd200, 0x2200, 0x2200, 0x2200, 0x2d00, 0x2200, 0x2100, 0x2100, 0x2200, 0x2200, 0x2d00, 0x2200, 0x4400, 0x1100, 0x0000, 0x0100, 0x0100, 0x0000, 0x0000, 0x0000, 0x6700, 0x6700, 0x6700, 0x6700, 0x6700, 0x6700, 0x6700, 0x6600, 0x1100, 0x4400, 0x2200, 0x2200, 0x2200, 0x2200, 0x2200, 0x2200, 0x2200, 0x1100, 0x3300, 0x3300, 0x1300, 0x2100, 0x2200, 0x2200, 0x4400, 0x1100, 0x1300, 0x3300, 0x3300, 0x1100, 0x0000, 0x0000, 0x7700, 0x7700, 0x7700, 0x7700, 0x1100, 0x1300, 0x7100, 0x7700, 0x1100, 0x4400, 0x2200, 0x2200, 0xd200, 0x2200, 0x2200, 0xd200, 0x2200, 0x1100, 0x3300, 0x3300, 0x3300, 0x3300, 0x1300, 0x1300, 0x1300, 0x3300, 0x3300, 0x3300, 0x3300, 0x1100, 0xb000, 0x0000, 0xaa00, 0xaa00, 0xaa00, 0xaa00, 0x1a00, 0x3100, 0x3300, 0x1300, 0x1100, 0x4400, 0x2200, 0x2d00, 0x2200, 0x2200, 0x2200, 0x2200, 0x1100, 0x3300, 0x3300, 0x3300, 0xb100, 0x1100, 0x3300, 0x3300, 0x3100, 0x3300, 0xb100, 0x1100, 0x3300, 0x3300, 0x1100, 0x0000, 0xa800, 0xa800, 0xa800, 0xa800, 0xa800, 0xa800, 0x1800, 0x1a00, 0x1100, 0x4400, 0x2200, 0x2200, 0x2200, 0x2200, 0x2d00, 0x2200, 0x1100, 0x3300, 0xcc00, 0xcc00, 0x3300, 0x3100, 0x3300, 0x3100, 0x3100, 0x3300, 0x3100, 0x3300, 0xcc00, 0xcc00, 0x1100, 0x0000, 0x8800, 0x8800, 0x8800, 0x8800, 0x8800, 0x8800, 0x8800, 0x8100, 0x1100, 0x4100, 0x4400, 0x2400, 0x2400, 0x2400, 0x2400, 0x2400, 0x2400, 0x1400, 0x3100, 0x3300, 0x3300, 0x1300, 0x1300, 0x1300, 0x1300, 0x1300, 0x1300, 0x3300, 0x3100, 0x1000, 0x0000, 0x0000, 0x9900, 0x9900, 0x9900, 0x9900, 0x9900, 0x9900, 0x1100, 0x3300, 0x3300, 0x3100, 0x1000, 0x1100, 0x1300, 0x1300, 0x1100, 0x1000, 0x1000, 0x1000, 0x1000, 0x1100, 0x1300, 0x1300, 0x1100, 0x1100, 0x1300, 0x1300, 0x1100, 0x1000, 0x0000, 0x0000, 0x0000, 0x0000, 0x9000, 0x9000, 0x9000, 0x9000, 0x9000, 0x9000, 0x9000, 0x1000, 0x1000, 0x0000, 0x0000, 0xbb00, 0x1000, 0x1000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1000, 0x1000, 0x0000, 0x0000, 0x1000, 0x1000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xb000, 0xb000, 0x0b00, 0xb000, 0xb000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xbb00, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0500, 0x0500, 0x0500, 0x0500, 0x0500, 0x0500, 0x0000, 0x0000, 0x0000, 0x0000, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0b00, 0x0b00, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x5500, 0x5500, 0x5500, 0x5500, 0x5500, 0x5500, 0x5500, 0x5500, 0x5100, 0x1400, 0x4400, 0x4200, 0x4200, 0x4200, 0x4200, 0x4200, 0x4200, 0x4200, 0x4200, 0x4200, 0x4200, 0x4200, 0x4200, 0x4400, 0x1400, 0x0100, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x6600, 0x6600, 0x6600, 0x6600, 0x6600, 0x6600, 0x5600, 0x5600, 0x1100, 0x4400, 0x2200, 0x2200, 0xd200, 0x2200, 0x2200, 0x2200, 0x2d00, 0x2200, 0x2100, 0x2100, 0x2200, 0x2200, 0x2d00, 0x2200, 0x4400, 0x1100, 0x0000, 0x0100, 0x0100, 0x0000, 0x0000, 0x0000, 0x6700, 0x6700, 0x6700, 0x6700, 0x6700, 0x6700, 0x6600, 0x6600, 0x1100, 0x4400, 0x2200, 0x2200, 0x2200, 0x2200, 0x2200, 0x2200, 0x2200, 0x1100, 0x3300, 0x3300, 0x1300, 0x2100, 0x2200, 0x2200, 0x4400, 0x1100, 0x1300, 0x3300, 0x3300, 0x1100, 0x0000, 0x0000, 0x7700, 0x7700, 0x7700, 0x7700, 0x7700, 0x7700, 0x7700, 0x7700, 0x1100, 0x4400, 0x2200, 0x2200, 0xd200, 0x2200, 0x2200, 0xd200, 0x2200, 0x1100, 0x3300, 0x3300, 0x3300, 0x3300, 0x1300, 0x1300, 0x1300, 0x3300, 0x3300, 0x3300, 0x3300, 0x1100, 0x0000, 0x0000, 0xaa00, 0xaa00, 0xaa00, 0xaa00, 0x1100, 0x1300, 0x1300, 0x1300, 0x1100, 0x4400, 0x2200, 0x2d00, 0x2200, 0x2200, 0x2200, 0x2200, 0x1100, 0x3300, 0x3300, 0x3300, 0xb100, 0x1100, 0x3300, 0x3300, 0x3100, 0x3300, 0xb100, 0x1100, 0x3300, 0x3300, 0x1100, 0x0000, 0xa800, 0xa800, 0xa800, 0xa800, 0xa800, 0x1800, 0x1a00, 0x1a00, 0x1100, 0x4400, 0x2200, 0x2200, 0x2200, 0x2200, 0x2d00, 0x2200, 0x1100, 0x3300, 0xcc00, 0xcc00, 0x3300, 0x3100, 0x3300, 0x3100, 0x3100, 0x3300, 0x3100, 0x3300, 0xcc00, 0xcc00, 0x1100, 0x0000, 0x8800, 0x8800, 0x8800, 0x8800, 0x8800, 0x8800, 0x8800, 0x8800, 0x1100, 0x4100, 0x4400, 0x2400, 0x2400, 0x2400, 0x2400, 0x2400, 0x2400, 0x1400, 0x3100, 0x3300, 0x3300, 0x1300, 0x1300, 0x1300, 0x1300, 0x1300, 0x1300, 0x3300, 0x3100, 0x1000, 0x0000, 0x0000, 0x9900, 0x9900, 0x9900, 0x9900, 0x9900, 0x9900, 0x8900, 0x1100, 0x3300, 0x3300, 0x1100, 0x1000, 0x1100, 0x1300, 0x1300, 0x1100, 0x1000, 0x1000, 0x1000, 0x1000, 0x1100, 0x1300, 0x1300, 0x1100, 0x1100, 0x1300, 0x1300, 0x1100, 0x0000, 0x0000, 0x0000, 0x0000, 0x9000, 0x9000, 0x9000, 0x9b00, 0x9000, 0x9000, 0x9000, 0x1000, 0x1000, 0x1000, 0x0000, 0x0000, 0x0000, 0x1000, 0x1000, 0x1000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1000, 0x1000, 0x1000, 0x0000, 0x1000, 0x1000, 0x1000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xb000, 0x0b00, 0xb000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xbb00, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0500, 0x0500, 0x0500, 0x0500, 0x0500, 0x0500, 0xb500, 0xb500, 0x0b00, 0xb000, 0xb000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x5500, 0x5500, 0x5500, 0x5500, 0x5500, 0x5500, 0x5500, 0x5500, 0x5500, 0x5100, 0x1400, 0x1400, 0x1400, 0x1400, 0x1400, 0x1400, 0x1400, 0x1400, 0x1400, 0x1400, 0x1400, 0x1400, 0x1400, 0x1400, 0x0100, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x5600, 0x5600, 0x5600, 0x5600, 0x5600, 0x5600, 0x5600, 0x6600, 0x1100, 0x4400, 0x4200, 0x2200, 0x2d00, 0x2200, 0x2200, 0x2200, 0x2200, 0x2200, 0x2200, 0x2200, 0x2200, 0x2200, 0x2200, 0x4200, 0x4400, 0x1100, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x6600, 0x6600, 0x6600, 0x6600, 0x6600, 0x6600, 0x6600, 0x6700, 0x1100, 0x4400, 0x2200, 0x2200, 0x2200, 0x2200, 0x2200, 0x2200, 0xd200, 0x2200, 0x2100, 0x1300, 0x1300, 0x2100, 0xd200, 0x2200, 0x4400, 0x1100, 0x0000, 0x0100, 0x1300, 0x1300, 0x0100, 0x0000, 0x7700, 0x7700, 0x7700, 0x7700, 0x7700, 0x7700, 0x7700, 0x7700, 0x1100, 0x4400, 0x2200, 0x2200, 0x2d00, 0x2200, 0x2200, 0x2d00, 0x2200, 0x2200, 0x1100, 0x3300, 0x3300, 0x3300, 0x1300, 0x2100, 0x4100, 0x1100, 0x1300, 0x3300, 0x3300, 0x3300, 0x1100, 0x0000, 0x7a00, 0x7a00, 0x7a00, 0x7a00, 0x7a00, 0x7a00, 0x7100, 0xa100, 0x1100, 0x4400, 0x2200, 0x2200, 0x2200, 0x2200, 0x2200, 0x2200, 0x2200, 0x2100, 0x1300, 0x3300, 0x3300, 0x3b00, 0x3100, 0x3300, 0x3300, 0x3300, 0x3300, 0x3b00, 0x3100, 0x3300, 0x1300, 0x0100, 0xaa00, 0xaa00, 0xaa00, 0xaa00, 0xa100, 0x1300, 0x3300, 0x3100, 0x1100, 0x4400, 0x2200, 0xd200, 0x2200, 0x2200, 0x2200, 0x2200, 0x2200, 0x1100, 0x3300, 0x3c00, 0x3c00, 0x1300, 0x1300, 0x3300, 0x3300, 0x1300, 0x3300, 0x1300, 0x1300, 0x3c00, 0x3c00, 0x1100, 0x8800, 0x8800, 0x8800, 0x8800, 0x1100, 0x3100, 0x1800, 0x8800, 0x1100, 0x4400, 0x2400, 0x2200, 0x2200, 0x2200, 0xd200, 0x2200, 0x2200, 0x1200, 0x3100, 0xc300, 0xc300, 0x3300, 0x1100, 0x3100, 0x1100, 0x1100, 0x3100, 0x1100, 0x3300, 0xc300, 0xc100, 0x1000, 0x8900, 0x8900, 0x8900, 0x8900, 0x8900, 0x8900, 0x8900, 0x9100, 0x1300, 0x1300, 0x4100, 0x4100, 0x4100, 0x4100, 0x4100, 0x4100, 0x4100, 0x4100, 0x4100, 0x1100, 0x3100, 0x3100, 0x3100, 0x3100, 0x3100, 0x3100, 0x3100, 0x3100, 0x3100, 0x1000, 0x0b00, 0x0000, 0x9900, 0x9900, 0x9900, 0x9900, 0x9900, 0x9900, 0x9900, 0x1100, 0x3100, 0x3100, 0x1000, 0x0000, 0x1000, 0x3100, 0x3100, 0x1100, 0x0000, 0x0000, 0x0000, 0x0000, 0x1000, 0x3100, 0x3100, 0x1100, 0x1000, 0x3100, 0x3100, 0x1100, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xb000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xb000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0b00, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xb000, 0x0500, 0xb500, 0x0500, 0x0500, 0x0500, 0x0500, 0x0500, 0x0500, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x5500, 0x5500, 0x5500, 0x5500, 0x5500, 0x5500, 0x5500, 0x5500, 0x5500, 0x5100, 0x1400, 0x1400, 0x1400, 0x1400, 0x1400, 0x1400, 0x1400, 0x1400, 0x1400, 0x1400, 0x1400, 0x1400, 0x1400, 0x1400, 0x0100, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x5600, 0x5600, 0x5600, 0x5600, 0x5600, 0x5600, 0x6600, 0x6600, 0x1100, 0x4400, 0x4200, 0x2200, 0x2d00, 0x2200, 0x2200, 0x2200, 0x2200, 0x2200, 0x2200, 0x2200, 0x2200, 0x2200, 0x2200, 0x4200, 0x4400, 0x1100, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x6600, 0x6600, 0x6600, 0x6600, 0x6600, 0x6600, 0x6700, 0x6700, 0x1100, 0x4400, 0x2200, 0x2200, 0x2200, 0x2200, 0x2200, 0x2200, 0xd200, 0x2100, 0x1300, 0x1300, 0x2100, 0x2200, 0xd200, 0x2200, 0x4400, 0x1100, 0x0100, 0x1300, 0x1300, 0x0100, 0x0000, 0x0000, 0x7700, 0x7700, 0x7700, 0x7700, 0x7700, 0x7700, 0x7700, 0x7700, 0x1100, 0x4400, 0x2200, 0x2200, 0x2d00, 0x2200, 0x2200, 0x2d00, 0x2200, 0x1100, 0x3300, 0x3300, 0x3300, 0x1300, 0x2100, 0x2100, 0x4100, 0x1300, 0x3300, 0x3300, 0x3300, 0x1100, 0x0000, 0x0000, 0x7a00, 0x7a00, 0x7a00, 0x7a00, 0x7100, 0x7100, 0xa100, 0xa100, 0x1100, 0x4400, 0x2200, 0x2200, 0x2200, 0x2200, 0x2200, 0x2200, 0x2100, 0x1300, 0x3300, 0x3300, 0x3b00, 0x3100, 0x3300, 0x3300, 0x3300, 0x3300, 0x3b00, 0x3100, 0x3300, 0x1300, 0x0100, 0x0000, 0xaa00, 0xaa00, 0xaa00, 0xaa00, 0x1a00, 0x3100, 0x3100, 0x3100, 0x1100, 0x4400, 0x2200, 0xd200, 0x2200, 0x2200, 0x2200, 0x2200, 0x1100, 0x3300, 0x3c00, 0x3c00, 0x1300, 0x1300, 0x3300, 0x3300, 0x1300, 0x3300, 0x1300, 0x1300, 0x3c00, 0x3c00, 0x1100, 0x0000, 0x8800, 0x8800, 0x8800, 0x8800, 0x8800, 0x8800, 0x8800, 0x8800, 0x1100, 0x4400, 0x2400, 0x2200, 0x2200, 0x2200, 0xd200, 0x2200, 0x1200, 0x3100, 0xc300, 0xc300, 0x3300, 0x1100, 0x3100, 0x1100, 0x1100, 0x3100, 0x1100, 0x3300, 0xc300, 0xc100, 0x1000, 0x0000, 0x8900, 0x8900, 0x8900, 0x8900, 0x8900, 0x8900, 0x9100, 0x1300, 0x1300, 0x1300, 0x4100, 0x4100, 0x4100, 0x4100, 0x4100, 0x4100, 0x4100, 0x4100, 0x1100, 0x3100, 0x3100, 0x3100, 0x3100, 0x3100, 0x3100, 0x3100, 0x3100, 0x3100, 0x1000, 0x0000, 0x0000, 0x0000, 0x9900, 0x9900, 0x9900, 0x9900, 0x9900, 0x9900, 0x1900, 0x3100, 0x3100, 0x1000, 0x0000, 0x1000, 0x3100, 0x3100, 0x1000, 0x0000, 0x0000, 0x0000, 0xb000, 0x1000, 0x3100, 0x3100, 0x1000, 0x1000, 0x3100, 0x3100, 0x1000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xb000, 0xb000, 0x0000, 0x0000, 0x0000, 0xb000, 0xb000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 41 | :exit dat 0 42 | :monitor dat 0 43 | :not_found SET PC, 0 44 | -------------------------------------------------------------------------------- /programs/preprocessor.dasm: -------------------------------------------------------------------------------- 1 | #macro brk { nop } ; ignore breakpoints 2 | 3 | #macro do(x, y, z) { ; silly macro to "do" anything 4 | x y, z 5 | } 6 | 7 | #macro push(x) { ; test use of reserved words 8 | SET PUSH x 9 | } 10 | 11 | #macro pop(x) { 12 | do(SET, x, POP) 13 | } 14 | 15 | SET A 0x0001 ; do some normal stuff 16 | SET B 2 17 | push(A) ; call some macros 18 | push(B) 19 | pop(X) 20 | pop(Y) 21 | do(SET, A, B) 22 | brk 23 | -------------------------------------------------------------------------------- /programs/test.dasm: -------------------------------------------------------------------------------- 1 | ; blank out character 0 2 | SET [0x8180] 0 3 | SET [0x8181] 0 4 | 5 | ; make whole screen white on blue 6 | SET SP 0x8000 7 | :loop1 8 | SET PEEK 0xf180 9 | ADD SP 1 10 | ADD A 1 11 | IFG 0x8180 SP 12 | SET PC loop1 13 | 14 | ; add text from data 15 | SET SP 0x8000 16 | ADD SP 33 17 | SET A data 18 | :loop2 19 | BOR PEEK [A] 20 | ADD SP 1 21 | ADD A 1 22 | IFG 0x8180 SP 23 | SET PC loop2 24 | 25 | :end 26 | SET PC end 27 | 28 | :data 29 | DAT "LIKE A BOSS" 30 | -------------------------------------------------------------------------------- /programs/tetris.dasm: -------------------------------------------------------------------------------- 1 | JSR clear 2 | SET A, 0 3 | SET B, 0 4 | SET C, 0 5 | SET X, 0 6 | SET Y, 0 7 | SET X, 0 8 | SET Z, 0 9 | SET I, 0 10 | SET J, 0 11 | 12 | ; retrieve some entropy... 13 | SET C, 0x0700 14 | SET A, 0x8022; 0x8000 + 32 * 1 + 2 15 | SET B, entropy_str1 16 | JSR print 17 | SET A, 0x8042; 0x8000 + 32 * 2 + 2 18 | SET B, entropy_str2 19 | JSR print 20 | 21 | SET I, 0 22 | :cyc4 23 | ADD I, 1 24 | JSR next_key 25 | IFE A, 0 26 | SET PC, cyc4 27 | MUL [rand_seed], 36313 28 | ADD [rand_seed], A 29 | MUL [rand_seed], 36313 30 | ADD [rand_seed], I 31 | 32 | SET A, 0x8000 ; clear screen 33 | :cyc3 34 | SET [A], 0 35 | ADD A, 1 36 | IFG 0x8200, A ; 0x8000 + 32*12, A ; set 16 line screen 37 | SET PC, cyc3 38 | 39 | ; init 40 | SET A, 0x8000 41 | SET B, 0 42 | :cyc1 43 | SET [A], 0x0700 44 | ADD A, 1 45 | ADD B, 1 46 | IFG 10, B 47 | SET PC, jmp1 48 | SET B, 0 49 | ADD A, 22 ;32 - 10 50 | :jmp1 51 | IFG 0x8200, A; 0x8000 + 32*12, A 52 | SET PC, cyc1 53 | 54 | SET C, 0xf700 55 | SET A, 0x8021 ; 0x8000 + 32 * 1 + 1 ; (1, 1) 56 | SET B, score_str 57 | JSR print 58 | SET A, 0x8061 ; 0x8000 + 32 * 3 + 1 ; (1, 3) 59 | SET B, level_str 60 | JSR print 61 | SET A, 0x80A1 ; 0x8000 + 32 * 5 + 1 ; (1, 5) 62 | SET B, next_str 63 | JSR print 64 | SET A, 0x8000 65 | :cyc2 66 | ADD A, 10 ; SET [A + 10], 0x0100 67 | SET [A], 0x0100 68 | SUB A, 10 69 | ADD A, 31 ; SET [A + 31], 0x0100 70 | SET [A], 0x0100 71 | ADD A, 1 72 | IFG 0x8200, A ; 0x8000 + 32*12, A 73 | SET PC, cyc2 74 | 75 | JSR update_score 76 | JSR update_level 77 | JSR select_next_piece 78 | 79 | SET PC, main_loop 80 | 81 | ;;;;;;;;;;;;;;; 82 | ; SUBROUTINES ; 83 | ;;;;;;;;;;;;;;; 84 | 85 | :clear 86 | SET PUSH, A 87 | SET PUSH, B 88 | 89 | SET A, [video_mem] 90 | SET B, [video_col] 91 | 92 | :clear_loop 93 | SET [A], B 94 | ADD A, 1 95 | IFE A, 0x8200 96 | SET PC, clear_end 97 | SET PC, clear_loop 98 | 99 | :clear_end 100 | SET [video_cur], [video_mem] 101 | SET B, POP 102 | SET A, POP 103 | SET PC, POP 104 | 105 | :print ; takes coord at A, string to output at B, style at C 106 | IFE [B], 0 107 | SET PC, POP 108 | SET [A], [B] 109 | BOR [A], C 110 | ADD A, 1 111 | ADD B, 1 112 | SET PC, print 113 | 114 | :next_rand ; takes no arguments, returns random word in A 115 | MUL [rand_seed], 10061 116 | ADD [rand_seed], 1 117 | SET A, [rand_seed] 118 | SET PC, POP 119 | :rand_seed 120 | DAT 0xC00F ; TODO: collect entropy at start to randomize game 121 | 122 | :next_key ; reads next key to A from keyboard (based on Notch's code) 123 | SET A, [0x9000] 124 | SET [0x9000], 0 125 | SET PC, POP 126 | :keypointer 127 | DAT 0 128 | 129 | :select_next_piece ; selects next piece 130 | JSR next_rand 131 | MOD A, 7 132 | SET [cur_piece], [next_piece] 133 | SET [next_piece], A 134 | SET [piece_pos], 0x8011; 0x8000 + 17 135 | SET PC, POP 136 | :cur_piece 137 | DAT 0 138 | :cur_rot 139 | DAT 0 140 | :next_piece 141 | DAT 0 142 | :piece_pos 143 | DAT 0x8011 ; 0x8000 + 17 144 | 145 | :update_score ; display current score 146 | SET A, [score] 147 | SET C, 0x8048 ; 0x8000 + 32*2 + 8 148 | :score_cyc1 149 | SET B, A 150 | MOD B, 10 151 | BOR B, 0xe730 152 | SET [C], B 153 | SUB C, 1 154 | DIV A, 10 155 | IFG A, 0 156 | SET PC, score_cyc1 157 | SET PC, POP 158 | 159 | :update_level ; display current level 160 | SET A, [level] 161 | SET C, 0x8088 ; 0x8000 + 32*4 + 8 162 | :level_cyc1 163 | SET B, A 164 | MOD B, 10 165 | BOR B, 0xe730 166 | SET [C], B 167 | SUB C, 1 168 | DIV A, 10 169 | IFG A, 0 170 | SET PC, level_cyc1 171 | SET PC, POP 172 | 173 | :show_cur_piece ; display/clear/check current piece (A=0 - clear, A=1 - display, A=2 - check), if A=2 doesn't actually place anything, return B=1 is position is valid, 0 otherwise 174 | SET X, [piece_pos] ; place block at [X] (display) 175 | SET Y, [cur_piece] ; ...from [Y] (pieces array) 176 | SHL Y, 2 177 | BOR Y, [cur_rot] 178 | SHL Y, 4 179 | ADD Y, pieces 180 | SET I, 0 ; index 181 | :piece_cyc1 182 | SET B, [Y] 183 | IFE B, 0 184 | SET PC, piece_jmp1 185 | IFG 2, A 186 | SET PC, piece_jmp2 187 | IFG X, 0x8200; 0x8000 + 32*12 188 | ADD PC, 3 189 | IFE [X], 0 190 | SET PC, piece_jmp1 191 | SET B, 0 192 | SET PC, POP 193 | :piece_jmp2 194 | IFE A, 0 195 | SET B, 0 196 | SET [X], B 197 | ADD X, 1 ; SET [X + 1], B 198 | SET [X], B 199 | SUB X, 1 200 | :piece_jmp1 201 | ADD I, 1 202 | ADD X, 2 203 | ADD Y, 1 204 | SET B, 1 205 | IFE I, 16 206 | SET PC, POP 207 | IFB I, 3 208 | SET PC, piece_cyc1 209 | ADD X, 24 ;32 - 8 210 | SET PC, piece_cyc1 211 | 212 | :show_next_piece ; redraw next piece 213 | SET X, 0x80E1; 0x8000 + 32*7 + 1 ; place block at [X] (display) 214 | SET Y, [next_piece] ; ...from [Y] (pieces array) 215 | SHL Y, 6 216 | ADD Y, pieces 217 | SET I, 0 ; index 218 | :npiece_cyc1 219 | SET B, [Y] 220 | IFE B, 0 221 | SET B, 0x0700 222 | SET [X], B 223 | ADD X, 1 ;SET [X + 1], B 224 | SET [X], B 225 | SUB X, 1 226 | :npiece_jmp1 227 | ADD I, 1 228 | ADD X, 2 229 | ADD Y, 1 230 | SET B, 1 231 | IFE I, 16 232 | SET PC, POP 233 | IFB I, 3 234 | SET PC, npiece_cyc1 235 | ADD X, 24 ; 32 - 8 236 | SET PC, npiece_cyc1 237 | 238 | :scan_lines ; search for complete lines, remove them and move all other down; update score & level 239 | SET A, 0x81EB ; 0x8000 + 32*11 + 11 ; start of next line to fill 240 | SET B, A ; start of next line to check 241 | SET J, 0 ; num of lines skipped 242 | :scan_cyc2 243 | SET I, 0 ; horizontal index 244 | SET X, B 245 | :scan_cyc1 246 | IFE [X], 0 247 | SET PC, scan_jmp1 248 | ADD X, 2 249 | ADD I, 1 250 | IFG 10, I 251 | SET PC, scan_cyc1 252 | ADD J, 1 ; no gaps found, increase num of complete rows 253 | SUB B, 32 254 | IFE J, 4 255 | SET PC, scan_jmp1 256 | IFG B, 0x8000 257 | SET PC, scan_cyc2 258 | :scan_jmp1 ; found a gap, or no more gaps can be found 259 | IFE A, B 260 | SET PC, scan_jmp2 ; no need to move anything, continue 261 | SET I, 0 262 | :scan_cyc3 263 | SET [A], [B] 264 | ADD I, 1 265 | ADD A, 1 266 | ADD B, 1 267 | IFG 20, I 268 | SET PC, scan_cyc3 269 | SUB A, 20 270 | SUB B, 20 271 | :scan_jmp2 272 | SUB A, 32 273 | IFG 0x8000, A 274 | SET PC, scan_end 275 | SUB B, 32 276 | IFE J, 4 277 | SET PC, scan_jmp1 278 | IFG 0x8000, B 279 | SET PC, scan_jmp1 280 | SET PC, scan_cyc2 281 | :scan_end 282 | IFE J, 0 283 | SET PC, POP 284 | ADD [lines], J 285 | ADD J, lines_score ; SET J, [lines_score + J] 286 | SET J, [J] 287 | MUL J, [level] 288 | ADD [score], J 289 | JSR update_score 290 | IFG 10, [lines] 291 | SET PC, POP 292 | SET [lines], 0 293 | ADD [level], 1 294 | JSR update_level 295 | SET PC, POP 296 | 297 | :main_loop 298 | JSR select_next_piece 299 | SET A, 2 300 | JSR show_cur_piece ; check if we can drop next piece 301 | IFE B, 0 302 | SET PC, game_over 303 | JSR show_next_piece ; redraw next piece 304 | SET A, [level] 305 | ADD A, level_cycles ; SET [cycle_num], [level_cycles + A] 306 | SET [cycle_num], [A] 307 | 308 | :drop_loop 309 | SET Z, [cycle_num] 310 | :wait_loop_redraw ; "heavy" way, redraw current piece 311 | SET A, 1 312 | JSR show_cur_piece 313 | :wait_loop ; "light" way, when no keys were pressed 314 | IFE Z, 0 315 | SET PC, drop_jmp 316 | SUB Z, 1 317 | ; read from keyboard 318 | JSR next_key 319 | IFE A, 1 320 | SET PC, key_left 321 | IFE A, 2 322 | SET PC, key_right 323 | IFE A, 3 324 | SET PC, key_up 325 | IFE A, 4 326 | SET PC, key_down 327 | SET PC, wait_loop 328 | 329 | :key_left 330 | SET A, 0 331 | JSR show_cur_piece 332 | SUB [piece_pos], 2 333 | SET A, 2 334 | JSR show_cur_piece 335 | IFE B, 1 336 | SET PC, wait_loop_redraw 337 | ADD [piece_pos], 2 338 | SET PC, wait_loop_redraw 339 | 340 | :key_right 341 | SET A, 0 342 | JSR show_cur_piece 343 | ADD [piece_pos], 2 344 | SET A, 2 345 | JSR show_cur_piece 346 | IFE B, 1 347 | SET PC, wait_loop_redraw 348 | SUB [piece_pos], 2 349 | SET PC, wait_loop_redraw 350 | 351 | :key_down 352 | SET [cycle_num], 100 353 | SET Z, 100 354 | SET PC, wait_loop 355 | 356 | :key_up 357 | SET A, 0 358 | JSR show_cur_piece 359 | ADD [cur_rot], 1 360 | AND [cur_rot], 3 361 | SET A, 2 362 | JSR show_cur_piece 363 | IFE B, 1 364 | SET PC, wait_loop_redraw 365 | SUB [cur_rot], 1 366 | AND [cur_rot], 3 367 | SET PC, wait_loop_redraw 368 | 369 | 370 | :drop_jmp 371 | ; lower current piece 372 | SET A, 0 373 | JSR show_cur_piece 374 | ADD [piece_pos], 32 375 | SET A, 2 376 | JSR show_cur_piece 377 | IFE B, 1 378 | SET PC, drop_loop 379 | SUB [piece_pos], 32 380 | SET A, 1 381 | JSR show_cur_piece 382 | JSR scan_lines 383 | SET PC, main_loop 384 | 385 | :game_over ; print blinking game over message 386 | SET C, 0xf480 387 | SET A, 0x8089 ; 0x8000 + 32 * 4 + 9 ; (9, 4) 388 | SET B, game_over_pad 389 | JSR print 390 | SET A, 0x80A9 ; 0x8000 + 32 * 5 + 9 ; (9, 5) 391 | SET B, game_over_str 392 | JSR print 393 | SET A, 0x80C4 ; 0x8000 + 32 * 6 + 9 ; (9, 6) 394 | SET B, restart_str 395 | JSR print 396 | 397 | :restart 398 | JSR next_key 399 | IFN A, 0 400 | SET PC, 0 401 | SET PC, restart 402 | 403 | :cycle_num 404 | DAT 2500 405 | :level_cycles 406 | DAT 2500, 2500, 2000, 1500, 1000, 800, 650, 500, 300, 200, 100, 75, 50, 30, 20, 10, 5, 3, 2, 1 407 | :lines_score 408 | DAT 0, 2, 5, 15, 60 409 | :lines 410 | DAT 0 411 | :level 412 | DAT 1 413 | :score 414 | DAT 0 415 | :entropy_str1 416 | DAT "Press any key to", 0 417 | :entropy_str2 418 | DAT " make game random:", 0 419 | :score_str 420 | DAT "Score:", 0 421 | :level_str 422 | DAT "Level:", 0 423 | :next_str 424 | DAT "Next:", 0 425 | :game_over_pad 426 | DAT " ", 0 427 | :game_over_str 428 | DAT " Game Over! ", 0 429 | :restart_str 430 | DAT " Press any key to restart ", 0 431 | :pieces 432 | ; I 433 | DAT 0, 0x0c00, 0, 0, 0, 0x0c00, 0, 0, 0, 0x0c00, 0, 0, 0, 0x0c00, 0, 0 434 | DAT 0, 0, 0, 0, 0x0c00, 0x0c00, 0x0c00, 0x0c00, 0, 0, 0, 0, 0, 0, 0, 0 435 | DAT 0, 0x0c00, 0, 0, 0, 0x0c00, 0, 0, 0, 0x0c00, 0, 0, 0, 0x0c00, 0, 0 436 | DAT 0, 0, 0, 0, 0x0c00, 0x0c00, 0x0c00, 0x0c00, 0, 0, 0, 0, 0, 0, 0, 0 437 | ; J 438 | DAT 0x0d00, 0, 0, 0, 0x0d00, 0x0d00, 0x0d00, 0, 0, 0, 0, 0, 0, 0, 0, 0 439 | DAT 0, 0x0d00, 0x0d00, 0, 0, 0x0d00, 0, 0, 0, 0x0d00, 0, 0, 0, 0, 0, 0 440 | DAT 0, 0, 0, 0, 0x0d00, 0x0d00, 0x0d00, 0, 0, 0, 0x0d00, 0, 0, 0, 0, 0 441 | DAT 0, 0x0d00, 0, 0, 0, 0x0d00, 0, 0, 0x0d00, 0x0d00, 0, 0, 0, 0, 0, 0 442 | ; L 443 | DAT 0, 0, 0x0e00, 0, 0x0e00, 0x0e00, 0x0e00, 0, 0, 0, 0, 0, 0, 0, 0, 0 444 | DAT 0, 0x0e00, 0, 0, 0, 0x0e00, 0, 0, 0, 0x0e00, 0x0e00, 0, 0, 0, 0, 0 445 | DAT 0, 0, 0, 0, 0x0e00, 0x0e00, 0x0e00, 0, 0x0e00, 0, 0, 0, 0, 0, 0, 0 446 | DAT 0x0e00, 0x0e00, 0, 0, 0, 0x0e00, 0, 0, 0, 0x0e00, 0, 0, 0, 0, 0, 0 447 | ; O 448 | DAT 0x0b00, 0x0b00, 0, 0, 0x0b00, 0x0b00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 449 | DAT 0x0b00, 0x0b00, 0, 0, 0x0b00, 0x0b00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 450 | DAT 0x0b00, 0x0b00, 0, 0, 0x0b00, 0x0b00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 451 | DAT 0x0b00, 0x0b00, 0, 0, 0x0b00, 0x0b00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 452 | ; S 453 | DAT 0, 0x0900, 0x0900, 0, 0x0900, 0x0900, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 454 | DAT 0, 0x0900, 0, 0, 0, 0x0900, 0x0900, 0, 0, 0, 0x0900, 0, 0, 0, 0, 0 455 | DAT 0, 0x0900, 0x0900, 0, 0x0900, 0x0900, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 456 | DAT 0, 0x0900, 0, 0, 0, 0x0900, 0x0900, 0, 0, 0, 0x0900, 0, 0, 0, 0, 0 457 | ; T 458 | DAT 0x0800, 0x0800, 0x0800, 0, 0, 0x0800, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 459 | DAT 0, 0x0800, 0, 0, 0x0800, 0x0800, 0, 0, 0, 0x0800, 0, 0, 0, 0, 0, 0 460 | DAT 0, 0x0800, 0, 0, 0x0800, 0x0800, 0x0800, 0, 0, 0, 0, 0, 0, 0, 0, 0 461 | DAT 0, 0x0800, 0, 0, 0, 0x0800, 0x0800, 0, 0, 0x0800, 0, 0, 0, 0, 0, 0 462 | ; Z 463 | DAT 0x0a00, 0x0a00, 0, 0, 0, 0x0a00, 0x0a00, 0, 0, 0, 0, 0, 0, 0, 0, 0 464 | DAT 0, 0, 0x0a00, 0, 0, 0x0a00, 0x0a00, 0, 0, 0x0a00, 0, 0, 0, 0, 0, 0 465 | DAT 0x0a00, 0x0a00, 0, 0, 0, 0x0a00, 0x0a00, 0, 0, 0, 0, 0, 0, 0, 0, 0 466 | DAT 0, 0, 0x0a00, 0, 0, 0x0a00, 0x0a00, 0, 0, 0x0a00, 0, 0, 0, 0, 0, 0 467 | 468 | :video_col dat 0x7000 469 | :video_cur dat 0x8000 470 | :video_mem dat 0x8000 471 | -------------------------------------------------------------------------------- /screenshots/debug.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fogleman/DCPU-16/db6f7aa15cdee8f3e285c11ee5dd7d286fe46551/screenshots/debug.png -------------------------------------------------------------------------------- /screenshots/editor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fogleman/DCPU-16/db6f7aa15cdee8f3e285c11ee5dd7d286fe46551/screenshots/editor.png -------------------------------------------------------------------------------- /screenshots/no_debug.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fogleman/DCPU-16/db6f7aa15cdee8f3e285c11ee5dd7d286fe46551/screenshots/no_debug.png -------------------------------------------------------------------------------- /screenshots/nyan.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fogleman/DCPU-16/db6f7aa15cdee8f3e285c11ee5dd7d286fe46551/screenshots/nyan.gif -------------------------------------------------------------------------------- /screenshots/syntax.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fogleman/DCPU-16/db6f7aa15cdee8f3e285c11ee5dd7d286fe46551/screenshots/syntax.png -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import distutils 2 | import os 3 | import py2exe 4 | import shutil 5 | import sys 6 | 7 | def run_py2exe(): 8 | py2exe.__version__ 9 | sys.argv.append('py2exe') 10 | distutils.core.setup( 11 | options = {"py2exe":{ 12 | "compressed": True, 13 | "optimize": 1, 14 | "bundle_files": 1, 15 | "excludes": ['Tkconstants', 'Tkinter', 'tcl'], 16 | "dll_excludes": ['msvcp90.dll'], 17 | }}, 18 | windows = [{ 19 | "script": "main.py", 20 | "dest_base": "dcpu16", 21 | "icon_resources": [(1, "icons/icon.ico")], 22 | "other_resources": [(24, 1, MANIFEST)], 23 | }], 24 | zipfile=None, 25 | ) 26 | 27 | def copy_file(src): 28 | print 'Copying:', src 29 | dst = os.path.join('dist', src) 30 | try: 31 | os.makedirs(os.path.split(dst)[0]) 32 | except Exception: 33 | pass 34 | shutil.copyfile(src, dst) 35 | 36 | def copy_directory(src): 37 | for path, _, files in os.walk(src): 38 | if '.svn' in path: 39 | continue 40 | for filename in files: 41 | copy_file(os.path.join(path, filename)) 42 | 43 | def main(): 44 | run_py2exe() 45 | copy_directory('Microsoft.VC90.CRT') 46 | copy_directory('programs') 47 | copy_file('_emulator.dll') 48 | 49 | MANIFEST = ''' 50 | 52 | 58 | Star Rocket Level Editor 1.0 59 | 60 | 61 | 62 | 66 | 67 | 68 | 69 | 70 | 71 | 78 | 79 | 80 | 81 | 82 | 90 | 91 | 92 | 93 | ''' 94 | 95 | if __name__ == '__main__': 96 | main() 97 | -------------------------------------------------------------------------------- /util/font.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fogleman/DCPU-16/db6f7aa15cdee8f3e285c11ee5dd7d286fe46551/util/font.png -------------------------------------------------------------------------------- /util/font.py: -------------------------------------------------------------------------------- 1 | import wx 2 | 3 | def main(): 4 | _app = wx.App(None) 5 | result = [] 6 | image = wx.ImageFromBitmap(wx.Bitmap('font.png')) 7 | for row in range(4): 8 | for col in range(32): 9 | value = 0 10 | mask = 1 11 | for i in range(3, -1, -1): 12 | for j in range(8): 13 | x = col * 4 + i 14 | y = row * 8 + j 15 | on = image.GetRed(x, y) > 128 16 | if on: 17 | value |= mask 18 | mask <<= 1 19 | a = (value >> 16) & 0xffff 20 | b = value & 0xffff 21 | result.append(a) 22 | result.append(b) 23 | return result 24 | 25 | if __name__ == '__main__': 26 | data = main() 27 | while data: 28 | row = data[:8] 29 | del data[:8] 30 | print ' %s,' % ', '.join('0x%04x' % x for x in row) 31 | -------------------------------------------------------------------------------- /util/icons.py: -------------------------------------------------------------------------------- 1 | import base64 2 | import os 3 | import sys 4 | 5 | EXTENSIONS = set([ 6 | '.png', 7 | ]) 8 | 9 | IGNORE = set([ 10 | 'icon24.png', 11 | 'icon48.png', 12 | 'icon256.png', 13 | ]) 14 | 15 | def print_data(data): 16 | size = 70 17 | offset = 0 18 | length = len(data) 19 | while offset < length: 20 | print ' "%s"' % data[offset:offset+size] 21 | offset += size 22 | 23 | def generate(folder): 24 | print '# Automatically generated file!' 25 | print 'from wx.lib.embeddedimage import PyEmbeddedImage' 26 | print 27 | for name in os.listdir(folder): 28 | if name in IGNORE: 29 | continue 30 | if name[-4:] not in EXTENSIONS: 31 | continue 32 | path = os.path.join(folder, name) 33 | base = name[:-4] 34 | with open(path, 'rb') as f: 35 | encoded = base64.b64encode(f.read()) 36 | print '%s = PyEmbeddedImage(' % base 37 | print_data(encoded) 38 | print ')' 39 | print 40 | 41 | if __name__ == '__main__': 42 | args = sys.argv[1:] 43 | if len(args) == 1: 44 | generate(args[0]) 45 | else: 46 | print 'Usage: python icons.py folder_name' 47 | --------------------------------------------------------------------------------