pizza boy at c7bc6ee376028b3766de8d7a02e60ab794841f45

This commit is contained in:
nattthebear 2017-06-13 18:02:52 -04:00
parent c33271e534
commit cbbc922cf7
33 changed files with 10192 additions and 0 deletions

Binary file not shown.

18
waterbox/pizza/Makefile Normal file
View File

@ -0,0 +1,18 @@
CFLAGS=-O3
ifeq ($(OS),Windows_NT)
LIBS=-lrt `sdl2-config --libs`
CFLAGS+=-w
else
LIBS=-lrt -lSDL2 -pthread
endif
all: libpizza.a
gcc $(CFLAGS) pizza.c -I lib lib/libpizza.a -o emu-pizza $(LIBS)
libpizza.a:
make -C lib
clean:
rm -f *.o
make -C cpu clean
make -C system clean

49
waterbox/pizza/README.md Normal file
View File

@ -0,0 +1,49 @@
# Emu-pizza
A new born Gameboy Classic/Color emulator....
Requirements
-----------
Emu-pizza requires libSDL2 to compile and run Space Invaders and Gameboy games. To install it
on an APT based distro:
```
sudo apt-get install libsdl2-dev
```
on a YUM based distro:
```
sudo yum install SDL2-devel
```
Compile
-------
```
make
```
Usage
-----
```
emu-pizza [gameboy rom]
```
Gameboy keys
-------------------
* Arrows -- Arrows (rly?)
* Enter -- Start
* Space -- Select
* Z/X -- A/B buttons
* Q -- Exit
Supported ROMS
--------------
* Almost totality of Gameboy roms
Todo
----
* Serial cable emulation
Credits
-------
Thanks to [Emulator 101](http://www.emulator101.com), the source of all my current knowledge on 8080 emulation

View File

@ -0,0 +1,9 @@
SRCS=$(wildcard *.c)
CFLAGS=-I.. -c -pthread -O3 -Wall
all:
gcc $(CFLAGS) $(SRCS)
ar rcs libpizza.a *.o
clean:
rm -f *.o

View File

@ -0,0 +1,200 @@
/*
This file is part of Emu-Pizza
Emu-Pizza is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Emu-Pizza is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Emu-Pizza. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdint.h>
#include <stdio.h>
#include <errno.h>
#include <libgen.h>
#include <string.h>
#include <sys/stat.h>
#include "global.h"
#include "mmu.h"
#include "utils.h"
/* buffer big enough to contain the largest possible ROM */
uint8_t rom[2 << 24];
/* battery backed RAM & RTC*/
char file_sav[1024];
char file_rtc[1024];
/* internal use prototype */
int __mkdirp (char *path, mode_t omode);
/* guess what */
/* return values */
/* 0: OK */
/* 1: Can't open/read file */
/* 2: Unknown cartridge */
char cartridge_load(char *file_gb)
{
FILE *fp;
int i,z = 0;
/* open ROM file */
if ((fp = fopen(file_gb, "r")) == NULL)
return 1;
/* read all the content into rom buffer */
size_t sz = fread(rom, 1, (2 << 24), fp);
/* check for errors */
if (sz < 1)
return 1;
/* close */
fclose(fp);
/* gameboy color? */
if (rom[0x143] == 0xC0 || rom[0x143] == 0x80)
{
utils_log("Gameboy Color cartridge\n");
global_cgb = 1;
}
else
{
utils_log("Gameboy Classic cartridge\n");
global_cgb = 0;
}
/* get cartridge infos */
uint8_t mbc = rom[0x147];
utils_log("Cartridge code: %02x\n", mbc);
switch (mbc)
{
case 0x00: utils_log("ROM ONLY\n"); break;
case 0x01: utils_log("MBC1\n"); break;
case 0x02: utils_log("MBC1 + RAM\n"); break;
case 0x03: utils_log("MBC1 + RAM + BATTERY\n"); break;
case 0x05: utils_log("MBC2\n"); break;
case 0x06: mmu_init_ram(512); utils_log("MBC2 + BATTERY\n"); break;
case 0x10: utils_log("MBC3 + TIMER + RAM + BATTERY\n"); break;
case 0x11: utils_log("MBC3\n"); break;
case 0x12: utils_log("MBC3 + RAM\n"); break;
case 0x13: utils_log("MBC3 + RAM + BATTERY\n"); break;
case 0x19: utils_log("MBC5\n"); break;
case 0x1A: utils_log("MBC5 + RAM\n"); break;
case 0x1B: utils_log("MBC5 + RAM + BATTERY\n"); break;
case 0x1C: global_rumble = 1;
utils_log("MBC5 + RUMBLE\n");
break;
case 0x1D: global_rumble = 1;
utils_log("MBC5 + RUMBLE + RAM\n");
break;
case 0x1E: global_rumble = 1;
utils_log("MBC5 + RUMBLE + RAM + BATTERY\n");
break;
default: utils_log("Unknown cartridge type: %02x\n", mbc);
return 2;
}
/* title */
for (i=0x134; i<0x143; i++)
if (rom[i] > 0x40 && rom[i] < 0x5B)
global_cart_name[z++] = rom[i];
global_cart_name[z] = '\0';
utils_log("%s\n", global_cart_name);
/* get ROM banks */
uint8_t byte = rom[0x148];
utils_log("ROM: ");
switch (byte)
{
case 0x00: utils_log("0 banks\n"); break;
case 0x01: utils_log("4 banks\n"); break;
case 0x02: utils_log("8 banks\n"); break;
case 0x03: utils_log("16 banks\n"); break;
case 0x04: utils_log("32 banks\n"); break;
case 0x05: utils_log("64 banks\n"); break;
case 0x06: utils_log("128 banks\n"); break;
case 0x07: utils_log("256 banks\n"); break;
case 0x52: utils_log("72 banks\n"); break;
case 0x53: utils_log("80 banks\n"); break;
case 0x54: utils_log("96 banks\n"); break;
}
/* init MMU */
mmu_init(mbc, byte);
/* get RAM banks */
byte = rom[0x149];
utils_log("RAM: ");
switch (byte)
{
case 0x00: utils_log("NO RAM\n"); break;
case 0x01: mmu_init_ram(1 << 11); utils_log("2 kB\n"); break;
case 0x02:
/* MBC5 got bigger values */
if (mbc >= 0x19 && mbc <= 0x1E)
{
mmu_init_ram(1 << 16);
utils_log("64 kB\n");
}
else
{
mmu_init_ram(1 << 13);
utils_log("8 kB\n");
}
break;
case 0x03: mmu_init_ram(1 << 15); utils_log("32 kB\n"); break;
case 0x04: mmu_init_ram(1 << 17); utils_log("128 kB\n"); break;
case 0x05: mmu_init_ram(1 << 16); utils_log("64 kB\n"); break;
}
/* save base name of the rom */
strncpy(global_rom_name, basename(file_gb), 256);
/* build file.sav */
snprintf(file_sav, sizeof(file_sav), "%s/%s.sav",
global_save_folder, global_rom_name);
/* build file.rtc */
snprintf(file_rtc, sizeof(file_rtc), "%s/%s.rtc",
global_save_folder, global_rom_name);
/* restore saved RAM if it's the case */
mmu_restore_ram(file_sav);
/* restore saved RTC if it's the case */
mmu_restore_rtc(file_rtc);
/* load FULL ROM at 0x0000 address of system memory */
mmu_load_cartridge(rom, sz);
return 0;
}
void cartridge_term()
{
/* save persistent data (battery backed RAM and RTC clock) */
mmu_save_ram(file_sav);
mmu_save_rtc(file_rtc);
}

View File

@ -0,0 +1,29 @@
/*
This file is part of Emu-Pizza
Emu-Pizza is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Emu-Pizza is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Emu-Pizza. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __CARTRIDGE_HDR__
#define __CARTRIDGE_HDR__
#include <stdint.h>
/* prototypes */
char cartridge_load(char *file_nm);
void cartridge_term();
#endif

409
waterbox/pizza/lib/cycles.c Normal file
View File

@ -0,0 +1,409 @@
/*
This file is part of Emu-Pizza
Emu-Pizza is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Emu-Pizza is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Emu-Pizza. If not, see <http://www.gnu.org/licenses/>.
*/
#include <semaphore.h>
#include <signal.h>
#include <string.h>
#include <time.h>
#include "cycles.h"
#include "global.h"
#include "gpu.h"
#include "mmu.h"
#include "serial.h"
#include "sound.h"
#include "timer.h"
#include "interrupt.h"
#include "utils.h"
/* timer stuff */
struct itimerspec cycles_timer;
timer_t cycles_timer_id = 0;
struct sigevent cycles_te;
struct sigaction cycles_sa;
interrupts_flags_t *cycles_if;
/* instance of the main struct */
cycles_t cycles = { 0, 0, 0, 0 };
#define CYCLES_PAUSES 256
/* sync timing */
struct timespec deadline;
/* hard sync stuff (for remote connection) */
uint8_t cycles_hs_mode = 0;
/* type of next */
typedef enum
{
CYCLES_NEXT_TYPE_CYCLES,
CYCLES_NEXT_TYPE_CYCLES_HS,
CYCLES_NEXT_TYPE_DMA,
} cycles_next_type_enum_e;
/* closest next and its type */
uint_fast32_t cycles_very_next;
cycles_next_type_enum_e cycles_next_type;
/* set hard sync mode. sync is given by the remote peer + local timer */
void cycles_start_hs()
{
utils_log("Hard sync mode ON\n");
/* boolean set to on */
cycles_hs_mode = 1;
}
void cycles_stop_hs()
{
utils_log("Hard sync mode OFF\n");
/* boolean set to on */
cycles_hs_mode = 0;
}
/* set double or normal speed */
void cycles_set_speed(char dbl)
{
/* set global */
global_cpu_double_speed = dbl;
/* update clock */
if (global_cpu_double_speed)
cycles.clock = 4194304 * 2;
else
cycles.clock = 4194304;
/* calculate the mask */
cycles_change_emulation_speed();
}
/* set emulation speed */
void cycles_change_emulation_speed()
{
switch (global_emulation_speed)
{
case GLOBAL_EMULATION_SPEED_QUARTER:
cycles.step = ((4194304 / CYCLES_PAUSES) / 4
<< global_cpu_double_speed);
break;
case GLOBAL_EMULATION_SPEED_HALF:
cycles.step = ((4194304 / CYCLES_PAUSES) / 2
<< global_cpu_double_speed);
break;
case GLOBAL_EMULATION_SPEED_NORMAL:
cycles.step = ((4194304 / CYCLES_PAUSES)
<< global_cpu_double_speed);
break;
case GLOBAL_EMULATION_SPEED_DOUBLE:
cycles.step = ((4194304 / CYCLES_PAUSES) * 2
<< global_cpu_double_speed);
break;
case GLOBAL_EMULATION_SPEED_4X:
cycles.step = ((4194304 / CYCLES_PAUSES) * 4
<< global_cpu_double_speed);
break;
}
}
void cycles_closest_next()
{
int_fast32_t diff = cycles.cnt - cycles.next;
/* init */
cycles_very_next = cycles.next;
cycles_next_type = CYCLES_NEXT_TYPE_CYCLES;
int_fast32_t diff_new = cycles.cnt - mmu.dma_next;
/* DMA? */
if (diff_new < diff)
{
/* this is the new lowest */
cycles_very_next = mmu.dma_next;
cycles_next_type = CYCLES_NEXT_TYPE_DMA;
}
}
/* this function is gonna be called every M-cycle = 4 ticks of CPU */
void cycles_step()
{
cycles.cnt += 4;
/*
while (cycles.cnt >= cycles_very_next)
{
switch (cycles_next_type)
{
case CYCLES_NEXT_TYPE_CYCLES:
deadline.tv_nsec += 1000000000 / CYCLES_PAUSES;
if (deadline.tv_nsec > 1000000000)
{
deadline.tv_sec += 1;
deadline.tv_nsec -= 1000000000;
}
clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME,
&deadline, NULL);
cycles.next += cycles.step;
if (cycles.cnt % cycles.clock == 0)
cycles.seconds++;
break;
case CYCLES_NEXT_TYPE_DMA:
memcpy(&mmu.memory[0xFE00], &mmu.memory[mmu.dma_address], 160);
mmu.dma_address = 0x0000;
mmu.dma_next = 1;
break;
}
cycles_closest_next();
}
*/
/* 65536 == cpu clock / CYCLES_PAUSES pauses every second */
if (cycles.cnt == cycles.next)
{
deadline.tv_nsec += 1000000000 / CYCLES_PAUSES;
if (deadline.tv_nsec > 1000000000)
{
deadline.tv_sec += 1;
deadline.tv_nsec -= 1000000000;
}
clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &deadline, NULL);
cycles.next += cycles.step;
/* update current running seconds */
if (cycles.cnt % cycles.clock == 0)
cycles.seconds++;
}
/* hard sync next step */
if (cycles.cnt == cycles.hs_next)
{
/* set cycles for hard sync */
cycles.hs_next += ((4096 * 4) << global_cpu_double_speed);
/* hard sync is on? */
if (cycles_hs_mode)
{
/* send my status and wait for peer status back */
serial_send_byte();
/* wait for reply */
serial_wait_data();
/* verify if we need to trigger an interrupt */
serial_verify_intr();
}
}
/* DMA */
if (mmu.dma_next == cycles.cnt)
{
memcpy(&mmu.memory[0xFE00], &mmu.memory[mmu.dma_address], 160);
/* reset address */
mmu.dma_address = 0x0000;
/* reset */
mmu.dma_next = 1;
}
/* update GPU state */
if (gpu.next == cycles.cnt)
gpu_step();
/* fs clock */
if (sound.fs_cycles_next == cycles.cnt)
sound_step_fs();
/* channel one */
if (sound.channel_one.duty_cycles_next == cycles.cnt)
sound_step_ch1();
/* channel two */
if (sound.channel_two.duty_cycles_next == cycles.cnt)
sound_step_ch2();
/* channel three */
if (sound.channel_three.cycles_next <= cycles.cnt)
sound_step_ch3();
/* channel four */
if (sound.channel_four.cycles_next == cycles.cnt)
sound_step_ch4();
/* time to generate a sample? */
if (sound.sample_cycles_next_rounded == cycles.cnt)
sound_step_sample();
/* update timer state */
if (cycles.cnt == timer.next)
{
timer.next += 256;
timer.div++;
}
/* timer is on? */
if (timer.sub_next == cycles.cnt)
{
timer.sub_next += timer.threshold;
timer.cnt++;
/* cnt value > 255? trigger an interrupt */
if (timer.cnt > 255)
{
timer.cnt = timer.mod;
/* trigger timer interrupt */
cycles_if->timer = 1;
}
}
/* update serial state */
if (serial.next == cycles.cnt)
{
/* nullize serial next */
serial.next -= 1;
/* reset counter */
serial.bits_sent = 0;
/* gotta reply with 0xff when asking for ff01 */
serial.data = 0xFF;
/* reset transfer_start flag to yell I'M DONE */
serial.transfer_start = 0;
/* if not connected, trig the fucking interrupt */
cycles_if->serial_io = 1;
}
}
/* things to do when vsync kicks in */
void cycles_vblank()
{
return;
}
/* stuff tied to entering into hblank state */
void cycles_hdma()
{
/* HDMA (only CGB) */
if (mmu.hdma_to_transfer)
{
/* hblank transfer */
if (mmu.hdma_transfer_mode)
{
/* transfer when line is changed and we're into HBLANK phase */
if (mmu.memory[0xFF44] < 143 &&
mmu.hdma_current_line != mmu.memory[0xFF44] &&
(mmu.memory[0xFF41] & 0x03) == 0x00)
{
/* update current line */
mmu.hdma_current_line = mmu.memory[0xFF44];
/* copy 0x10 bytes */
if (mmu.vram_idx)
memcpy(mmu_addr_vram1() + mmu.hdma_dst_address - 0x8000,
&mmu.memory[mmu.hdma_src_address], 0x10);
else
memcpy(mmu_addr_vram0() + mmu.hdma_dst_address - 0x8000,
&mmu.memory[mmu.hdma_src_address], 0x10);
/* decrease bytes to transfer */
mmu.hdma_to_transfer -= 0x10;
/* increase pointers */
mmu.hdma_dst_address += 0x10;
mmu.hdma_src_address += 0x10;
}
}
}
}
char cycles_init()
{
/* CLOCK */
clock_gettime(CLOCK_MONOTONIC, &deadline);
cycles.inited = 1;
/* interrupt registers */
cycles_if = mmu_addr(0xFF0F);
/* init clock and counter */
cycles.clock = 4194304;
cycles.cnt = 0;
cycles.hs_next = 70224;
/* mask for pauses cycles fast calc */
cycles.step = 4194304 / CYCLES_PAUSES;
cycles.next = 4194304 / CYCLES_PAUSES;
return 0;
}
char cycles_start_timer()
{
/* just pick new time reference */
clock_gettime(CLOCK_MONOTONIC, &deadline);
return 0;
}
void cycles_stop_timer()
{
if (cycles.inited == 0)
return;
}
void cycles_term()
{
}
void cycles_save_stat(FILE *fp)
{
fwrite(&cycles, 1, sizeof(cycles_t), fp);
}
void cycles_restore_stat(FILE *fp)
{
fread(&cycles, 1, sizeof(cycles_t), fp);
/* recalc speed stuff */
cycles_change_emulation_speed();
}

View File

@ -0,0 +1,77 @@
/*
This file is part of Emu-Pizza
Emu-Pizza is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Emu-Pizza is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Emu-Pizza. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __CYCLES_HDR__
#define __CYCLES_HDR__
#include <stdint.h>
#include <stdio.h>
typedef struct cycles_s
{
/* am i init'ed? */
uint_fast32_t inited;
/* ticks counter */
uint_fast32_t cnt;
/* CPU clock */
uint_fast32_t clock;
/* handy for calculation */
uint_fast32_t next;
/* step varying on cpu and emulation speed */
uint_fast32_t step;
/* total running seconds */
uint_fast32_t seconds;
/* 2 spares */
uint_fast32_t hs_next;
uint_fast32_t spare2;
} cycles_t;
extern cycles_t cycles;
// extern uint8_t cycles_hs_local_cnt;
// extern uint8_t cycles_hs_peer_cnt;
/* callback function */
typedef void (*cycles_send_cb_t) (uint32_t v);
/* prototypes */
void cycles_change_emulation_speed();
void cycles_hdma();
char cycles_init();
void cycles_restore_stat(FILE *fp);
void cycles_save_stat(FILE *fp);
void cycles_set_speed(char dbl);
void cycles_start_hs();
char cycles_start_timer();
void cycles_step();
void cycles_stop_hs();
void cycles_stop_timer();
void cycles_term();
void cycles_vblank();
#endif

View File

@ -0,0 +1,415 @@
/*
This file is part of Emu-Pizza
Emu-Pizza is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Emu-Pizza is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Emu-Pizza. If not, see <http://www.gnu.org/licenses/>.
*/
#include <semaphore.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <time.h>
#include <unistd.h>
#include "cartridge.h"
#include "sound.h"
#include "mmu.h"
#include "cycles.h"
#include "gpu.h"
#include "global.h"
#include "input.h"
#include "timer.h"
#include "serial.h"
#include "utils.h"
#include "z80_gameboy_regs.h"
#include "z80_gameboy.h"
/* semaphore for pauses */
sem_t gameboy_sem;
char gameboy_inited = 0;
void gameboy_init()
{
/* init global values */
// global_init();
/* init z80 */
z80_init();
/* init cycles syncronizer */
cycles_init();
/* init input */
input_init();
/* init timer */
timer_init();
/* init serial */
serial_init();
/* init sound (this will start audio thread) */
sound_init();
/* reset GPU counters */
gpu_reset();
/* reset to default values */
mmu_write_no_cyc(0xFF05, 0x00);
mmu_write_no_cyc(0xFF06, 0x00);
mmu_write_no_cyc(0xFF07, 0x00);
mmu_write_no_cyc(0xFF10, 0x80);
mmu_write_no_cyc(0xFF11, 0xBF);
mmu_write_no_cyc(0xFF12, 0xF3);
mmu_write_no_cyc(0xFF14, 0xBF);
mmu_write_no_cyc(0xFF16, 0x3F);
mmu_write_no_cyc(0xFF17, 0x00);
mmu_write_no_cyc(0xFF19, 0xBF);
mmu_write_no_cyc(0xFF1A, 0x7F);
mmu_write_no_cyc(0xFF1B, 0xFF);
mmu_write_no_cyc(0xFF1C, 0x9F);
mmu_write_no_cyc(0xFF1E, 0xBF);
mmu_write_no_cyc(0xFF20, 0xFF);
mmu_write_no_cyc(0xFF21, 0x00);
mmu_write_no_cyc(0xFF22, 0x00);
mmu_write_no_cyc(0xFF23, 0xBF);
mmu_write_no_cyc(0xFF24, 0x77);
mmu_write_no_cyc(0xFF25, 0xF3);
mmu_write_no_cyc(0xFF26, 0xF1);
mmu_write_no_cyc(0xFF40, 0x91);
mmu_write_no_cyc(0xFF41, 0x80);
mmu_write_no_cyc(0xFF42, 0x00);
mmu_write_no_cyc(0xFF43, 0x00);
mmu_write_no_cyc(0xFF44, 0x00);
mmu_write_no_cyc(0xFF45, 0x00);
mmu_write_no_cyc(0xFF47, 0xFC);
mmu_write_no_cyc(0xFF48, 0xFF);
mmu_write_no_cyc(0xFF49, 0xFF);
mmu_write_no_cyc(0xFF4A, 0x00);
mmu_write_no_cyc(0xFF4B, 0x00);
mmu_write_no_cyc(0xFF98, 0xDC);
mmu_write_no_cyc(0xFFFF, 0x00);
mmu_write_no_cyc(0xC000, 0x08);
mmu_write_no_cyc(0xFFFE, 0x69);
if (global_cgb)
state.a = 0x11;
else
state.a = 0x00;
state.b = 0x00;
state.c = 0x13;
state.d = 0x00;
state.e = 0xd8;
state.h = 0x01;
state.l = 0x4d;
state.pc = 0x0100;
state.sp = 0xFFFE;
*state.f = 0xB0;
/* init semaphore for pauses */
sem_init(&gameboy_sem, 0, 0);
/* mark as inited */
gameboy_inited = 1;
return;
}
void gameboy_set_pause(char pause)
{
if (!gameboy_inited)
return;
if (pause == global_pause)
return;
global_pause = pause;
if (pause)
{
/* wait a bit - i need the main cycle fall into global_pause check */
usleep(100000);
/* stop timer */
cycles_stop_timer();
}
else
{
/* restart timer */
cycles_start_timer();
/* wake up */
sem_post(&gameboy_sem);
}
}
void gameboy_run()
{
uint8_t op;
/* reset counter */
cycles.cnt = 0;
/* get interrupt flags and interrupt enables */
uint8_t *int_e;
uint8_t *int_f;
/* pointers to memory location of interrupt enables/flags */
int_e = mmu_addr(0xFFFF);
int_f = mmu_addr(0xFF0F);
/* start at normal speed */
global_cpu_double_speed = 0;
/* run stuff! */
/* mechanism is simple. */
/* 1) execute instruction 2) update cycles counter 3) check interrupts */
/* and repeat forever */
while (!global_quit)
{
/*if (global_slow_down)
{
usleep(100000);
global_slow_down = 0;
}*/
/* pause? */
while (global_pause)
sem_wait(&gameboy_sem);
/* get op */
op = mmu_read(state.pc);
/* print out CPU state if enabled by debug flag */
if (global_debug)
{
utils_log("OP: %02x F: %02x PC: %04x:%02x:%02x SP: %04x:%02x:%02x ",
op, *state.f & 0xd0, state.pc,
mmu_read_no_cyc(state.pc + 1),
mmu_read_no_cyc(state.pc + 2), state.sp,
mmu_read_no_cyc(state.sp),
mmu_read_no_cyc(state.sp + 1));
utils_log("A: %02x BC: %04x DE: %04x HL: %04x FF41: %02x "
"FF44: %02x ENAB: %02x INTE: %02x INTF: %02x\n",
state.a, *state.bc,
*state.de, *state.hl,
mmu_read_no_cyc(0xFF41),
mmu_read_no_cyc(0xFF44),
state.int_enable,
*int_e, *int_f);
}
/* execute instruction by the GB Z80 version */
z80_execute(op);
/* if last op was Interrupt Enable (0xFB) */
/* we need to check for INTR on next cycle */
if (op == 0xFB)
continue;
/* interrupts filtered by enable flags */
uint8_t int_r = (*int_f & *int_e);
/* check for interrupts */
if ((state.int_enable || op == 0x76) && (int_r != 0))
{
/* discard useless bits */
if ((int_r & 0x1F) == 0x00)
continue;
/* beware of instruction that doesn't move PC! */
/* like HALT (0x76) */
if (op == 0x76)
{
state.pc++;
if (state.int_enable == 0)
continue;
}
/* reset int-enable flag, it will be restored after a RETI op */
state.int_enable = 0;
if ((int_r & 0x01) == 0x01)
{
/* vblank interrupt triggers RST 5 */
/* reset flag */
*int_f &= 0xFE;
/* handle the interrupt */
z80_intr(0x0040);
}
else if ((int_r & 0x02) == 0x02)
{
/* LCD Stat interrupt */
/* reset flag */
*int_f &= 0xFD;
/* handle the interrupt! */
z80_intr(0x0048);
}
else if ((int_r & 0x04) == 0x04)
{
/* timer interrupt */
/* reset flag */
*int_f &= 0xFB;
/* handle the interrupt! */
z80_intr(0x0050);
}
else if ((int_r & 0x08) == 0x08)
{
/* serial interrupt */
/* reset flag */
*int_f &= 0xF7;
/* handle the interrupt! */
z80_intr(0x0058);
}
}
}
/* terminate all the stuff */
cartridge_term();
sound_term();
mmu_term();
return;
}
void gameboy_stop()
{
global_quit = 1;
/* wake up */
if (global_pause)
{
global_pause = 0;
sem_post(&gameboy_sem);
}
/* unlock threads stuck during reading */
sound_term();
/* shutdown semaphore limitator */
cycles_term();
}
char gameboy_restore_stat(int idx)
{
char path[256];
char buf[6];
/* ensure i'm in pause */
gameboy_set_pause(1);
/* build output file name */
snprintf(path, sizeof(path), "%s/%s.%d.stat", global_save_folder,
global_rom_name, idx);
FILE *fp = fopen(path, "r+");
if (fp == NULL)
{
utils_log("Cannot open stat file\n");
return 1;
}
/* read version */
fread(buf, 1, 6, fp);
if (memcmp(buf, "000001", 6))
{
utils_log("Version of stat file doesnt match\n");
return 1;
}
/* restore CPU status */
fread(&state, 1, sizeof(z80_state_t), fp);
state.f = (uint8_t *) &state.flags;
state.bc = (uint16_t *) &state.c;
state.de = (uint16_t *) &state.e;
state.hl = (uint16_t *) &state.l;
/* dump every module */
cycles_restore_stat(fp);
sound_restore_stat(fp);
gpu_restore_stat(fp);
serial_restore_stat(fp);
mmu_restore_stat(fp);
fclose(fp);
return 0;
}
char gameboy_save_stat(int idx)
{
char path[256];
/* ensure i'm in pause */
gameboy_set_pause(1);
/* build output file name */
snprintf(path, sizeof(path), "%s/%s.%d.stat", global_save_folder,
global_rom_name, idx);
FILE *fp = fopen(path, "w+");
if (fp == NULL)
return 1;
/* dump current version */
fwrite("000001", 1, 6, fp);
/* dump cpu status */
fwrite(&state, 1, sizeof(z80_state_t), fp);
/* dump every module */
cycles_save_stat(fp);
sound_save_stat(fp);
gpu_save_stat(fp);
serial_save_stat(fp);
mmu_save_stat(fp);
fclose(fp);
/* now dump raw data of frame buffer */
snprintf(path, sizeof(path), "%s/%s.%d.fb", global_save_folder,
global_rom_name, idx);
fp = fopen(path, "w+");
if (fp == NULL)
return 1;
/* dump frame buffer pixels */
gpu_save_fb(fp);
fclose(fp);
return 0;
}

View File

@ -0,0 +1,31 @@
/*
This file is part of Emu-Pizza
Emu-Pizza is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Emu-Pizza is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Emu-Pizza. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __GAMEBOY_HDR__
#define __GAMEBOY_HDR__
/* prototypes */
void gameboy_init();
void gameboy_run();
char gameboy_restore_stat(int idx);
char gameboy_save_stat(int idx);
void gameboy_set_pause(char pause);
void gameboy_stop();
#endif

View File

@ -0,0 +1,56 @@
/*
This file is part of Emu-Pizza
Emu-Pizza is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Emu-Pizza is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Emu-Pizza. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <strings.h>
#include "global.h"
char global_cart_name[256];
char global_cgb;
char global_cpu_double_speed;
char global_debug;
char global_emulation_speed;
char global_next_frame;
char global_pause;
char global_quit;
char global_record_audio;
char global_rom_name[256];
char global_rumble;
char global_slow_down;
char global_save_folder[256];
char global_window;
void global_init()
{
global_quit = 0;
global_pause = 0;
global_window = 1;
global_debug = 0;
global_cgb = 0;
global_cpu_double_speed = 0;
global_slow_down = 0;
global_record_audio = 0;
global_next_frame = 0;
global_rumble = 0;
global_emulation_speed = GLOBAL_EMULATION_SPEED_NORMAL;
// bzero(global_save_folder, 256);
bzero(global_rom_name, 256);
sprintf(global_cart_name, "NOCARTIRDGE");
}

View File

@ -0,0 +1,51 @@
/*
This file is part of Emu-Pizza
Emu-Pizza is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Emu-Pizza is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Emu-Pizza. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __GLOBAL__
#define __GLOBAL__
/* defines */
enum {
GLOBAL_EMULATION_SPEED_QUARTER,
GLOBAL_EMULATION_SPEED_HALF,
GLOBAL_EMULATION_SPEED_NORMAL,
GLOBAL_EMULATION_SPEED_DOUBLE,
GLOBAL_EMULATION_SPEED_4X
};
extern char global_quit;
extern char global_pause;
extern char global_window;
extern char global_debug;
extern char global_cgb;
extern char global_next_frame;
// extern char global_started;
extern char global_cpu_double_speed;
extern char global_slow_down;
extern char global_record_audio;
extern char global_emulation_speed;
extern char global_rumble;
extern char global_save_folder[256];
extern char global_rom_name[256];
extern char global_cart_name[256];
/* prototypes */
void global_init();
#endif

1179
waterbox/pizza/lib/gpu.c Normal file

File diff suppressed because it is too large Load Diff

144
waterbox/pizza/lib/gpu.h Normal file
View File

@ -0,0 +1,144 @@
/*
This file is part of Emu-Pizza
Emu-Pizza is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Emu-Pizza is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Emu-Pizza. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __GPU_HDR__
#define __GPU_HDR__
#include <stdio.h>
#include <stdint.h>
/* callback function */
typedef void (*gpu_frame_ready_cb_t) ();
/* prototypes */
void gpu_dump_oam();
uint16_t *gpu_get_frame_buffer();
void gpu_init(gpu_frame_ready_cb_t cb);
void gpu_reset();
void gpu_restore_stat(FILE *fp);
void gpu_save_stat(FILE *fp);
void gpu_save_fb(FILE *fp);
void gpu_set_speed(char speed);
void gpu_step();
void gpu_toggle(uint8_t state);
void gpu_write_reg(uint16_t a, uint8_t v);
uint8_t gpu_read_reg(uint16_t a);
/* Gameboy LCD Control - R/W accessing 0xFF40 address */
typedef struct gpu_lcd_ctrl_s
{
uint8_t bg:1; /* 0 = BG off, 1 = BG on */
uint8_t sprites:1; /* ??? */
uint8_t sprites_size:1; /* 0 = 8x8, 1 = 8x16 */
uint8_t bg_tiles_map:1; /* 0 = 9800-9BFF, 1 = 9C00-9FFF */
uint8_t bg_tiles:1; /* 0 = 8800-97FF, 1 = 8000-8FFF */
uint8_t window:1; /* 0 = window off, 1 = on */
uint8_t window_tiles_map:1; /* 0 = 9800-9BFF, 1 = 9C00-9FFF */
uint8_t display:1; /* 0 = LCD off, 1 = LCD on */
} gpu_lcd_ctrl_t;
/* Gameboy LCD Status - R/W accessing 0xFF41 address */
typedef struct gpu_lcd_status_s
{
uint8_t mode:2;
uint8_t ly_coincidence:1;
uint8_t ir_mode_00:1;
uint8_t ir_mode_01:1;
uint8_t ir_mode_10:1;
uint8_t ir_ly_coincidence:1;
uint8_t spare:1;
} gpu_lcd_status_t;
/* RGB color */
typedef struct rgb_s
{
uint8_t r;
uint8_t g;
uint8_t b;
uint8_t a;
} rgb_t;
/* Gameboy GPU status */
typedef struct gpu_s
{
gpu_lcd_ctrl_t *lcd_ctrl;
gpu_lcd_status_t *lcd_status;
/* scroll positions */
uint8_t *scroll_x;
uint8_t *scroll_y;
/* window position */
uint8_t *window_x;
uint8_t *window_y;
/* current scanline and it's compare values */
uint8_t *ly;
uint8_t *lyc;
/* clocks counter */
uint_fast32_t next;
/* gpu step span */
uint_fast32_t step;
/* window last drawn lines */
uint8_t window_last_ly;
uint8_t window_skipped_lines;
uint16_t spare;
/* frame counter */
uint_fast16_t frame_counter;
/* BG palette */
uint16_t bg_palette[4];
/* Obj palette 0/1 */
uint16_t obj_palette_0[4];
uint16_t obj_palette_1[4];
/* CGB palette for background */
uint16_t cgb_palette_bg_rgb565[0x20];
uint16_t cgb_palette_bg[0x20];
uint8_t cgb_palette_bg_idx;
uint8_t cgb_palette_bg_autoinc;
uint16_t spare2;
/* CGB palette for sprites */
uint16_t cgb_palette_oam_rgb565[0x20];
uint16_t cgb_palette_oam[0x20];
uint8_t cgb_palette_oam_idx;
uint8_t cgb_palette_oam_autoinc;
uint16_t spare3;
/* frame buffer */
uint16_t frame_buffer_prev[160 * 144];
uint16_t frame_buffer[160 * 144];
uint8_t priority[160 * 144];
uint8_t palette_idx[160 * 144];
uint_fast32_t spare4;
uint_fast32_t spare5;
} gpu_t;
extern gpu_t gpu;
#endif

102
waterbox/pizza/lib/input.c Normal file
View File

@ -0,0 +1,102 @@
/*
This file is part of Emu-Pizza
Emu-Pizza is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Emu-Pizza is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Emu-Pizza. If not, see <http://www.gnu.org/licenses/>.
*/
#include "global.h"
#include "utils.h"
#include <stdint.h>
/* button states */
char input_key_left;
char input_key_right;
char input_key_up;
char input_key_down;
char input_key_a;
char input_key_b;
char input_key_select;
char input_key_start;
uint8_t input_init()
{
input_key_left = 0;
input_key_right = 0;
input_key_up = 0;
input_key_down = 0;
input_key_a = 0;
input_key_b = 0;
input_key_select = 0;
input_key_start = 0;
return 0;
}
uint8_t input_get_keys(uint8_t line)
{
uint8_t v = line | 0x0f;
if ((line & 0x30) == 0x20)
{
/* RIGHT pressed? */
if (input_key_right)
v ^= 0x01;
/* LEFT pressed? */
if (input_key_left)
v ^= 0x02;
/* UP pressed? */
if (input_key_up)
v ^= 0x04;
/* DOWN pressed? */
if (input_key_down)
v ^= 0x08;
}
if ((line & 0x30) == 0x10)
{
/* A pressed? */
if (input_key_a)
v ^= 0x01;
/* B pressed? */
if (input_key_b)
v ^= 0x02;
/* SELECT pressed? */
if (input_key_select)
v ^= 0x04;
/* START pressed? */
if (input_key_start)
v ^= 0x08;
}
return (v | 0xc0);
}
void input_set_key_right(char state) { input_key_right = state; }
void input_set_key_left(char state) { input_key_left = state; }
void input_set_key_up(char state) { input_key_up = state; }
void input_set_key_down(char state) { input_key_down = state; }
void input_set_key_a(char state) { input_key_a = state; }
void input_set_key_b(char state) { input_key_b = state; }
void input_set_key_select(char state) { input_key_select = state; }
void input_set_key_start(char state) { input_key_start = state; }

View File

@ -0,0 +1,35 @@
/*
This file is part of Emu-Pizza
Emu-Pizza is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Emu-Pizza is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Emu-Pizza. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __INPUT_HDR__
#define __INPUT_HDR__
/* prototypes */
uint8_t input_get_keys(uint8_t line);
uint8_t input_init();
void input_set_key_left(char state);
void input_set_key_right(char state);
void input_set_key_up(char state);
void input_set_key_down(char state);
void input_set_key_a(char state);
void input_set_key_b(char state);
void input_set_key_select(char state);
void input_set_key_start(char state);
#endif

View File

@ -0,0 +1,35 @@
/*
This file is part of Emu-Pizza
Emu-Pizza is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Emu-Pizza is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Emu-Pizza. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __INTERRUPTS_HDR__
#define __INTERRUPTS_HDR__
#include <stdint.h>
typedef struct interrupts_flags_s
{
uint8_t lcd_vblank:1;
uint8_t lcd_ctrl:1;
uint8_t timer:1;
uint8_t serial_io:1;
uint8_t pins1013:1;
uint8_t spare:3;
} interrupts_flags_t;
#endif

1323
waterbox/pizza/lib/mmu.c Normal file

File diff suppressed because it is too large Load Diff

152
waterbox/pizza/lib/mmu.h Normal file
View File

@ -0,0 +1,152 @@
/*
This file is part of Emu-Pizza
Emu-Pizza is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Emu-Pizza is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Emu-Pizza. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __MMU_HDR__
#define __MMU_HDR__
#include <stdio.h>
#include <stdint.h>
#include <sys/time.h>
typedef struct mmu_gamegenie_s {
/* data necessary */
uint16_t address;
uint8_t old_value;
uint8_t new_value;
} mmu_gamegenie_t;
typedef struct mmu_gameshark_s {
/* data necessary */
uint16_t address;
uint8_t ram_bank;
uint8_t new_value;
} mmu_gameshark_t;
#define MMU_GAMEGENIE_MAX 4
#define MMU_GAMESHARK_MAX 32
typedef struct mmu_s {
/* main 64K of memory */
uint8_t memory[65536];
/* vram in standby */
uint8_t vram0[0x2000];
uint8_t vram1[0x2000];
/* vram current idx */
uint8_t vram_idx;
uint8_t spare;
uint16_t spare2;
/* internal RAM */
uint8_t ram_internal[0x2000];
uint8_t ram_external_enabled;
uint8_t ram_current_bank;
/* cartridge type */
uint8_t carttype;
/* number of switchable roms */
uint8_t roms;
/* current ROM bank */
uint8_t rom_current_bank;
/* type of banking */
uint8_t banking;
/* working RAM (only CGB) */
uint8_t wram[0x8000];
/* current WRAM bank (only CGB) */
uint8_t wram_current_bank;
uint8_t spare3;
uint16_t spare4;
/* DMA transfer stuff */
uint_fast16_t dma_address;
uint_fast16_t dma_cycles;
/* HDMA transfer stuff */
uint16_t hdma_src_address;
uint16_t hdma_dst_address;
uint16_t hdma_to_transfer;
uint8_t hdma_transfer_mode;
uint8_t hdma_current_line;
/* RTC stuff */
uint8_t rtc_mode;
uint8_t spare5;
uint16_t spare6;
time_t rtc_time;
time_t rtc_latch_time;
/* Gamegenie */
uint8_t gg_count;
mmu_gamegenie_t gg_array[MMU_GAMEGENIE_MAX];
/* Gameshark */
uint8_t gs_count;
mmu_gameshark_t gs_array[MMU_GAMESHARK_MAX];
uint_fast32_t dma_next;
uint_fast32_t spare8;
} mmu_t;
extern mmu_t mmu;
/* callback function */
typedef void (*mmu_rumble_cb_t) (uint8_t onoff);
/* functions prototypes */
void *mmu_addr(uint16_t a);
void *mmu_addr_vram0();
void *mmu_addr_vram1();
void mmu_apply_gg();
void mmu_apply_gs();
void mmu_dump_all();
void mmu_init(uint8_t c, uint8_t rn);
void mmu_init_ram(uint32_t c);
void mmu_load(uint8_t *data, size_t sz, uint16_t a);
void mmu_load_cartridge(uint8_t *data, size_t sz);
void mmu_move(uint16_t d, uint16_t s);
uint8_t mmu_read_no_cyc(uint16_t a);
uint8_t mmu_read(uint16_t a);
unsigned int mmu_read_16(uint16_t a);
void mmu_restore_ram(char *fn);
void mmu_restore_rtc(char *fn);
void mmu_restore_stat(FILE *fp);
void mmu_save_ram(char *fn);
void mmu_save_rtc(char *fn);
void mmu_save_stat(FILE *fp);
char mmu_set_cheat(char *cheat);
void mmu_set_rumble_cb(mmu_rumble_cb_t cb);
void mmu_step();
void mmu_term();
void mmu_write_no_cyc(uint16_t a, uint8_t v);
void mmu_write(uint16_t a, uint8_t v);
void mmu_write_16(uint16_t a, uint16_t v);
#endif

View File

@ -0,0 +1,409 @@
/*
This file is part of Emu-Pizza
Emu-Pizza is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Emu-Pizza is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Emu-Pizza. If not, see <http://www.gnu.org/licenses/>.
*/
#include <arpa/inet.h>
#include <errno.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#include "cycles.h"
#include "global.h"
#include "network.h"
#include "serial.h"
#include "utils.h"
/* network special binary semaphore */
/* typedef struct network_sem_s {
pthread_mutex_t mutex;
pthread_cond_t cvar;
int v;
} network_sem_t; */
/* network sockets */
int network_sock_broad = -1;
int network_sock_bound = -1;
/* peer addr */
struct sockaddr_in network_peer_addr;
/* uuid to identify myself */
unsigned int network_uuid;
/* uuid to identify peer */
unsigned int network_peer_uuid;
/* progressive number (debug purposes) */
uint8_t network_prog_recv = 0;
uint8_t network_prog_sent = 0;
/* track that network is running */
unsigned char network_running = 0;
/* broadcast address */
char network_broadcast_addr[16];
/* network thread */
pthread_t network_thread;
/* semaphorone */
// network_sem_t network_sem;
/* function to call when connected to another Pizza Boy */
network_cb_t network_connected_cb;
network_cb_t network_disconnected_cb;
/* timeout to declare peer disconnected */
uint8_t network_timeout = 10;
uint8_t prot = 0, pret = 0;
/* prototypes */
void network_send_data(uint8_t v, uint8_t clock, uint8_t transfer_start);
void *network_start_thread(void *args);
/* is network running? */
char network_is_running()
{
return network_running;
}
/* start network thread */
void network_start(network_cb_t connected_cb, network_cb_t disconnected_cb,
char *broadcast_addr)
{
/* init semaphore */
// network_sem_init(&network_sem);
/* reset bool */
network_running = 0;
/* set callback */
network_connected_cb = connected_cb;
network_disconnected_cb = disconnected_cb;
/* save broadcast addr */
strncpy(network_broadcast_addr, broadcast_addr, 16);
/* start thread! */
pthread_create(&network_thread, NULL, network_start_thread, NULL);
}
/* stop network thread */
void network_stop()
{
/* already stopped? */
if (network_running == 0)
return;
/* tell thread to stop */
network_running = 0;
/* wait for it to exit */
pthread_join(network_thread, NULL);
}
void *network_start_thread(void *args)
{
utils_log("Starting network thread\n");
/* open socket sending broadcast messages */
network_sock_broad = socket(AF_INET, SOCK_DGRAM, 0);
/* exit on error */
if (network_sock_broad < 1)
{
utils_log("Error opening broadcast socket");
return NULL;
}
/* open socket sending/receiving serial cable data */
network_sock_bound = socket(AF_INET, SOCK_DGRAM, 0);
/* exit on error */
if (network_sock_bound < 1)
{
utils_log("Error opening serial-link socket");
close (network_sock_broad);
return NULL;
}
/* enable to broadcast */
int enable=1;
setsockopt(network_sock_broad, SOL_SOCKET, SO_BROADCAST,
&enable, sizeof(enable));
/* prepare dest stuff */
struct sockaddr_in broadcast_addr;
struct sockaddr_in bound_addr;
struct sockaddr_in addr_from;
socklen_t addr_from_len = sizeof(addr_from);
memset(&broadcast_addr, 0, sizeof(broadcast_addr));
broadcast_addr.sin_family = AF_INET;
// broadcast_addr.sin_addr.s_addr = INADDR_BROADCAST;
// inet_aton("239.255.0.37",
// (struct in_addr *) &broadcast_addr.sin_addr.s_addr);
// inet_aton("192.168.100.255",
inet_aton(network_broadcast_addr,
(struct in_addr *) &broadcast_addr.sin_addr.s_addr);
broadcast_addr.sin_port = htons(64333);
/* setup listening socket */
memset(&bound_addr, 0, sizeof(bound_addr));
bound_addr.sin_family = AF_INET;
bound_addr.sin_addr.s_addr = INADDR_ANY;
bound_addr.sin_port = htons(64333);
/* bind to selected port */
if (bind(network_sock_bound, (struct sockaddr *) &bound_addr,
sizeof(bound_addr)))
{
utils_log("Error binding to port 64333");
/* close sockets and exit */
close(network_sock_broad);
close(network_sock_bound);
return NULL;
}
/* assign it to our multicast group */
/* struct ip_mreq mreq;
mreq.imr_multiaddr.s_addr=inet_addr("239.255.0.37");
mreq.imr_interface.s_addr=htonl(INADDR_ANY);
if (setsockopt(network_sock_bound, IPPROTO_IP, IP_ADD_MEMBERSHIP,
&mreq, sizeof(mreq)) < 0)
{
utils_log("Error joining multicast network");
close(network_sock_broad);
close(network_sock_bound);
return NULL;
}*/
fd_set rfds;
char buf[64];
int ret;
ssize_t recv_ret;
struct timeval tv;
int timeouts = 4;
// unsigned int v, clock, prog;
/* message parts */
char msg_type;
unsigned int msg_uuid;
char msg_content[64];
/* generate a random uuid */
srand(time(NULL));
network_uuid = rand() & 0xFFFFFFFF;
/* set callback in case of data to send */
serial_set_send_cb(&network_send_data);
/* declare network is running */
network_running = 1;
utils_log("Network thread started\n");
/* loop forever */
while (network_running)
{
FD_ZERO(&rfds);
FD_SET(network_sock_bound, &rfds);
/* wait one second */
tv.tv_sec = 1;
tv.tv_usec = 0;
/* one second timeout OR something received */
ret = select(network_sock_bound + 1, &rfds, NULL, NULL, &tv);
/* error! */
if (ret == -1)
break;
/* ret 0 = timeout */
if (ret == 0)
{
if (++timeouts == 3)
{
/* build output message */
sprintf(buf, "B%08x%s", network_uuid, global_cart_name);
/* send broadcast message */
sendto(network_sock_broad, buf, strlen(buf), 0,
(struct sockaddr *) &broadcast_addr,
sizeof(broadcast_addr));
utils_log("Sending broadcast message %s\n", buf);
timeouts = 0;
}
if (serial.peer_connected)
{
if (--network_timeout == 0)
{
/* notify serial module */
serial.peer_connected = 0;
/* stop Hard Sync mode */
cycles_stop_hs();
/* notify by the cb */
if (network_disconnected_cb)
(*network_disconnected_cb) ();
}
}
}
else
{
/* reset message content */
bzero(buf, sizeof(buf));
bzero(msg_content, sizeof(msg_content));
/* exit if an error occour */
if ((recv_ret = recvfrom(network_sock_bound, buf, 64, 0,
(struct sockaddr *) &addr_from,
(socklen_t *) &addr_from_len)) < 1)
break;
/* extract message type (1st byte) */
msg_type = buf[0];
/* is it broadcast? */
//if (sscanf(buf, "%c%08x%s",
// &msg_type, &msg_uuid, msg_content) == 3)
// {
/* was it send by myself? */
// if (msg_uuid != network_uuid)
// {
/* is it a serial data message? */
if (msg_type == 'M')
{
network_prog_recv = (uint8_t) buf[3];
/* buf[1] contains value - buf[2] contains serial clock */
/* tell serial module something has arrived */
serial_recv_byte((uint8_t) buf[1], (uint8_t) buf[2], buf[4]);
}
else if (msg_type == 'B')
{
/* extract parts from broadcast message */
sscanf(buf, "%c%08x%s", &msg_type, &msg_uuid, msg_content);
/* myself? */
if (network_uuid == msg_uuid)
continue;
/* not the same game? */
if (strcmp(msg_content, global_cart_name) != 0)
continue;
/* someone is claiming is playing with the same game? */
if (serial.peer_connected == 0)
{
/* save peer uuid */
network_peer_uuid = msg_uuid;
/* refresh timeout */
network_timeout = 10;
/* save sender */
memcpy(&network_peer_addr, &addr_from,
sizeof(struct sockaddr_in));
/* just change dst port */
network_peer_addr.sin_port = htons(64333);
/* notify the other peer by sending a b message */
sprintf(buf, "B%08x%s", network_uuid,
global_cart_name);
/* send broadcast message */
sendto(network_sock_broad, buf, strlen(buf), 0,
(struct sockaddr *) &network_peer_addr,
sizeof(network_peer_addr));
/* log that peer is connected */
utils_log("Peer connected: %s\n",
inet_ntoa(network_peer_addr.sin_addr));
/* YEAH */
serial.peer_connected = 1;
/* notify by the cb */
if (network_connected_cb)
(*network_connected_cb) ();
/* start hard sync */
cycles_start_hs();
}
else
{
/* refresh timeout */
if (network_peer_uuid == msg_uuid)
network_timeout = 10;
}
}
}
}
/* free serial */
serial.peer_connected = 0;
/* stop hard sync mode */
cycles_stop_hs();
/* close sockets */
close(network_sock_broad);
close(network_sock_bound);
return NULL;
}
void network_send_data(uint8_t v, uint8_t clock, uint8_t transfer_start)
{
char msg[5];
/* format message */
network_prog_sent = ((network_prog_sent + 1) & 0xff);
msg[0] = 'M';
msg[1] = v;
msg[2] = clock;
msg[3] = network_prog_sent;
msg[4] = transfer_start;
if (network_prog_sent != network_prog_recv &&
network_prog_sent != (uint8_t) (network_prog_recv + 1))
global_quit = 1;
/* send */
sendto(network_sock_bound, msg, 5, 0,
(struct sockaddr *) &network_peer_addr, sizeof(network_peer_addr));
}

View File

@ -0,0 +1,35 @@
/*
This file is part of Emu-Pizza
Emu-Pizza is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Emu-Pizza is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Emu-Pizza. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __NETWORK_HDR__
#define __NETWORK_HDR__
#include <stdint.h>
/* callback function */
typedef void (*network_cb_t) ();
/* prototypes */
char network_is_running();
void network_start(network_cb_t connected_cb,
network_cb_t disconnected_cb,
char *broadcast_addr);
void network_stop();
#endif

238
waterbox/pizza/lib/serial.c Normal file
View File

@ -0,0 +1,238 @@
/*
This file is part of Emu-Pizza
Emu-Pizza is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Emu-Pizza is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Emu-Pizza. If not, see <http://www.gnu.org/licenses/>.
*/
#include <pthread.h>
#include "cycles.h"
#include "interrupt.h"
#include "mmu.h"
#include "serial.h"
#include "utils.h"
/* main variable */
serial_t serial;
/* function to call when frame is ready */
serial_data_send_cb_t serial_data_send_cb;
interrupts_flags_t *serial_if;
/* mutexes for serial sync */
pthread_cond_t serial_cond;
pthread_mutex_t serial_mutex;
/* second message before the first was handled? */
uint8_t serial_second_set = 0;
uint8_t serial_second_data = 0;
uint8_t serial_second_clock = 0;
uint8_t serial_second_transfer_start = 0;
uint8_t serial_waiting_data = 0;
void serial_verify_intr()
{
if (serial.data_recv && serial.data_sent)
{
serial.data_recv = 0;
serial.data_sent = 0;
/* valid couple of messages for a serial interrupt? */
if ((serial.data_recv_clock != serial.data_sent_clock) &&
serial.data_recv_transfer_start &&
serial.data_sent_transfer_start)
{
/* put received data into 0xFF01 (serial.data) */
/* and notify with an interrupt */
serial.transfer_start = 0;
serial.data = serial.data_to_recv;
serial_if->serial_io = 1;
}
/* a message is already on queue? */
if (serial_second_set)
{
serial_second_set = 0;
serial.data_recv = 1;
serial.data_to_recv = serial_second_data;
serial.data_recv_clock = serial_second_clock;
serial.data_recv_transfer_start = serial_second_transfer_start;
}
}
}
void serial_init()
{
/* pointer to interrupt flags */
serial_if = mmu_addr(0xFF0F);
/* init counters */
serial.bits_sent = 0;
/* start as not connected */
serial.peer_connected = 0;
/* init semaphore for sync */
pthread_mutex_init(&serial_mutex, NULL);
pthread_cond_init(&serial_cond, NULL);
}
void serial_save_stat(FILE *fp)
{
fwrite(&serial, 1, sizeof(serial_t), fp);
}
void serial_restore_stat(FILE *fp)
{
fread(&serial, 1, sizeof(serial_t), fp);
}
void serial_write_reg(uint16_t a, uint8_t v)
{
/* lock the serial */
pthread_mutex_lock(&serial_mutex);
switch (a)
{
case 0xFF01:
serial.data = v; goto end;
case 0xFF02:
serial.clock = v & 0x01;
serial.speed = (v & 0x02) ? 0x01 : 0x00;
serial.spare = ((v >> 2) & 0x1F);
serial.transfer_start = (v & 0x80) ? 0x01 : 0x00;
/* reset? */
serial.data_sent = 0;
}
if (serial.transfer_start &&
!serial.peer_connected &&
serial.clock)
{
if (serial.speed)
serial.next = cycles.cnt + 8 * 8;
else
serial.next = cycles.cnt + 256 * 8;
}
end:
/* unlock the serial */
pthread_mutex_unlock(&serial_mutex);
}
uint8_t serial_read_reg(uint16_t a)
{
uint8_t v = 0xFF;
switch (a)
{
case 0xFF01: v = serial.data; break;
case 0xFF02: v = ((serial.clock) ? 0x01 : 0x00) |
((serial.speed) ? 0x02 : 0x00) |
(serial.spare << 2) |
((serial.transfer_start) ? 0x80 : 0x00);
}
return v;
}
void serial_recv_byte(uint8_t v, uint8_t clock, uint8_t transfer_start)
{
/* lock the serial */
pthread_mutex_lock(&serial_mutex);
/* second message during same span time? */
if (serial.data_recv)
{
/* store it. handle it later */
serial_second_set = 1;
serial_second_data = v;
serial_second_clock = clock;
serial_second_transfer_start = transfer_start;
goto end;
}
/* received side OK */
serial.data_recv = 1;
serial.data_recv_clock = clock;
serial.data_to_recv = v;
serial.data_recv_transfer_start = transfer_start;
/* notify main thread in case it's waiting */
if (serial_waiting_data)
pthread_cond_signal(&serial_cond);
end:
/* unlock the serial */
pthread_mutex_unlock(&serial_mutex);
}
void serial_send_byte()
{
/* lock the serial */
pthread_mutex_lock(&serial_mutex);
serial.data_sent = 1;
serial.data_to_send = serial.data;
serial.data_sent_clock = serial.clock;
serial.data_sent_transfer_start = serial.transfer_start;
if (serial_data_send_cb)
(*serial_data_send_cb) (serial.data, serial.clock,
serial.transfer_start);
/* unlock the serial */
pthread_mutex_unlock(&serial_mutex);
}
void serial_set_send_cb(serial_data_send_cb_t cb)
{
serial_data_send_cb = cb;
}
void serial_wait_data()
{
/* lock the serial */
pthread_mutex_lock(&serial_mutex);
if (serial.data_sent && serial.data_recv == 0)
{
/* wait max 3 seconds */
struct timespec wait;
wait.tv_sec = time(NULL) + 3;
/* this is very important to avoid EINVAL return! */
wait.tv_nsec = 0;
/* declare i'm waiting for data */
serial_waiting_data = 1;
/* notify something has arrived */
pthread_cond_timedwait(&serial_cond, &serial_mutex, &wait);
/* not waiting anymore */
serial_waiting_data = 0;
}
/* unlock the serial */
pthread_mutex_unlock(&serial_mutex);
}

View File

@ -0,0 +1,98 @@
/*
This file is part of Emu-Pizza
Emu-Pizza is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Emu-Pizza is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Emu-Pizza. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __SERIAL_HDR__
#define __SERIAL_HDR__
#include <stdint.h>
#include <stdio.h>
typedef struct serial_ctrl_s
{
uint8_t clock;
uint8_t speed;
uint8_t spare;
uint8_t transfer_start;
} serial_ctrl_t;
typedef struct serial_s {
/* pointer to serial controller register */
// serial_ctrl_t ctrl;
uint8_t clock;
uint8_t speed;
uint8_t spare;
uint8_t transfer_start;
/* pointer to FF01 data */
uint8_t data;
/* sent bits */
uint8_t bits_sent;
/* data to send */
uint8_t data_to_send;
/* peer clock */
uint8_t data_to_recv;
/* counter */
uint_fast32_t next;
/* peer connected? */
uint8_t peer_connected:1;
uint8_t data_sent:1;
uint8_t data_sent_clock:1;
uint8_t data_sent_transfer_start:1;
uint8_t data_recv:1;
uint8_t data_recv_clock:1;
uint8_t data_recv_transfer_start:1;
uint8_t spare10:1;
uint8_t spare2;
uint8_t spare3;
uint8_t spare4;
uint_fast32_t last_send_cnt;
} serial_t;
extern serial_t serial;
/* callback when receive something on serial */
typedef void (*serial_data_send_cb_t) (uint8_t v, uint8_t clock,
uint8_t transfer_start);
/* prototypes */
void serial_init();
void serial_lock();
void serial_write_reg(uint16_t a, uint8_t v);
void serial_verify_intr();
uint8_t serial_read_reg(uint16_t a);
void serial_recv_byte(uint8_t v, uint8_t clock, uint8_t transfer_start);
void serial_recv_clock();
void serial_save_stat(FILE *fp);
void serial_send_byte();
void serial_set_send_cb(serial_data_send_cb_t cb);
void serial_restore_stat(FILE *fp);
void serial_unlock();
void serial_wait_data();
#endif

1484
waterbox/pizza/lib/sound.c Normal file

File diff suppressed because it is too large Load Diff

334
waterbox/pizza/lib/sound.h Normal file
View File

@ -0,0 +1,334 @@
/*
This file is part of Emu-Pizza
Emu-Pizza is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Emu-Pizza is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Emu-Pizza. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __SOUND_HDR__
#define __SOUND_HDR__
#define SOUND_FREQ_MAX 48000
#define SOUND_SAMPLES 4096
#define SOUND_BUF_SZ (SOUND_SAMPLES * 3)
#define SOUND_BUF_TMP_SZ (SOUND_SAMPLES / 2)
typedef struct nr10_s
{
uint8_t shift:3;
uint8_t negate:1;
uint8_t sweep_period:3;
uint8_t spare:1;
} nr10_t;
typedef struct nr11_s
{
uint8_t length_load:6;
uint8_t duty:2;
} nr11_t;
typedef struct nr12_s
{
uint8_t period:3;
uint8_t add:1;
uint8_t volume:4;
} nr12_t;
typedef struct nr13_s
{
uint8_t frequency_lsb;
} nr13_t;
typedef struct nr14_s
{
uint8_t frequency_msb:3;
uint8_t spare:3;
uint8_t length_enable:1;
uint8_t trigger:1;
} nr14_t;
typedef struct nr21_s
{
uint8_t length_load:6;
uint8_t duty:2;
} nr21_t;
typedef struct nr22_s
{
uint8_t period:3;
uint8_t add:1;
uint8_t volume:4;
} nr22_t;
typedef struct nr23_s
{
uint8_t frequency_lsb;
} nr23_t;
typedef struct nr24_s
{
uint8_t frequency_msb:3;
uint8_t spare:3;
uint8_t length_enable:1;
uint8_t trigger:1;
} nr24_t;
typedef struct nr30_s
{
uint8_t spare:7;
uint8_t dac:1;
} nr30_t;
typedef struct nr31_s
{
uint8_t length_load;
} nr31_t;
typedef struct nr32_s
{
uint8_t spare:5;
uint8_t volume_code:2;
uint8_t spare2:1;
} nr32_t;
typedef struct nr33_s
{
uint8_t frequency_lsb;
} nr33_t;
typedef struct nr34_s
{
uint8_t frequency_msb:3;
uint8_t spare:3;
uint8_t length_enable:1;
uint8_t trigger:1;
} nr34_t;
typedef struct nr41_s
{
uint8_t length_load:6;
uint8_t spare:2;
} nr41_t;
typedef struct nr42_s
{
uint8_t period:3;
uint8_t add:1;
uint8_t volume:4;
} nr42_t;
typedef struct nr43_s
{
uint8_t divisor:3;
uint8_t width:1;
uint8_t shift:4;
} nr43_t;
typedef struct nr44_s
{
uint8_t spare:6;
uint8_t length_enable:1;
uint8_t trigger:1;
} nr44_t;
typedef struct nr50_s
{
uint8_t so1_volume:3;
uint8_t vin_to_so1:1;
uint8_t so2_volume:3;
uint8_t vin_to_so2:1;
} nr50_t;
typedef struct nr51_s
{
uint8_t ch1_to_so1:1;
uint8_t ch2_to_so1:1;
uint8_t ch3_to_so1:1;
uint8_t ch4_to_so1:1;
uint8_t ch1_to_so2:1;
uint8_t ch2_to_so2:1;
uint8_t ch3_to_so2:1;
uint8_t ch4_to_so2:1;
} nr51_t;
typedef struct nr52_s
{
uint8_t spare:7;
uint8_t power:1;
} nr52_t;
typedef struct channel_square_s
{
uint8_t active;
uint8_t duty;
uint8_t duty_idx;
uint8_t envelope_cnt;
uint_fast16_t duty_cycles;
uint_fast16_t duty_cycles_next;
uint_fast32_t length;
uint_fast32_t frequency;
int16_t sample;
int16_t spare;
uint_fast16_t sweep_active;
uint_fast16_t sweep_cnt;
uint_fast16_t sweep_neg;
uint_fast16_t sweep_next;
int16_t volume;
int16_t spare2;
uint32_t sweep_shadow_frequency;
} channel_square_t;
typedef struct channel_wave_s
{
uint8_t active;
uint8_t index;
uint16_t ram_access;
int16_t sample;
int16_t spare;
int16_t wave[32];
uint_fast32_t cycles;
uint_fast32_t cycles_next;
uint_fast32_t ram_access_next;
uint_fast32_t length;
} channel_wave_t;
typedef struct channel_noise_s
{
uint8_t active;
uint8_t envelope_cnt;
uint16_t spare;
uint_fast32_t length;
uint_fast16_t period_lfsr;
uint_fast32_t cycles_next;
int16_t volume;
int16_t sample;
uint16_t reg;
uint16_t spare2;
} channel_noise_t;
typedef struct sound_s
{
nr10_t *nr10;
nr11_t *nr11;
nr12_t *nr12;
nr13_t *nr13;
nr14_t *nr14;
nr21_t *nr21;
nr22_t *nr22;
nr23_t *nr23;
nr24_t *nr24;
nr30_t *nr30;
nr31_t *nr31;
nr32_t *nr32;
nr33_t *nr33;
nr34_t *nr34;
nr41_t *nr41;
nr42_t *nr42;
nr43_t *nr43;
nr44_t *nr44;
nr50_t *nr50;
nr51_t *nr51;
nr52_t *nr52;
uint8_t *wave_table;
channel_square_t channel_one;
channel_square_t channel_two;
channel_wave_t channel_three;
channel_noise_t channel_four;
/* emulation speed stuff */
uint_fast16_t frame_counter;
uint_fast16_t frame_multiplier;
/* circular audio buffer stuff */
uint_fast16_t buf_rd;
uint_fast16_t buf_wr;
uint_fast16_t buf_available;
uint_fast16_t buf_empty;
uint_fast16_t buf_full;
int16_t buf[SOUND_BUF_SZ];
int16_t buf_tmp[SOUND_BUF_TMP_SZ];
uint_fast16_t buf_tmp_wr;
/* output rate */
uint_fast32_t output_rate;
/* CPU cycles to internal cycles counters */
uint_fast32_t fs_cycles;
uint_fast32_t fs_cycles_idx;
uint_fast32_t fs_cycles_next;
uint_fast32_t sample_cycles;
uint_fast32_t sample_cycles_remainder;
uint_fast32_t sample_cycles_next;
uint_fast32_t sample_cycles_next_rounded;
/* steps length */
uint_fast32_t step_int;
uint_fast32_t step_int1000;
uint_fast32_t spare;
uint_fast32_t spare2;
} sound_t;
extern sound_t sound;
/* prototypes */
void sound_change_emulation_speed();
int sound_get_samples();
void sound_init();
void sound_read_buffer(void *userdata, uint8_t *stream, int snd_len);
uint8_t sound_read_reg(uint16_t a, uint8_t v);
void sound_restore_stat(FILE *fp);
void sound_save_stat(FILE *fp);
void sound_set_speed(char dbl);
void sound_set_output_rate(int freq);
void sound_step_fs();
void sound_step_ch1();
void sound_step_ch2();
void sound_step_ch3();
void sound_step_ch4();
void sound_step_sample();
void sound_term();
void sound_write_reg(uint16_t a, uint8_t v);
#endif

View File

@ -0,0 +1,8 @@
//
// Created by DVDN on 14/07/2016.
//
#ifndef PIZZABOY_TESTONE_H
#define PIZZABOY_TESTONE_H
#endif //PIZZABOY_TESTONE_H

View File

@ -0,0 +1,79 @@
/*
This file is part of Emu-Pizza
Emu-Pizza is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Emu-Pizza is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Emu-Pizza. If not, see <http://www.gnu.org/licenses/>.
*/
#include "cycles.h"
#include "interrupt.h"
#include "mmu.h"
#include "timer.h"
/* pointer to interrupt flags (handy) */
interrupts_flags_t *timer_if;
void timer_init()
{
/* reset values */
timer.next = 256;
timer.sub = 0;
/* pointer to interrupt flags */
timer_if = mmu_addr(0xFF0F);
}
void timer_write_reg(uint16_t a, uint8_t v)
{
switch (a)
{
case 0xFF04: timer.div = 0; return;
case 0xFF05: timer.cnt = v; return;
case 0xFF06: timer.mod = v; return;
case 0xFF07: timer.ctrl = v;
}
if (timer.ctrl & 0x04)
timer.active = 1;
else
timer.active = 0;
switch (timer.ctrl & 0x03)
{
case 0x00: timer.threshold = 1024; break;
case 0x01: timer.threshold = 16; break;
case 0x02: timer.threshold = 64; break;
case 0x03: timer.threshold = 256; break;
}
if (timer.active)
timer.sub_next = cycles.cnt + timer.threshold;
}
uint8_t timer_read_reg(uint16_t a)
{
switch (a)
{
case 0xFF04: return timer.div;
case 0xFF05: return timer.cnt;
case 0xFF06: return timer.mod;
case 0xFF07: return timer.ctrl;
}
return 0xFF;
}

View File

@ -0,0 +1,65 @@
/*
This file is part of Emu-Pizza
Emu-Pizza is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Emu-Pizza is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Emu-Pizza. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __TIMER_HDR__
#define __TIMER_HDR__
#include <stdint.h>
/* timer status */
typedef struct timer_gb_s
{
/* is it active? */
uint8_t active;
/* divider - 0xFF04 */
uint8_t div;
/* modulo - 0xFF06 */
uint8_t mod;
/* control - 0xFF07 */
uint8_t ctrl;
/* counter - 0xFF05 */
uint_fast32_t cnt;
/* threshold */
uint32_t threshold;
/* current value */
uint_fast32_t sub;
uint_fast32_t next;
/* spare */
uint_fast32_t sub_next;
uint_fast32_t spare2;
} timer_gb_t;
/* global status of timer */
timer_gb_t timer;
/* prototypes */
void timer_init();
void timer_step();
void timer_write_reg(uint16_t a, uint8_t v);
uint8_t timer_read_reg(uint16_t a);
#endif

139
waterbox/pizza/lib/utils.c Normal file
View File

@ -0,0 +1,139 @@
/*
This file is part of Emu-Pizza
Emu-Pizza is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Emu-Pizza is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Emu-Pizza. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef __ANDROID__
#include <android/log.h>
#else
#include <stdio.h>
#endif
#include <errno.h>
#include <stdarg.h>
#include <sys/time.h>
#include "cycles.h"
#include "gpu.h"
#include "utils.h"
uint32_t prev_cycles = 0;
void utils_log(const char *format, ...)
{
char buf[256];
va_list args;
va_start(args, format);
#ifdef __ANDROID__
vsnprintf(buf, 256, format, args);
__android_log_write(ANDROID_LOG_INFO, "Pizza", buf);
#else
vsnprintf(buf, 256, format, args);
printf(buf);
#endif
va_end(args);
}
void utils_log_urgent(const char *format, ...)
{
char buf[256];
va_list args;
va_start(args, format);
#ifdef __ANDROID__
vsnprintf(buf, 256, format, args);
__android_log_write(ANDROID_LOG_INFO, "Pizza", buf);
#else
vsnprintf(buf, 256, format, args);
printf(buf);
#endif
va_end(args);
}
void utils_ts_log(const char *format, ...)
{
va_list args;
va_start(args, format);
char buf[256];
struct timeval tv;
#ifdef __ANDROID__
vsnprintf(buf, 256, format, args);
__android_log_write(ANDROID_LOG_INFO, "Pizza", buf);
#else
vsprintf(buf, format, args);
gettimeofday(&tv, NULL);
// printf("%ld - %s\n", tv.tv_sec, buf);
printf("LINE %u - CYCLES %u - DIFF %u - %ld:%06ld - %s",
*(gpu.ly), cycles.cnt, cycles.cnt - prev_cycles,
tv.tv_sec, tv.tv_usec, buf);
prev_cycles = cycles.cnt;
#endif
va_end(args);
}
void utils_binary_sem_init(utils_binary_sem_t *p)
{
pthread_mutex_init(&p->mutex, NULL);
pthread_cond_init(&p->cvar, NULL);
p->v = 0;
}
void utils_binary_sem_post(utils_binary_sem_t *p)
{
pthread_mutex_lock(&p->mutex);
p->v = 1;
pthread_cond_signal(&p->cvar);
pthread_mutex_unlock(&p->mutex);
}
void utils_binary_sem_wait(utils_binary_sem_t *p, unsigned int nanosecs)
{
struct timespec ts;
ts.tv_sec = time(NULL) + nanosecs / 1000000000;
ts.tv_nsec = nanosecs % 1000000000;
pthread_mutex_lock(&p->mutex);
while (!p->v)
if (pthread_cond_timedwait(&p->cvar, &p->mutex, &ts) == ETIMEDOUT)
break;
p->v = 0;
pthread_mutex_unlock(&p->mutex);
}

View File

@ -0,0 +1,42 @@
/*
This file is part of Emu-Pizza
Emu-Pizza is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Emu-Pizza is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Emu-Pizza. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __UTILS_HDR__
#define __UTILS_HDR__
#include <pthread.h>
/* binary semaphore */
typedef struct utils_binary_sem_s
{
pthread_mutex_t mutex;
pthread_cond_t cvar;
int v;
} utils_binary_sem_t;
/* prototypes */
void utils_binary_sem_init(utils_binary_sem_t *p);
void utils_binary_sem_post(utils_binary_sem_t *p);
void utils_binary_sem_wait(utils_binary_sem_t *p, unsigned int nanosecs);
void utils_log(const char *format, ...);
void utils_log_urgent(const char *format, ...);
void utils_ts_log(const char *format, ...);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,48 @@
/*
This file is part of Emu-Pizza
Emu-Pizza is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Emu-Pizza is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Emu-Pizza. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef Z80_REGS_H
#define Z80_REGS_H
#include <stdint.h>
/* structs emulating z80 registers and flags */
typedef struct z80_flags_s
{
uint8_t spare:4;
uint8_t cy:1;
uint8_t ac:1;
uint8_t n:1;
uint8_t z:1;
} z80_flags_t;
/* flags offsets */
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#define FLAG_OFFSET_CY 4
#define FLAG_OFFSET_AC 5
#define FLAG_OFFSET_N 6
#define FLAG_OFFSET_Z 7
#endif
#endif

419
waterbox/pizza/pizza.c Normal file
View File

@ -0,0 +1,419 @@
/*
This file is part of Emu-Pizza
Emu-Pizza is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Emu-Pizza is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Emu-Pizza. If not, see <http://www.gnu.org/licenses/>.
*/
#include <errno.h>
#include <SDL2/SDL.h>
#include <stdio.h>
#include <stdint.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "cartridge.h"
#include "cycles.h"
#include "gameboy.h"
#include "global.h"
#include "gpu.h"
#include "input.h"
#include "network.h"
#include "sound.h"
#include "serial.h"
/* proto */
void cb();
void connected_cb();
void disconnected_cb();
void rumble_cb(uint8_t rumble);
void network_send_data(uint8_t v);
void *start_thread(void *args);
void *start_thread_network(void *args);
/* frame buffer pointer */
uint16_t *fb;
/* magnify rate */
float magnify_rate = 1.f;
/* emulator thread */
pthread_t thread;
/* SDL video stuff */
SDL_Window *window;
SDL_Surface *screenSurface;
SDL_Surface *windowSurface;
/* cartridge name */
char cart_name[64];
int main(int argc, char **argv)
{
/* SDL variables */
SDL_Event e;
SDL_AudioSpec desired;
SDL_AudioSpec obtained;
/* init global variables */
global_init();
/* set global folder */
snprintf(global_save_folder, sizeof(global_save_folder), "/tmp/str/save/");
__mkdirp(global_save_folder, S_IRWXU);
/* first, load cartridge */
char ret = cartridge_load(argv[1]);
if (ret != 0)
return 1;
/* apply cheat */
/* tetris */
/* mmu_set_cheat("00063D6E9");
mmu_set_cheat("3E064D5D0");
mmu_set_cheat("04065D087"); */
/* samurai shodown */
// mmu_set_cheat("11F86E3B6");
//)
// mmu_set_cheat("3EB60D7F1");
//
/* gameshark aladdin */
// mmu_set_cheat("01100ADC");
/* gameshark wario land */
// mmu_set_cheat("809965A9");
// mmu_apply_gg();
/* initialize SDL video */
if (SDL_Init(SDL_INIT_VIDEO) < 0 )
{
printf( "SDL could not initialize! SDL_Error: %s\n", SDL_GetError() );
return 1;
}
window = SDL_CreateWindow("Emu Pizza - Gameboy",
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
160 * magnify_rate, 144 * magnify_rate,
SDL_WINDOW_SHOWN);
/* get window surface */
windowSurface = SDL_GetWindowSurface(window);
screenSurface = SDL_ConvertSurfaceFormat(windowSurface,
SDL_PIXELFORMAT_RGB565,
0);
gameboy_init();
/* initialize SDL audio */
SDL_Init(SDL_INIT_AUDIO);
desired.freq = 44100;
desired.samples = SOUND_SAMPLES / 2;
desired.format = AUDIO_S16SYS;
desired.channels = 2;
desired.callback = sound_read_buffer;
desired.userdata = NULL;
/* Open audio */
if (SDL_OpenAudio(&desired, &obtained) == 0)
SDL_PauseAudio(0);
else
{
printf("Cannot open audio device!!\n");
return 1;
}
/* init GPU */
gpu_init(&cb);
/* set sound output rate */
sound_set_output_rate(44100);
/* set rumble cb */
mmu_set_rumble_cb(&rumble_cb);
/* get frame buffer reference */
fb = gpu_get_frame_buffer();
/* start thread! */
pthread_create(&thread, NULL, start_thread, NULL);
/* start network thread! */
network_start(&connected_cb, &disconnected_cb, "192.168.100.255");
/* loop forever */
while (!global_quit)
{
/* aaaaaaaaaaaaaand finally, check for SDL events */
/* SDL_WaitEvent should be better but somehow, */
/* it interfer with my cycles timer */
if (SDL_PollEvent(&e) == 0)
{
usleep(100000);
continue;
}
switch (e.type)
{
case SDL_QUIT:
global_quit = 1;
break;
case SDL_KEYDOWN:
switch (e.key.keysym.sym)
{
case (SDLK_1): gameboy_set_pause(1);
gameboy_save_stat(0);
gameboy_set_pause(0);
break;
case (SDLK_2): gameboy_set_pause(1);
gameboy_restore_stat(0);
gameboy_set_pause(0);
break;
case (SDLK_9): network_start(&connected_cb,
&disconnected_cb,
"192.168.100.255"); break;
case (SDLK_0): network_stop(); break;
case (SDLK_q): global_quit = 1; break;
case (SDLK_d): global_debug ^= 0x01; break;
case (SDLK_s): global_slow_down = 1; break;
case (SDLK_w): global_window ^= 0x01; break;
case (SDLK_n): gameboy_set_pause(0);
global_next_frame = 1; break;
case (SDLK_PLUS):
if (global_emulation_speed !=
GLOBAL_EMULATION_SPEED_4X)
{
global_emulation_speed++;
cycles_change_emulation_speed();
sound_change_emulation_speed();
}
break;
case (SDLK_MINUS):
if (global_emulation_speed !=
GLOBAL_EMULATION_SPEED_QUARTER)
{
global_emulation_speed--;
cycles_change_emulation_speed();
sound_change_emulation_speed();
}
break;
case (SDLK_p): gameboy_set_pause(global_pause ^ 0x01);
break;
case (SDLK_m): mmu_dump_all(); break;
case (SDLK_SPACE): input_set_key_select(1); break;
case (SDLK_RETURN): input_set_key_start(1); break;
case (SDLK_UP): input_set_key_up(1); break;
case (SDLK_DOWN): input_set_key_down(1); break;
case (SDLK_RIGHT): input_set_key_right(1); break;
case (SDLK_LEFT): input_set_key_left(1); break;
case (SDLK_z): input_set_key_b(1); break;
case (SDLK_x): input_set_key_a(1); break;
}
break;
case SDL_KEYUP:
switch (e.key.keysym.sym)
{
case (SDLK_SPACE): input_set_key_select(0); break;
case (SDLK_RETURN): input_set_key_start(0); break;
case (SDLK_UP): input_set_key_up(0); break;
case (SDLK_DOWN): input_set_key_down(0); break;
case (SDLK_RIGHT): input_set_key_right(0); break;
case (SDLK_LEFT): input_set_key_left(0); break;
case (SDLK_z): input_set_key_b(0); break;
case (SDLK_x): input_set_key_a(0); break;
}
break;
}
}
/* join emulation thread */
pthread_join(thread, NULL);
/* stop network thread! */
network_stop();
utils_log("Total cycles %d\n", cycles.cnt);
utils_log("Total running seconds %d\n", cycles.seconds);
return 0;
}
void *start_thread(void *args)
{
/* run until break or global_quit is set */
gameboy_run();
/* tell main thread it's over */
global_quit = 1;
}
void cb()
{
uint16_t *pixel = screenSurface->pixels;
/* magnify! */
if (magnify_rate > 1)
{
int x,y,p;
float px, py = 0;
uint16_t *line = malloc(sizeof(uint16_t) * 160 * magnify_rate);
for (y=0; y<144; y++)
{
px = 0;
for (x=0; x<160; x++)
{
for (; px<magnify_rate; px++)
line[(int) (px + (x * magnify_rate))] =
fb[x + (y * 160)];
px -= magnify_rate;
}
for (; py<magnify_rate; py++)
memcpy(&pixel[(int) (((y * magnify_rate) + py) *
160 * magnify_rate)],
line, sizeof(uint16_t) * 160 * magnify_rate);
py -= magnify_rate;
}
free(line);
}
else
{
/* just copy GPU frame buffer into SDL frame buffer */
memcpy(pixel, fb, 160 * 144 * sizeof(uint16_t));
}
SDL_ConvertPixels(screenSurface->w, screenSurface->h,
screenSurface->format->format,
screenSurface->pixels, screenSurface->pitch,
SDL_PIXELFORMAT_ARGB8888,
windowSurface->pixels, windowSurface->pitch);
/* Update the surface */
SDL_UpdateWindowSurface(window);
}
void connected_cb()
{
utils_log("Connected\n");
}
void disconnected_cb()
{
utils_log("Disconnected\n");
}
void rumble_cb(uint8_t rumble)
{
if (rumble)
printf("RUMBLE\n");
}
/*
* Returns 1 if a directory has been created,
* 2 if it already existed, and 0 on failure.
*/
int __mkdirp (char *path, mode_t omode)
{
struct stat sb;
mode_t numask, oumask;
int first, last, retval;
char *p;
p = path;
oumask = 0;
retval = 1;
if (p[0] == '/') /* Skip leading '/'. */
++p;
for (first = 1, last = 0; !last ; ++p)
{
if (p[0] == '\0')
last = 1;
else if (p[0] != '/')
continue;
*p = '\0';
if (!last && p[1] == '\0')
last = 1;
if (first)
{
oumask = umask(0);
numask = oumask & ~(S_IWUSR | S_IXUSR);
(void) umask(numask);
first = 0;
}
if (last)
(void) umask(oumask);
if (mkdir(path, last ? omode : S_IRWXU | S_IRWXG | S_IRWXO) < 0)
{
if (errno == EEXIST || errno == EISDIR)
{
if (stat(path, &sb) < 0)
{
retval = 0;
break;
}
else if (!S_ISDIR(sb.st_mode))
{
if (last)
errno = EEXIST;
else
errno = ENOTDIR;
retval = 0;
break;
}
if (last)
retval = 2;
}
else
{
retval = 0;
break;
}
}
if (!last)
*p = '/';
}
if (!first && !last)
(void) umask(oumask);
return retval;
}