218 lines
8.1 KiB
JavaScript
218 lines
8.1 KiB
JavaScript
const fs = require('fs')
|
|
|
|
main()
|
|
|
|
function main() {
|
|
let instructions = JSON.parse(fs.readFileSync('generated/instructions.json', 'utf8').toString())
|
|
|
|
// Exclude exceptions
|
|
const exceptions = [
|
|
'MOVUAL', 'MOVUALP', // unknown Read_Unaligned_Long functions
|
|
'ICBI', 'OCBI', 'OCBP', 'OCBWB', 'PREFI', 'SLEEP', 'SYNCO', 'SETRC', 'SETRCI', // Call to unknown functions
|
|
'LDSMX0', 'LDSMX1', 'LDSMY0', 'LDSMY1', 'LDCMSR', 'LDRC', 'LDRCI', 'CLRDMXY', 'TRAPA', // DSP-related
|
|
]
|
|
for (let i = 0; i < instructions.length; i++) {
|
|
if (exceptions.includes(instructions[i].name) ||
|
|
instructions[i].name.includes('_BANK') ||
|
|
instructions[i].implementation.includes('PTEH') ||
|
|
instructions[i].implementation.includes('PTEL') ||
|
|
instructions[i].implementation.includes('TLB') ||
|
|
instructions[i].implementation.includes('LDST') ||
|
|
instructions[i].implementation.includes('MISS')
|
|
) {
|
|
instructions.splice(i, 1)
|
|
i--
|
|
}
|
|
}
|
|
instructions = instructions.slice(0, 157)
|
|
|
|
console.log(instructions.map(e => e.name))
|
|
|
|
// instructions.c
|
|
const selector = writeInstructionSelector(instructions)
|
|
const implementations = writeInstructionImplementations(instructions)
|
|
fs.writeFileSync('generated/instructions.c', selector + '\n\n' + implementations)
|
|
|
|
// instruction.h
|
|
const header = instructions.map(instruction => `void ${instruction.name}(cpu_t* cpu, uint16_t instruction);\n`).join('')
|
|
fs.writeFileSync('generated/instruction.h', header)
|
|
}
|
|
|
|
function writeInstructionSelector(instructions) {
|
|
const reg16 = /[01]{16}/ // 0000000000001011
|
|
const reg8 = /[01]{8}[a-z]{8}/ // 00000000nnnnmmmm
|
|
const reg4 = /[01]{4}[a-z]{12}/ // 0000nnnnmmmmdddd
|
|
const reg48 = /[01]{4}[a-z]{4}[0-1]{8}/ // 0100nnnn00010010
|
|
const reg44 = /[01]{4}[a-z]{8}[01]{4}/ // 0110nnnnmmmm1000
|
|
|
|
const filterInstructions = (reg) => instructions.filter(e => e.code.match(reg))
|
|
|
|
let str = `#include "../headers/instructions/instructions.h"
|
|
|
|
// Returns a pointer to the function that implements the corresponding instruction
|
|
void (*get_instruction_impl(uint16_t instruction))(cpu_t*, uint16_t) {
|
|
uint8_t high = (instruction >> 12) & 0x0F;
|
|
uint8_t high8 = instruction >> 8;
|
|
uint8_t low = instruction & 0x0F;
|
|
uint8_t low8 = instruction & 0xFF;\n\n`
|
|
|
|
// Reg 16
|
|
for (const instruction of filterInstructions(reg16)) {
|
|
str += `\tif (instruction == 0b${instruction.code}) return ${instruction.name};\n`
|
|
}
|
|
|
|
// Reg 8
|
|
for (const instruction of filterInstructions(reg8)) {
|
|
str += `\tif (high8 == 0b${instruction.code.slice(0, 8)}) return ${instruction.name};\n`
|
|
}
|
|
|
|
// Reg 4
|
|
for (const instruction of filterInstructions(reg4)) {
|
|
str += `\tif (high == 0b${instruction.code.slice(0, 4)}) return ${instruction.name};\n`
|
|
}
|
|
|
|
// Get all unique combinations of the first 4 bits in the instruction codes, sorted by count
|
|
const getUniqueInstructions4 = (instructions) => Object.entries(
|
|
instructions.reduce((acc, e) => {
|
|
const key = e.code.slice(0, 4)
|
|
acc[key] = acc[key] ? acc[key] + 1 : 1
|
|
return acc
|
|
}, {})
|
|
).sort((a, b) => b[1] - a[1])
|
|
|
|
// Reg 48
|
|
const instructions48 = filterInstructions(reg48)
|
|
const unique48 = getUniqueInstructions4(instructions48)
|
|
|
|
for (const [code] of unique48) {
|
|
str += `\tif (high == 0b${code}) {\n`
|
|
|
|
for (const instruction of instructions48.filter(e => e.code.slice(0, 4) == code)) {
|
|
str += `\t\tif (low8 == 0b${instruction.code.slice(8, 16)}) return ${instruction.name};\n`
|
|
}
|
|
|
|
str += `\t}\n`
|
|
}
|
|
|
|
// Reg 44
|
|
const instructions44 = filterInstructions(reg44)
|
|
const unique44 = getUniqueInstructions4(instructions44)
|
|
|
|
for (const [code] of unique44) {
|
|
str += `\tif (high == 0b${code}) {\n`
|
|
|
|
for (const instruction of instructions44.filter(e => e.code.slice(0, 4) == code)) {
|
|
str += `\t\tif (low == 0b${instruction.code.slice(12, 16)}) return ${instruction.name};\n`
|
|
}
|
|
|
|
str += `\t}\n`
|
|
}
|
|
|
|
str += `\n\tprint_binary(instruction, 16);`
|
|
str += `\n\tcritical_error(" => Unknown instruction: 0x%04X\\n", instruction);\n}`
|
|
|
|
return str
|
|
}
|
|
|
|
function writeInstructionImplementations(instructions) {
|
|
const header = fs.readFileSync('header.c', 'utf8').toString()
|
|
let str = header + '\n\n'
|
|
|
|
// Extract variable character, start and end position from a binary code: 0100nnnnmmmm1100 -> n: 5-8, m: 9-12
|
|
const extractVariablesInfos = (str) => {
|
|
let variables = []
|
|
let currentChar = null
|
|
|
|
const beginVariable = (char, start) => { currentChar = { char, start } }
|
|
const endVariable = (end) => variables.push({...currentChar, end: end + 1})
|
|
|
|
for (let i = 0; i < 16; i++) {
|
|
const isDigit = Number.isInteger(Number.parseInt(str[i]))
|
|
|
|
// Current char is 0-1
|
|
if (isDigit) {
|
|
// If we were reading a variable, end it
|
|
if (currentChar != null) {
|
|
endVariable(i - 1)
|
|
currentChar = null
|
|
}
|
|
continue
|
|
}
|
|
// Current char is a letter
|
|
else {
|
|
// If we were not reading a variable, start it
|
|
if (currentChar == null) {
|
|
beginVariable(str[i], i)
|
|
}
|
|
// Otherwise if the current char is different from the previous one,
|
|
// end the variable and start a new one
|
|
else if (currentChar.char != str[i]) {
|
|
endVariable(i - 1)
|
|
beginVariable(str[i], i)
|
|
}
|
|
// Otherwise if we reached the end of the string, end the variable
|
|
else if (i == 15) {
|
|
endVariable(i)
|
|
}
|
|
}
|
|
}
|
|
|
|
// console.log(str, variables)
|
|
|
|
return variables
|
|
}
|
|
|
|
const createVariableImplementation = ({char, type, start, end}) => {
|
|
let str = `${type} ${char} = `
|
|
|
|
// Size 4
|
|
if (start == 4 && end == 8) str += `(instruction >> 8) & 0xF;`
|
|
else if (start == 8 && end == 12) str += `(instruction >> 4) & 0xF;`
|
|
else if (start == 12 && end == 16) str += `instruction & 0xF;`
|
|
|
|
// Size 8
|
|
else if (start == 8 && end == 16) str += `instruction & 0xFF;`
|
|
|
|
// Size 12
|
|
else if (start == 4 && end == 16) str += `instruction & 0xFFF;`
|
|
|
|
else throw new Error(`Unknown variable position: ${start} - ${end}`)
|
|
|
|
return str
|
|
}
|
|
|
|
for (const instruction of instructions) {
|
|
// Write function declaration
|
|
str += `// ${instruction.name} / ${instruction.desc} - ${instruction.code}\n`
|
|
str += `void ${instruction.name}(cpu_t* cpu, uint16_t instruction) {\n`
|
|
|
|
// Extract variables starting and ending positions in instruction code
|
|
const variableInfos = extractVariablesInfos(instruction.code)
|
|
|
|
// Extract variables types from operands string
|
|
const variableTypes = Object.fromEntries(
|
|
instruction.operands.replaceAll(/[()]/gm, '')
|
|
.split(',')
|
|
.map(e => e.trim().split(' ').reverse())
|
|
)
|
|
|
|
// Write variable declarations
|
|
for (const variableInfo of variableInfos) {
|
|
if (variableTypes[variableInfo.char] == null) {
|
|
console.log(instruction, variableInfos)
|
|
throw new Error('Missing variable type for ' + variableInfo.char)
|
|
}
|
|
variableInfo.type = variableTypes[variableInfo.char].toLowerCase()
|
|
|
|
str += ' '
|
|
str += createVariableImplementation(variableInfo) + '\n'
|
|
}
|
|
|
|
// Write instruction implementation
|
|
const impl = instruction.implementation.slice(instruction.implementation.indexOf('{') + 1)
|
|
.replaceAll(/^ /gm, ' ')
|
|
str += `${impl}\n\n`
|
|
}
|
|
|
|
return str
|
|
} |