-
Notifications
You must be signed in to change notification settings - Fork 168
/
make_bytecode.py
761 lines (685 loc) · 26.9 KB
/
make_bytecode.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
import sys
import ds3_preprocessor
import ast
import myast
from keywords import *
# CPU instructions
OP_NOP = ("NOP", 0)
OP_PUSHC = ("PUSHC", 1)
OP_PUSHV = ("PUSHV", 2)
OP_POP = ("POP", 3)
OP_BRZ = ("BRZ", 4)
OP_JMP = ("JMP", 5)
OP_CALL = ("CALL", 6)
OP_RET = ("RET", 7)
OP_HALT = ("HALT", 8)
# Comparisons
OP_EQ = ("EQ", 9)
OP_NOTEQ = ("NOTEQ", 10)
# left value on bottom, right value on top
OP_LT = ("LT", 11)
OP_LTE = ("LTE", 12)
OP_GT = ("GT", 13)
OP_GTE = ("GTE", 14)
# Arithmetics
OP_ADD = ("ADD", 15)
# left value on bottom, right value on top
OP_SUB = ("SUB", 16)
OP_MULT = ("MULT", 17)
OP_DIV = ("DIV", 18)
OP_MOD = ("MOD", 19)
OP_POW = ("POW", 20)
# Bitwise operation
OP_LSHIFT = ("LSHIFT", 21)
OP_RSHIFT = ("RSHIFT", 22)
OP_BITOR = ("BITOR", 23)
OP_BITAND = ("BITAND", 24)
# Logical operation
OP_LOGIAND = ("LOGIAND", 25)
OP_LOGIOR = ("LOGIOR", 26)
OP_DELAY = ("DELAY", 27)
OP_KUP = ("KUP", 28)
OP_KDOWN = ("KDOWN", 29)
OP_MSCL = ("MSCL", 30)
OP_MMOV = ("MMOV", 31)
OP_SWCF = ("SWCF", 32)
OP_SWCC = ("SWCC", 33)
OP_SWCR = ("SWCR", 34)
OP_STR = ("STR", 35)
OP_STRLN = ("STRLN", 36)
OP_EMUK = ("EMUK", 37)
OP_OLC = ("OLC", 38)
OP_OLP = ("OLP", 39)
OP_OLU = ("OLU", 40)
OP_OLB = ("OLB", 41)
OP_OLR = ("OLR", 42)
OP_BCLR = ("BCLR", 43)
OP_PREVP = ("PREVP", 44)
OP_NEXTP = ("NEXTP", 45)
OP_GOTOP = ("GOTOP", 46)
OP_SLEEP = ("SLEEP", 47)
arith_lookup = {
"Eq" : OP_EQ,
"NotEq" : OP_NOTEQ,
"Lt" : OP_LT,
"LtE" : OP_LTE,
"Gt" : OP_GT,
"GtE" : OP_GTE,
"Add" : OP_ADD,
"Sub" : OP_SUB,
"Mult" : OP_MULT,
"Div" : OP_DIV,
"Mod" : OP_MOD,
"Pow" : OP_POW,
"LShift" : OP_LSHIFT,
"RShift" : OP_RSHIFT,
"BitOr" : OP_BITOR,
"BitAnd" : OP_BITAND,
"And" : OP_LOGIAND,
"Or" : OP_LOGIOR,
}
zero = 0
endianness = 'little'
var_boundary = 0x1f
INSTRUCTION_SIZE_BYTES = 3
if_skip_table = None
if_info_list = None
while_lookup = None
var_lookup = None
compact_program_listing = None
label_dict = None
func_lookup = None
str_lookup = None
def get_empty_instruction():
return {
'opcode':OP_NOP,
'oparg':None,
'label':None,
'comment':None,
'addr':None}
def print_instruction(instruction):
if instruction['label'] is not None:
print(f"~~~~{instruction['label']}:")
if instruction['addr'] is not None:
print(str(instruction['addr']).ljust(5), end='')
print(instruction['opcode'][0].ljust(10), end='')
tempstr = ""
if instruction['oparg'] is not None:
tempstr = f"{instruction['oparg']}".ljust(6)
if isinstance(instruction['oparg'], int):
tempstr += f"{hex(instruction['oparg'])}".ljust(6)
print(tempstr.ljust(20), end='')
tempstr = ""
if instruction['comment'] is not None:
tempstr = ";" + str(instruction['comment'])
print(tempstr)
def print_asslist(lll):
print()
for item in lll:
print_instruction(item)
print()
def visit_node(node, instruction_list):
# print(node.__dict__)
# a node can be Name, Constant, and operations such as ADD, SUB, COMPARE, etc
if isinstance(node, ast.Name):
this_instruction = get_empty_instruction()
this_instruction['opcode'] = OP_PUSHV
this_instruction['oparg'] = str(node.id)
instruction_list.append(this_instruction)
elif isinstance(node, ast.Constant):
this_instruction = get_empty_instruction()
this_instruction['opcode'] = OP_PUSHC
this_instruction['oparg'] = int(node.value)
instruction_list.append(this_instruction)
elif isinstance(node, ast.Compare):
op_name = node.ops[0].__class__.__name__
if op_name not in arith_lookup:
raise ValueError("unknown AST operation")
this_instruction = get_empty_instruction()
this_instruction['opcode'] = arith_lookup[op_name]
instruction_list.append(this_instruction)
elif isinstance(node, ast.BoolOp):
op_name = node.op.__class__.__name__
if op_name not in arith_lookup:
raise ValueError("unknown AST operation")
this_instruction = get_empty_instruction()
this_instruction['opcode'] = arith_lookup[op_name]
# multiple boolean comparisons
for x in range(len(node.values) - 1):
instruction_list.append(this_instruction)
elif isinstance(node, ast.BinOp):
op_name = node.op.__class__.__name__
if op_name not in arith_lookup:
raise ValueError("unknown AST operation")
this_instruction = get_empty_instruction()
this_instruction['opcode'] = arith_lookup[op_name]
instruction_list.append(this_instruction)
"""
this generates a list of instructions to calculate the result of an expression
once executed, the result should be the only item left in the stack
don't forget to pop it out to somewhere to do whatever is needed
"""
def evaluate_expr(expr):
instruction_list = []
root = ast.parse(expr).body[0].value
if myast.is_walkable(root):
myast.postorder_walk(root, visit_node, instruction_list, expr)
elif isinstance(root, ast.Constant):
this_instruction = get_empty_instruction()
this_instruction['opcode'] = OP_PUSHC
this_instruction['oparg'] = root.value
instruction_list.append(this_instruction)
elif isinstance(root, ast.Name):
this_instruction = get_empty_instruction()
this_instruction['opcode'] = OP_PUSHV
this_instruction['oparg'] = str(root.id)
instruction_list.append(this_instruction)
else:
raise ValueError(f"unknown ast node: {root}")
return instruction_list
def replace_operators(this_line):
return this_line.replace("$", "").replace("||", " or ").replace("&&", " and ").replace("^", "**")
def assign_var(var_keyword, pgm_line):
var_sides = pgm_line.split(var_keyword, 1)[-1].split('=', 1)
lvalue = var_sides[0].split("$")[-1].rstrip()
rvalue = var_sides[1].lstrip()
rvalue = replace_operators(rvalue)
ins_list = evaluate_expr(rvalue)
this_instruction = get_empty_instruction()
this_instruction['opcode'] = OP_POP
this_instruction['oparg'] = lvalue
ins_list.append(this_instruction)
for item in ins_list:
item['comment'] = pgm_line
return ins_list
def get_if_label(lnum, if_info):
for item in if_info:
if item['else'] == lnum:
return "ELSE"
if item['end_if'] == lnum:
return "ENDIF"
if lnum in item['else_if']:
return "ELSEIF"
raise ValueError("get_if_label")
def parse_if(pgm_line, lnum):
expression = pgm_line.split("IF", 1)[-1].split("THEN")[0].strip()
expression = replace_operators(expression)
assembly_listing = evaluate_expr(expression)
# expression result is now the only item on arithmetic stack
this_instruction = get_empty_instruction()
this_instruction['opcode'] = OP_BRZ
label_name = f"{get_if_label(if_skip_table[lnum], if_info_list)}@{if_skip_table[lnum]}"
this_instruction['oparg'] = label_name
label_dict[if_skip_table[lnum]] = label_name
assembly_listing.append(this_instruction)
for item in assembly_listing:
item['comment'] = pgm_line
return assembly_listing
def parse_while(pgm_line, lnum):
expression = pgm_line.replace(cmd_WHILE, '', 1).strip()
expression = replace_operators(expression)
assembly_listing = evaluate_expr(expression)
label_name = f'WHILE@{lnum}'
assembly_listing[0]['label'] = label_name
label_dict[lnum] = label_name
this_instruction = get_empty_instruction()
this_instruction['opcode'] = OP_BRZ
label_name = f'ENDWHILE@{while_lookup[lnum]}'
this_instruction['oparg'] = label_name
label_dict[while_lookup[lnum]] = label_name
assembly_listing.append(this_instruction)
for item in assembly_listing:
item['comment'] = pgm_line
return assembly_listing
def find_endif(lnum, if_info):
for item in if_info:
if item['else'] == lnum:
return item['end_if']
for yyy in item['else_if']:
if yyy == lnum:
return item['end_if']
raise ValueError("find_endif")
def add_jmp(lnum, if_info):
this_instruction = get_empty_instruction()
this_instruction['opcode'] = OP_JMP
endif_location = find_endif(lnum, if_info)
label_name = f"ENDIF@{endif_location}"
this_instruction['oparg'] = label_name
label_dict[endif_location] = label_name
return this_instruction
def make_delay_instruction(comment):
this_instruction = get_empty_instruction()
this_instruction['opcode'] = OP_DELAY
this_instruction['comment'] = comment
return this_instruction
def parse_expression(pgm_line):
expression = pgm_line.split(' ', 1)[1].strip()
expression = replace_operators(expression)
ins_list = evaluate_expr(expression)
for item in ins_list:
item['comment'] = pgm_line
return ins_list
def get_key_combined_value(keyname):
if keyname in ds3_keyname_dict:
key_code = ds3_keyname_dict[keyname][0]
key_type = ds3_keyname_dict[keyname][1]
else:
key_code = ord(keyname[0])
key_type = KEY_TYPE_CHAR
return ((key_type % 0xff) << 8) | (key_code % 0xff)
def get_mmov_combined_value(pgm_line):
split = [x for x in pgm_line.split(' ') if len(x) > 0]
x_value = int(split[1])
y_value = int(split[2])
if x_value < 0:
x_value += 256
if y_value < 0:
y_value += 256
return ((x_value % 0xff) << 8) | (y_value % 0xff)
def get_mouse_wheel_value(pgm_line):
split = [x for x in pgm_line.split(' ') if len(x) > 0]
amount = int(split[-1])
if amount < 0:
amount += 256
return amount
def get_partial_varname_addr(msg, vad):
if len(msg) == 0:
return None, None
for x in range(len(msg)+1):
partial_name = msg[:x]
if partial_name in vad:
return partial_name, vad[partial_name]
return None, None
def replace_var_in_str(msg, vad):
bytearr = bytearray()
curr = 0
while curr < len(msg):
this_letter = msg[curr]
if this_letter == "$":
var_name, var_addr = get_partial_varname_addr(msg[curr+1:], vad)
if var_name is not None:
curr += len(var_name)
bytearr += var_boundary.to_bytes(1, endianness)
bytearr += var_addr.to_bytes(2, endianness)
bytearr += var_boundary.to_bytes(1, endianness)
else:
bytearr += this_letter.encode()
else:
bytearr += this_letter.encode()
curr += 1
return bytearr
def get_swc_arg(name):
try:
return int(name)
except:
pass
return name[1:]
def parse_color(pgm_line):
ins_list = []
split = [x for x in pgm_line.split(' ') if len(x) > 0]
for item in split[1:]:
value = get_swc_arg(item)
this_instruction = get_empty_instruction()
if isinstance(value, int):
this_instruction['opcode'] = OP_PUSHC
else:
this_instruction['opcode'] = OP_PUSHV
this_instruction['oparg'] = value
this_instruction['comment'] = pgm_line
ins_list.append(this_instruction)
this_instruction = get_empty_instruction()
this_instruction['opcode'] = OP_SWCC
this_instruction['comment'] = pgm_line
ins_list.append(this_instruction)
return ins_list
def parse_swcf(pgm_line):
ins_list = []
split = [x for x in pgm_line.split(' ') if len(x) > 0]
for item in split[1:]:
value = get_swc_arg(item)
this_instruction = get_empty_instruction()
if isinstance(value, int):
this_instruction['opcode'] = OP_PUSHC
else:
this_instruction['opcode'] = OP_PUSHV
this_instruction['oparg'] = value
this_instruction['comment'] = pgm_line
ins_list.append(this_instruction)
this_instruction = get_empty_instruction()
this_instruction['opcode'] = OP_SWCF
this_instruction['comment'] = pgm_line
ins_list.append(this_instruction)
return ins_list
def parse_swcr(pgm_line):
ins_list = []
split = [x for x in pgm_line.split(' ') if len(x) > 0]
value = get_swc_arg(split[1])
this_instruction = get_empty_instruction()
if isinstance(value, int):
this_instruction['opcode'] = OP_PUSHC
else:
this_instruction['opcode'] = OP_PUSHV
this_instruction['oparg'] = value
this_instruction['comment'] = pgm_line
ins_list.append(this_instruction)
this_instruction = get_empty_instruction()
this_instruction['opcode'] = OP_SWCR
this_instruction['comment'] = pgm_line
ins_list.append(this_instruction)
return ins_list
def parse_olc(pgm_line):
ins_list = []
split = [x for x in pgm_line.split(' ') if len(x) > 0]
for item in split[1:]:
value = get_swc_arg(item)
this_instruction = get_empty_instruction()
if isinstance(value, int):
this_instruction['opcode'] = OP_PUSHC
else:
this_instruction['opcode'] = OP_PUSHV
this_instruction['oparg'] = value
this_instruction['comment'] = pgm_line
ins_list.append(this_instruction)
this_instruction = get_empty_instruction()
this_instruction['opcode'] = OP_OLC
this_instruction['comment'] = pgm_line
ins_list.append(this_instruction)
return ins_list
def make_dsb(program_listing, profile_list=None):
global if_skip_table
global if_info_list
global while_lookup
global var_lookup
global compact_program_listing
global label_dict
global func_lookup
global str_lookup
# result_dict should at least contain is_success and comments
result_dict = ds3_preprocessor.run_all(program_listing)
if result_dict["is_success"] is False:
print("\n\n\n>>>>>>>>>> ERROR FOUND\n\n")
for key in result_dict:
print(f'{key}: {result_dict[key]}')
print("\n\n\n>>>>>>>>>> END ERROR REPORT\n\n")
raise ValueError(f"{result_dict['comments']}")
if_skip_table = result_dict['if_skip_table']
if_info_list = result_dict["if_info"]
while_lookup = result_dict['while_table_bidirectional']
var_lookup = result_dict['var_table']
compact_program_listing = [x[1] for x in result_dict['compact_listing']]
label_dict = {}
func_lookup = result_dict['func_table']
str_lookup = {}
break_dict = result_dict['break_dict']
continue_dict = result_dict['continue_dict']
print("--------- Program Listing After Preprocessing: ---------")
for index, item in enumerate(compact_program_listing):
print(str(index+1).ljust(4), item)
print()
assembly_listing = []
for lnum, this_line in enumerate(compact_program_listing):
lnum += 1
this_instruction = get_empty_instruction()
this_instruction['comment'] = this_line
first_word = this_line.split()[0]
if first_word == cmd_VAR_DECLARE:
assembly_listing += assign_var(cmd_VAR_DECLARE, this_line)
elif first_word[0] == "$":
assembly_listing += assign_var(cmd_VAR_ASSIGN, this_line)
elif first_word == cmd_IF:
assembly_listing += parse_if(this_line, lnum)
elif this_line.startswith(cmd_ELSE_IF):
assembly_listing.append(add_jmp(lnum, if_info_list))
temp_listing = parse_if(this_line, lnum)
if lnum in label_dict:
temp_listing[0]['label'] = label_dict[lnum]
assembly_listing += temp_listing
elif first_word == cmd_ELSE:
assembly_listing.append(add_jmp(lnum, if_info_list))
if lnum in label_dict:
this_instruction['label'] = label_dict[lnum]
assembly_listing.append(this_instruction)
elif first_word == cmd_END_IF:
if lnum in label_dict:
this_instruction['label'] = label_dict[lnum]
assembly_listing.append(this_instruction)
elif first_word == cmd_WHILE:
assembly_listing += parse_while(this_line, lnum)
elif first_word == cmd_END_WHILE:
this_instruction['opcode'] = OP_JMP
this_instruction['oparg'] = label_dict[while_lookup[lnum]]
this_instruction['comment'] = None
assembly_listing.append(this_instruction)
this_instruction = get_empty_instruction()
this_instruction['comment'] = this_line
this_instruction['label'] = label_dict[lnum]
assembly_listing.append(this_instruction)
elif first_word == cmd_FUNCTION:
fun_name = this_line.split()[1].split('()')[0]
this_instruction['opcode'] = OP_JMP
fun_end_lnum = func_lookup[fun_name]['fun_end']
fun_end_label = f"FEND_{fun_name}@{fun_end_lnum}"
label_dict[fun_end_lnum] = fun_end_label
this_instruction['oparg'] = fun_end_label
this_instruction['comment'] = None
assembly_listing.append(this_instruction)
this_instruction = get_empty_instruction()
this_instruction['comment'] = this_line
fun_start_label = f"FUN_{fun_name}@{lnum}"
this_instruction['label'] = fun_start_label
label_dict[lnum] = fun_start_label
assembly_listing.append(this_instruction)
elif first_word == cmd_END_FUNCTION:
# RET, then NOP
this_instruction['opcode'] = OP_RET
this_instruction['comment'] = None
assembly_listing.append(this_instruction)
this_instruction = get_empty_instruction()
this_instruction['comment'] = this_line
this_instruction['label'] = label_dict[lnum]
assembly_listing.append(this_instruction)
elif first_word.endswith('()'):
fun_name = this_line.split('()')[0]
this_instruction['opcode'] = OP_CALL
this_instruction['oparg'] = label_dict[func_lookup[fun_name]['fun_start']]
assembly_listing.append(this_instruction)
elif this_line.startswith(cmd_STRING) or first_word == cmd_OLED_PRINT:
str_content = this_line.split(' ', 1)[-1]
if str_content not in str_lookup:
str_lookup[str_content] = lnum
if first_word == cmd_STRING:
this_instruction['opcode'] = OP_STR
elif first_word == cmd_STRINGLN:
this_instruction['opcode'] = OP_STRLN
elif first_word == cmd_OLED_PRINT:
this_instruction['opcode'] = OP_OLP
this_instruction['oparg'] = f"STR@{str_lookup[str_content]}"
assembly_listing.append(this_instruction)
elif first_word == cmd_DELAY:
assembly_listing += parse_expression(this_line)
assembly_listing.append(make_delay_instruction(this_line))
elif first_word == cmd_KEYDOWN:
this_instruction['opcode'] = OP_KDOWN
this_instruction['oparg'] = get_key_combined_value(this_line.split(' ')[-1])
assembly_listing.append(this_instruction)
elif first_word == cmd_EMUK:
this_instruction['opcode'] = OP_EMUK
this_instruction['oparg'] = get_key_combined_value(this_line.split(' ')[-1])
assembly_listing.append(this_instruction)
elif first_word == cmd_KEYUP:
this_instruction['opcode'] = OP_KUP
this_instruction['oparg'] = get_key_combined_value(this_line.split(' ')[-1])
assembly_listing.append(this_instruction)
elif first_word == cmd_RETURN:
this_instruction['opcode'] = OP_RET
assembly_listing.append(this_instruction)
elif first_word == cmd_HALT:
this_instruction['opcode'] = OP_HALT
assembly_listing.append(this_instruction)
elif first_word == cmd_MOUSE_MOVE:
this_instruction['opcode'] = OP_MMOV
this_instruction['oparg'] = get_mmov_combined_value(this_line)
assembly_listing.append(this_instruction)
elif first_word == cmd_MOUSE_WHEEL:
this_instruction['opcode'] = OP_MSCL
this_instruction['oparg'] = get_mouse_wheel_value(this_line)
assembly_listing.append(this_instruction)
elif first_word == cmd_SWCC:
assembly_listing += parse_color(this_line)
elif first_word == cmd_SWCF:
assembly_listing += parse_swcf(this_line)
elif first_word == cmd_SWCR:
assembly_listing += parse_swcr(this_line)
elif first_word == cmd_OLED_CURSOR:
assembly_listing += parse_olc(this_line)
elif first_word == cmd_OLED_UPDATE:
this_instruction['opcode'] = OP_OLU
assembly_listing.append(this_instruction)
elif first_word == cmd_OLED_BLANK:
this_instruction['opcode'] = OP_OLB
assembly_listing.append(this_instruction)
elif first_word == cmd_OLED_RESTORE:
this_instruction['opcode'] = OP_OLR
assembly_listing.append(this_instruction)
elif first_word == cmd_BCLR:
this_instruction['opcode'] = OP_BCLR
assembly_listing.append(this_instruction)
elif first_word == cmd_LOOP_BREAK:
this_instruction['opcode'] = OP_JMP
this_instruction['oparg'] = label_dict[break_dict[lnum]]
assembly_listing.append(this_instruction)
elif first_word == cmd_CONTINUE:
this_instruction['opcode'] = OP_JMP
this_instruction['oparg'] = label_dict[continue_dict[lnum]]
assembly_listing.append(this_instruction)
elif first_word == cmd_NEXT_PROFILE:
this_instruction['opcode'] = OP_NEXTP
assembly_listing.append(this_instruction)
elif first_word == cmd_PREV_PROFILE:
this_instruction['opcode'] = OP_PREVP
assembly_listing.append(this_instruction)
elif first_word == cmd_GOTO_PROFILE:
assembly_listing += parse_expression(this_line)
this_instruction['opcode'] = OP_GOTOP
assembly_listing.append(this_instruction)
elif first_word == cmd_GOTO_PROFILE_NAME:
cmd, profile_name = this_line.split(" ")
profile_index = 0
for index, profile in enumerate(profile_list):
if profile.name.lower() == profile_name.lower():
profile_index = index + 1
this_line = f"GOTO_PROFILE {profile_index}"
assembly_listing += parse_expression(this_line)
this_instruction['opcode'] = OP_GOTOP
assembly_listing.append(this_instruction)
elif first_word == cmd_DP_SLEEP:
this_instruction['opcode'] = OP_SLEEP
assembly_listing.append(this_instruction)
elif first_word in ds3_keyname_dict: # key combos
key_list = [x for x in this_line.split(" ") if len(x) > 0]
# press, from first to last
for item in key_list:
this_instruction = get_empty_instruction()
this_instruction['opcode'] = OP_KDOWN
this_instruction['oparg'] = get_key_combined_value(item)
this_instruction['comment'] = this_line
assembly_listing.append(this_instruction)
# release, from last to first
for item in reversed(key_list):
this_instruction = get_empty_instruction()
this_instruction['opcode'] = OP_KUP
this_instruction['oparg'] = get_key_combined_value(item)
this_instruction['comment'] = this_line
assembly_listing.append(this_instruction)
else:
raise ValueError(f"Unknown command: {this_line}")
this_instruction = get_empty_instruction()
this_instruction['opcode'] = OP_HALT
assembly_listing.append(this_instruction)
print("--------- Assembly Listing, Unresolved ---------")
print_asslist(assembly_listing)
# ------------------ generate binary ------------------
for index, item in enumerate(assembly_listing):
item['addr'] = index * INSTRUCTION_SIZE_BYTES
VAR_SIZE_BYTES = 2
var_addr_dict = {}
var_count = 0
for item in var_lookup:
if item in reserved_variable_dict:
var_addr_dict[item] = reserved_variable_dict[item]
else:
var_addr_dict[item] = var_count * VAR_SIZE_BYTES
var_count += 1
for item in assembly_listing:
if item['oparg'] in var_addr_dict:
item['oparg'] = var_addr_dict[item['oparg']]
for item in reserved_variable_dict:
var_lookup.pop(item, None)
if len(var_lookup) > 64:
raise ValueError("Too many variables")
str_list = []
for item in str_lookup:
this_str = {
'content': item,
'bytes': replace_var_in_str(item, var_addr_dict) + zero.to_bytes(1, endianness),
'lnum': str_lookup[item],
'addr': None}
str_list.append(this_str)
str_bin_start = len(assembly_listing) * INSTRUCTION_SIZE_BYTES
for index, item in enumerate(str_list):
if index == 0:
item['addr'] = str_bin_start
else:
item['addr'] = str_list[index-1]['addr'] + len(str_list[index-1]['bytes'])
# replace lables with real memory address
label_to_addr_dict = {}
for item in assembly_listing:
if item['label'] is not None:
label_to_addr_dict[item['label']] = item['addr']
for item in assembly_listing:
if item['opcode'] == OP_STR or item['opcode'] == OP_STRLN or item['opcode'] == OP_OLP:
str_lnum = int(item['oparg'].replace('STR@', ''))
for sssss in str_list:
if sssss['lnum'] == str_lnum:
item['oparg'] = sssss['addr']
if item['oparg'] is None:
continue
if isinstance(item['oparg'], str) and "@" in item['oparg']:
item['oparg'] = label_to_addr_dict[item['oparg']]
item['oparg'] = int(item['oparg'])
print("--------- Assembly Listing, Resolved ---------")
print_asslist(assembly_listing)
output_bin_array = bytearray()
for item in assembly_listing:
output_bin_array += item['opcode'][1].to_bytes(1, endianness)
this_arg = 0
if item['oparg'] is not None:
this_arg = item['oparg']
output_bin_array += this_arg.to_bytes(2, endianness)
# write zero-terminated strings
for item in str_list:
output_bin_array += item['bytes']
print('\n\n--------- Bytecode ---------')
for index, number in enumerate(output_bin_array):
print("0x{:02x}".format(number), end=' ')
if (index+1) % 9 == 0:
print()
print('\n')
# print("label_to_addr_dict:", label_to_addr_dict)
# print("var_addr_dict:", var_addr_dict)
# print('var_lookup:', var_lookup)
# print("str_bin_start:", str_bin_start)
# print("str_list:", str_list)
print(f'Binary Size: {len(output_bin_array)} Bytes')
return output_bin_array
if __name__ == "__main__":
if len(sys.argv) <= 2:
print(__file__, "ds3_script output")
exit()
text_file = open(sys.argv[1])
program_listing = text_file.read().split('\n')
text_file.close()
bin_arr = make_dsb(program_listing)
bin_out = open(sys.argv[2], 'wb')
bin_out.write(bin_arr)
bin_out.close()