OXYGENHUB
# 9120e87480a6 enhanced-x16-emulator / src / cpu / buildtables.py
10.1 KiB · Python · last changed chore(): initial commit 25 days ago source blame history raw
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, allowOverride=False):
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
        # The variant files are layered over 6502.opcodes. Most entries are
82
        # additions, but a few opcodes exist on both parts with different
83
        # timing, so a variant file is allowed to replace one. Only the variant
84
        # files may: a duplicate inside the base is still a mistake.
85
        assert allowOverride or opcodesList[opcode] is None, \
86
            "Duplicate {0:02x}".format(opcode)
87
88
        opcodesList[opcode] = {
89
            MODE_KEY_STR: matchLine.group(MODE_KEY_STR),
90
            ACTN_KEY_STR: matchLine.group(ACTN_KEY_STR),
91
            CYCLES_KEY_STR: matchLine.group(CYCLES_KEY_STR),
92
            OPCODE_KEY_STR: opcode
93
        }
94
95
96
#######################################################################################################################
97
#############################################  Fill unused slots with NOPs  ###########################################
98
#######################################################################################################################
99
def fillNop(opcodesList):
100
    for i in range(0, TOTAL_NUMBER_OPCODES):
101
        if opcodesList[i] is None:
102
            opcodesList[i] = {
103
                MODE_KEY_STR: EMPTY_NOP_MOD,
104
                ACTN_KEY_STR: EMPTY_NOP_ACTN,
105
                CYCLES_KEY_STR: EMPTY_NOP_CYCLS,
106
                OPCODE_KEY_STR: i
107
            }
108
109
110
#######################################################################################################################
111
###################################################  Output a table  ##################################################
112
#######################################################################################################################
113
def generateTable(hFileName, header, element, opcodesList):
114
    spacing = 5
115
    hFileName.write("{}{}{}".format("\n", header, "\n"))
116
    hFileName.write(TABLE_MAP.format("", ""))
117
    for row in range(0, OPCODE_ROW_LEN):
118
        elements = ",".join(
119
            ["{0:>9}".format(opcodesList[opcodeItem][element])[-spacing:] for opcodeItem in
120
             range(row * OPCODE_ROW_LEN, OPCODE_ROW_LEN * (row + 1))])
121
122
        hFileName.write("/* {0:X} */ {3}  {1}{2} /* {0:X} */\n".format(
123
            row,
124
            elements,
125
            " " if row == OPCODE_ROW_LEN - 1 else ",",
126
            " " if element == ACTN_KEY_STR else "")
127
        )
128
    hFileName.write("};\n")
129
130
131
132
#######################################################################################################################
133
###################################################  Output a list   ##################################################
134
#######################################################################################################################
135
def generateList(hFileName, header, elements):
136
    hFileName.write("{}{}{}".format("\n", header, "\n"))
137
    for row in range(0, OPCODE_ROW_LEN):
138
        hFileName.write("\t// ${0:X}X\n".format(row))
139
140
        for op in range(0, OPCODE_ROW_LEN):
141
            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"))
142
143
        hFileName.write("{}".format("" if row == OPCODE_ROW_LEN-1 else "\n"))
144
    hFileName.write("};\n")
145
146
147
148
#######################################################################################################################
149
########################################  Convert opcode structure to mnemonic  #######################################
150
#######################################################################################################################
151
def convertMnemonic(opInfo):
152
    modeStr = {
153
        "imp": "",
154
        "imp8": "#$%02x",
155
        "imm8": "#$%02x",
156
        "immm": "#$%%0%hhux",
157
        "immx": "#$%%0%hhux",
158
        "imm16": "#$%04x",
159
        "zp": "$%02x",
160
        "rel": "$%02x",
161
        "rel16": "$%04x",
162
        "zprel": "$%02x, $%04x",
163
        "zpx": "$%02x,x",
164
        "zpy": "$%02x,y",
165
        "abso": "$%04x",
166
        "absx": "$%04x,x",
167
        "absy": "$%04x,y",
168
        "absl": "$%06x",
169
        "abslx": "$%06x,x",
170
        "aindl": "[$%04x]",
171
        "ainx": "($%04x,x)",
172
        "indy": "($%02x),y",
173
        "indly": "[$%02x],y",
174
        "indx": "($%02x,x)",
175
        "ind": "($%04x)",
176
        "ind0": "($%02x)",
177
        "ind0p": "($%02x)",
178
        "indl0": "[$%02x]",
179
        "acc": "a",
180
        "sr": "$%02x,S",
181
        "sridy": "($%02x,S),y",
182
        "bmv": "$%02x,$%02x",
183
    }
184
    return "{} {}".format(opInfo[ACTN_KEY_STR], modeStr[opInfo[MODE_KEY_STR]])
185
186
187
#######################################################################################################################
188
##################################################  Load in opcodes  ##################################################
189
#######################################################################################################################
190
if __name__ == '__main__':
191
    opcodesList_c02 = [None] * TOTAL_NUMBER_OPCODES
192
193
    # Load 6502 opcodes list
194
    loadSource(OPCODES_6502_FNAME, opcodesList_c02)
195
    # Load 65C02 opcodes list
196
    loadSource(OPCODES_65c02_FNAME, opcodesList_c02, allowOverride=True)
197
198
    opcodesList_c816 = [None] * TOTAL_NUMBER_OPCODES
199
200
    # Load 6502 opcodes list
201
    loadSource(OPCODES_6502_FNAME, opcodesList_c816)
202
    # Load 65C816 opcodes list
203
    loadSource(OPCODES_65c816_FNAME, opcodesList_c816, allowOverride=True)
204
205
    # Fill opcodes list with NOP instructions
206
    fillNop(opcodesList_c02)
207
    fillNop(opcodesList_c816)
208
209
    # Create "TABLES_HEADER_FNAME" header file
210
    with open(TABLES_HEADER_FNAME, "w") as output_h_file:
211
        output_h_file.write("/* Generated by buildtables.py */\n")
212
        generateTable(output_h_file, ADDR_MODE_HEADER_C02, MODE_KEY_STR, opcodesList_c02)
213
        generateTable(output_h_file, ADDR_MODE_HEADER_C816, MODE_KEY_STR, opcodesList_c816)
214
        generateTable(output_h_file, ACTN_CODE_HEADER_C02, ACTN_KEY_STR, opcodesList_c02)
215
        generateTable(output_h_file, ACTN_CODE_HEADER_C816, ACTN_KEY_STR, opcodesList_c816)
216
        generateTable(output_h_file, MCHN_CYCLES_HEADER_C02, CYCLES_KEY_STR, opcodesList_c02)
217
        generateTable(output_h_file, MCHN_CYCLES_HEADER_C816, CYCLES_KEY_STR, opcodesList_c816)
218
219
    # Create disassembly "MNEMONICS_DISASSEM_HEADER_FNAME" header file.
220
    mnemonics_c02 = [convertMnemonic(opcodesList_c02[x]) for x in range(0, TOTAL_NUMBER_OPCODES)]
221
    mnemonics_c816 = [convertMnemonic(opcodesList_c816[x]) for x in range(0, TOTAL_NUMBER_OPCODES)]
222
    with open(MNEMONICS_DISASSEM_HEADER_FNAME, "w") as output_h_file:
223
        output_h_file.write("/* Generated by buildtables.py */\n")
224
        generateList(output_h_file, MNEMONICS_DISASSEM_HEADER_C02, mnemonics_c02)
225
        generateList(output_h_file, MNEMONICS_DISASSEM_HEADER_C816, mnemonics_c816)