mirror of
https://github.com/mlaux/gb6.git
synced 2026-04-19 17:16:48 +00:00
370 lines
10 KiB
C
370 lines
10 KiB
C
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "compiler.h"
|
|
|
|
void emit_byte(struct code_block *block, uint8_t byte)
|
|
{
|
|
if (block->length < sizeof(block->code)) {
|
|
block->code[block->length++] = byte;
|
|
}
|
|
}
|
|
|
|
void emit_word(struct code_block *block, uint16_t word)
|
|
{
|
|
emit_byte(block, word >> 8);
|
|
emit_byte(block, word & 0xff);
|
|
}
|
|
|
|
void emit_long(struct code_block *block, uint32_t val)
|
|
{
|
|
emit_byte(block, val >> 24);
|
|
emit_byte(block, val >> 16);
|
|
emit_byte(block, val >> 8);
|
|
emit_byte(block, val & 0xff);
|
|
}
|
|
|
|
void emit_moveq_dn(struct code_block *block, uint8_t reg, int8_t imm)
|
|
{
|
|
emit_byte(block, 0x70 | reg << 1);
|
|
emit_byte(block, (uint8_t) imm);
|
|
}
|
|
|
|
// move.b #imm, Dn
|
|
void emit_move_b_dn(struct code_block *block, uint8_t reg, int8_t imm)
|
|
{
|
|
// 01 = byte mode
|
|
// dest effective address = rrr 000
|
|
// src effective address = 111 100 means immediate
|
|
uint16_t ins = (1 << 12) | (reg << 9) | (7 << 3) | 4;
|
|
emit_word(block, ins);
|
|
emit_word(block, (uint8_t) imm);
|
|
}
|
|
|
|
// move.w #imm, Dn
|
|
void emit_move_w_dn(struct code_block *block, uint8_t reg, int16_t imm)
|
|
{
|
|
uint16_t ins = (3 << 12) | (reg << 9) | (7 << 3) | 4;
|
|
emit_word(block, ins);
|
|
emit_word(block, (uint16_t) imm);
|
|
}
|
|
|
|
// move.l #imm, Dn
|
|
void emit_move_l_dn(struct code_block *block, uint8_t reg, int32_t imm)
|
|
{
|
|
uint16_t ins = (2 << 12) | (reg << 9) | (7 << 3) | 4;
|
|
emit_word(block, ins);
|
|
emit_long(block, (uint32_t) imm);
|
|
}
|
|
|
|
// rol.w #8, Dn
|
|
void emit_rol_w_8(struct code_block *block, uint8_t reg)
|
|
{
|
|
// 1110 ccc d ss i 11 rrr
|
|
// ccc=000 (8), d=1 (left), ss=01 (word), i=0 (immediate), rrr=reg
|
|
emit_word(block, 0xe158 | reg);
|
|
}
|
|
|
|
// ror.w #8, Dn
|
|
void emit_ror_w_8(struct code_block *block, uint8_t reg)
|
|
{
|
|
// ccc=000 (8), d=0 (right), ss=01 (word), i=0 (immediate), rrr=reg
|
|
emit_word(block, 0xe058 | reg);
|
|
}
|
|
|
|
// swap Dn - exchange high and low 16-bit words
|
|
void emit_swap(struct code_block *block, uint8_t reg)
|
|
{
|
|
// 0100 1000 0100 0 rrr
|
|
emit_word(block, 0x4840 | reg);
|
|
}
|
|
|
|
// move.w An, Dn - copy address register to data register
|
|
void emit_move_w_an_dn(struct code_block *block, uint8_t areg, uint8_t dreg)
|
|
{
|
|
// 00 11 ddd 000 001 aaa
|
|
emit_word(block, 0x3008 | (dreg << 9) | areg);
|
|
}
|
|
|
|
// movea.w Dn, An - copy data register to address register
|
|
void emit_movea_w_dn_an(struct code_block *block, uint8_t dreg, uint8_t areg)
|
|
{
|
|
// 00 11 aaa 001 000 ddd
|
|
emit_word(block, 0x3040 | (areg << 9) | dreg);
|
|
}
|
|
|
|
void emit_movea_w_imm16(struct code_block *block, uint8_t areg, uint16_t val)
|
|
{
|
|
// 00 11 aaa 001 111 100
|
|
emit_word(block, 0x307c | areg << 9);
|
|
emit_word(block, val);
|
|
}
|
|
|
|
// subq.b #val, Dn
|
|
void emit_subq_b_dn(struct code_block *block, uint8_t dreg, uint8_t val)
|
|
{
|
|
uint16_t ddd;
|
|
|
|
// 0101 ddd 1 00 000 rrr
|
|
if (val == 0 || val > 8) {
|
|
printf("can only subq values between 1 and 8\n");
|
|
exit(1);
|
|
}
|
|
ddd = val == 8 ? 0 : val;
|
|
emit_word(block, 0x5100 | ddd << 9 | dreg);
|
|
}
|
|
|
|
// subq.w #val, An
|
|
void emit_subq_w_an(struct code_block *block, uint8_t areg, uint8_t val)
|
|
{
|
|
uint16_t ddd;
|
|
|
|
// 0101 ddd 1 01 001 aaa
|
|
if (val == 0 || val > 8) {
|
|
printf("can only subq values between 1 and 8\n");
|
|
exit(1);
|
|
}
|
|
ddd = val == 8 ? 0 : val;
|
|
emit_word(block, 0x5148 | ddd << 9 | areg);
|
|
}
|
|
|
|
// addq.w #val, An
|
|
void emit_addq_w_an(struct code_block *block, uint8_t areg, uint8_t val)
|
|
{
|
|
uint16_t ddd;
|
|
|
|
// 0101 ddd 0 01 001 aaa
|
|
if (val == 0 || val > 8) {
|
|
printf("can only addq values between 1 and 8\n");
|
|
exit(1);
|
|
}
|
|
ddd = val == 8 ? 0 : val;
|
|
emit_word(block, 0x5048 | ddd << 9 | areg);
|
|
}
|
|
|
|
// move.w Dn, (An) - store data register to memory via address register
|
|
void emit_move_w_dn_ind_an(struct code_block *block, uint8_t dreg, uint8_t areg)
|
|
{
|
|
// 00 11 aaa 010 000 ddd
|
|
emit_word(block, 0x3080 | (areg << 9) | dreg);
|
|
}
|
|
|
|
// move.w (An), Dn - load from memory via address register to data register
|
|
void emit_move_w_ind_an_dn(struct code_block *block, uint8_t areg, uint8_t dreg)
|
|
{
|
|
// 00 11 ddd 000 010 aaa
|
|
emit_word(block, 0x3010 | (dreg << 9) | areg);
|
|
}
|
|
|
|
// move.b Dn, (An) - store byte to memory via address register
|
|
void emit_move_b_dn_ind_an(struct code_block *block, uint8_t dreg, uint8_t areg)
|
|
{
|
|
// 00 01 aaa 010 000 ddd
|
|
emit_word(block, 0x1080 | (areg << 9) | dreg);
|
|
}
|
|
|
|
// move.b Dn, d(An) - store byte to memory with displacement
|
|
void emit_move_b_dn_disp_an(struct code_block *block, uint8_t dreg, int16_t disp, uint8_t areg)
|
|
{
|
|
// 00 01 aaa 101 000 ddd
|
|
emit_word(block, 0x1140 | (areg << 9) | dreg);
|
|
emit_word(block, disp);
|
|
}
|
|
|
|
// move.b (An), Dn - load byte from memory via address register
|
|
void emit_move_b_ind_an_dn(struct code_block *block, uint8_t areg, uint8_t dreg)
|
|
{
|
|
// 00 01 ddd 000 010 aaa
|
|
emit_word(block, 0x1010 | (dreg << 9) | areg);
|
|
}
|
|
|
|
// move.b d(An), Dn - load byte from memory with displacement
|
|
void emit_move_b_disp_an_dn(struct code_block *block, int16_t disp, uint8_t areg, uint8_t dreg)
|
|
{
|
|
// 00 01 ddd 000 101 aaa
|
|
emit_word(block, 0x1028 | (dreg << 9) | areg);
|
|
emit_word(block, disp);
|
|
}
|
|
|
|
// andi.l #imm, Dn
|
|
void emit_andi_l_dn(struct code_block *block, uint8_t dreg, uint32_t imm)
|
|
{
|
|
// 0000 0010 10 000 rrr
|
|
emit_word(block, 0x0280 | dreg);
|
|
emit_long(block, imm);
|
|
}
|
|
|
|
// cmp.b #imm, Dn
|
|
void emit_cmp_b_imm_dn(struct code_block *block, uint8_t dreg, uint8_t imm)
|
|
{
|
|
// 0000 1100 00 000 rrr
|
|
emit_word(block, 0x0c00 | dreg);
|
|
emit_word(block, imm);
|
|
}
|
|
|
|
// scc Dn - set byte based on condition
|
|
// cond: 0x7 = seq (Z=1), 0x6 = sne (Z=0), 0x5 = scs (C=1), 0x4 = scc (C=0)
|
|
void emit_scc(struct code_block *block, uint8_t cond, uint8_t dreg)
|
|
{
|
|
// 0101 cccc 11 000 rrr
|
|
emit_word(block, 0x50c0 | (cond << 8) | dreg);
|
|
}
|
|
|
|
// andi.b #imm, Dn
|
|
void emit_andi_b_dn(struct code_block *block, uint8_t dreg, uint8_t imm)
|
|
{
|
|
// 0000 0010 00 000 rrr
|
|
emit_word(block, 0x0200 | dreg);
|
|
emit_word(block, imm);
|
|
}
|
|
|
|
// ori.b #imm, Dn
|
|
void emit_ori_b_dn(struct code_block *block, uint8_t dreg, uint8_t imm)
|
|
{
|
|
// 0000 0000 00 000 rrr
|
|
emit_word(block, 0x0000 | dreg);
|
|
emit_word(block, imm);
|
|
}
|
|
|
|
// or.b Ds, Dd (result to Dd)
|
|
void emit_or_b_dn_dn(struct code_block *block, uint8_t src, uint8_t dest)
|
|
{
|
|
// 1000 ddd 0 00 000 sss (direction bit 0 = result to Dn)
|
|
emit_word(block, 0x8000 | (dest << 9) | src);
|
|
}
|
|
|
|
// Emit: rts
|
|
void emit_rts(struct code_block *block)
|
|
{
|
|
emit_word(block, 0x4e75);
|
|
}
|
|
|
|
// bra.w - branch always with 16-bit displacement
|
|
void emit_bra_w(struct code_block *block, int16_t disp)
|
|
{
|
|
emit_word(block, 0x6000);
|
|
emit_word(block, disp);
|
|
}
|
|
|
|
// beq.w - branch if Z=1 with 16-bit displacement
|
|
void emit_beq_w(struct code_block *block, int16_t disp)
|
|
{
|
|
emit_word(block, 0x6700);
|
|
emit_word(block, disp);
|
|
}
|
|
|
|
// bne.w - branch if Z=0 with 16-bit displacement
|
|
void emit_bne_w(struct code_block *block, int16_t disp)
|
|
{
|
|
emit_word(block, 0x6600);
|
|
emit_word(block, disp);
|
|
}
|
|
|
|
// btst #bit, Dn - test bit, sets Z=1 if bit is 0
|
|
void emit_btst_imm_dn(struct code_block *block, uint8_t bit, uint8_t dreg)
|
|
{
|
|
// 0000 1000 00 000 rrr, followed by bit number
|
|
emit_word(block, 0x0800 | dreg);
|
|
emit_word(block, bit);
|
|
}
|
|
|
|
// move.l Ds, Dd - copy data register to data register (long)
|
|
void emit_move_l_dn_dn(struct code_block *block, uint8_t src, uint8_t dest)
|
|
{
|
|
// 00 10 ddd 000 000 sss
|
|
emit_word(block, 0x2000 | (dest << 9) | src);
|
|
}
|
|
|
|
// move.w Ds, Dd - copy data register to data register (word)
|
|
void emit_move_w_dn_dn(struct code_block *block, uint8_t src, uint8_t dest)
|
|
{
|
|
// 00 11 ddd 000 000 sss
|
|
emit_word(block, 0x3000 | (dest << 9) | src);
|
|
}
|
|
|
|
// lsl.w #imm, Dn - logical shift left word by immediate
|
|
void emit_lsl_w_imm_dn(struct code_block *block, uint8_t count, uint8_t dreg)
|
|
{
|
|
uint16_t ccc;
|
|
|
|
// 1110 ccc 1 01 i 01 rrr (i=0 for immediate, size=01 for word)
|
|
if (count == 0 || count > 8) {
|
|
printf("can only lsl by 1-8\n");
|
|
exit(1);
|
|
}
|
|
ccc = count == 8 ? 0 : count;
|
|
emit_word(block, 0xe148 | (ccc << 9) | dreg);
|
|
}
|
|
|
|
// move.w Dn, -(A7) - push word from data register
|
|
void emit_push_w_dn(struct code_block *block, uint8_t dreg)
|
|
{
|
|
// 00 11 111 100 000 ddd (dest = -(A7), src = Dn)
|
|
emit_word(block, 0x3f00 | dreg);
|
|
}
|
|
|
|
// move.l d(An), -(A7) - push long from memory with displacement
|
|
void emit_push_l_disp_an(struct code_block *block, int16_t disp, uint8_t areg)
|
|
{
|
|
// 00 10 111 100 101 aaa (dest = -(A7), src = d(An))
|
|
emit_word(block, 0x2f28 | areg);
|
|
emit_word(block, disp);
|
|
}
|
|
|
|
// movea.l d(An), Ad - load address from memory with displacement
|
|
void emit_movea_l_disp_an_an(struct code_block *block, int16_t disp, uint8_t src_areg, uint8_t dest_areg)
|
|
{
|
|
// 00 10 ddd 001 101 sss
|
|
emit_word(block, 0x2068 | (dest_areg << 9) | src_areg);
|
|
emit_word(block, disp);
|
|
}
|
|
|
|
// jsr (An) - jump to subroutine via address register
|
|
void emit_jsr_ind_an(struct code_block *block, uint8_t areg)
|
|
{
|
|
// 0100 1110 10 010 aaa
|
|
emit_word(block, 0x4e90 | areg);
|
|
}
|
|
|
|
// addq.l #val, An - add quick to address register (long)
|
|
void emit_addq_l_an(struct code_block *block, uint8_t areg, uint8_t val)
|
|
{
|
|
uint16_t ddd;
|
|
|
|
// 0101 ddd 0 10 001 aaa
|
|
if (val == 0 || val > 8) {
|
|
printf("can only addq values between 1 and 8\n");
|
|
exit(1);
|
|
}
|
|
ddd = val == 8 ? 0 : val;
|
|
emit_word(block, 0x5088 | (ddd << 9) | areg);
|
|
}
|
|
|
|
// movem.l <list>, -(A7) - push multiple registers
|
|
// mask uses reversed bit order for predecrement:
|
|
// bit 15=D0, 14=D1, ..., 8=D7, 7=A0, 6=A1, ..., 0=A7
|
|
void emit_movem_l_to_predec(struct code_block *block, uint16_t mask)
|
|
{
|
|
// 0100 1000 11 100 111 = 0x48e7
|
|
emit_word(block, 0x48e7);
|
|
emit_word(block, mask);
|
|
}
|
|
|
|
// movem.l (A7)+, <list> - pop multiple registers
|
|
// mask uses normal bit order for postincrement:
|
|
// bit 0=D0, 1=D1, ..., 7=D7, 8=A0, 9=A1, ..., 15=A7
|
|
void emit_movem_l_from_postinc(struct code_block *block, uint16_t mask)
|
|
{
|
|
// 0100 1100 11 011 111 = 0x4cdf
|
|
emit_word(block, 0x4cdf);
|
|
emit_word(block, mask);
|
|
}
|
|
|
|
// move.b Dn, Dm - copy data register to data register (byte)
|
|
void emit_move_b_dn_dn(struct code_block *block, uint8_t src, uint8_t dest)
|
|
{
|
|
// 00 01 ddd 000 000 sss
|
|
emit_word(block, 0x1000 | (dest << 9) | src);
|
|
}
|