├── 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 | 
19 |
20 | 
21 |
22 | 
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 |
--------------------------------------------------------------------------------