oberon-risc-emu/risc.c
2014-03-15 22:48:19 +01:00

425 lines
10 KiB
C

#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "risc.h"
#include "risc-sd.h"
#define MemSize 0x100000
#define MemWords (MemSize / 4)
#define ROMStart 0x0FE000
#define ROMWords 512
#define DisplayStart 0x0E7F00
#define DIsplayEnd (DisplayStart + 1024*768/8)
#define RegStart 0x0FFFC0
struct RISC {
uint32_t PC;
uint32_t R[16];
uint32_t H;
bool Z, N, C, V;
uint32_t progress;
uint32_t current_tick;
uint32_t mouse;
uint8_t key_buf[16];
uint32_t key_cnt;
uint32_t leds;
uint32_t spi_selected;
struct Disk *sd_card;
uint32_t RAM[MemWords];
uint32_t ROM[ROMWords];
};
enum {
MOV, LSL, ASR, ROR,
AND, ANN, IOR, XOR,
ADD, SUB, MUL, DIV,
FAD, FSB, FML, FDV,
};
static void risc_single_step(struct RISC *risc);
static uint32_t risc_load_word(struct RISC *risc, uint32_t address);
static uint8_t risc_load_byte(struct RISC *risc, uint32_t address);
static void risc_store_word(struct RISC *risc, uint32_t address, uint32_t value);
static void risc_store_byte(struct RISC *risc, uint32_t address, uint8_t value);
static uint32_t risc_load_register(struct RISC *risc, uint32_t address);
static void risc_store_register(struct RISC *risc, uint32_t address, uint32_t value);
// Not very portable :)
union float_cvt { float f; uint32_t u; };
static float u2f(uint32_t u) { return (union float_cvt){ .u = u }.f; }
static float f2u(float f) { return (union float_cvt){ .f = f }.u; }
static const uint32_t bootloader[ROMWords] = {
#include "risc-boot.inc"
};
struct RISC *risc_new(const char *disk_file) {
struct RISC *risc = calloc(1, sizeof(*risc));
memcpy(risc->ROM, bootloader, sizeof(risc->ROM));
risc->PC = ROMStart/4;
if (disk_file) {
risc->sd_card = disk_new(disk_file);
}
return risc;
}
void risc_free(struct RISC *risc) {
disk_free(risc->sd_card);
free(risc);
}
void risc_run(struct RISC *risc, int cycles) {
risc->progress = 20;
// The progress value is used to detect that the RISC cpu is busy
// waiting on the millisecond counter or on the keyboard ready
// bit. In that case it's better to just pause emulation until the
// next frame.
for (int i = 0; i < cycles && risc->progress; i++) {
risc_single_step(risc);
}
}
static void risc_single_step(struct RISC *risc) {
uint32_t ir;
if (risc->PC < ROMStart/4) {
ir = risc->RAM[risc->PC];
} else {
ir = risc->ROM[risc->PC - ROMStart/4];
}
risc->PC++;
const uint32_t pbit = 0x80000000;
const uint32_t qbit = 0x40000000;
const uint32_t ubit = 0x20000000;
const uint32_t vbit = 0x10000000;
if ((ir & pbit) == 0) {
// Register instructions
uint32_t a = (ir & 0x0F000000) >> 24;
uint32_t b = (ir & 0x00F00000) >> 20;
uint32_t op = (ir & 0x000F0000) >> 16;
uint32_t im = ir & 0x0000FFFF;
uint32_t c = ir & 0x0000000F;
uint32_t a_val, b_val, c_val;
b_val = risc->R[b];
if ((ir & qbit) == 0) {
c_val = risc->R[c];
} else if ((ir & vbit) == 0) {
c_val = im;
} else {
c_val = 0xFFFF0000 | im;
}
switch (op) {
case MOV: {
if ((ir & ubit) == 0) {
a_val = c_val;
} else if ((ir & qbit) != 0) {
a_val = c_val << 16;
} else if ((ir & vbit) != 0) {
a_val = 0xD0 | // FIXME ???
(risc->N * 0x80000000) |
(risc->Z * 0x40000000) |
(risc->C * 0x20000000) |
(risc->V * 0x10000000);
} else {
a_val = risc->H;
}
break;
}
case LSL: {
a_val = b_val << (c_val & 31);
break;
}
case ASR: {
a_val = ((int32_t) b_val) >> (c_val & 31);
break;
}
case ROR: {
a_val = (b_val >> (c_val & 31)) | (b_val << (-c_val & 31));
break;
}
case AND: {
a_val = b_val & c_val;
break;
}
case ANN: {
a_val = b_val & ~c_val;
break;
}
case IOR: {
a_val = b_val | c_val;
break;
}
case XOR: {
a_val = b_val ^ c_val;
break;
}
case ADD: {
uint64_t tmp = (uint64_t)b_val + c_val;
if ((ir & ubit) & risc->C) {
tmp++;
}
a_val = (uint32_t) tmp;
risc->C = tmp >> 32;
risc->V = (~(b_val ^ c_val) & (a_val ^ b_val)) >> 31;
break;
}
case SUB: {
uint64_t tmp = (uint64_t)b_val - c_val;
if ((ir & ubit) & risc->C) {
tmp--;
}
a_val = (uint32_t)tmp;
risc->C = tmp >> 32;
risc->V = ((b_val ^ c_val) & (a_val ^ b_val)) >> 31;
break;
}
case MUL: {
uint64_t tmp;
if ((ir & ubit)) {
tmp = (int64_t)(int32_t)b_val * (int64_t)(int32_t)c_val;
} else {
tmp = (uint64_t)b_val * (uint64_t)c_val;
}
a_val = (uint32_t)tmp;
risc->H = tmp >> 32;
break;
}
case DIV: {
a_val = (int32_t)b_val / (int32_t)c_val;
risc->H = (int32_t)b_val % (int32_t)c_val;
break;
}
case FAD: {
a_val = f2u(u2f(b_val) + u2f(c_val));
break;
}
case FSB: {
a_val = f2u(u2f(b_val) - u2f(c_val));
break;
}
case FML: {
a_val = f2u(u2f(b_val) * u2f(c_val));
break;
}
case FDV: {
a_val = f2u(u2f(b_val) / u2f(c_val));
break;
}
}
risc->R[a] = a_val;
risc->Z = a_val == 0;
risc->N = (int32_t)a_val < 0;
}
else if ((ir & qbit) == 0) {
// Memory instructions
uint32_t a = (ir & 0x0F000000) >> 24;
uint32_t b = (ir & 0x00F00000) >> 20;
uint32_t off = ir & 0x000FFFFF;
uint32_t address = (risc->R[b] + off) % MemSize;
if ((ir & ubit) == 0) {
uint32_t a_val;
if ((ir & vbit) == 0) {
a_val = risc_load_word(risc, address);
} else {
a_val = risc_load_byte(risc, address);
}
risc->R[a] = a_val;
risc->Z = a_val == 0;
risc->N = (int32_t)a_val < 0;
} else {
if ((ir & vbit) == 0) {
risc_store_word(risc, address, risc->R[a]);
} else {
risc_store_byte(risc, address, risc->R[a]);
}
}
}
else {
// Branch instructions
bool t;
switch ((ir >> 24) & 15) {
case 0: t = risc->N; break;
case 1: t = risc->Z; break;
case 2: t = risc->C; break;
case 3: t = risc->V; break;
case 4: t = risc->C || risc->Z; break;
case 5: t = risc->N != risc->V; break;
case 6: t = (risc->N != risc->V) || risc->Z; break;
case 7: t = true; break;
case 8: t = !risc->N; break;
case 9: t = !risc->Z; break;
case 10: t = !risc->C; break;
case 11: t = !risc->V; break;
case 12: t = !(risc->C || risc->Z); break;
case 13: t = !(risc->N != risc->V); break;
case 14: t = !((risc->N != risc->V) || risc->Z); break;
case 15: t = false; break;
}
if (t) {
if ((ir & vbit) != 0) {
risc->R[15] = risc->PC * 4;
}
if ((ir & ubit) == 0) {
uint32_t c = ir & 0x0000000F;
risc->PC = (risc->R[c] / 4) % MemWords;
} else {
uint32_t off = ir & 0x00FFFFFF;
risc->PC = (risc->PC + off) % MemWords;
}
}
}
}
static uint32_t risc_load_word(struct RISC *risc, uint32_t address) {
if (address < RegStart) {
return risc->RAM[address/4];
} else {
return risc_load_register(risc, address);
}
}
static uint8_t risc_load_byte(struct RISC *risc, uint32_t address) {
uint32_t w = risc_load_word(risc, address);
return w >> ((address & 3) * 8);
}
static void risc_store_word(struct RISC *risc, uint32_t address, uint32_t value) {
if (address < RegStart) {
risc->RAM[address/4] = value;
} else {
risc_store_register(risc, address, value);
}
}
static void risc_store_byte(struct RISC *risc, uint32_t address, uint8_t value) {
if (address < RegStart) {
uint32_t w = risc_load_word(risc, address);
uint32_t shift = (address & 3) * 8;
w &= ~(0xFFu << shift);
w |= (uint32_t)value << shift;
risc_store_word(risc, address, w);
}
}
static uint32_t risc_load_register(struct RISC *risc, uint32_t address) {
switch (address - RegStart) {
case 0: {
// millisecond counter
risc->progress--;
return risc->current_tick;
}
case 4: {
// switches
return 0;
}
case 16: {
// SPI data
if (risc->spi_selected == 1 && risc->sd_card) {
return disk_read(risc->sd_card);
}
return 255;
}
case 20: {
// SPI status
return 1;
}
case 24: {
// mouse
uint32_t mouse = risc->mouse;
if (risc->key_cnt > 0) {
mouse |= 0x10000000;
} else {
risc->progress--;
}
return mouse;
}
case 28: {
if (risc->key_cnt > 0) {
uint8_t scancode = risc->key_buf[0];
risc->key_cnt--;
memmove(&risc->key_buf[0], &risc->key_buf[1], risc->key_cnt);
return scancode;
}
return 0;
}
default: {
return 0;
}
}
}
static void risc_store_register(struct RISC *risc, uint32_t address, uint32_t value) {
switch (address - RegStart) {
case 4: {
// LED control
risc->leds = value;
printf("LEDs: ");
for (int i = 7; i >= 0; i--) {
if (risc->leds & (1 << i)) {
printf("%d", i);
} else {
printf("-");
}
}
printf("\n");
break;
}
case 16: {
if (risc->spi_selected == 1 && risc->sd_card) {
disk_write(risc->sd_card, value);
}
break;
}
case 20: {
// SPI control
risc->spi_selected = value & 3;
break;
}
}
}
void risc_set_time(struct RISC *risc, uint32_t tick) {
risc->current_tick = tick;
}
void risc_mouse_moved(struct RISC *risc, int mouse_x, int mouse_y) {
if (mouse_x >= 0 && mouse_x < 1024) {
risc->mouse = (risc->mouse & ~0x00000FFF) | mouse_x;
}
if (mouse_y >= 0 && mouse_y < 1024) {
risc->mouse = (risc->mouse & ~0x00FFF000) | (mouse_y << 12);
}
}
void risc_mouse_button(struct RISC *risc, int button, bool down) {
if (button >= 1 && button < 4) {
int bit = 1 << (27 - button);
risc->mouse &= ~bit;
if (down) {
risc->mouse |= bit;
}
}
}
void risc_keyboard_input(struct RISC *risc, uint8_t *scancodes, uint32_t len) {
if (sizeof(risc->key_buf) - risc->key_cnt >= len) {
memmove(&risc->key_buf[risc->key_cnt], scancodes, len);
risc->key_cnt += len;
}
}
uint32_t *risc_get_framebuffer_ptr(struct RISC *risc) {
return &risc->RAM[DisplayStart/4];
}