mirror of
https://github.com/mlaux/gb6.git
synced 2024-06-20 00:29:27 +00:00
Compare commits
3 Commits
8d87ee1f26
...
0351226479
Author | SHA1 | Date | |
---|---|---|---|
|
0351226479 | ||
|
950a759abb | ||
|
b23d52737e |
21
LICENSE
Normal file
21
LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2022 Matthew Laux
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -54,8 +54,8 @@ GLuint make_output_texture() {
|
|||
glGenTextures(1, &image_texture);
|
||||
glBindTexture(GL_TEXTURE_2D, image_texture);
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
|
||||
return image_texture;
|
||||
}
|
||||
|
@ -66,7 +66,8 @@ void convert_output(struct lcd *lcd) {
|
|||
for (y = 0; y < 256; y++) {
|
||||
for (x = 0; x < 256; x++) {
|
||||
int val = lcd->buf[y * 256 + x];
|
||||
int fill = val ? 255 : 0;
|
||||
int fill = 255 - val * 85;
|
||||
//int fill = val ? 255 : 0;
|
||||
output_image[out_index++] = fill;
|
||||
output_image[out_index++] = fill;
|
||||
output_image[out_index++] = fill;
|
||||
|
|
17
src/cpu.c
17
src/cpu.c
|
@ -174,7 +174,16 @@ static u8 shift_right(struct cpu *cpu, u8 value)
|
|||
|
||||
static u8 swap(struct cpu *cpu, u8 value)
|
||||
{
|
||||
return ((value & 0xf0) >> 4) | ((value & 0x0f) << 4);
|
||||
u8 ret = ((value & 0xf0) >> 4) | ((value & 0x0f) << 4);
|
||||
if(ret == 0) {
|
||||
set_flag(cpu, FLAG_ZERO);
|
||||
} else {
|
||||
clear_flag(cpu, FLAG_ZERO);
|
||||
}
|
||||
clear_flag(cpu, FLAG_SIGN);
|
||||
clear_flag(cpu, FLAG_HALF_CARRY);
|
||||
clear_flag(cpu, FLAG_CARRY);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void xor(struct cpu *regs, u8 value)
|
||||
|
@ -634,6 +643,8 @@ void cpu_step(struct cpu *cpu)
|
|||
break;
|
||||
case 0x2f: // CPL
|
||||
cpu->a = ~cpu->a;
|
||||
set_flag(cpu, FLAG_SIGN);
|
||||
set_flag(cpu, FLAG_HALF_CARRY);
|
||||
break;
|
||||
case 0x31: // LD SP,d16
|
||||
cpu->sp = read16(cpu, cpu->pc);
|
||||
|
@ -756,6 +767,10 @@ void cpu_step(struct cpu *cpu)
|
|||
case 0xb5: or(cpu, cpu->l); break;
|
||||
case 0xb6: or(cpu, read8(cpu, read_hl(cpu))); break;
|
||||
case 0xb7: or(cpu, cpu->a); break;
|
||||
case 0xf6:
|
||||
or(cpu, read8(cpu, cpu->pc));
|
||||
cpu->pc++;
|
||||
break;
|
||||
|
||||
// CP
|
||||
case 0xb8: subtract(cpu, cpu->b, 0, 1); break;
|
||||
|
|
40
src/dmg.c
40
src/dmg.c
|
@ -39,8 +39,17 @@ void dmg_set_button(struct dmg *dmg, int field, int button, int pressed)
|
|||
|
||||
static u8 get_button_state(struct dmg *dmg)
|
||||
{
|
||||
return dmg->action_selected ? dmg->action_buttons : dmg->joypad;
|
||||
u8 ret = 0;
|
||||
if (dmg->action_selected) {
|
||||
ret |= dmg->action_buttons;
|
||||
}
|
||||
if (dmg->joypad_selected) {
|
||||
ret |= dmg->joypad;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int counter;
|
||||
|
||||
u8 dmg_read(void *_dmg, u16 address)
|
||||
{
|
||||
|
@ -66,6 +75,9 @@ u8 dmg_read(void *_dmg, u16 address)
|
|||
return dmg->zero_page[address - 0xff80];
|
||||
} else if (address == 0xff00) {
|
||||
return get_button_state(dmg);
|
||||
} else if (address == 0xff04) {
|
||||
counter++;
|
||||
return counter;
|
||||
} else if (address == 0xff0f) {
|
||||
return dmg->interrupt_requested;
|
||||
} else if (address == 0xffff) {
|
||||
|
@ -93,12 +105,21 @@ void dmg_write(void *_dmg, u16 address, u8 data)
|
|||
} else if (address < 0xc000) {
|
||||
// TODO switchable ram bank
|
||||
} else if (address < 0xe000) {
|
||||
// printf("write ram %04x %02x\n", address, data);
|
||||
dmg->main_ram[address - 0xc000] = data;
|
||||
} else if (address == 0xFF46) {
|
||||
u16 src = data << 8;
|
||||
int k = 0;
|
||||
// printf("oam dma %04x\n", src);
|
||||
for (u16 addr = src; addr < src + 0xa0; addr++) {
|
||||
dmg->lcd->oam[k++] = dmg_read(dmg, addr);
|
||||
}
|
||||
} else if (lcd_is_valid_addr(address)) {
|
||||
lcd_write(dmg->lcd, address, data);
|
||||
} else if (address >= 0xff80 && address <= 0xfffe) {
|
||||
dmg->zero_page[address - 0xff80] = data;
|
||||
} else if (address == 0xff00) {
|
||||
dmg->joypad_selected = data & (1 << 4);
|
||||
dmg->action_selected = data & (1 << 5);
|
||||
} else if (address == 0xff0f) {
|
||||
dmg->interrupt_requested = data;
|
||||
|
@ -126,9 +147,19 @@ void dmg_step(void *_dmg)
|
|||
if (dmg->cpu->cycle_count - dmg->last_lcd_update >= 456) {
|
||||
dmg->last_lcd_update = dmg->cpu->cycle_count;
|
||||
int next_scanline = lcd_step(dmg->lcd);
|
||||
|
||||
// update LYC
|
||||
if (next_scanline == lcd_read(dmg->lcd, REG_LYC)) {
|
||||
lcd_set_bit(dmg->lcd, REG_STAT, STAT_FLAG_MATCH);
|
||||
dmg_request_interrupt(dmg, INT_LCDSTAT);
|
||||
} else {
|
||||
lcd_clear_bit(dmg->lcd, REG_STAT, STAT_FLAG_MATCH);
|
||||
}
|
||||
|
||||
if (next_scanline == 144) {
|
||||
// vblank has started, draw all the stuff from ram into the lcd
|
||||
dmg_request_interrupt(dmg, INT_VBLANK);
|
||||
dmg_request_interrupt(dmg, INT_SERIAL);
|
||||
|
||||
int lcdc = lcd_read(dmg->lcd, REG_LCDC);
|
||||
int bg_base = (lcdc & LCDC_BG_TILE_MAP) ? 0x9c00 : 0x9800;
|
||||
|
@ -136,7 +167,7 @@ void dmg_step(void *_dmg)
|
|||
int use_unsigned = lcdc & LCDC_BG_TILE_DATA;
|
||||
int tilebase = use_unsigned ? 0x8000 : 0x9000;
|
||||
|
||||
printf("%04x %04x %04x\n", bg_base, window_base, tilebase);
|
||||
//printf("%04x %04x %04x\n", bg_base, window_base, tilebase);
|
||||
|
||||
int k = 0, off = 0;
|
||||
int tile_y = 0, tile_x = 0;
|
||||
|
@ -155,9 +186,8 @@ void dmg_step(void *_dmg)
|
|||
int data1 = dmg_read(dmg, eff_addr + b);
|
||||
int data2 = dmg_read(dmg, eff_addr + b + 1);
|
||||
for (i = 7; i >= 0; i--) {
|
||||
// monochrome for now
|
||||
dmg->lcd->buf[off] = (data1 & (1 << i)) ? 1 : 0;
|
||||
//dmg->lcd->buf[off] |= (data2 & (1 << i)) ? 1 : 0;
|
||||
dmg->lcd->buf[off] = ((data1 & (1 << i)) ? 1 : 0);// << 1;
|
||||
//dmg->lcd->buf[off] |= (data1 & (1 << i)) ? 1 : 0;
|
||||
off++;
|
||||
}
|
||||
off += 248;
|
||||
|
|
|
@ -25,6 +25,7 @@ struct dmg {
|
|||
u8 video_ram[0x2000];
|
||||
u8 zero_page[0x80];
|
||||
u32 last_lcd_update;
|
||||
int joypad_selected;
|
||||
int action_selected; // non-0 if A/B/start/select selected, 0 for directions
|
||||
u8 interrupt_enabled;
|
||||
u8 interrupt_requested;
|
||||
|
|
15
src/lcd.c
15
src/lcd.c
|
@ -5,12 +5,12 @@
|
|||
#include "types.h"
|
||||
#include "lcd.h"
|
||||
|
||||
static void 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));
|
||||
}
|
||||
|
||||
static void 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));
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ static void clear_bit(struct lcd *lcd, u16 addr, u8 bit)
|
|||
void lcd_new(struct lcd *lcd)
|
||||
{
|
||||
lcd->buf = malloc(256 * 256);
|
||||
memset(lcd->buf, 0, 256 * 256);
|
||||
// todo < 8 bpp
|
||||
lcd->pixels = malloc(LCD_WIDTH * LCD_HEIGHT);
|
||||
}
|
||||
|
@ -41,9 +42,6 @@ void lcd_write(struct lcd *lcd, u16 addr, u8 value)
|
|||
lcd->oam[addr - 0xfe00] = value;
|
||||
} else {
|
||||
lcd->regs[addr - REG_LCD_BASE] = value;
|
||||
if (addr == 0xFF46) {
|
||||
// OAM DMA
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -64,13 +62,6 @@ void lcd_copy(struct lcd *lcd)
|
|||
|
||||
int lcd_step(struct lcd *lcd)
|
||||
{
|
||||
// update LYC
|
||||
if (lcd_read(lcd, REG_LY) == lcd_read(lcd, REG_LYC)) {
|
||||
set_bit(lcd, REG_STAT, STAT_FLAG_MATCH);
|
||||
} else {
|
||||
clear_bit(lcd, REG_STAT, STAT_FLAG_MATCH);
|
||||
}
|
||||
|
||||
// step to next scanline 0-153
|
||||
u8 next_scanline = (lcd_read(lcd, REG_LY) + 1) % 154;
|
||||
lcd_write(lcd, REG_LY, next_scanline);
|
||||
|
|
|
@ -48,6 +48,9 @@ void lcd_write(struct lcd *lcd, u16 addr, u8 value);
|
|||
|
||||
void lcd_put_pixel(struct lcd *lcd, u8 x, u8 y, u8 value);
|
||||
|
||||
void lcd_set_bit(struct lcd *lcd, u16 addr, u8 bit);
|
||||
void lcd_clear_bit(struct lcd *lcd, u16 addr, u8 bit);
|
||||
|
||||
// i feel like i'm going to need to call this every cycle and update regs
|
||||
int lcd_step(struct lcd *lcd);
|
||||
void lcd_copy(struct lcd *lcd);
|
||||
|
|
|
@ -19,6 +19,7 @@ int rom_load(struct rom *rom, const char *filename)
|
|||
|
||||
rom->type = 0; // TODO read type from cart
|
||||
rom->data = malloc(len);
|
||||
rom->length = len;
|
||||
if (fread(rom->data, 1, len, fp) < len) {
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -4,11 +4,11 @@
|
|||
#include "types.h"
|
||||
|
||||
struct rom {
|
||||
u32 length;
|
||||
int type;
|
||||
u8 *data;
|
||||
};
|
||||
|
||||
|
||||
int rom_load(struct rom *rom, const char *filename);
|
||||
|
||||
void rom_free(struct rom *rom);
|
||||
|
|
|
@ -3,6 +3,8 @@ cmake_minimum_required(VERSION 3.9)
|
|||
|
||||
include_directories(../src)
|
||||
|
||||
set(CMAKE_CXX_FLAGS "-O3")
|
||||
|
||||
add_application(Emulator
|
||||
../src/bootstrap.c
|
||||
../src/cpu.c
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
/* Game Boy emulator for 68k Macs
|
||||
Compiled with Symantec THINK C 5.0
|
||||
(c) 2013 Matt Laux
|
||||
|
||||
emulator.c - entry point */
|
||||
|
||||
#include <stdio.h>
|
||||
|
@ -17,14 +14,24 @@
|
|||
|
||||
#include "emulator.h"
|
||||
|
||||
#include "dmg.h"
|
||||
#include "cpu.h"
|
||||
#include "rom.h"
|
||||
#include "lcd.h"
|
||||
|
||||
WindowPtr g_wp;
|
||||
DialogPtr stateDialog;
|
||||
unsigned char g_running;
|
||||
unsigned char emulationOn;
|
||||
|
||||
static Point windowPt = { WINDOW_Y, WINDOW_X };
|
||||
|
||||
static Rect windowBounds = { WINDOW_Y, WINDOW_X, WINDOW_Y + WINDOW_HEIGHT, WINDOW_X + WINDOW_WIDTH };
|
||||
|
||||
emu_state theState;
|
||||
struct cpu cpu;
|
||||
struct rom rom;
|
||||
struct lcd lcd;
|
||||
struct dmg dmg;
|
||||
|
||||
void InitEverything(void)
|
||||
{
|
||||
|
@ -45,10 +52,35 @@ void InitEverything(void)
|
|||
g_running = 1;
|
||||
}
|
||||
|
||||
char offscreen[32 * 256];
|
||||
Rect offscreenRect = { 0, 0, 256, 256 };
|
||||
|
||||
BitMap offscreenBmp;
|
||||
|
||||
int lastTicks;
|
||||
|
||||
void Render(void)
|
||||
{
|
||||
MoveTo(10, 10);
|
||||
DrawString("\pTest 123");
|
||||
long k = 0, dst;
|
||||
for (dst = 0; dst < 32 * 256; dst++) {
|
||||
offscreen[dst] = lcd.buf[k++] << 7
|
||||
| lcd.buf[k++] << 6
|
||||
| lcd.buf[k++] << 5
|
||||
| lcd.buf[k++] << 4
|
||||
| lcd.buf[k++] << 3
|
||||
| lcd.buf[k++] << 2
|
||||
| lcd.buf[k++] << 1
|
||||
| lcd.buf[k++];
|
||||
}
|
||||
SetPort(g_wp);
|
||||
CopyBits(&offscreenBmp, &g_wp->portBits, &offscreenRect, &g_wp->portRect, srcCopy, NULL);
|
||||
|
||||
//EraseRect(&g_wp->portRect);
|
||||
MoveTo(10, 160);
|
||||
char debug[128];
|
||||
sprintf(debug, "PC: %04x", cpu.pc);
|
||||
C2PStr(debug);
|
||||
DrawString(debug);
|
||||
}
|
||||
|
||||
void StartEmulation(void)
|
||||
|
@ -57,7 +89,10 @@ void StartEmulation(void)
|
|||
noGrowDocProc, (WindowPtr) -1, true, 0);
|
||||
SetPort(g_wp);
|
||||
|
||||
|
||||
offscreenBmp.baseAddr = offscreen;
|
||||
offscreenBmp.bounds = offscreenRect;
|
||||
offscreenBmp.rowBytes = 32;
|
||||
emulationOn = 1;
|
||||
}
|
||||
|
||||
bool LoadRom(StrFileName fileName, short vRefNum)
|
||||
|
@ -66,29 +101,28 @@ bool LoadRom(StrFileName fileName, short vRefNum)
|
|||
short fileNo;
|
||||
long amtRead;
|
||||
|
||||
if(theState.rom != NULL) {
|
||||
if(rom.data != NULL) {
|
||||
// unload existing ROM
|
||||
free((char *) theState.rom);
|
||||
theState.romLength = 0;
|
||||
free((char *) rom.data);
|
||||
rom.length = 0;
|
||||
}
|
||||
|
||||
|
||||
err = FSOpen(fileName, vRefNum, &fileNo);
|
||||
|
||||
if(err != noErr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
GetEOF(fileNo, (long *) &theState.romLength);
|
||||
theState.rom = (unsigned char *) malloc(theState.romLength);
|
||||
if(theState.rom == NULL) {
|
||||
GetEOF(fileNo, (long *) &rom.length);
|
||||
rom.data = (unsigned char *) malloc(rom.length);
|
||||
if(rom.data == NULL) {
|
||||
Alert(ALRT_NOT_ENOUGH_RAM, NULL);
|
||||
return false;
|
||||
}
|
||||
|
||||
amtRead = theState.romLength;
|
||||
amtRead = rom.length;
|
||||
|
||||
FSRead(fileNo, &amtRead, theState.rom);
|
||||
FSRead(fileNo, &amtRead, rom.data);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -112,7 +146,6 @@ bool ShowOpenBox(void)
|
|||
return false;
|
||||
}
|
||||
|
||||
DialogPtr stateDialog;
|
||||
void ShowStateDialog(void)
|
||||
{
|
||||
DialogPtr dp;
|
||||
|
@ -207,15 +240,34 @@ void OnMouseDown(EventRecord *pEvt)
|
|||
}
|
||||
|
||||
// -- ENTRY POINT --
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
EventRecord evt;
|
||||
|
||||
int executed;
|
||||
int paused = 0;
|
||||
int pause_next = 0;
|
||||
|
||||
InitEverything();
|
||||
|
||||
lcd_new(&lcd);
|
||||
dmg_new(&dmg, &cpu, &rom, &lcd);
|
||||
cpu_bind_mem_model(&cpu, &dmg, dmg_read, dmg_write);
|
||||
|
||||
cpu.pc = 0x100;
|
||||
|
||||
int start = TickCount();
|
||||
while(g_running) {
|
||||
if(WaitNextEvent(everyEvent, &evt, 10, 0) != nullEvent) {
|
||||
if (emulationOn) {
|
||||
dmg_step(&dmg);
|
||||
int now = TickCount();
|
||||
if (now > lastTicks + 100) {
|
||||
lastTicks = now;
|
||||
Render();
|
||||
}
|
||||
}
|
||||
|
||||
if(WaitNextEvent(everyEvent, &evt, 0, 0) != nullEvent) {
|
||||
if (IsDialogEvent(&evt)) {
|
||||
DialogRef hitBox;
|
||||
DialogItemIndex hitItem;
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
/* Game Boy emulator for 68k Macs
|
||||
Compiled with Symantec THINK C 5.0
|
||||
(c) 2013 Matt Laux
|
||||
|
||||
emulator.h - declarations for emulator.c */
|
||||
|
||||
#ifndef EMULATOR_H
|
||||
|
@ -44,12 +41,4 @@ typedef unsigned char bool;
|
|||
#define true 1
|
||||
#define false 0
|
||||
|
||||
typedef struct _emu_state {
|
||||
struct dmg *cpu;
|
||||
u8 *rom;
|
||||
unsigned long int romLength;
|
||||
} emu_state;
|
||||
|
||||
extern emu_state theState;
|
||||
|
||||
#endif
|
|
@ -4,4 +4,3 @@ void lcd_draw(struct lcd *lcd)
|
|||
{
|
||||
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user