OXYGENHUB
◆ main x16-emulator / src / cpu / buildtables.py
9.7 KiB · Python · last changed chore(): initial commit 15 days ago source blame history raw
03a3459e Andy McCall 1
# *******************************************************************************************
2
# *******************************************************************************************
3
#
4
#		File:			buildtables.py
5
#		Date:			3rd September 2019
6
#		Purpose:		Creates files tables.h from the .opcodes descriptors
7
#						Creates disassembly include file.
8
#		Author:			Paul Robson ([email protected])
9
#		Formatted By: 	Jeries Abedrabbo ([email protected])
10
#
11
# *******************************************************************************************
12
# *******************************************************************************************
13
14
import re
15
16
############# CONSTANTS ##############
17
######################################
18
########### KEY CONSTANTS ############
19
ACTN_KEY_STR = "action"
20
CYCLES_KEY_STR = "cycles"
21
MODE_KEY_STR = "mode"
22
OPCODE_KEY_STR = "opcode"
23
24
######################################
25
########### REGEX CONSTANTS ##########
26
OPCODE_REGEX_STR = "^(?P<{actnCode}>\\w+)\\s+(?P<{addrMode}>\\w+)\\s+(?P<{mchnCycles}>\\d)\\s+\\$(?P<{opCode}>[0-9a-fA-F]+)$".format(
27
    actnCode=ACTN_KEY_STR,
28
    addrMode=MODE_KEY_STR,
29
    mchnCycles=CYCLES_KEY_STR,
30
    opCode=OPCODE_KEY_STR
31
)
32
33
#####################################
34
########## HEADER CONSTANTS #########
35
ADDR_MODE_HEADER_C02 = "static void (*addrtable_c02[256])() = {"
36
ACTN_CODE_HEADER_C02 = "static void (*optable_c02[256])() = {"
37
MCHN_CYCLES_HEADER_C02 = "static const uint32_t ticktable_c02[256] = {"
38
MNEMONICS_DISASSEM_HEADER_C02 = "static const char *mnemonics_c02[256] = {"
39
ADDR_MODE_HEADER_C816 = "static void (*addrtable_c816[256])() = {"
40
ACTN_CODE_HEADER_C816 = "static void (*optable_c816[256])() = {"
41
MCHN_CYCLES_HEADER_C816 = "static const uint32_t ticktable_c816[256] = {"
42
MNEMONICS_DISASSEM_HEADER_C816 = "static const char *mnemonics_c816[256] = {"
43
TABLE_MAP = "/*{0:8}|  0  |  1  |  2  |  3  |  4  |  5  |  6  |  7  |  8  |  9  |  A  |  B  |  C  |  D  |  E  |  F  |{0:5}*/\n"
44
45
#####################################
46
######### OPCODE CONSTANTS ##########
47
EMPTY_NOP_ACTN = "nop"
48
EMPTY_NOP_CYCLS = "1"
49
EMPTY_NOP_MOD = "imp"
50
OPCODE_ROW_LEN = 16
51
TOTAL_NUMBER_OPCODES = 2 ** 8
52
53
#####################################
54
############# FILENAMES #############
55
TABLES_HEADER_FNAME = "tables.h"
56
MNEMONICS_DISASSEM_HEADER_FNAME = "mnemonics.h"
57
OPCODES_6502_FNAME = "6502.opcodes"
58
OPCODES_65c02_FNAME = "65c02.opcodes"
59
OPCODES_65c816_FNAME = "65c816.opcodes"
60
61
62
#####################################
63
#####################################
64
65
66
#######################################################################################################################
67
####################################  Load in a source file with opcode descriptors  ##################################
68
#######################################################################################################################
69
def loadSource(srcFile, opcodesList):
70
    # Read file line by line and omit committed
71
    fileLinesObject = open(srcFile).readlines()
72
    strippedOpcodeLines = [opcodeLine.strip() for opcodeLine in fileLinesObject if
73
                           opcodeLine.strip() != "" and not opcodeLine.startswith(";")]
74
75
    # Import Opcodes
76
    for opcodeLine in strippedOpcodeLines:
77
        matchLine = re.match(OPCODE_REGEX_STR, opcodeLine)
78
        assert matchLine is not None, "Format {}".format(opcodeLine)
79
80
        opcode = int(matchLine.group(OPCODE_KEY_STR), 16)
81
        assert opcodesList[opcode] is None, "Duplicate {0:02x}".format(opcode)
82
83
        opcodesList[opcode] = {
84
            MODE_KEY_STR: matchLine.group(MODE_KEY_STR),
85
            ACTN_KEY_STR: matchLine.group(ACTN_KEY_STR),
86
            CYCLES_KEY_STR: matchLine.group(CYCLES_KEY_STR),
87
            OPCODE_KEY_STR: opcode
88
        }
89
90
91
#######################################################################################################################
92
#############################################  Fill unused slots with NOPs  ###########################################
93
#######################################################################################################################
94
def fillNop(opcodesList):
95
    for i in range(0, TOTAL_NUMBER_OPCODES):
96
        if opcodesList[i] is None:
97
            opcodesList[i] = {
98
                MODE_KEY_STR: EMPTY_NOP_MOD,
99
                ACTN_KEY_STR: EMPTY_NOP_ACTN,
100
                CYCLES_KEY_STR: EMPTY_NOP_CYCLS,
101
                OPCODE_KEY_STR: i
102
            }
103
104
105
#######################################################################################################################
106
###################################################  Output a table  ##################################################
107
#######################################################################################################################
108
def generateTable(hFileName, header, element, opcodesList):
109
    spacing = 5
110
    hFileName.write("{}{}{}".format("\n", header, "\n"))
111
    hFileName.write(TABLE_MAP.format("", ""))
112
    for row in range(0, OPCODE_ROW_LEN):
113
        elements = ",".join(
114
            ["{0:>9}".format(opcodesList[opcodeItem][element])[-spacing:] for opcodeItem in
115
             range(row * OPCODE_ROW_LEN, OPCODE_ROW_LEN * (row + 1))])
116
117
        hFileName.write("/* {0:X} */ {3}  {1}{2} /* {0:X} */\n".format(
118
            row,
119
            elements,
120
            " " if row == OPCODE_ROW_LEN - 1 else ",",
121
            " " if element == ACTN_KEY_STR else "")
122
        )
123
    hFileName.write("};\n")
124
125
126
127
#######################################################################################################################
128
###################################################  Output a list   ##################################################
129
#######################################################################################################################
130
def generateList(hFileName, header, elements):
131
    hFileName.write("{}{}{}".format("\n", header, "\n"))
132
    for row in range(0, OPCODE_ROW_LEN):
133
        hFileName.write("\t// ${0:X}X\n".format(row))
134
135
        for op in range(0, OPCODE_ROW_LEN):
136
            hFileName.write("\t/* ${0:X}{1:X} */ \"{2}\"{3}".format(row, op, elements[row * OPCODE_ROW_LEN + op], "" if row == OPCODE_ROW_LEN-1 and op == OPCODE_ROW_LEN-1 else ",\n"))
137
138
        hFileName.write("{}".format("" if row == OPCODE_ROW_LEN-1 else "\n"))
139
    hFileName.write("};\n")
140
141
142
143
#######################################################################################################################
144
########################################  Convert opcode structure to mnemonic  #######################################
145
#######################################################################################################################
146
def convertMnemonic(opInfo):
147
    modeStr = {
148
        "imp": "",
149
        "imp8": "#$%02x",
150
        "imm8": "#$%02x",
151
        "immm": "#$%%0%hhux",
152
        "immx": "#$%%0%hhux",
153
        "imm16": "#$%04x",
154
        "zp": "$%02x",
155
        "rel": "$%02x",
156
        "rel16": "$%04x",
157
        "zprel": "$%02x, $%04x",
158
        "zpx": "$%02x,x",
159
        "zpy": "$%02x,y",
160
        "abso": "$%04x",
161
        "absx": "$%04x,x",
162
        "absy": "$%04x,y",
163
        "absl": "$%06x",
164
        "abslx": "$%06x,x",
165
        "aindl": "[$%04x]",
166
        "ainx": "($%04x,x)",
167
        "indy": "($%02x),y",
168
        "indly": "[$%02x],y",
169
        "indx": "($%02x,x)",
170
        "ind": "($%04x)",
171
        "ind0": "($%02x)",
172
        "ind0p": "($%02x)",
173
        "indl0": "[$%02x]",
174
        "acc": "a",
175
        "sr": "$%02x,S",
176
        "sridy": "($%02x,S),y",
177
        "bmv": "$%02x,$%02x",
178
    }
179
    return "{} {}".format(opInfo[ACTN_KEY_STR], modeStr[opInfo[MODE_KEY_STR]])
180
181
182
#######################################################################################################################
183
##################################################  Load in opcodes  ##################################################
184
#######################################################################################################################
185
if __name__ == '__main__':
186
    opcodesList_c02 = [None] * TOTAL_NUMBER_OPCODES
187
188
    # Load 6502 opcodes list
189
    loadSource(OPCODES_6502_FNAME, opcodesList_c02)
190
    # Load 65C02 opcodes list
191
    loadSource(OPCODES_65c02_FNAME, opcodesList_c02)
192
193
    opcodesList_c816 = [None] * TOTAL_NUMBER_OPCODES
194
195
    # Load 6502 opcodes list
196
    loadSource(OPCODES_6502_FNAME, opcodesList_c816)
197
    # Load 65C816 opcodes list
198
    loadSource(OPCODES_65c816_FNAME, opcodesList_c816)
199
200
    # Fill opcodes list with NOP instructions
201
    fillNop(opcodesList_c02)
202
    fillNop(opcodesList_c816)
203
204
    # Create "TABLES_HEADER_FNAME" header file
205
    with open(TABLES_HEADER_FNAME, "w") as output_h_file:
206
        output_h_file.write("/* Generated by buildtables.py */\n")
207
        generateTable(output_h_file, ADDR_MODE_HEADER_C02, MODE_KEY_STR, opcodesList_c02)
208
        generateTable(output_h_file, ADDR_MODE_HEADER_C816, MODE_KEY_STR, opcodesList_c816)
209
        generateTable(output_h_file, ACTN_CODE_HEADER_C02, ACTN_KEY_STR, opcodesList_c02)
210
        generateTable(output_h_file, ACTN_CODE_HEADER_C816, ACTN_KEY_STR, opcodesList_c816)
211
        generateTable(output_h_file, MCHN_CYCLES_HEADER_C02, CYCLES_KEY_STR, opcodesList_c02)
212
        generateTable(output_h_file, MCHN_CYCLES_HEADER_C816, CYCLES_KEY_STR, opcodesList_c816)
213
214
    # Create disassembly "MNEMONICS_DISASSEM_HEADER_FNAME" header file.
215
    mnemonics_c02 = [convertMnemonic(opcodesList_c02[x]) for x in range(0, TOTAL_NUMBER_OPCODES)]
216
    mnemonics_c816 = [convertMnemonic(opcodesList_c816[x]) for x in range(0, TOTAL_NUMBER_OPCODES)]
217
    with open(MNEMONICS_DISASSEM_HEADER_FNAME, "w") as output_h_file:
218
        output_h_file.write("/* Generated by buildtables.py */\n")
219
        generateList(output_h_file, MNEMONICS_DISASSEM_HEADER_C02, mnemonics_c02)
220
        generateList(output_h_file, MNEMONICS_DISASSEM_HEADER_C816, mnemonics_c816)