Compare commits

...

3 Commits

Author SHA1 Message Date
Matthew Laux 0351226479 move license 2022-07-20 22:34:06 -05:00
Matthew Laux 950a759abb mit license stuff 2022-07-20 22:33:20 -05:00
Matthew Laux b23d52737e draw for system 6. it's slow 2022-07-20 22:21:36 -05:00
13 changed files with 158 additions and 53 deletions

21
LICENSE Normal file
View 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.

View File

@ -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;

View File

@ -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;

View File

@ -39,9 +39,18 @@ 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)
{
struct dmg *dmg = (struct dmg *) _dmg;
@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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;
}

View File

@ -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);

View File

@ -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

View File

@ -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,12 +101,11 @@ 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);
@ -79,16 +113,16 @@ bool LoadRom(StrFileName fileName, short vRefNum)
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;

View File

@ -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

View File

@ -4,4 +4,3 @@ void lcd_draw(struct lcd *lcd)
{
}