-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathprogram.py
186 lines (159 loc) · 7.21 KB
/
program.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
"""
Copyright (C) 2013-2022 Craig Thomas
This project uses an MIT style license - see LICENSE for details.
This file contains the main Program class for the CoCo Assembler.
"""
# I M P O R T S ###############################################################
from cocoasm.exceptions import TranslationError, ValueTypeError
from cocoasm.statement import Statement
from cocoasm.values import AddressValue, NoneValue
from cocoasm.virtualfiles.source_file import SourceFile
# C L A S S E S ###############################################################
class Program(object):
"""
The Program class represents an actual Color Computer program. Each Program
contains a list of statements. Additionally, a Program keeps track of all
the user-defined symbols in the program.
"""
def __init__(self):
self.symbol_table = dict()
self.statements = []
self.address = 0x0
self.origin = NoneValue()
self.name = None
def process(self, source_file):
"""
Processes a filename for assembly.
:param source_file: the source file to process
"""
self.statements = self.parse(source_file)
self.translate_statements()
@classmethod
def parse(cls, contents):
"""
Parses a single file and saves the set of statements.
:param contents: a list of strings, each string represents one line of assembly
"""
statements = []
for line in contents:
statement = Statement(line)
if not statement.is_empty and not statement.is_comment_only:
statements.append(statement)
return statements
@classmethod
def process_mnemonics(cls, statements):
"""
Given a list of statements, processes the mnemonics on each statement, and
assigns each statement an Instruction object. If the statement is the
pseudo operation INCLUDE, then it will parse the statements with the
associated include file.
:param statements: the list of statements to process
:return: a list of processed statements
"""
processed_statements = []
for statement in statements:
include_filename = statement.get_include_filename()
if include_filename:
include_source = SourceFile(include_filename)
include_source.read_file()
include = cls.process_mnemonics(cls.parse(include_source.get_buffer()))
processed_statements.extend(include)
else:
processed_statements.extend([statement])
return processed_statements
def save_symbol(self, index, statement):
"""
Checks a statement for a label and saves it to the symbol table, along with
the index into the list of statements where the label occurs. Will raise a
TranslationError if the label already exists in the symbol table.
:param index: the index into the list of statements where the label occurs
:param statement: the statement with the label
"""
label = statement.label
if label:
if label in self.symbol_table:
raise TranslationError("Label [" + label + "] redefined", statement)
if statement.instruction.is_pseudo_define:
self.symbol_table[label] = statement.operand.value
else:
self.symbol_table[label] = AddressValue(index)
def translate_statements(self):
"""
Translates all the parsed statements into their respective
opcodes.
"""
self.statements = self.process_mnemonics(self.statements)
for index, statement in enumerate(self.statements):
self.save_symbol(index, statement)
for index, statement in enumerate(self.statements):
statement.resolve_symbols(self.symbol_table)
for index, statement in enumerate(self.statements):
statement.translate()
while not self.all_sizes_fixed():
for index, statement in enumerate(self.statements):
if not statement.fixed_size:
statement.determine_pcr_relative_sizes(self.statements, index)
address = 0
for index, statement in enumerate(self.statements):
address = statement.set_address(address)
address += statement.code_pkg.size
for index, statement in enumerate(self.statements):
statement.fix_addresses(self.statements, index)
# Update the symbol table with the proper addresses
for symbol, value in self.symbol_table.items():
if value.is_address():
self.symbol_table[symbol] = self.statements[value.int].code_pkg.address
# Find the origin and name of the project
for statement in self.statements:
if statement.instruction.is_origin:
self.origin = statement.code_pkg.address
if statement.instruction.is_name:
self.name = statement.operand.operand_string
def get_binary_array(self):
"""
Returns an array containing the machine code statements for the
assembled program.
:return: returns the assembled program bytes
"""
machine_codes = []
for statement in self.statements:
if not statement.is_empty and not statement.is_comment_only:
for index in range(0, statement.code_pkg.op_code.hex_len(), 2):
op_code = statement.code_pkg.op_code.hex()
hex_byte = "{}{}".format(op_code[index], op_code[index+1])
machine_codes.append(int(hex_byte, 16))
for index in range(0, statement.code_pkg.post_byte.hex_len(), 2):
post_byte = statement.code_pkg.post_byte.hex()
hex_byte = "{}{}".format(post_byte[index], post_byte[index + 1])
machine_codes.append(int(hex_byte, 16))
for index in range(0, statement.code_pkg.additional.hex_len(), 2):
additional = statement.code_pkg.additional.hex()
hex_byte = "{}{}".format(additional[index], additional[index + 1])
machine_codes.append(int(hex_byte, 16))
return machine_codes
def all_sizes_fixed(self):
"""
Checks to see if all of the statements have fixed sizes. Returns
True if all the sizes are fixed, False otherwise.
"""
for statement in self.statements:
if not statement.fixed_size:
return False
return True
def get_symbol_table(self):
"""
Returns a list of strings. Each string contains one entry from the symbol table.
"""
lines = []
for symbol, value in self.symbol_table.items():
lines.append("${} {}".format(value.hex().ljust(4, ' '), symbol))
return lines
def get_statements(self):
"""
Returns a list of strings. Each string represents one assembled statement
"""
lines = []
for index, statement in enumerate(self.statements):
lines.append("{}".format(str(statement)))
return lines
# E N D O F F I L E #######################################################