fx9860-emulator-playground/scripts/write_instructions.js

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
}