qemu-cr16/target/cr16c/disas.c
2026-05-04 14:31:19 +02:00

148 lines
4.2 KiB
C

/*
* CR16C disassembler, adapted from the AVR one
*
* Copyright (c) 2025 fridtjof <fridtjof@das-labor.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "qemu/osdep.h"
#include "cpu.h"
// TODO
typedef struct {
disassemble_info *info;
uint16_t next_word;
bool next_word_used;
} DisasContext;
static uint64_t decode_load_bytes(DisasContext *ctx, uint64_t insn, int i, int n) {
for(; i < n; i+=2) {
insn |= (uint64_t)translator_lduw(ctx->env, &ctx->base, ctx->base.pc_next) << (48 - i * 8);
ctx->base.pc_next += 2;
}
if (i == n)
return insn;
else {
gen_helper_raise_illegal_instruction(tcg_env);
return 0;
}
}
static int16_t u4_load_s16(DisasContext *ctx, int val) {
if (val == 0x9) {
return -1;
}
else if (val == 0xB) {
int16_t val_ld = cpu_ldsw_le_data(ctx->env, ctx->base.pc_next);
ctx->base.pc_next += 2;
return val_ld;
}
return val;
}
static uint16_t load_u16(DisasContext *ctx) {
ctx->next_word_used = 1;
uint16_t imm = cpu_lduw_code(ctx->env, ctx->base.pc_next);
ctx->base.pc_next += 2;
return imm;
}
static uint8_t get_disp4(DisasContext *ctx, uint8_t disp) {
return disp << 1;
}
static int32_t disp8_get_dest(DisasContext* ctx, int32_t disp) {
int32_t dest = ctx->base.pc_next - 2;
if (disp == 0xFFFFFF80) {
dest += cpu_ldsw_le_data(ctx->env, ctx->base.pc_next);
ctx->base.pc_next += 2;
}
else {
dest += disp << 1;
}
return dest;
};
static uint32_t reloc_abs20(DisasContext *ctx, uint32_t addr) {
if (addr > 0xEFFFF) {
addr |= 0xF00000;
}
return addr;
}
/* Include the auto-generated decoder. */
static bool decode_insn(DisasContext *ctx, uint16_t insn);
#include "decode-insn.c.inc"
#define output(mnemonic, format, ...) \
(pctx->info->fprintf_func(pctx->info->stream, "%-9s " format, \
mnemonic, ##__VA_ARGS__))
int cr16c_print_insn(bfd_vma addr, disassemble_info *info)
{
DisasContext ctx = { info };
DisasContext *pctx = &ctx;
bfd_byte buffer[4];
uint16_t insn;
int status;
status = info->read_memory_func(addr, buffer, 2, info);
if (status != 0) {
info->memory_error_func(status, addr, info);
return -1;
}
insn = bfd_getl16(buffer);
(void)insn;
status = info->read_memory_func(addr + 2, buffer + 2, 2, info);
if (status == 0) {
ctx.next_word = bfd_getl16(buffer + 2);
}
//if (!decode_insn(&ctx, insn)) {
output(".db", "0x%02x, 0x%02x", buffer[0], buffer[1]);
//}
if (!ctx.next_word_used) {
return 2;
} else if (status == 0) {
return 4;
}
info->memory_error_func(status, addr + 2, info);
return -1;
}
#define INSN(opcode, format, ...) \
static bool trans_##opcode(DisasContext *pctx, arg_##opcode * a) \
{ \
output(#opcode, format, ##__VA_ARGS__); \
return true; \
}
#define INSN_MNEMONIC(opcode, mnemonic, format, ...) \
static bool trans_##opcode(DisasContext *pctx, arg_##opcode * a) \
{ \
output(mnemonic, format, ##__VA_ARGS__); \
return true; \
}
static const char width[] = {
'x', 'B', 'W', 'x', 'D'
};
INSN(MOV_imm, "%c, 0x%x, r%d", width[a->width], a->imm, a->rd)
INSN(MOV_reg, "%c r%d, r%d", width[a->width], a->rs, a->rd)
INSN(MOVD_reg, "r%d, r%d", a->rs, a->rd)