148 lines
4.2 KiB
C
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)
|