mc_compiler.py (9852B)
1 #!/usr/bin/env python3 2 3 # Copyright 2022 Gerd Beuster (gerd@frombelow.net). This is free soft 4 # under the GNU GPL v3 license or any later version. See COPYING in 5 # the root directory for details. 6 7 import sys 8 9 pc_set_uncond = 1 << 0 10 pc_set_if_zero = 1 << 1 11 pc_set_if_nonzero = 1 << 2 12 pc_inc = 1 << 3 13 dp_set = 1 << 4 14 dp_get = 1 << 5 15 mem_set = 1 << 6 16 reg_a_get = 1 << 7 17 reg_a_set = 0b001 << 8 18 reg_a_inc = 0b010 << 8 19 reg_a_dec = 0b011 << 8 20 reg_a_rol = 0b100 << 8 21 reg_a_ror = 0b101 << 8 22 reg_a_inv = 0b110 << 8 23 mip_set = 1 << 11 24 alu_add = 0b001 << 12 25 alu_sub = 0b010 << 12 26 # Multiplication and division occpuy too many PLBs 27 # alu_mul = 0b011 << 12 28 # alu_div = 0b100 << 12 29 alu_and = 0b101 << 12 30 alu_or = 0b110 << 12 31 alu_xor = 0b111 << 12 32 33 # Loading the address bus 34 # 35 # In every cycle, the CPU loads the address bus with either 36 # the value of the program counter or the data pointer. 37 # Therefore we only need dp_get, a flag indicating if the address bus 38 # loads the data pointer. If this flag is not set, the address bus 39 # loads the program counter. 40 41 # Loading the data bus 42 # 43 # In every cycle, the CPU loads the data bus with either 44 # the value from accumulator, the memory, or the alu. 45 # Accumulator access has priority. If reg_a_get is not set, 46 # the data bus is loaded from the ALU if alu_... is set and from memory 47 # otherwise. 48 49 # Summary: 50 51 # - Unless dp_get is set, the address bus is loaded from the PC on every 52 # instruction. 53 # - If neither alu_... nor reg_a_get is set, the data bus is loaded 54 # from memory on every instructions 55 # 56 # Therefore microcode 0 loads the data bus with the memory location 57 # given by the PC. 58 59 opcode = [['NOP', 60 [ 61 # First instruction implicitly loads address bus with PC 62 # and reads memory at that location. The memory content is loaded on the data bus in the next step, 63 # and the MIP is set from the data bus. 64 # The PC is incremented such that the operand of the 65 # next instruction can be fetched in the next step 66 0, 67 pc_inc | mip_set, 68 ]], 69 ['LDA', 'direct', 70 [ 71 # We want to load from the memory address pointed to 72 # by the PC. We read the memory address (i.e. the pointer value) ... 73 0, 74 # ... and store it DP. 75 dp_set, 76 # Now we transfer this value to the address bus, read this memory 77 # address ... 78 dp_get, 79 # ... and store it in the accumulator. We also increment 80 # the PC such that we can fetch the next instruction 81 # and the following step. 82 dp_get | reg_a_set | pc_inc, 83 # We are done. Fetch next instruction and increment PC such that 84 # the next instruction can load its argument. 85 0, 86 pc_inc | mip_set, 87 ]], 88 ['LDA', 'immediate', 89 [ 90 reg_a_set | pc_inc, 91 0, 92 pc_inc | mip_set, 93 ]], 94 ['LDA', 'indirect', 95 [ 96 0, 97 dp_set, 98 dp_get | dp_set, 99 dp_get, 100 dp_get | reg_a_set | pc_inc, 101 0, 102 pc_inc | mip_set, 103 ]], 104 ['STA', 'direct', 105 [ 106 0, 107 dp_set, 108 dp_get | mem_set | reg_a_get | pc_inc, 109 0, 110 pc_inc | mip_set, 111 ]], 112 ['STA', 'indirect', 113 [ 114 0, 115 dp_set, 116 dp_get | dp_set, 117 dp_get | mem_set |reg_a_get | pc_inc, 118 0, 119 pc_inc | mip_set, 120 ]], 121 ['JMP', 'direct', 122 [ 123 0, 124 pc_set_uncond, 125 0, 126 pc_inc | mip_set, 127 ]], 128 ['JNZ', 'direct', 129 [ 130 0, 131 pc_inc | pc_set_if_nonzero, 132 0, 133 pc_inc | mip_set, 134 ]], 135 ['JZE', 'direct', 136 [ 137 0, 138 pc_inc | pc_set_if_zero, 139 0, 140 pc_inc | mip_set, 141 ]], 142 ['ADD', 'direct', 143 [ 144 0, 145 dp_set, 146 dp_get, 147 dp_get, 148 alu_add | pc_inc | reg_a_set, 149 0, 150 pc_inc | mip_set, 151 ]], 152 ['ADD', 'indirect', 153 [ 154 0, 155 dp_set, 156 dp_get | dp_set, 157 dp_get, 158 dp_get, 159 pc_inc | alu_add | reg_a_set, 160 0, 161 pc_inc | mip_set, 162 ]], 163 ['ADD', 'immediate', 164 [ 165 0, 166 alu_add | pc_inc | reg_a_set, 167 0, 168 pc_inc | mip_set, 169 ]], 170 ['SUB', 'direct', 171 [ 172 0, 173 dp_set, 174 dp_get, 175 dp_get, 176 alu_sub | pc_inc | reg_a_set, 177 0, 178 pc_inc | mip_set, 179 ]], 180 ['SUB', 'indirect', 181 [ 182 0, 183 dp_set, 184 dp_get | dp_set, 185 dp_get, 186 dp_get, 187 pc_inc | alu_sub | reg_a_set, 188 0, 189 pc_inc | mip_set, 190 ]], 191 ['SUB', 'immediate', 192 [ 193 0, 194 alu_sub | pc_inc | reg_a_set, 195 0, 196 pc_inc | mip_set, 197 ]], 198 ['AND', 'direct', 199 [ 200 0, 201 dp_set, 202 dp_get, 203 dp_get, 204 alu_and | pc_inc | reg_a_set, 205 0, 206 pc_inc | mip_set, 207 ]], 208 ['AND', 'indirect', 209 [ 210 0, 211 dp_set, 212 dp_get | dp_set, 213 dp_get, 214 dp_get, 215 pc_inc | alu_and | reg_a_set, 216 0, 217 pc_inc | mip_set, 218 ]], 219 ['AND', 'immediate', 220 [ 221 0, 222 alu_and | pc_inc | reg_a_set, 223 0, 224 pc_inc | mip_set, 225 ]], 226 ['ORA', 'direct', 227 [ 228 0, 229 dp_set, 230 dp_get, 231 dp_get, 232 alu_or | pc_inc | reg_a_set, 233 0, 234 pc_inc | mip_set, 235 ]], 236 ['ORA', 'indirect', 237 [ 238 0, 239 dp_set, 240 dp_get | dp_set, 241 dp_get, 242 dp_get, 243 pc_inc | alu_or | reg_a_set, 244 0, 245 pc_inc | mip_set, 246 ]], 247 ['ORA', 'immediate', 248 [ 249 0, 250 alu_or | pc_inc | reg_a_set, 251 0, 252 pc_inc | mip_set, 253 ]], 254 ['XOR', 'direct', 255 [ 256 0, 257 dp_set, 258 dp_get, 259 dp_get, 260 alu_xor | pc_inc | reg_a_set, 261 0, 262 pc_inc | mip_set, 263 ]], 264 ['XOR', 'indirect', 265 [ 266 0, 267 dp_set, 268 dp_get | dp_set, 269 dp_get, 270 dp_get, 271 pc_inc | alu_xor | reg_a_set, 272 0, 273 pc_inc | mip_set, 274 ]], 275 ['XOR', 'immediate', 276 [ 277 0, 278 alu_xor | pc_inc | reg_a_set, 279 0, 280 pc_inc | mip_set, 281 ]], 282 ['INC', 283 [ 284 reg_a_inc, 285 pc_inc | mip_set, 286 ]], 287 ['DEC', 288 [ 289 reg_a_dec, 290 pc_inc | mip_set, 291 ]], 292 ['ROL', 293 [ 294 reg_a_rol, 295 pc_inc | mip_set, 296 ]], 297 ['ROR', 298 [ 299 reg_a_ror, 300 pc_inc | mip_set, 301 ]], 302 ['INV', 303 [ 304 reg_a_inv, 305 pc_inc | mip_set, 306 ]], 307 ] 308 309 if len(sys.argv) != 2: 310 sys.stderr.write("Usage: mc_compiler.py microcode|opcode\n") 311 sys.exit(-1) 312 313 if sys.argv[1] == 'microcode': 314 # Since ROM data has a width of 8 bit, we split the microcode into two 315 # rom files. 316 with open('microcode_rom_lsb.dat', 'w') as r_lsb: 317 r_lsb.write('// Microcode - LSB\n') 318 with open('microcode_rom_msb.dat', 'w') as r_msb: 319 r_msb.write('// Microcode - MSB\n') 320 byte_count = 0 321 for o in opcode: 322 r_lsb.write('// {}\n'.format(o[0])) 323 r_msb.write('// {}\n'.format(o[0])) 324 r_lsb.write("".join(["{:02X} ".format(x & 0xFF) for x in o[-1]]) + "\n") 325 r_msb.write("".join(["{:02X} ".format(x >> 8) for x in o[-1]]) + "\n") 326 byte_count += len(o[1]) 327 r_lsb.write('// Number of instructions: {}\n'.format(byte_count)) 328 r_msb.write('// Number of instructions: {}\n'.format(byte_count)) 329 330 331 if sys.argv[1] == 'opcodes': 332 byte_count = 0 333 with open('opcodes.pl', 'w') as f: 334 f.write(':- discontiguous opcode_to_byte/2.\n') 335 for o in opcode: 336 if(len(o) == 2): 337 # Opcode without addressing mode 338 f.write('opcode_to_byte("{}", {}).\n'.format(o[0], byte_count)) 339 else: 340 # Opcode with addressing mode 341 f.write('opcode_to_byte("{}", {}, {}).\n'.format(o[0], o[1], byte_count)) 342 byte_count += len(o[-1])