Compare commits

...

5 Commits

Author SHA1 Message Date
Matthew Laux
b0147a60d4 color 0 as transparent 2022-08-03 02:20:38 -05:00
Matthew Laux
f79100694d fix 8x16, inconsistent flags for lcd (1 << n) vs just n 2022-08-03 02:10:15 -05:00
Matthew Laux
e32535b016 support x and y flip, try to support 8x16 but i need to change the rendering to per-scanline 2022-08-03 01:22:22 -05:00
Matthew Laux
a9ecd14598 reenable rendering for now 2022-08-02 15:11:51 -05:00
Matthew Laux
2b6a315b0e speedup on 68k by eliminating indirect calls 2022-08-02 14:47:38 -05:00
11 changed files with 141 additions and 111 deletions

View File

@ -26,11 +26,9 @@ int emulator_main(int argc, char *argv[])
lcd_new(&lcd); lcd_new(&lcd);
// this might be too much abstraction but it'll let me
// test the cpu, rom, and dmg independently and use the cpu
// for other non-GB stuff
dmg_new(&dmg, &cpu, &rom, &lcd); dmg_new(&dmg, &cpu, &rom, &lcd);
cpu_bind_mem_model(&cpu, &dmg, dmg_read, dmg_write); cpu.dmg = &dmg;
// cpu_bind_mem_model(&cpu, &dmg, dmg_read, dmg_write);
cpu.pc = 0x100; cpu.pc = 0x100;

View File

@ -152,11 +152,9 @@ int main(int argc, char *argv[])
lcd_new(&lcd); lcd_new(&lcd);
// this might be too much abstraction but it'll let me
// test the cpu, rom, and dmg independently and use the cpu
// for other non-GB stuff
dmg_new(&dmg, &cpu, &rom, &lcd); dmg_new(&dmg, &cpu, &rom, &lcd);
cpu_bind_mem_model(&cpu, &dmg, dmg_read, dmg_write); cpu.dmg = &dmg;
// cpu_bind_mem_model(&cpu, &dmg, dmg_read, dmg_write);
cpu.pc = 0x100; cpu.pc = 0x100;

View File

@ -2,20 +2,10 @@
#include <stdlib.h> #include <stdlib.h>
#include "cpu.h" #include "cpu.h"
#include "dmg.h"
#include "types.h" #include "types.h"
#include "instructions.h" #include "instructions.h"
void cpu_bind_mem_model(
struct cpu *cpu,
void *mem_model,
u8 (*mem_read)(void *, u16),
void (*mem_write)(void *, u16, u8)
) {
cpu->mem_model = mem_model;
cpu->mem_read = mem_read;
cpu->mem_write = mem_write;
}
int flag_isset(struct cpu *cpu, int flag) int flag_isset(struct cpu *cpu, int flag)
{ {
return (cpu->f & flag) != 0; return (cpu->f & flag) != 0;
@ -66,7 +56,7 @@ void cpu_panic(struct cpu *cpu)
static inline u8 read8(struct cpu *cpu, u16 address) static inline u8 read8(struct cpu *cpu, u16 address)
{ {
return cpu->mem_read(cpu->mem_model, address); return dmg_read(cpu->dmg, address);
} }
static inline u16 read16(struct cpu *cpu, u16 address) static inline u16 read16(struct cpu *cpu, u16 address)
@ -78,12 +68,12 @@ static inline u16 read16(struct cpu *cpu, u16 address)
static inline void write8(struct cpu *cpu, u16 address, u8 data) static inline void write8(struct cpu *cpu, u16 address, u8 data)
{ {
cpu->mem_write(cpu->mem_model, address, data); dmg_write(cpu->dmg, address, data);
} }
static inline void write16(struct cpu *cpu, u16 address, u16 data) static inline void write16(struct cpu *cpu, u16 address, u16 data)
{ {
cpu->mem_write(cpu->mem_model, address, data); dmg_write(cpu->dmg, address, data);
} }
static void inc_with_carry(struct cpu *regs, u8 *reg) static void inc_with_carry(struct cpu *regs, u8 *reg)
@ -493,15 +483,15 @@ static u16 check_interrupts(struct cpu *cpu)
return 0; return 0;
} }
u16 enabled = cpu->mem_read(cpu->mem_model, 0xffff); u16 enabled = dmg_read(cpu->dmg, 0xffff);
u16 requested = cpu->mem_read(cpu->mem_model, 0xff0f); u16 requested = dmg_read(cpu->dmg, 0xff0f);
for (k = 0; k < NUM_INTERRUPTS; k++) { for (k = 0; k < NUM_INTERRUPTS; k++) {
int check = 1 << k; int check = 1 << k;
if ((enabled & check) && (requested & check)) { if ((enabled & check) && (requested & check)) {
// clear request flag for this interrupt and disable all further // clear request flag for this interrupt and disable all further
// interrupts until service routine executes EI or RETI // interrupts until service routine executes EI or RETI
cpu->mem_write(cpu->mem_model, 0xff0f, requested & ~check); dmg_write(cpu->dmg, 0xff0f, requested & ~check);
cpu->interrupt_enable = 0; cpu->interrupt_enable = 0;
return handlers[k]; return handlers[k];
} }
@ -522,7 +512,7 @@ void cpu_step(struct cpu *cpu)
return; return;
} }
u8 opc = cpu->mem_read(cpu->mem_model, cpu->pc); u8 opc = dmg_read(cpu->dmg, cpu->pc);
#ifdef GB6_DEBUG #ifdef GB6_DEBUG
printf("0x%04x %s\n", cpu->pc, instructions[opc].format); printf("0x%04x %s\n", cpu->pc, instructions[opc].format);
#endif #endif
@ -792,6 +782,24 @@ void cpu_step(struct cpu *cpu)
cpu->cycle_count += instructions[opc].cycles_branch - instructions[opc].cycles; cpu->cycle_count += instructions[opc].cycles_branch - instructions[opc].cycles;
} }
break; break;
case 0xd4: // CALL NC, u16
temp16 = read16(cpu, cpu->pc);
cpu->pc += 2;
if (!flag_isset(cpu, FLAG_CARRY)) {
push(cpu, cpu->pc);
cpu->pc = temp16;
cpu->cycle_count += instructions[opc].cycles_branch - instructions[opc].cycles;
}
break;
case 0xdc: // CALL C, u16
temp16 = read16(cpu, cpu->pc);
cpu->pc += 2;
if (flag_isset(cpu, FLAG_CARRY)) {
push(cpu, cpu->pc);
cpu->pc = temp16;
cpu->cycle_count += instructions[opc].cycles_branch - instructions[opc].cycles;
}
break;
case 0xd0: // RET NC case 0xd0: // RET NC
if (!flag_isset(cpu, FLAG_CARRY)) { if (!flag_isset(cpu, FLAG_CARRY)) {
cpu->pc = pop(cpu); cpu->pc = pop(cpu);

View File

@ -3,6 +3,8 @@
#include "types.h" #include "types.h"
struct dmg;
struct cpu struct cpu
{ {
u8 a; u8 a;
@ -18,17 +20,18 @@ struct cpu
u32 cycle_count; u32 cycle_count;
u8 interrupt_enable; u8 interrupt_enable;
u8 (*mem_read)(void *, u16); struct dmg *dmg;
void (*mem_write)(void *, u16, u8); // u8 (*mem_read)(void *, u16);
void *mem_model; // void (*mem_write)(void *, u16, u8);
// void *mem_model;
}; };
void cpu_bind_mem_model( // void cpu_bind_mem_model(
struct cpu *cpu, // struct cpu *cpu,
void *mem_model, // void *mem_model,
u8 (*mem_read)(void *, u16), // u8 (*mem_read)(void *, u16),
void (*mem_write)(void *, u16, u8) // void (*mem_write)(void *, u16, u8)
); // );
void cpu_step(struct cpu *cpu); void cpu_step(struct cpu *cpu);
int flag_isset(struct cpu *cpu, int flag); int flag_isset(struct cpu *cpu, int flag);

View File

@ -188,16 +188,18 @@ struct oam_entry {
u8 attrs; u8 attrs;
}; };
// TODO: only ten per scanline, priority // TODO: only ten per scanline, priority, attributes, move to lcd.c
static void render_objs(struct dmg *dmg) static void render_objs(struct dmg *dmg)
{ {
struct oam_entry *oam = (struct oam_entry *) dmg->lcd->oam; struct oam_entry *oam = (struct oam_entry *) dmg->lcd->oam;
int k, lcd_x, lcd_y, off; int k, lcd_x, lcd_y, off;
int tall = lcd_isset(dmg->lcd, REG_LCDC, LCDC_OBJ_SIZE);
for (k = 0; k < 40; k++, oam++) { for (k = 0; k < 40; k++, oam++) {
if (oam->pos_y == 0 || oam->pos_y >= 160) { if (oam->pos_y == 0 || oam->pos_y >= 160) {
continue; continue;
} }
if (oam->pos_x == 0 || oam->pos_y >= 168) { if (oam->pos_x == 0 || oam->pos_x >= 168) {
continue; continue;
} }
@ -206,17 +208,31 @@ static void render_objs(struct dmg *dmg)
off = 160 * lcd_y + lcd_x; off = 160 * lcd_y + lcd_x;
int eff_addr = 0x8000 + 16 * oam->tile; int eff_addr = 0x8000 + 16 * oam->tile;
int b, i; int b, i, limit = 16;
for (b = 0; b < 16; b += 2) { if (tall) {
int data1 = dmg_read(dmg, eff_addr + b); limit = 32;
int data2 = dmg_read(dmg, eff_addr + b + 1); }
for (b = 0; b < limit; b += 2) {
int use_tile = b;
if (oam->attrs & OAM_ATTR_MIRROR_Y) {
use_tile = (limit - 2) - b;
}
int data1 = dmg_read(dmg, eff_addr + use_tile);
int data2 = dmg_read(dmg, eff_addr + use_tile + 1);
for (i = 7; i >= 0; i--) { for (i = 7; i >= 0; i--) {
if (off < 0 || off >= 160 * 144) { if (off < 0 || off >= 160 * 144) {
// terrible clipping. need to not have an if per-pixel // terrible clipping. need to not have an if per-pixel
continue; continue;
} }
dmg->lcd->pixels[off] = ((data1 & (1 << i)) ? 1 : 0) << 1; int use_index = i;
dmg->lcd->pixels[off] |= (data2 & (1 << i)) ? 1 : 0; if (oam->attrs & OAM_ATTR_MIRROR_X) {
use_index = 7 - i;
}
int color = ((data1 & (1 << use_index)) ? 1 : 0) << 1
| ((data2 & (1 << use_index)) ? 1 : 0);
if (color) {
dmg->lcd->pixels[off] = color;
}
off++; off++;
} }
off += 152; off += 152;

View File

@ -7,18 +7,18 @@
void lcd_set_bit(struct lcd *lcd, u16 addr, u8 bit) void lcd_set_bit(struct lcd *lcd, u16 addr, u8 bit)
{ {
lcd_write(lcd, addr, lcd_read(lcd, addr) | (1 << bit)); lcd_write(lcd, addr, lcd_read(lcd, addr) | bit);
} }
void lcd_clear_bit(struct lcd *lcd, u16 addr, u8 bit) void lcd_clear_bit(struct lcd *lcd, u16 addr, u8 bit)
{ {
lcd_write(lcd, addr, lcd_read(lcd, addr) & ~(1 << bit)); lcd_write(lcd, addr, lcd_read(lcd, addr) & ~bit);
} }
int lcd_isset(struct lcd *lcd, u16 addr, u8 bit) int lcd_isset(struct lcd *lcd, u16 addr, u8 bit)
{ {
u8 val = lcd_read(lcd, addr); u8 val = lcd_read(lcd, addr);
return val & (1 << bit); return val & bit;
} }
void lcd_set_mode(struct lcd *lcd, int mode) void lcd_set_mode(struct lcd *lcd, int mode)

View File

@ -23,11 +23,11 @@
#define REG_LCD_LAST REG_WX #define REG_LCD_LAST REG_WX
#define STAT_FLAG_MATCH 2 #define STAT_FLAG_MATCH (1 << 2)
#define STAT_INTR_SOURCE_HBLANK 3 #define STAT_INTR_SOURCE_HBLANK (1 << 3)
#define STAT_INTR_SOURCE_VBLANK 4 #define STAT_INTR_SOURCE_VBLANK (1 << 4)
#define STAT_INTR_SOURCE_MODE2 5 #define STAT_INTR_SOURCE_MODE2 (1 << 5)
#define STAT_INTR_SOURCE_MATCH 6 #define STAT_INTR_SOURCE_MATCH (1 << 6)
#define LCDC_ENABLE_BG (1 << 0) #define LCDC_ENABLE_BG (1 << 0)
@ -39,6 +39,11 @@
#define LCDC_WINDOW_TILE_MAP (1 << 6) #define LCDC_WINDOW_TILE_MAP (1 << 6)
#define LCDC_ENABLE (1 << 7) #define LCDC_ENABLE (1 << 7)
#define OAM_ATTR_PALETTE (1 << 4)
#define OAM_ATTR_MIRROR_X (1 << 5)
#define OAM_ATTR_MIRROR_Y (1 << 6)
#define OAM_ATTR_BEHIND_BG (1 << 7)
struct lcd { struct lcd {
u8 oam[0xa0]; u8 oam[0xa0];
u8 regs[0x0c]; u8 regs[0x0c];

View File

@ -6,16 +6,6 @@
#include "dmg.h" #include "dmg.h"
#include "rom.h" #include "rom.h"
static int mbc_noop_read(struct mbc *mbc, struct dmg *dmg, u16 addr, u8 *out_data)
{
return 0;
}
static int mbc_noop_write(struct mbc *mbc, struct dmg *dmg, u16 addr, u8 data)
{
return 0;
}
static int mbc1_read(struct mbc *mbc, struct dmg *dmg, u16 addr, u8 *out_data) static int mbc1_read(struct mbc *mbc, struct dmg *dmg, u16 addr, u8 *out_data)
{ {
if (addr >= 0x4000 && addr <= 0x7fff) { if (addr >= 0x4000 && addr <= 0x7fff) {
@ -60,35 +50,29 @@ static int mbc1_write(struct mbc *mbc, struct dmg *dmg, u16 addr, u8 data)
return 0; return 0;
} }
struct mbc mbc_noop = {
mbc_noop_read,
mbc_noop_write,
};
struct mbc mbc1 = {
mbc1_read,
mbc1_write,
};
struct mbc *mbc_new(int type) struct mbc *mbc_new(int type)
{ {
switch (type) { static struct mbc mbc;
case 0: if (type > 3) {
return &mbc_noop; return NULL;
case 1:
case 2:
case 3:
return &mbc1;
} }
return NULL; mbc.type = type;
return &mbc;
} }
int mbc_read(struct mbc *mbc, struct dmg *dmg, u16 addr, u8 *out_data) int mbc_read(struct mbc *mbc, struct dmg *dmg, u16 addr, u8 *out_data)
{ {
return mbc->read_fn(mbc, dmg, addr, out_data); if (mbc->type == 0) {
return 0;
}
return mbc1_read(mbc, dmg, addr, out_data);
} }
int mbc_write(struct mbc *mbc, struct dmg *dmg, u16 addr, u8 data) int mbc_write(struct mbc *mbc, struct dmg *dmg, u16 addr, u8 data)
{ {
return mbc->write_fn(mbc, dmg, addr, data); if (mbc->type == 0) {
return 0;
}
return mbc1_write(mbc, dmg, addr, data);
} }

View File

@ -6,8 +6,6 @@
struct dmg; struct dmg;
struct mbc { struct mbc {
int (*read_fn)(struct mbc *, struct dmg *, u16, u8 *);
int (*write_fn)(struct mbc *, struct dmg *, u16, u8);
int type; int type;
int rom_bank; int rom_bank;
int ram_bank; int ram_bank;
@ -16,7 +14,10 @@ struct mbc {
}; };
struct mbc *mbc_new(int type); struct mbc *mbc_new(int type);
// set *out_data and return 1 if handled, return 0 for base dmg behavior
int mbc_read(struct mbc *mbc, struct dmg *dmg, u16 addr, u8 *out_data); int mbc_read(struct mbc *mbc, struct dmg *dmg, u16 addr, u8 *out_data);
// return 1 if handled, return 0 for base dmg behavior
int mbc_write(struct mbc *mbc, struct dmg *dmg, u16 addr, u8 data); int mbc_write(struct mbc *mbc, struct dmg *dmg, u16 addr, u8 data);
#endif #endif

View File

@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.9)
include_directories(../src) include_directories(../src)
set(CMAKE_CXX_FLAGS "-O3") set(CMAKE_C_FLAGS "-O3")
add_application(Emulator add_application(Emulator
../src/bootstrap.c ../src/bootstrap.c

View File

@ -59,18 +59,18 @@ Rect offscreenRect = { 0, 0, 144, 160 };
BitMap offscreenBmp; BitMap offscreenBmp;
int lastTicks; int execTime;
void Render(void) void Render(void)
{ {
long k = 0, dst, bit; // long k = 0, dst, bit;
for (dst = 0; dst < 20 * 144; dst++) { // for (dst = 0; dst < 20 * 144; dst++) {
for (bit = 7; bit >= 0; bit--) { // for (bit = 7; bit >= 0; bit--) {
offscreen[dst] |= lcd.pixels[k++]; // offscreen[dst] |= lcd.pixels[k++];
offscreen[dst] <<= 1; // offscreen[dst] <<= 1;
} // }
dst++; // dst++;
} // }
/* /*
offscreen[dst] = lcd.pixels[k++] << 7; offscreen[dst] = lcd.pixels[k++] << 7;
@ -83,16 +83,32 @@ void Render(void)
offscreen[dst] |= lcd.pixels[k++]; offscreen[dst] |= lcd.pixels[k++];
*/ */
SetPort(g_wp); SetPort(g_wp);
CopyBits(&offscreenBmp, &g_wp->portBits, &offscreenRect, &offscreenRect, srcCopy, NULL); // CopyBits(&offscreenBmp, &g_wp->portBits, &offscreenRect, &offscreenRect, srcCopy, NULL);
//EraseRect(&g_wp->portRect); EraseRect(&g_wp->portRect);
// MoveTo(10, 160); MoveTo(10, 180);
// char debug[128]; char debug[128];
// sprintf(debug, "PC: %04x", cpu.pc); double ms = execTime / 600.0;
// C2PStr(debug); sprintf(debug, "10000 in %d ticks, %.2f ms per instruction", execTime, ms);
// DrawString(debug); C2PStr(debug);
DrawString(debug);
} }
// 417 ticks
// 10000 instructions
// 0.0417 tick per instruction
// 1/60 second per tick
// 0.000695 second per instruction
// 0.695 ms per instruction
// REAL GB:
// 4194304 Hz
// 1048576 NOPs per second
// 174763 CALL 16s per second
// 0.001 ms per NOP
// 0.006 ms per CALL 16
void StartEmulation(void) void StartEmulation(void)
{ {
g_wp = NewWindow(0, &windowBounds, WINDOW_TITLE, true, g_wp = NewWindow(0, &windowBounds, WINDOW_TITLE, true,
@ -188,7 +204,7 @@ void ShowAboutBox(void)
// DrawDialog(dp); // DrawDialog(dp);
// while(!GetNextEvent(mDownMask, &e)); // while(!GetNextEvent(mDownMask, &e));
// while(WaitMouseUp()); // while(WaitMouseUp());
DisposeDialog(dp); DisposeDialog(dp);
} }
@ -264,25 +280,26 @@ int main(int argc, char *argv[])
int executed; int executed;
int paused = 0; int paused = 0;
int pause_next = 0; int pause_next = 0;
InitEverything(); InitEverything();
lcd_new(&lcd); lcd_new(&lcd);
dmg_new(&dmg, &cpu, &rom, &lcd); dmg_new(&dmg, &cpu, &rom, &lcd);
cpu_bind_mem_model(&cpu, &dmg, dmg_read, dmg_write); cpu.dmg = &dmg;
// cpu_bind_mem_model(&cpu, &dmg, dmg_read, dmg_write);
cpu.pc = 0x100; cpu.pc = 0x100;
int start = TickCount();
while(g_running) { while(g_running) {
if (emulationOn) { if (emulationOn) {
int k;
int start = TickCount();
for (k = 0; k < 10000; k++) {
dmg_step(&dmg); dmg_step(&dmg);
int now = TickCount(); }
if (now > lastTicks + 100) { execTime = TickCount() - start;
lastTicks = now; emulationOn = false;
Render(); Render();
}
if (Button()) g_running = false;
} else { } else {
if(WaitNextEvent(everyEvent, &evt, 0, 0) != nullEvent) { if(WaitNextEvent(everyEvent, &evt, 0, 0) != nullEvent) {
if (IsDialogEvent(&evt)) { if (IsDialogEvent(&evt)) {