sh-dis/generate_decoder.py
Zack Buhman 3209054b8f decode: implement PR- and SZ- instruction decode discrimination
Some SH4 floating point instructions have ambiguous encodings that
depend on the state of FPSCR bits. Exhaustively, these are:

    FMOV XDm,DRn              (sz 1)
    FMOV FRm,FRn              (sz 0)

    FMOV DRm,@(R0,Rn)         (sz 1)
    FMOV.S FRm,@(R0,Rn)       (sz 0)

    FCMP/EQ DRm,DRn           (pr 1)
    FCMP/EQ FRm,FRn           (pr 0)

    FMOV @Rm+,XDn             (sz 1)
    FMOV.S @Rm+,FRn           (sz 0)

    FMOV XDm,@(R0,Rn)         (sz 1)
    FMOV.S FRm,@(R0,Rn)       (sz 0)

    FMUL DRm,DRn              (pr 1)
    FMUL FRm,FRn              (pr 0)

    FMOV.S @Rm+,FRn           (sz 0)
    FMOV @Rm+,DRn             (sz 1)

    FLOAT FPUL,DRn            (pr 1)
    FLOAT FPUL,FRn            (pr 0)

    FNEG DRn                  (pr 1)
    FNEG FRn                  (pr 0)

    FTRC DRm,FPUL             (pr 1)
    FTRC FRm,FPUL             (pr 0)

    FMOV.S @(R0,Rm),FRn       (sz 0)
    FMOV @(R0,Rm),DRn         (sz 1)

    FMOV.S @Rm,FRn            (sz 0)
    FMOV @Rm,DRn              (sz 1)

    FMOV XDm,XDn              (sz 1)
    FMOV FRm,FRn              (sz 0)

    FABS FRn                  (pr 0)
    FABS DRn                  (pr 1)

    FMOV.S FRm,@Rn            (sz 0)
    FMOV DRm,@Rn              (sz 1)

    FSUB DRm,DRn              (pr 1)
    FSUB FRm,FRn              (pr 0)

    FDIV DRm,DRn              (pr 1)
    FDIV FRm,FRn              (pr 0)

    FCMP/GT DRm,DRn           (pr 1)
    FCMP/GT FRm,FRn           (pr 0)

    FMOV DRm,DRn              (sz 1)
    FMOV FRm,FRn              (sz 0)

    FMOV.S FRm,@-Rn           (sz 0)
    FMOV DRm,@-Rn             (sz 1)

    FADD DRm,DRn              (pr 1)
    FADD FRm,FRn              (pr 0)

    FMOV.S FRm,@Rn            (sz 0)
    FMOV XDm,@Rn              (sz 1)

    FMOV.S @(R0,Rm),FRn       (sz 0)
    FMOV @(R0,Rm),XDn         (sz 1)

    FMOV DRm,XDn              (sz 1)
    FMOV FRm,FRn              (sz 0)

    FMOV XDm,@-Rn             (sz 1)
    FMOV.S FRm,@-Rn           (sz 0)

    FSQRT DRn                 (pr 1)
    FSQRT FRn                 (pr 0)

    FMOV.S @Rm,FRn            (sz 0)
    FMOV @Rm,XDn              (sz 1)

This commit automatically calculates which FPU instructions require
FPSCR-discrimination, and automatically inserts the appropriate
conditional logic in the instruction decoder.
2024-04-26 18:51:01 +08:00

168 lines
6.2 KiB
Python

import sys
from collections import defaultdict
from instruction_table import untabulate_instructions_sh4
from instruction_table import untabulate_instructions_sh2
from instruction_function_name import instruction_function_name
from generate import renderer
from generate_impl import parse_header
from generate_impl import read_ins_source
from disabled_instructions import disabled_instructions
def b16(n):
bits = []
for i in range(16):
bits.append((n >> i) & 1)
return '0b' + ''.join(map(str, reversed(bits)))
def format_variables(ins):
s = ins.operands
for i, name in enumerate(ins.variables):
if 'disp' in s and name == 'd':
name = 'disp'
if 'imm' in s and name == 'i':
name = 'imm'
if 'label' in s and name == 'd':
name = 'label'
s = s.replace(name, "%d")
return s
def render_print(ins):
variable_args = ", " + ", ".join(ins.variables) if ins.variables else ""
operands = format_variables(ins)
if operands:
yield f'snprintf(operand_buf, size, "{operands}"{variable_args});'
else:
yield "operand_buf[0] = 0;";
yield f'*instruction_buf = "{ins.instruction}";'
def render_execute(ins):
function_name = instruction_function_name(ins)
variable_args = ", " + ", ".join(ins.variables) if ins.variables else ""
yield f"{function_name}(state, map{variable_args});"
def render_mask_switch(body_func, mask, instructions, discriminators):
yield f"switch (code & {b16(mask)}) {{"
for ins in instructions:
yield f"case {b16(ins.code.code_bits)}: // {ins.instruction} {ins.operands}"
yield "{"
if (ins.instruction, ins.operands) in discriminators:
(variable, value), = discriminators[(ins.instruction, ins.operands)]
yield f"if (state->fpscr.bits.{variable} == {value}) {{"
for variable in ins.variables:
operand_var = ins.code.operands[variable]
yield f"uint32_t {variable} = (code >> {operand_var.lsb}) & ((1 << {operand_var.length}) - 1);"
yield from body_func(ins)
yield f"return DECODE__DEFINED;"
if (ins.instruction, ins.operands) in discriminators:
yield "} else break;"
yield "}"
yield "}"
def render_mask_switches(body_func, by_mask, discriminators):
yield "{"
for mask, instructions in by_mask.items():
instructions = sorted(instructions, key=lambda i: i.code.code_bits)
yield from render_mask_switch(body_func, mask, instructions, discriminators)
yield "return DECODE__UNDEFINED;"
yield "}"
def render_decode_and_execute(by_mask, discriminators):
yield f"enum decode_status decode_and_execute_instruction(struct architectural_state * state, struct memory_map * map, uint16_t code)"
yield from render_mask_switches(render_execute, by_mask, discriminators)
def render_decode_and_print(by_mask, discriminators):
yield f"enum decode_status decode_and_print_instruction(struct architectural_state * state, struct memory_map * map, uint16_t code, char const ** instruction_buf, char * operand_buf, uint32_t size)"
yield from render_mask_switches(render_print, by_mask, discriminators)
def header_execute():
yield '#include "decode_execute.h"'
yield '#include "impl.h"'
yield ""
def header_print():
yield '#include <stdio.h>'
yield ""
yield '#include "decode_print.h"'
yield ""
s = set()
def symmetric_available(a):
return {
'Available only when SZ=0': 'Available only when PR=0',
'Available only when PR=0': 'Available only when SZ=0',
'Available only when PR=1 and SZ=0': 'Available only when PR=0 and SZ=1',
'Available only when PR=0 and SZ=1': 'Available only when PR=1 and SZ=0',
}[a]
def parse_available(a):
return {
'Available only when PR=1 and SZ=0': {"pr": 1, "sz": 0},
'Available only when SZ=0' : {"sz": 0},
'Available only when PR=0' : {"pr": 0},
'Available only when PR=0 and SZ=1': {"pr": 0, "sz": 1},
}[a]
def main():
dup = set()
by_instruction_operands = {}
table = untabulate_instructions_sh4()
for ins1 in table:
buf = read_ins_source(ins1)
_, available, _ = parse_header(buf)
assert (ins1.instruction, ins1.operands) not in by_instruction_operands
by_instruction_operands[(ins1.instruction, ins1.operands)] = (ins1, available)
for ins2 in table:
if ins1 is ins2:
continue
if ins1.code.code_bits & ins2.code.mask_bits == ins2.code.code_bits:
assert (ins1.instruction, ins1.operands) != (ins2.instruction, ins2.operands)
dup.add(frozenset([(ins1.instruction, ins1.operands),
(ins2.instruction, ins2.operands)]))
discriminators = {}
for dup1, dup2 in dup:
ins1, available1 = by_instruction_operands[dup1]
ins2, available2 = by_instruction_operands[dup2]
assert available1 is not None and available2 is not None, (dup1, dup2)
av1 = parse_available(available1)
av2 = parse_available(available2)
assert symmetric_available(available1) != available2, (ins1, available1, ins2, available2)
intersection = set(av1.keys()) & set(av2.keys())
av1 = tuple((k, v) for k, v in av1.items() if k in intersection)
av2 = tuple((k, v) for k, v in av2.items() if k in intersection)
assert av1 != av2
discriminators[(ins1.instruction, ins1.operands)] = av1
discriminators[(ins2.instruction, ins2.operands)] = av2
by_mask = defaultdict(list)
masks = []
for ins in untabulate_instructions_sh4():
if ins.instruction in disabled_instructions:
continue
if ins.code.mask_bits not in masks:
masks.append(ins.code.mask_bits)
by_mask[ins.code.mask_bits].append(ins)
render, out = renderer()
render(header_execute())
render(render_decode_and_execute(by_mask, discriminators))
with open(sys.argv[1], 'w') as f:
f.write(out.getvalue())
render, out = renderer()
render(header_print())
render(render_decode_and_print(by_mask, discriminators))
with open(sys.argv[2], 'w') as f:
f.write(out.getvalue())
if __name__ == "__main__":
main()