diff --git a/waterbox/emulibc/libemuhost.so b/waterbox/emulibc/libemuhost.so
new file mode 100644
index 0000000000..ba581bfe47
Binary files /dev/null and b/waterbox/emulibc/libemuhost.so differ
diff --git a/waterbox/pizza/Makefile b/waterbox/pizza/Makefile
new file mode 100644
index 0000000000..6e56488b97
--- /dev/null
+++ b/waterbox/pizza/Makefile
@@ -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
diff --git a/waterbox/pizza/README.md b/waterbox/pizza/README.md
new file mode 100644
index 0000000000..edc069a750
--- /dev/null
+++ b/waterbox/pizza/README.md
@@ -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
diff --git a/waterbox/pizza/lib/Makefile b/waterbox/pizza/lib/Makefile
new file mode 100644
index 0000000000..2ad536bfff
--- /dev/null
+++ b/waterbox/pizza/lib/Makefile
@@ -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
diff --git a/waterbox/pizza/lib/cartridge.c b/waterbox/pizza/lib/cartridge.c
new file mode 100644
index 0000000000..44ecf42584
--- /dev/null
+++ b/waterbox/pizza/lib/cartridge.c
@@ -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 .
+
+*/
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#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);
+}
diff --git a/waterbox/pizza/lib/cartridge.h b/waterbox/pizza/lib/cartridge.h
new file mode 100644
index 0000000000..e8781c5f8c
--- /dev/null
+++ b/waterbox/pizza/lib/cartridge.h
@@ -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 .
+
+*/
+
+#ifndef __CARTRIDGE_HDR__
+#define __CARTRIDGE_HDR__
+
+#include
+
+/* prototypes */
+char cartridge_load(char *file_nm);
+void cartridge_term();
+
+#endif
diff --git a/waterbox/pizza/lib/cycles.c b/waterbox/pizza/lib/cycles.c
new file mode 100644
index 0000000000..559d4e4664
--- /dev/null
+++ b/waterbox/pizza/lib/cycles.c
@@ -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 .
+
+*/
+
+#include
+#include
+#include
+#include
+
+#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();
+}
+
diff --git a/waterbox/pizza/lib/cycles.h b/waterbox/pizza/lib/cycles.h
new file mode 100644
index 0000000000..2369166b6f
--- /dev/null
+++ b/waterbox/pizza/lib/cycles.h
@@ -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 .
+
+*/
+
+#ifndef __CYCLES_HDR__
+#define __CYCLES_HDR__
+
+#include
+#include
+
+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
diff --git a/waterbox/pizza/lib/gameboy.c b/waterbox/pizza/lib/gameboy.c
new file mode 100644
index 0000000000..f628a7ad83
--- /dev/null
+++ b/waterbox/pizza/lib/gameboy.c
@@ -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 .
+
+*/
+
+#include
+#include
+#include
+#include
+#include
+#include
+#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;
+}
+
diff --git a/waterbox/pizza/lib/gameboy.h b/waterbox/pizza/lib/gameboy.h
new file mode 100644
index 0000000000..3aedf6be03
--- /dev/null
+++ b/waterbox/pizza/lib/gameboy.h
@@ -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 .
+
+*/
+
+#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
diff --git a/waterbox/pizza/lib/global.c b/waterbox/pizza/lib/global.c
new file mode 100644
index 0000000000..bc2cd623ae
--- /dev/null
+++ b/waterbox/pizza/lib/global.c
@@ -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 .
+
+*/
+
+#include
+#include
+
+#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");
+}
diff --git a/waterbox/pizza/lib/global.h b/waterbox/pizza/lib/global.h
new file mode 100644
index 0000000000..020455c8d0
--- /dev/null
+++ b/waterbox/pizza/lib/global.h
@@ -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 .
+
+*/
+
+#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
diff --git a/waterbox/pizza/lib/gpu.c b/waterbox/pizza/lib/gpu.c
new file mode 100644
index 0000000000..dfeb2df046
--- /dev/null
+++ b/waterbox/pizza/lib/gpu.c
@@ -0,0 +1,1179 @@
+/*
+
+ 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 .
+
+*/
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "cycles.h"
+#include "gameboy.h"
+#include "global.h"
+#include "gpu.h"
+#include "interrupt.h"
+#include "mmu.h"
+#include "utils.h"
+
+/* Gameboy OAM 4 bytes data */
+typedef struct gpu_oam_s
+{
+ uint8_t y;
+ uint8_t x;
+ uint8_t pattern;
+
+ uint8_t palette_cgb:3;
+ uint8_t vram_bank:1;
+ uint8_t palette:1;
+ uint8_t x_flip:1;
+ uint8_t y_flip:1;
+ uint8_t priority:1;
+
+} gpu_oam_t;
+
+/* Gameboy Color additional tile attributes */
+typedef struct gpu_cgb_bg_tile_s
+{
+ uint8_t palette:3;
+ uint8_t vram_bank:1;
+ uint8_t spare:1;
+ uint8_t x_flip:1;
+ uint8_t y_flip:1;
+ uint8_t priority:1;
+
+} gpu_cgb_bg_tile_t;
+
+/* ordered sprite list */
+typedef struct oam_list_s
+{
+ int idx;
+ struct oam_list_s *next;
+} oam_list_t;
+
+/* pointer to interrupt flags (handy) */
+interrupts_flags_t *gpu_if;
+
+/* internal functions prototypes */
+void gpu_draw_sprite_line(gpu_oam_t *oam,
+ uint8_t sprites_size,
+ uint8_t line);
+void gpu_draw_window_line(int tile_idx, uint8_t frame_x,
+ uint8_t frame_y, uint8_t line);
+
+/* 2 bit to 8 bit color lookup */
+static uint16_t gpu_color_lookup[] = { 0xFFFF, 0xAD55, 0x52AA, 0x0000 };
+
+/* function to call when frame is ready */
+gpu_frame_ready_cb_t gpu_frame_ready_cb;
+
+/* global state of GPU */
+gpu_t gpu;
+
+
+void gpu_dump_oam()
+{
+ /* make it point to the first OAM object */
+ gpu_oam_t *oam = (gpu_oam_t *) mmu_addr(0xFE00);
+
+ int i;
+
+ for (i=0; i<40; i++)
+ {
+ if (oam[i].x != 0 && oam[i].y != 0)
+ printf("OAM X %d Y %d VRAM %d PATTERN %d\n", oam[i].x, oam[i].y,
+ oam[i].vram_bank,
+ oam[i].pattern);
+ }
+}
+
+
+/* init pointers */
+void gpu_init_pointers()
+{
+ /* make gpu field points to the related memory area */
+ gpu.lcd_ctrl = mmu_addr(0xFF40);
+ gpu.lcd_status = mmu_addr(0xFF41);
+ gpu.scroll_y = mmu_addr(0xFF42);
+ gpu.scroll_x = mmu_addr(0xFF43);
+ gpu.window_y = mmu_addr(0xFF4A);
+ gpu.window_x = mmu_addr(0xFF4B);
+ gpu.ly = mmu_addr(0xFF44);
+ gpu.lyc = mmu_addr(0xFF45);
+ gpu_if = mmu_addr(0xFF0F);
+}
+
+/* reset */
+void gpu_reset()
+{
+ /* init counters */
+ gpu.next = 456 << global_cpu_double_speed;
+ gpu.frame_counter = 0;
+
+}
+
+/* init GPU states */
+void gpu_init(gpu_frame_ready_cb_t cb)
+{
+ /* reset gpu structure */
+ bzero(&gpu, sizeof(gpu_t));
+
+ /* init memory pointers */
+ gpu_init_pointers();
+
+ /* init counters */
+ gpu.next = 456 << global_cpu_double_speed;
+ gpu.frame_counter = 0;
+
+ /* step for normal CPU speed */
+ gpu.step = 4;
+
+ /* init palette */
+ memcpy(gpu.bg_palette, gpu_color_lookup, sizeof(uint16_t) * 4);
+ memcpy(gpu.obj_palette_0, gpu_color_lookup, sizeof(uint16_t) * 4);
+ memcpy(gpu.obj_palette_1, gpu_color_lookup, sizeof(uint16_t) * 4);
+
+ /* set callback */
+ gpu_frame_ready_cb = cb;
+}
+
+/* turn on/off lcd */
+void gpu_toggle(uint8_t state)
+{
+ /* from off to on */
+ if (state & 0x80)
+ {
+ /* LCD turned on */
+ gpu.next = cycles.cnt + (456 << global_cpu_double_speed);
+ *gpu.ly = 0;
+ (*gpu.lcd_status).mode = 0x00;
+ (*gpu.lcd_status).ly_coincidence = 0x00;
+ }
+ else
+ {
+ /* LCD turned off - reset stuff */
+ gpu.next = cycles.cnt - 1; // + (80 << global_cpu_double_speed);
+ *gpu.ly = 0;
+ (*gpu.lcd_status).mode = 0x00;
+ }
+}
+
+/* push frame on screen */
+void gpu_draw_frame()
+{
+ /* increase frame counter */
+ gpu.frame_counter++;
+
+ /* is it the case to push samples? */
+ if ((global_emulation_speed == GLOBAL_EMULATION_SPEED_DOUBLE &&
+ (gpu.frame_counter & 0x0001) != 0) ||
+ (global_emulation_speed == GLOBAL_EMULATION_SPEED_4X &&
+ (gpu.frame_counter & 0x0003) != 0))
+ return;
+
+ uint_fast32_t i,r,g,b,r2,g2,b2,res;
+
+ /* simulate shitty gameboy response time of LCD */
+ /* by calculating an average between current and previous frame */
+ //for (i=0; i<(144*160); i++)
+ for (i=0; i<(144 * 160); i++)
+ {
+/* r = gpu.frame_buffer[i] & 0x1F;
+ g = gpu.frame_buffer[i] >> 5 & 0x3F;
+ b = gpu.frame_buffer[i] >> 11 & 0x1F;
+
+ r2 = gpu.frame_buffer_prev[i] & 0x1F;
+ g2 = gpu.frame_buffer_prev[i] >> 5 & 0x3F;
+ b2 = gpu.frame_buffer_prev[i] >> 11 & 0x1F;
+
+ gpu.frame_buffer_prev[i] = gpu.frame_buffer[i];
+
+ gpu.frame_buffer[i] = (uint16_t) ((r + r2) >> 1) |
+ (((g + g2) >> 1) << 5) |
+ (((b + b2) >> 1) << 11);*/
+
+ r = gpu.frame_buffer[i] & 0x001F;
+ g = gpu.frame_buffer[i] & 0x07E0;
+ b = gpu.frame_buffer[i] & 0xF800;
+
+ r2 = gpu.frame_buffer_prev[i] & 0x001F;
+ g2 = gpu.frame_buffer_prev[i] & 0x07E0;
+ b2 = gpu.frame_buffer_prev[i] & 0xF800;
+
+ // gpu.frame_buffer_prev[i] = gpu.frame_buffer[i];
+
+ res = ((r + r2) >> 1) |
+ (((g + g2) >> 1) & 0x07E0) |
+ (((b + b2) >> 1) & 0xF800);
+
+ gpu.frame_buffer_prev[i] = gpu.frame_buffer[i];
+ gpu.frame_buffer[i] = res;
+ }
+
+ /* call the callback */
+ if (gpu_frame_ready_cb)
+ (*gpu_frame_ready_cb) ();
+
+ /* reset priority matrix */
+ bzero(gpu.priority, 160 * 144);
+ bzero(gpu.palette_idx, 160 * 144);
+
+ if (global_next_frame)
+ {
+ global_next_frame = 0;
+ gameboy_set_pause(1);
+ }
+
+ return;
+}
+
+/* get pointer to frame buffer */
+uint16_t *gpu_get_frame_buffer()
+{
+ return gpu.frame_buffer;
+}
+
+/* draw a single line */
+void gpu_draw_line(uint8_t line)
+{
+ /* avoid mess */
+ if (line > 144)
+ return;
+
+ /* is it the case to push samples? */
+ if ((global_emulation_speed == GLOBAL_EMULATION_SPEED_DOUBLE &&
+ (gpu.frame_counter & 0x0001) != 0) ||
+ (global_emulation_speed == GLOBAL_EMULATION_SPEED_4X &&
+ (gpu.frame_counter & 0x0003) != 0))
+ return;
+
+ int i, t, y, px_start, px_drawn;
+ uint8_t *tiles_map, tile_subline, palette_idx, x_flip, priority;
+ uint16_t tiles_addr, tile_n, tile_idx, tile_line;
+ uint16_t tile_y;
+
+ /* gotta show BG? Answer is always YES in case of Gameboy Color */
+ if ((*gpu.lcd_ctrl).bg || global_cgb)
+ {
+ gpu_cgb_bg_tile_t *tiles_map_cgb = NULL;
+ uint8_t *tiles = NULL;
+ uint16_t *palette;
+
+ if (global_cgb)
+ {
+ /* CGB tile map into VRAM0 */
+ tiles_map = mmu_addr_vram0() + ((*gpu.lcd_ctrl).bg_tiles_map ?
+ 0x1C00 : 0x1800);
+
+ /* additional attribute table is into VRAM1 */
+ tiles_map_cgb = mmu_addr_vram1() + ((*gpu.lcd_ctrl).bg_tiles_map ?
+ 0x1C00 : 0x1800);
+ }
+ else
+ {
+ /* never flip */
+ x_flip = 0;
+
+ /* get tile map offset */
+ tiles_map = mmu_addr((*gpu.lcd_ctrl).bg_tiles_map ?
+ 0x9C00 : 0x9800);
+
+ if ((*gpu.lcd_ctrl).bg_tiles)
+ tiles_addr = 0x8000;
+ else
+ tiles_addr = 0x9000;
+
+ /* get absolute address of tiles area */
+ tiles = mmu_addr(tiles_addr);
+
+ /* monochrome GB uses a single BG palette */
+ palette = gpu.bg_palette;
+
+ /* always priority = 0 */
+ priority = 0;
+ }
+
+ /* calc tile y */
+ tile_y = (*(gpu.scroll_y) + line) & 0xFF;
+
+ /* calc first tile idx */
+ tile_idx = ((tile_y >> 3) * 32) + (*(gpu.scroll_x) / 8);
+
+ /* tile line because if we reach the end of the line, */
+ /* we have to rewind to the first tile of the same line */
+ tile_line = ((tile_y >> 3) * 32);
+
+ /* calc first pixel of frame buffer of the current line */
+ uint_fast16_t pos_fb = line * 160;
+ uint_fast16_t pos;
+
+ /* calc tile subline */
+ tile_subline = tile_y % 8;
+
+ /* walk through different tiles */
+ for (t=0; t<21; t++)
+ {
+ /* resolv tile data memory area */
+ if ((*gpu.lcd_ctrl).bg_tiles == 0)
+ tile_n = (int8_t) tiles_map[tile_idx];
+ else
+ tile_n = (tiles_map[tile_idx] & 0x00FF);
+
+ /* if color gameboy, resolv which palette is bound */
+ if (global_cgb)
+ {
+ /* extract palette index (0-31) */
+ palette_idx = tiles_map_cgb[tile_idx].palette;
+
+ /* get palette pointer to 4 (16bit) colors */
+ palette =
+ (uint16_t *) &gpu.cgb_palette_bg_rgb565[palette_idx * 4];
+
+ /* get priority of the tile */
+ priority = tiles_map_cgb[tile_idx].priority;
+
+ if (tiles_map_cgb[tile_idx].vram_bank)
+ tiles = mmu_addr_vram1() +
+ ((*gpu.lcd_ctrl).bg_tiles ? 0x0000 : 0x1000);
+ else
+ tiles = mmu_addr_vram0() +
+ ((*gpu.lcd_ctrl).bg_tiles ? 0x0000 : 0x1000);
+
+ /* calc subline in case of flip_y */
+ if (tiles_map_cgb[tile_idx].y_flip)
+ tile_subline = 7 - (tile_y % 8);
+ else
+ tile_subline = tile_y % 8;
+
+ /* save x_flip */
+ x_flip = tiles_map_cgb[tile_idx].x_flip;
+ }
+
+ /* calc tile data pointer */
+ int16_t tile_ptr = (tile_n * 16) + (tile_subline * 2);
+
+ /* pixels are handled in a super shitty way */
+ /* bit 0 of the pixel is taken from even position tile bytes */
+ /* bit 1 of the pixel is taken from odd position tile bytes */
+
+ uint8_t pxa[8];
+ uint8_t shft;
+ uint8_t b1 = *(tiles + tile_ptr);
+ uint8_t b2 = *(tiles + tile_ptr + 1);
+
+ for (y=0; y<8; y++)
+ {
+ if (x_flip)
+ shft = (1 << (7 - y));
+ else
+ shft = (1 << y);
+
+ pxa[y] = ((b1 & shft) ? 1 : 0) |
+ ((b2 & shft) ? 2 : 0);
+ }
+
+ /* particular cases for first and last tile */
+ /* (could be shown just a part) */
+ if (t == 0)
+ {
+ px_start = (*(gpu.scroll_x) % 8);
+
+ px_drawn = 8 - px_start;
+
+ /* set n pixels */
+ for (i=0; i= (oam[i].y - 16))
+ {
+ /* color GB uses memory position as priority criteria */
+ if (global_cgb)
+ {
+ sort[j++] = i;
+ continue;
+ }
+
+ /* find its position on sort array */
+ for (j=0; j<40; j++)
+ {
+ if (sort[j] == -1)
+ {
+ sort[j] = i;
+ break;
+ }
+
+ if (global_cgb)
+ continue;
+
+ if ((oam[i].y < oam[sort[j]].y) ||
+ ((oam[i].y == oam[sort[j]].y) &&
+ (oam[i].x < oam[sort[j]].x)))
+ {
+ int z;
+
+ for (z=40; z>j; z--)
+ sort[z] = sort[z-1];
+
+ sort[j] = i;
+ break;
+ }
+ }
+ }
+ }
+
+ /* draw ordered sprite list */
+ for (i=0; i<40 && sort[i] != -1; i++)
+ gpu_draw_sprite_line(&oam[sort[i]],
+ (*gpu.lcd_ctrl).sprites_size, line);
+
+ }
+
+ /* wanna show window? */
+ if (global_window && (*gpu.lcd_ctrl).window)
+ {
+ /* at least the current line is covering the window area? */
+ if (line < *(gpu.window_y))
+ return;
+
+ /* TODO - reset this in a better place */
+ if (line == *(gpu.window_y))
+ gpu.window_skipped_lines = 0;
+
+ int z, first_z;
+ uint8_t tile_pos_x, tile_pos_y;
+
+ /* gotta draw a window? check if it is inside screen coordinates */
+ if (*(gpu.window_y) >= 144 ||
+ *(gpu.window_x) >= 160)
+ {
+ gpu.window_skipped_lines++;
+ return;
+ }
+
+ /* calc the first interesting tile */
+ first_z = ((line - *(gpu.window_y) -
+ gpu.window_skipped_lines) >> 3) << 5;
+
+ for (z=first_z; z> 5) << 3) + *(gpu.window_y) +
+ gpu.window_skipped_lines;
+
+ /* gone over the current line? */
+ if (tile_pos_y > line)
+ break;
+
+ if (tile_pos_y < (line - 7))
+ continue;
+
+ /* gone over the screen visible X? */
+ /* being between last column and first one is valid */
+ if (tile_pos_x >= 160 && tile_pos_x < 248)
+ break;
+
+ /* gone over the screen visible section? stop it */
+ if (tile_pos_y >= 144) // || (tile_pos_x >= 160))
+ break;
+
+ /* put tile on frame buffer */
+ gpu_draw_window_line(z, (uint8_t) tile_pos_x,
+ (uint8_t) tile_pos_y, line);
+ }
+ }
+}
+
+
+
+/* draw a tile in x,y coordinates */
+void gpu_draw_window_line(int tile_idx, uint8_t frame_x,
+ uint8_t frame_y, uint8_t line)
+{
+ int i, p, y, pos;
+ int16_t tile_n;
+ uint8_t *tiles_map;
+ gpu_cgb_bg_tile_t *tiles_map_cgb = NULL;
+ uint8_t *tiles, x_flip;
+ uint16_t *palette;
+
+ if (global_cgb)
+ {
+ /* CGB tile map into VRAM0 */
+ tiles_map = mmu_addr_vram0() + ((*gpu.lcd_ctrl).window_tiles_map ?
+ 0x1C00 : 0x1800);
+
+ /* additional attribute table is into VRAM1 */
+ tiles_map_cgb = mmu_addr_vram1() + ((*gpu.lcd_ctrl).window_tiles_map ?
+ 0x1C00 : 0x1800);
+
+ /* get palette index */
+ uint8_t palette_idx = tiles_map_cgb[tile_idx].palette;
+ x_flip = tiles_map_cgb[tile_idx].x_flip;
+
+ /* get palette pointer to 4 (16bit) colors */
+ palette = (uint16_t *) &gpu.cgb_palette_bg_rgb565[palette_idx * 4];
+
+ /* attribute table will tell us where is the tile */
+ if (tiles_map_cgb[tile_idx].vram_bank)
+ tiles = mmu_addr_vram1() +
+ ((*gpu.lcd_ctrl).bg_tiles ? 0x0000 : 0x1000);
+ else
+ tiles = mmu_addr_vram0() +
+ ((*gpu.lcd_ctrl).bg_tiles ? 0x0000 : 0x1000);
+
+ }
+ else
+ {
+ /* get tile map offset */
+ tiles_map = mmu_addr((*gpu.lcd_ctrl).window_tiles_map ?
+ 0x9C00 : 0x9800);
+
+ /* get tile offset */
+ if ((*gpu.lcd_ctrl).bg_tiles)
+ tiles = mmu_addr(0x8000);
+ else
+ tiles = mmu_addr(0x9000);
+
+ /* monochrome GB uses a single BG palette */
+ palette = gpu.bg_palette;
+
+ /* never flip */
+ x_flip = 0;
+ }
+
+ /* obtain tile number */
+ if ((*gpu.lcd_ctrl).bg_tiles == 0)
+ tile_n = (int8_t) tiles_map[tile_idx];
+ else
+ tile_n = (tiles_map[tile_idx] & 0x00ff);
+
+ /* calc vertical offset INSIDE the tile */
+ p = (line - frame_y) * 2;
+
+ /* calc frame position buffer for 4 pixels */
+ uint32_t pos_fb = (line * 160);
+
+ /* calc tile pointer */
+ int16_t tile_ptr = (tile_n * 16) + p;
+
+ /* pixels are handled in a super shitty way */
+ /* bit 0 of the pixel is taken from even position tile bytes */
+ /* bit 1 of the pixel is taken from odd position tile bytes */
+
+ uint8_t pxa[8];
+ uint8_t shft;
+
+ for (y=0; y<8; y++)
+ {
+ //uint8_t shft = (1 << y);
+
+ if (x_flip)
+ shft = (1 << (7 - y));
+ else
+ shft = (1 << y);
+
+ pxa[y] = ((*(tiles + tile_ptr) & shft) ? 1 : 0) |
+ ((*(tiles + tile_ptr + 1) & shft) ? 2 : 0);
+ }
+
+ /* set 8 pixels (full tile line) */
+ for (i=0; i<8; i++)
+ {
+ /* over the last column? */
+ uint8_t x = frame_x + (7 - i);
+
+ if (x > 159)
+ continue;
+
+ /* calc position on frame buffer */
+ pos = pos_fb + x;
+
+ /* can overwrite sprites? depends on pixel priority */
+ if (gpu.priority[pos] != 0x02)
+ gpu.frame_buffer[pos] = palette[pxa[i]];
+ }
+}
+
+/* draw a sprite tile in x,y coordinates */
+void gpu_draw_sprite_line(gpu_oam_t *oam, uint8_t sprites_size, uint8_t line)
+{
+ int_fast32_t x, y, pos, fb_x, off;
+ uint_fast16_t p, i, j;
+ uint8_t sprite_bytes;
+ int16_t tile_ptr;
+ uint16_t *palette;
+ uint8_t *tiles;
+
+ /* is it the case to push samples? */
+/* if ((global_emulation_speed == GLOBAL_EMULATION_SPEED_DOUBLE &&
+ (gpu.frame_counter & 0x01) != 0) ||
+ (global_emulation_speed == GLOBAL_EMULATION_SPEED_4X &&
+ (gpu.frame_counter & 0x03) != 0))
+ return; */
+
+ /* REMEMBER! position of sprites is relative to the visible screen area */
+ /* ... and y is shifted by 16 pixels, x by 8 */
+ y = oam->y - 16;
+ x = oam->x - 8;
+
+ if (x < -7)
+ return;
+
+ /* first pixel on frame buffer position */
+ uint32_t tile_pos_fb = (y * 160) + x;
+
+ /* choose palette */
+ if (global_cgb)
+ {
+ uint8_t palette_idx = oam->palette_cgb;
+
+ /* get palette pointer to 4 (16bit) colors */
+ palette = (uint16_t *) &gpu.cgb_palette_oam_rgb565[palette_idx * 4];
+
+ /* tiles are into vram0 */
+ if (oam->vram_bank)
+ tiles = mmu_addr_vram1();
+ else
+ tiles = mmu_addr_vram0();
+ }
+ else
+ {
+ /* tiles are int fixed 0x8000 address */
+ tiles = mmu_addr(0x8000);
+
+ if (oam->palette)
+ palette = gpu.obj_palette_1;
+ else
+ palette = gpu.obj_palette_0;
+ }
+
+ /* calc sprite in byte */
+ sprite_bytes = 16 * (sprites_size + 1);
+
+ /* walk through 8x8 pixels (2bit per pixel -> 4 pixels per byte) */
+ /* 1 line is 8 pixels -> 2 bytes per line */
+ for (p=0; py_flip)
+ tile_ptr = (oam->pattern * 16) + (sprite_bytes - p - 2);
+ else
+ tile_ptr = (oam->pattern * 16) + p;
+
+ /* pixels are handled in a super shitty way */
+ /* bit 0 of the pixel is taken from even position tile bytes */
+ /* bit 1 of the pixel is taken from odd position tile bytes */
+
+ uint8_t pxa[8];
+
+ for (j=0; j<8; j++)
+ {
+ uint8_t shft = (1 << j);
+
+ pxa[j] = ((*(tiles + tile_ptr) & shft) ? 1 : 0) |
+ ((*(tiles + tile_ptr + 1) & shft) ? 2 : 0);
+ }
+
+ /* set 8 pixels (full tile line) */
+ for (i=0; i<8; i++)
+ {
+ if (oam->x_flip)
+ off = i;
+ else
+ off = 7 - i;
+
+ /* is it on screen? */
+ fb_x = x + off;
+
+ if (fb_x < 0 || fb_x > 160)
+ continue;
+
+ /* set serial position on frame buffer */
+ pos = pos_fb + off;
+
+ /* is it inside the screen? */
+ if (pos >= 144 * 160 || pos < 0)
+ continue;
+
+ if (global_cgb)
+ {
+ /* sprite color 0 = transparent */
+ if (pxa[i] != 0x00)
+ {
+ /* flag clr = sprites always on top of bg and window */
+ if ((*gpu.lcd_ctrl).bg == 0)
+ {
+ gpu.frame_buffer[pos] = palette[pxa[i]];
+ gpu.priority[pos] = 0x02;
+ }
+ else
+ {
+ if (((gpu.priority[pos] == 0) &&
+ (oam->priority == 0 ||
+ (oam->priority == 1 &&
+ gpu.palette_idx[pos] == 0x00))) ||
+ (gpu.priority[pos] == 1 &&
+ gpu.palette_idx[pos] == 0x00))
+ {
+ gpu.frame_buffer[pos] = palette[pxa[i]];
+ gpu.priority[pos] = (oam->priority ? 0x00 : 0x02);
+ }
+ }
+ }
+ }
+ else
+ {
+ /* push on screen pixels not set to zero (transparent) */
+ /* and if the priority is set to one, overwrite just */
+ /* bg pixels set to zero */
+ if ((pxa[i] != 0x00) &&
+ (oam->priority == 0 ||
+ (oam->priority == 1 &&
+ gpu.frame_buffer[pos] == gpu.bg_palette[0x00])))
+ {
+ gpu.frame_buffer[pos] = palette[pxa[i]];
+ gpu.priority[pos] = (oam->priority ? 0x00 : 0x02);
+ }
+ }
+ }
+ }
+}
+
+/* update GPU internal state given CPU T-states */
+void gpu_step()
+{
+ char ly_changed = 0;
+ char mode_changed = 0;
+
+ /* take different action based on current state */
+ switch((*gpu.lcd_status).mode)
+ {
+ /*
+ * during HBLANK (CPU can access VRAM)
+ */
+ case 0:
+ /* handle HDMA stuff during hblank */
+ cycles_hdma();
+
+ /*
+ * if current line == 143 (and it's about to turn 144)
+ * enter mode 01 (VBLANK)
+ */
+ if (*gpu.ly == 143)
+ {
+ /* notify mode has changes */
+ mode_changed = 1;
+
+ (*gpu.lcd_status).mode = 0x01;
+
+ /* mode one lasts 456 cycles */
+ gpu.next = cycles.cnt +
+ (456 << global_cpu_double_speed);
+
+ /* DRAW! TODO */
+ /* CHECK INTERRUPTS! TODO */
+ cycles_vblank();
+
+ /* set VBLANK interrupt flag */
+ gpu_if->lcd_vblank = 1;
+
+ /* apply gameshark patches */
+ mmu_apply_gs();
+
+ /* and finally push it on screen! */
+ gpu_draw_frame();
+ }
+ else
+ {
+ /* notify mode has changed */
+ mode_changed = 1;
+
+ /* enter OAM mode */
+ (*gpu.lcd_status).mode = 0x02;
+
+ /* mode 2 needs 80 cycles */
+ gpu.next = cycles.cnt +
+ (80 << global_cpu_double_speed);
+
+ }
+
+ /* notify mode has changed */
+ ly_changed = 1;
+
+ /* inc current line */
+ (*gpu.ly)++;
+
+// cycles_hblank(*gpu.ly);
+
+ break;
+
+ /*
+ * during VBLANK (CPU can access VRAM)
+ */
+ case 1:
+ /* notify ly has changed */
+ ly_changed = 1;
+
+ /* inc current line */
+ (*gpu.ly)++;
+
+ /* reached the bottom? */
+ if ((*gpu.ly) > 153)
+ {
+ /* go back to line 0 */
+ (*gpu.ly) = 0;
+
+ /* switch to OAM mode */
+ (*gpu.lcd_status).mode = 0x02;
+
+ /* */
+ gpu.next =
+ cycles.cnt + (80 << global_cpu_double_speed);
+ }
+ else
+ gpu.next =
+ cycles.cnt + (456 << global_cpu_double_speed);
+
+ break;
+
+ /*
+ * during OAM (LCD access FE00-FE90, so CPU cannot)
+ */
+ case 2:
+ /* reset clock counter */
+ gpu.next =
+ cycles.cnt + (172 << global_cpu_double_speed);
+
+ /* notify mode has changed */
+ mode_changed = 1;
+
+ /* switch to VRAM mode */
+ (*gpu.lcd_status).mode = 0x03;
+
+ break;
+
+ /*
+ * during VRAM (LCD access both OAM and VRAM, so CPU cannot)
+ */
+ case 3:
+ /* reset clock counter */
+ gpu.next =
+ cycles.cnt + (204 << global_cpu_double_speed);
+
+ /* notify mode has changed */
+ mode_changed = 1;
+
+ /* go back to HBLANK mode */
+ (*gpu.lcd_status).mode = 0x00;
+
+ /* draw line */
+ gpu_draw_line(*gpu.ly);
+
+ /* notify cycles */
+// cycles_hblank(*gpu.ly);
+
+ //printf("COLLA %d\n", *gpu.ly);
+
+ break;
+ }
+
+ /* ly changed? is it the case to trig an interrupt? */
+ if (ly_changed)
+ {
+ /* check if we gotta trigger an interrupt */
+ if ((*gpu.ly) == (*gpu.lyc))
+ {
+ /* set lcd status flags indicating there's a concidence */
+ (*gpu.lcd_status).ly_coincidence = 1;
+
+ /* an interrupt is desiderable? */
+ if ((*gpu.lcd_status).ir_ly_coincidence)
+ gpu_if->lcd_ctrl = 1;
+ }
+ else
+ {
+ /* set lcd status flags indicating there's NOT a concidence */
+ (*gpu.lcd_status).ly_coincidence = 0;
+ }
+ }
+
+ /* mode changed? is is the case to trig an interrupt? */
+ if (mode_changed)
+ {
+ if ((*gpu.lcd_status).mode == 0x00 &&
+ (*gpu.lcd_status).ir_mode_00)
+ gpu_if->lcd_ctrl = 1;
+ else if ((*gpu.lcd_status).mode == 0x01 &&
+ (*gpu.lcd_status).ir_mode_01)
+ gpu_if->lcd_ctrl = 1;
+ else if ((*gpu.lcd_status).mode == 0x02 &&
+ (*gpu.lcd_status).ir_mode_10)
+ gpu_if->lcd_ctrl = 1;
+ }
+}
+
+uint8_t gpu_read_reg(uint16_t a)
+{
+ switch (a)
+ {
+ case 0xFF68:
+
+ return (gpu.cgb_palette_bg_autoinc << 7 | gpu.cgb_palette_bg_idx);
+
+ case 0xFF69:
+
+ if ((gpu.cgb_palette_bg_idx & 0x01) == 0x00)
+ return gpu.cgb_palette_bg[gpu.cgb_palette_bg_idx / 2] &
+ 0x00ff;
+ else
+ return (gpu.cgb_palette_bg[gpu.cgb_palette_bg_idx / 2] &
+ 0xff00) >> 8;
+
+ case 0xFF6A:
+
+ return (gpu.cgb_palette_oam_autoinc << 7 | gpu.cgb_palette_oam_idx);
+
+ case 0xFF6B:
+
+ if ((gpu.cgb_palette_oam_idx & 0x01) == 0x00)
+ return gpu.cgb_palette_oam[gpu.cgb_palette_oam_idx / 2] &
+ 0x00ff;
+ else
+ return (gpu.cgb_palette_oam[gpu.cgb_palette_oam_idx / 2] &
+ 0xff00) >> 8;
+
+
+ }
+
+ return 0x00;
+}
+
+void gpu_write_reg(uint16_t a, uint8_t v)
+{
+ int i;
+ uint8_t r,g,b;
+
+ switch (a)
+ {
+ case 0xFF47:
+
+ gpu.bg_palette[0] = gpu_color_lookup[v & 0x03];
+ gpu.bg_palette[1] = gpu_color_lookup[(v & 0x0c) >> 2];
+ gpu.bg_palette[2] = gpu_color_lookup[(v & 0x30) >> 4];
+ gpu.bg_palette[3] = gpu_color_lookup[(v & 0xc0) >> 6];
+
+ break;
+
+ case 0xFF48:
+
+ gpu.obj_palette_0[0] = gpu_color_lookup[v & 0x03];
+ gpu.obj_palette_0[1] = gpu_color_lookup[(v & 0x0c) >> 2];
+ gpu.obj_palette_0[2] = gpu_color_lookup[(v & 0x30) >> 4];
+ gpu.obj_palette_0[3] = gpu_color_lookup[(v & 0xc0) >> 6];
+
+ break;
+
+ case 0xFF49:
+
+ gpu.obj_palette_1[0] = gpu_color_lookup[v & 0x03];
+ gpu.obj_palette_1[1] = gpu_color_lookup[(v & 0x0c) >> 2];
+ gpu.obj_palette_1[2] = gpu_color_lookup[(v & 0x30) >> 4];
+ gpu.obj_palette_1[3] = gpu_color_lookup[(v & 0xc0) >> 6];
+
+ break;
+
+ case 0xFF68:
+
+ gpu.cgb_palette_bg_idx = (v & 0x3f);
+ gpu.cgb_palette_bg_autoinc = ((v & 0x80) == 0x80);
+
+ break;
+
+ case 0xFF69:
+
+ i = gpu.cgb_palette_bg_idx / 2;
+
+ if ((gpu.cgb_palette_bg_idx & 0x01) == 0x00)
+ {
+ gpu.cgb_palette_bg[i] &= 0xff00;
+ gpu.cgb_palette_bg[i] |= v;
+ }
+ else
+ {
+ gpu.cgb_palette_bg[i] &= 0x00ff;
+ gpu.cgb_palette_bg[i] |= (v << 8);
+ }
+
+ r = gpu.cgb_palette_bg[i] & 0x1F;
+ g = gpu.cgb_palette_bg[i] >> 5 & 0x1F;
+ b = gpu.cgb_palette_bg[i] >> 10 & 0x1F;
+
+ gpu.cgb_palette_bg_rgb565[i] =
+ (((r * 13 + g * 2 + b + 8) << 7) & 0xF800) |
+ ((g * 3 + b + 1) >> 1) << 5 |
+ ((r * 3 + g * 2 + b * 11 + 8) >> 4);
+
+ if (gpu.cgb_palette_bg_autoinc)
+ gpu.cgb_palette_bg_idx = ((gpu.cgb_palette_bg_idx + 1) & 0x3f);
+
+ break;
+
+ case 0xFF6A:
+
+ gpu.cgb_palette_oam_idx = v & 0x3f;
+ gpu.cgb_palette_oam_autoinc = ((v & 0x80) == 0x80);
+
+ break;
+
+ case 0xFF6B:
+
+ i = gpu.cgb_palette_oam_idx / 2;
+
+ if ((gpu.cgb_palette_oam_idx & 0x01) == 0x00)
+ {
+ gpu.cgb_palette_oam[i] &= 0xff00;
+ gpu.cgb_palette_oam[i] |= v;
+ }
+ else
+ {
+ gpu.cgb_palette_oam[i] &= 0x00ff;
+ gpu.cgb_palette_oam[i] |= (v << 8);
+ }
+
+ r = gpu.cgb_palette_oam[i] & 0x1F;
+ g = gpu.cgb_palette_oam[i] >> 5 & 0x1F;
+ b = gpu.cgb_palette_oam[i] >> 10 & 0x1F;
+
+ gpu.cgb_palette_oam_rgb565[i] =
+ (((r * 13 + g * 2 + b + 8) << 7) & 0xF800) |
+ ((g * 3 + b + 1) >> 1) << 5 |
+ ((r * 3 + g * 2 + b * 11 + 8) >> 4);
+
+ if (gpu.cgb_palette_oam_autoinc)
+ gpu.cgb_palette_oam_idx =
+ ((gpu.cgb_palette_oam_idx + 1) & 0x3f);
+
+ break;
+
+ }
+}
+
+void gpu_set_speed(char speed)
+{
+ if (speed == 1)
+ gpu.step = 2;
+ else
+ gpu.step = 4;
+}
+
+void gpu_save_fb(FILE *fp)
+{
+ fwrite(&gpu.frame_buffer, 1, sizeof(int16_t) * 144 * 160, fp);
+}
+
+void gpu_save_stat(FILE *fp)
+{
+ fwrite(&gpu, 1, sizeof(gpu_t), fp);
+}
+
+void gpu_restore_stat(FILE *fp)
+{
+ fread(&gpu, 1, sizeof(gpu_t), fp);
+
+ gpu_init_pointers();
+}
+
diff --git a/waterbox/pizza/lib/gpu.h b/waterbox/pizza/lib/gpu.h
new file mode 100644
index 0000000000..710594ca33
--- /dev/null
+++ b/waterbox/pizza/lib/gpu.h
@@ -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 .
+
+*/
+
+#ifndef __GPU_HDR__
+#define __GPU_HDR__
+
+#include
+#include
+
+/* 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
diff --git a/waterbox/pizza/lib/input.c b/waterbox/pizza/lib/input.c
new file mode 100644
index 0000000000..c8680b0c30
--- /dev/null
+++ b/waterbox/pizza/lib/input.c
@@ -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 .
+
+*/
+
+#include "global.h"
+#include "utils.h"
+
+#include
+
+/* 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; }
+
diff --git a/waterbox/pizza/lib/input.h b/waterbox/pizza/lib/input.h
new file mode 100644
index 0000000000..dde0428cae
--- /dev/null
+++ b/waterbox/pizza/lib/input.h
@@ -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 .
+
+*/
+
+#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
diff --git a/waterbox/pizza/lib/interrupt.h b/waterbox/pizza/lib/interrupt.h
new file mode 100644
index 0000000000..0fbc4b0873
--- /dev/null
+++ b/waterbox/pizza/lib/interrupt.h
@@ -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 .
+
+*/
+
+#ifndef __INTERRUPTS_HDR__
+#define __INTERRUPTS_HDR__
+
+#include
+
+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
\ No newline at end of file
diff --git a/waterbox/pizza/lib/mmu.c b/waterbox/pizza/lib/mmu.c
new file mode 100644
index 0000000000..d330697d8c
--- /dev/null
+++ b/waterbox/pizza/lib/mmu.c
@@ -0,0 +1,1323 @@
+/*
+
+ 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 .
+
+*/
+
+#include "cycles.h"
+#include "global.h"
+#include "gpu.h"
+#include "interrupt.h"
+#include "input.h"
+#include "mmu.h"
+#include "sound.h"
+#include "serial.h"
+#include "timer.h"
+#include "utils.h"
+
+#include
+#include
+#include
+#include
+#include
+
+/* GAMEBOY MEMORY AREAS
+
+0x0000 - 0x00FF - BIOS
+0x0000 - 0x3FFF - First 16k of game ROM (permanent)
+0x4000 - 0x7FFF - ROM banks (switchable)
+0x8000 - 0x9FFF - Video RAM (8kb - keeps pixels data)
+0xA000 - 0xBFFF - External RAM (switchable, it was on cartridge,
+ 8kb banks, max 32k, NON volatile)
+0xC000 - 0xDFFF - Gameboy RAM
+0xE000 - 0xEFFF - ????????????????
+0xFE00 - 0xFF7F - I/O
+0xFF80 - 0xFFFE - Temp RAM
+0xFFFF - Turn on/off interrupts
+
+*/
+
+/* cartridge memory (max 8MB) */
+uint8_t cart_memory[1 << 22];
+
+/* RAM memory area */
+uint8_t *ram;
+uint32_t ram_sz;
+
+/* main struct */
+mmu_t mmu;
+
+/* function to call when rumble */
+mmu_rumble_cb_t mmu_rumble_cb = NULL;
+
+
+/* return absolute memory address */
+void *mmu_addr(uint16_t a)
+{
+ return (void *) &mmu.memory[a];
+}
+
+/* return absolute memory address */
+void *mmu_addr_vram0()
+{
+ return (void *) &mmu.vram0;
+}
+
+/* return absolute memory address */
+void *mmu_addr_vram1()
+{
+ return (void *) &mmu.vram1;
+}
+
+/* modify rom in case of Gamegenie cheat */
+void mmu_apply_gg()
+{
+ return;
+
+ /* a wild cheat can occour */
+ if (mmu.gg_count == 0)
+ return;
+
+ int i;
+
+ for (i=0; i> 8;
+ }
+ }
+ else
+ return mmu.memory[a];
+ }
+
+ /* RAM */
+ if (a < 0xE000)
+ return mmu.memory[a];
+
+ /* RAM mirror */
+ if (a < 0xFE00)
+ return mmu.memory[a - 0x2000];
+
+ switch (a)
+ {
+ /* serial registers */
+ case 0xFF01:
+ case 0xFF02:
+ return serial_read_reg(a);
+
+ /* don't ask me why.... */
+ case 0xFF44:
+ return (mmu.memory[0xFF44] == 153 ? 0 : mmu.memory[0xFF44]);
+
+ /* sound registers */
+ case 0xFF10 ... 0xFF3F:
+ return sound_read_reg(a, mmu.memory[a]);
+
+ /* joypad reading */
+ case 0xFF00:
+ return input_get_keys(mmu.memory[a]);
+
+ /* CGB HDMA transfer */
+ case 0xFF55:
+
+ if (!global_cgb) break;
+
+ /* HDMA result */
+ if (mmu.hdma_to_transfer)
+ return (mmu.hdma_to_transfer / 0x10 - 0x01);
+ else
+ return 0xFF;
+
+ /* CGB color palette registers */
+ case 0xFF68:
+ case 0xFF69:
+ case 0xFF6A:
+ case 0xFF6B:
+
+ if (!global_cgb) break;
+
+ /* color palettes registers */
+ return gpu_read_reg(a);
+
+ /* timer registers */
+ case 0xFF04 ... 0xFF07:
+ return timer_read_reg(a);
+
+ }
+
+ return mmu.memory[a];
+}
+
+/* read 16 bit data from a memory addres */
+unsigned int mmu_read_16(uint16_t a)
+{
+ return (mmu_read(a) | (mmu_read(a + 1) << 8));
+}
+
+/* read 8 bit data from a memory addres (not affecting cycles) */
+uint8_t mmu_read_no_cyc(uint16_t a)
+{
+ if (a >= 0xE000 && a <= 0xFDFF)
+ return mmu.memory[a - 0x2000];
+
+ return mmu.memory[a];
+}
+
+void mmu_restore_ram(char *fn)
+{
+ /* save only if cartridge got a battery */
+ if (mmu.carttype == 0x03 ||
+ mmu.carttype == 0x06 ||
+ mmu.carttype == 0x09 ||
+ mmu.carttype == 0x0D ||
+ mmu.carttype == 0x0F ||
+ mmu.carttype == 0x10 ||
+ mmu.carttype == 0x13 ||
+ mmu.carttype == 0x17 ||
+ mmu.carttype == 0x1B ||
+ mmu.carttype == 0x1E ||
+ mmu.carttype == 0x22 ||
+ mmu.carttype == 0xFF)
+ {
+ FILE *fp = fopen(fn, "r+");
+
+ /* it could be not present */
+ if (fp == NULL)
+ return;
+
+ if (ram_sz <= 0x2000)
+ {
+ /* no need to put togheter pieces of ram banks */
+ fread(&mmu.memory[0xA000], ram_sz, 1, fp);
+ }
+ else
+ {
+ /* read entire file into ram buffer */
+ fread(mmu.ram_internal, 0x2000, 1, fp);
+ fread(ram, ram_sz, 1, fp);
+
+ /* copy internal RAM to 0xA000 address */
+ memcpy(&mmu.memory[0xA000], mmu.ram_internal, 0x2000);
+ }
+
+ fclose(fp);
+ }
+}
+
+void mmu_restore_rtc(char *fn)
+{
+ /* save only if cartridge got a battery */
+ if (mmu.carttype == 0x10 ||
+ mmu.carttype == 0x13)
+ {
+ FILE *fp = fopen(fn, "r+");
+
+ /* it could be not present */
+ if (fp == NULL)
+ {
+ /* just pick current time */
+ time(&mmu.rtc_time);
+ return;
+ }
+
+ /* read last saved time */
+ fscanf(fp, "%ld", &mmu.rtc_time);
+
+ fclose(fp);
+ }
+}
+
+void mmu_restore_stat(FILE *fp)
+{
+ fread(&mmu, 1, sizeof(mmu_t), fp);
+
+ if (ram_sz)
+ fread(ram, 1, ram_sz, fp);
+}
+
+void mmu_save_ram(char *fn)
+{
+ /* save only if cartridge got a battery */
+ if (mmu.carttype == 0x03 ||
+ mmu.carttype == 0x06 ||
+ mmu.carttype == 0x09 ||
+ mmu.carttype == 0x0d ||
+ mmu.carttype == 0x0f ||
+ mmu.carttype == 0x10 ||
+ mmu.carttype == 0x13 ||
+ mmu.carttype == 0x17 ||
+ mmu.carttype == 0x1b ||
+ mmu.carttype == 0x1e ||
+ mmu.carttype == 0x22 ||
+ mmu.carttype == 0xff)
+ {
+ FILE *fp = fopen(fn, "w+");
+
+ if (fp == NULL)
+ {
+ printf("Error dumping RAM\n");
+ return;
+ }
+
+ if (ram_sz <= 0x2000)
+ {
+ /* no need to put togheter pieces of ram banks */
+ fwrite(&mmu.memory[0xA000], ram_sz, 1, fp);
+ }
+ else
+ {
+ /* yes, i need to put togheter pieces */
+
+ /* save current used bank */
+ if (mmu.ram_external_enabled)
+ memcpy(&ram[0x2000 * mmu.ram_current_bank],
+ &mmu.memory[0xA000], 0x2000);
+ else
+ memcpy(mmu.ram_internal,
+ &mmu.memory[0xA000], 0x2000);
+
+ /* dump the entire internal + external RAM */
+ fwrite(mmu.ram_internal, 0x2000, 1, fp);
+ fwrite(ram, ram_sz, 1, fp);
+ }
+
+ fclose(fp);
+ }
+}
+
+void mmu_save_rtc(char *fn)
+{
+ /* save only if cartridge got a battery */
+ if (mmu.carttype == 0x10 ||
+ mmu.carttype == 0x13)
+ {
+ FILE *fp = fopen(fn, "w+");
+
+ if (fp == NULL)
+ {
+ printf("Error saving RTC\n");
+ return;
+ }
+
+ fprintf(fp, "%ld", mmu.rtc_time);
+ }
+}
+
+void mmu_save_stat(FILE *fp)
+{
+ fwrite(&mmu, 1, sizeof(mmu_t), fp);
+
+ if (ram_sz)
+ fwrite(ram, 1, ram_sz, fp);
+}
+
+char mmu_set_cheat(char *str)
+{
+ if (str == NULL)
+ return 1;
+
+ size_t len = strlen(str);
+
+ /* gamegenie is 9 char long, gameshark is 8 char long */
+ if (len < 8 || len > 9)
+ return 1;
+
+ unsigned int new_value, address, old_value;
+
+ /* gamegenie branch */
+ if (len == 9)
+ {
+ if (mmu.gg_count == MMU_GAMEGENIE_MAX)
+ {
+ utils_log("Max Gamegenie cheats reached");
+ return 1;
+ }
+
+ char tmp[5];
+
+ /* parse it (must be cleaned by - before) */
+ if (sscanf(str, "%02x", &new_value) < 1)
+ return 1;
+
+ /* build memory address */
+ tmp[0] = str[5];
+ tmp[1] = str[2];
+ tmp[2] = str[3];
+ tmp[3] = str[4];
+ tmp[4] = '\0';
+
+ if (sscanf(tmp, "%04x", &address) < 1)
+ return 1;
+
+ /* build old value */
+ tmp[0] = str[6];
+ tmp[1] = str[7];
+ tmp[2] = str[8];
+ tmp[3] = '\0';
+
+ if (sscanf(tmp, "%03x", &old_value) < 1)
+ return 1;
+
+ /* XOR data according do GameGenie specifications */
+ address ^= 0xF000;
+
+ if ((((old_value >> 8) ^ (old_value >> 4)) & 0x0F) != 0x08)
+ {
+ utils_log("Gamegenie cloak error\n");
+ return 1;
+ }
+
+ old_value = ((((old_value >> 2) & 0x03) |
+ ((old_value >> 6) & 0x3C) |
+ ((old_value << 6) & 0xC0)) ^ 0xBA);
+
+ /* save it into current array slot */
+ mmu.gg_array[mmu.gg_count].address = (uint16_t) address;
+ mmu.gg_array[mmu.gg_count].new_value = (uint8_t) new_value;
+ mmu.gg_array[mmu.gg_count].old_value = (uint8_t) old_value;
+
+ /* looks legit! activate it */
+ mmu.gg_count++;
+
+ return 0;
+ }
+ else
+ {
+ unsigned int ram_bank, mem_low, mem_high;
+
+ /* it must be a game shark cheat */
+ if (sscanf(str, "%02x%02x%02x%02x", &ram_bank, &new_value,
+ &mem_low, &mem_high) < 4)
+ {
+ utils_log("Wrong Gameshark format");
+ return 1;
+ }
+
+ if (mmu.gs_count == MMU_GAMESHARK_MAX)
+ {
+ utils_log("Max Gameshark cheats reached");
+ return 1;
+ }
+
+ /* save it */
+ mmu.gs_array[mmu.gs_count].address = (uint16_t)
+ (mem_low | (mem_high << 8));
+ mmu.gs_array[mmu.gs_count].ram_bank = ram_bank & 0x7F;
+ mmu.gs_array[mmu.gs_count].new_value = new_value;
+
+/* utils_log("Gameshark address %04x - bank %02x - value %02x\n",
+ mmu.gs_array[mmu.gs_count].address,
+ mmu.gs_array[mmu.gs_count].ram_bank,
+ mmu.gs_array[mmu.gs_count].new_value);*/
+
+ /* looks legit! activate it */
+ mmu.gs_count++;
+
+ return 0;
+ }
+
+ utils_log("Unknown cheat format");
+
+ return 1;
+}
+
+void mmu_set_rumble_cb(mmu_rumble_cb_t cb)
+{
+ mmu_rumble_cb = cb;
+}
+
+void mmu_term()
+{
+ if (ram)
+ {
+ free(ram);
+ ram = NULL;
+ }
+}
+
+/* write 16 bit block on a memory address */
+void mmu_write(uint16_t a, uint8_t v)
+{
+ /* update cycles AFTER memory set */
+ cycles_step();
+
+ /* color gameboy stuff */
+ if (global_cgb)
+ {
+ /* VRAM write? */
+ if (a >= 0x8000 && a < 0xA000)
+ {
+ if (mmu.vram_idx == 0)
+ mmu.vram0[a - 0x8000] = v;
+ else
+ mmu.vram1[a - 0x8000] = v;
+
+ return;
+ }
+ else
+ {
+ /* wanna access to RTC register? */
+ if (a >= 0xA000 && a <= 0xBFFF && mmu.rtc_mode != 0x00)
+ {
+ time_t t,s1,s2,m1,m2,h1,h2,d1,d2,days;
+
+ /* get current time */
+ time(&t);
+
+ /* extract parts in seconds from current and ref times */
+ s1 = t % 60;
+ s2 = mmu.rtc_time % 60;
+
+ m1 = (t - s1) % (60 * 60);
+ m2 = (mmu.rtc_time - s2) % (60 * 60);
+
+ h1 = (t - m1 - s1) % (60 * 60 * 24);
+ h2 = (mmu.rtc_time - m2 - s2) % (60 * 60 * 24);
+
+ d1 = t - h1 - m1 - s1;
+ d2 = mmu.rtc_time - h2 - m2 - s2;
+
+ switch (mmu.rtc_mode)
+ {
+ case 0x08:
+
+ /* remove seconds from current time */
+ mmu.rtc_time -= s2;
+
+ /* set new seconds */
+ mmu.rtc_time += (s1 - v);
+
+ return;
+
+ case 0x09:
+
+ /* remove seconds from current time */
+ mmu.rtc_time -= m2;
+
+ /* set new seconds */
+ mmu.rtc_time += (m1 - (v * 60));
+
+ return;
+
+ case 0x0A:
+
+ /* remove seconds from current time */
+ mmu.rtc_time -= h2;
+
+ /* set new seconds */
+ mmu.rtc_time += (h1 - (v * 60 * 24));
+
+ return;
+
+ case 0x0B:
+
+ days = (((d1 - d2) /
+ (60 * 60 * 24)) & 0xFF00) | v;
+
+ /* remove seconds from current time */
+ mmu.rtc_time -= d2;
+
+ /* set new seconds */
+ mmu.rtc_time += (d1 - (days * 60 * 60 * 24));
+
+ return;
+
+ case 0x0C:
+
+ days = (((d1 - d2) /
+ (60 * 60 * 24)) & 0xFEFF) | (v << 8);
+
+ /* remove seconds from current time */
+ mmu.rtc_time -= d2;
+
+ /* set new seconds */
+ mmu.rtc_time += (d1 - (days * 60 * 60 * 24));
+
+ return;
+ }
+ }
+ }
+
+ /* switch WRAM */
+ if (a == 0xFF70)
+ {
+ /* number goes from 1 to 7 */
+ uint8_t new = (v & 0x07);
+
+ if (new == 0)
+ new = 1;
+
+ if (new == mmu.wram_current_bank)
+ return;
+
+ /* save current bank */
+ memcpy(&mmu.wram[0x1000 * mmu.wram_current_bank],
+ &mmu.memory[0xD000], 0x1000);
+
+ mmu.wram_current_bank = new;
+
+ /* move new ram bank */
+ memcpy(&mmu.memory[0xD000],
+ &mmu.wram[0x1000 * mmu.wram_current_bank],
+ 0x1000);
+
+ /* save current bank */
+ mmu.memory[0xFF70] = new;
+
+ return;
+ }
+
+ if (a == 0xFF4F)
+ {
+ /* extract VRAM index from last bit */
+ mmu.vram_idx = (v & 0x01);
+
+ /* save current VRAM bank */
+ mmu.memory[0xFF4F] = mmu.vram_idx;
+
+ return;
+ }
+ }
+
+ /* wanna write on ROM? */
+ if (a < 0x8000)
+ {
+ /* return in case of ONLY ROM */
+ if (mmu.carttype == 0x00)
+ return;
+
+ /* TODO - MBC strategies */
+ uint8_t b = mmu.rom_current_bank;
+
+ switch (mmu.carttype)
+ {
+ /* MBC1 */
+ case 0x01:
+ case 0x02:
+ case 0x03:
+
+ if (a >= 0x2000 && a <= 0x3FFF)
+ {
+ /* reset 5 bits */
+ b = mmu.rom_current_bank & 0xE0;
+
+ /* set them with new value */
+ b |= v & 0x1F;
+
+ /* doesn't fit on max rom number? */
+ if (b > (2 << mmu.roms))
+ {
+ /* filter result to get a value < max rom number */
+ b %= (2 << mmu.roms);
+ }
+
+ /* 0x00 is not valid, switch it to 0x01 */
+ if (b == 0x00)
+ b = 0x01;
+ }
+ else if (a >= 0x4000 && a <= 0x5FFF)
+ {
+ /* ROM banking? it's about 2 higher bits */
+ if (mmu.banking == 0)
+ {
+ /* reset 5 bits */
+ b = mmu.rom_current_bank & 0x1F;
+
+ /* set them with new value */
+ b |= (v << 5);
+
+ /* doesn't fit on max rom number? */
+ if (b > (2 << mmu.roms))
+ {
+ /* filter result to get a value < max rom number */
+ b %= (2 << mmu.roms);
+ }
+ }
+ else
+ {
+ if ((0x2000 * v) < ram_sz)
+ {
+ /* save current bank */
+ memcpy(&ram[0x2000 * mmu.ram_current_bank],
+ &mmu.memory[0xA000], 0x2000);
+
+ mmu.ram_current_bank = v;
+
+ /* move new ram bank */
+ memcpy(&mmu.memory[0xA000],
+ &ram[0x2000 * mmu.ram_current_bank],
+ 0x2000);
+ }
+ }
+ }
+ else if (a >= 0x6000 && a <= 0x7FFF)
+ mmu.banking = v;
+
+ break;
+
+ /* MBC2 */
+ case 0x05:
+ case 0x06:
+
+ if (a >= 0x2000 && a <= 0x3FFF)
+ {
+ /* use lower nibble to set current bank */
+ b = v & 0x0f;
+
+ /*if (b != rom_current_bank)
+ memcpy(&memory[0x4000],
+ &cart_memory[b * 0x4000], 0x4000);
+
+ rom_current_bank = b;*/
+ }
+
+ break;
+
+ /* MBC3 */
+ case 0x10:
+ case 0x13:
+
+ if (a >= 0x0000 && a <= 0x1FFF)
+ {
+ if (v == 0x0A)
+ {
+ /* already enabled? */
+ if (mmu.ram_external_enabled)
+ return;
+
+ /* save current bank */
+ memcpy(mmu.ram_internal,
+ &mmu.memory[0xA000], 0x2000);
+
+ /* restore external ram bank */
+ memcpy(&mmu.memory[0xA000],
+ &ram[0x2000 * mmu.ram_current_bank],
+ 0x2000);
+
+ /* set external RAM eanbled flag */
+ mmu.ram_external_enabled = 1;
+
+ return;
+ }
+
+ if (v == 0x00)
+ {
+ /* already disabled? */
+ if (mmu.ram_external_enabled == 0)
+ return;
+
+ /* save current bank */
+ memcpy(&ram[0x2000 * mmu.ram_current_bank],
+ &mmu.memory[0xA000], 0x2000);
+
+ /* restore external ram bank */
+ memcpy(&mmu.memory[0xA000],
+ mmu.ram_internal, 0x2000);
+
+ /* clear external RAM eanbled flag */
+ mmu.ram_external_enabled = 0;
+ }
+ }
+ else if (a >= 0x2000 && a <= 0x3FFF)
+ {
+ /* set them with new value */
+ b = v & 0x7F;
+
+ /* doesn't fit on max rom number? */
+ if (b > (2 << mmu.roms))
+ {
+ /* filter result to get a value < max rom number */
+ b %= (2 << mmu.roms);
+ }
+
+ /* 0x00 is not valid, switch it to 0x01 */
+ if (b == 0x00)
+ b = 0x01;
+ }
+ else if (a >= 0x4000 && a <= 0x5FFF)
+ {
+ /* 0x00 to 0x07 is referred to RAM bank */
+ if (v < 0x08)
+ {
+ /* not on RTC mode anymore */
+ mmu.rtc_mode = 0x00;
+
+ if ((0x2000 * (v & 0x0f)) < ram_sz)
+ {
+ /* save current bank */
+ memcpy(&ram[0x2000 * mmu.ram_current_bank],
+ &mmu.memory[0xA000], 0x2000);
+
+ mmu.ram_current_bank = v & 0x0f;
+
+ /* move new ram bank */
+ memcpy(&mmu.memory[0xA000],
+ &ram[0x2000 * mmu.ram_current_bank],
+ 0x2000);
+ }
+ }
+ else if (v < 0x0d)
+ {
+ /* from 0x08 to 0x0C trigger RTC mode */
+ mmu.rtc_mode = v;
+ }
+
+ }
+ else if (a >= 0x6000 && a <= 0x7FFF)
+ {
+ /* latch clock data. move clock data to RTC registers */
+ time(&mmu.rtc_latch_time);
+ }
+
+
+ break;
+
+ /* MBC5 */
+ case 0x19:
+ case 0x1A:
+ case 0x1B:
+ case 0x1C:
+ case 0x1D:
+ case 0x1E:
+
+ if (a >= 0x0000 && a <= 0x1FFF)
+ {
+ if (v == 0x0A)
+ {
+ /* we got external RAM? some stupid game try */
+ /* to access it despite it doesn't have it */
+ if (ram_sz == 0)
+ return;
+
+ /* already enabled? */
+ if (mmu.ram_external_enabled)
+ return;
+
+ /* save current bank */
+ memcpy(mmu.ram_internal,
+ &mmu.memory[0xA000], 0x2000);
+
+ /* restore external ram bank */
+ memcpy(&mmu.memory[0xA000],
+ &ram[0x2000 * mmu.ram_current_bank],
+ 0x2000);
+
+ /* set external RAM eanbled flag */
+ mmu.ram_external_enabled = 1;
+
+ return;
+ }
+
+ if (v == 0x00)
+ {
+ /* we got external RAM? some stpd game try to do shit */
+ if (ram_sz == 0)
+ return;
+
+ /* already disabled? */
+ if (mmu.ram_external_enabled == 0)
+ return;
+
+ /* save current bank */
+ memcpy(&ram[0x2000 * mmu.ram_current_bank],
+ &mmu.memory[0xA000], 0x2000);
+
+ /* restore external ram bank */
+ memcpy(&mmu.memory[0xA000],
+ mmu.ram_internal, 0x2000);
+
+ /* clear external RAM eanbled flag */
+ mmu.ram_external_enabled = 0;
+ }
+ }
+ if (a >= 0x2000 && a <= 0x2FFF)
+ {
+ /* set them with new value */
+ b = (mmu.rom_current_bank & 0xFF00) | v;
+
+ /* doesn't fit on max rom number? */
+ if (b > (2 << mmu.roms))
+ {
+ /* filter result to get a value < max rom number */
+ b %= (2 << mmu.roms);
+ }
+ }
+ else if (a >= 0x3000 && a <= 0x3FFF)
+ {
+ /* set them with new value */
+ b = (mmu.rom_current_bank & 0x00FF) | ((v & 0x01) << 8);
+
+ /* doesn't fit on max rom number? */
+ if (b > (2 << mmu.roms))
+ {
+ /* filter result to get a value < max rom number */
+ b %= (2 << mmu.roms);
+ }
+ }
+ else if (a >= 0x4000 && a <= 0x5FFF)
+ {
+ uint8_t mask = 0x0F;
+
+ if (global_rumble)
+ {
+ mask = 0x07;
+
+ if (mmu_rumble_cb)
+ (*mmu_rumble_cb) ((v & 0x08) ? 1 : 0);
+
+ /* check if we want to appizz the motor */
+/* if (v & 0x08)
+ printf("APPIZZ MOTOR\n");
+ else
+ printf("SPEGN MOTOR\n");*/
+ }
+
+ if ((0x2000 * (v & mask)) < ram_sz)
+ {
+ /* is externa RAM enabled? */
+ if (!mmu.ram_external_enabled)
+ break;
+
+ /* wanna switch on the same bank? =\ just discard it */
+ if ((v & 0x0f) == mmu.ram_current_bank)
+ break;
+
+ /* save current bank */
+ memcpy(&ram[0x2000 * mmu.ram_current_bank],
+ &mmu.memory[0xA000], 0x2000);
+
+ mmu.ram_current_bank = (v & 0x0f);
+
+ /* move new ram bank */
+ memcpy(&mmu.memory[0xA000],
+ &ram[0x2000 * mmu.ram_current_bank],
+ 0x2000);
+ }
+ }
+
+ break;
+
+ }
+
+ /* need to switch? */
+ if (b != mmu.rom_current_bank)
+ {
+ /* copy from cartridge rom to GB switchable bank area */
+ memcpy(&mmu.memory[0x4000], &cart_memory[b * 0x4000], 0x4000);
+
+ /* save new current bank */
+ mmu.rom_current_bank = b;
+
+ /* re-apply cheats */
+// mmu_apply_gg();
+ }
+
+ return;
+ }
+
+ if (a >= 0xE000)
+ {
+ /* changes on sound registers? */
+ if (a >= 0xFF10 && a <= 0xFF3F)
+ {
+ /* set memory */
+ sound_write_reg(a, v);
+
+ return;
+ }
+
+ /* mirror area */
+ if (a >= 0xE000 && a <= 0xFDFF)
+ {
+ mmu.memory[a - 0x2000] = v;
+ return;
+ }
+
+ /* TODO - put them all */
+ switch(a)
+ {
+ case 0xFF01:
+ case 0xFF02:
+ serial_write_reg(a, v);
+ return;
+ case 0xFF04 ... 0xFF07:
+ timer_write_reg(a, v);
+ return;
+ }
+
+ /* LCD turned on/off? */
+ if (a == 0xFF40)
+ {
+ if ((v ^ mmu.memory[0xFF40]) & 0x80)
+ gpu_toggle(v);
+ }
+
+ /* only 5 high bits are writable */
+ if (a == 0xFF41)
+ {
+ mmu.memory[a] = (mmu.memory[a] & 0x07) | (v & 0xf8);
+ return;
+ }
+
+ /* palette update */
+ if ((a >= 0xFF47 && a <= 0xFF49) ||
+ (a >= 0xFF68 && a <= 0xFF6B))
+ gpu_write_reg(a, v);
+
+ /* CGB only registers */
+ if (global_cgb)
+ {
+ switch (a)
+ {
+ case 0xFF4D:
+
+ /* wanna switch speed? */
+ if (v & 0x01)
+ {
+ global_cpu_double_speed ^= 0x01;
+
+ /* update new clock */
+ // cycles_clock = 4194304 << global_double_speed;
+ cycles_set_speed(1);
+ sound_set_speed(1);
+ gpu_set_speed(1);
+
+ /* save into memory i'm working at double speed */
+ if (global_cpu_double_speed)
+ mmu.memory[a] = 0x80;
+ else
+ mmu.memory[a] = 0x00;
+ }
+
+ return;
+
+ case 0xFF52:
+
+ /* high byte of HDMA source address */
+ mmu.hdma_src_address &= 0xff00;
+
+ /* lower 4 bits are ignored */
+ mmu.hdma_src_address |= (v & 0xf0);
+
+ break;
+
+ case 0xFF51:
+
+ /* low byte of HDMA source address */
+ mmu.hdma_src_address &= 0x00ff;
+
+ /* highet 3 bits are ignored (always 100 binary) */
+ mmu.hdma_src_address |= (v << 8);
+
+ break;
+
+ case 0xFF54:
+
+ /* high byte of HDMA source address */
+ mmu.hdma_dst_address &= 0xff00;
+
+ /* lower 4 bits are ignored */
+ mmu.hdma_dst_address |= (v & 0xf0);
+
+ break;
+
+ case 0xFF53:
+
+ /* low byte of HDMA source address */
+ mmu.hdma_dst_address &= 0x00ff;
+
+ /* highet 3 bits are ignored (always 100 binary) */
+ mmu.hdma_dst_address |= ((v & 0x1f) | 0x80) << 8;
+
+ break;
+
+ case 0xFF55:
+
+ /* wanna stop HBLANK transfer? a zero on 7th bit will do */
+ if ((v & 0x80) == 0 &&
+ mmu.hdma_transfer_mode == 0x01 &&
+ mmu.hdma_to_transfer)
+ {
+ mmu.hdma_to_transfer = 0x00;
+ mmu.hdma_transfer_mode = 0x00;
+
+ return;
+ }
+
+ /* general (0) or hblank (1) ? */
+ mmu.hdma_transfer_mode = ((v & 0x80) ? 1 : 0);
+
+ /* calc how many bytes gotta be transferred */
+ uint16_t to_transfer = ((v & 0x7f) + 1) * 0x10;
+
+ /* general must be done immediately */
+ if (mmu.hdma_transfer_mode == 0)
+ {
+ /* copy right now */
+ if (mmu.vram_idx)
+ memcpy(mmu_addr_vram1() +
+ (mmu.hdma_dst_address - 0x8000),
+ &mmu.memory[mmu.hdma_src_address],
+ to_transfer);
+ else
+ memcpy(mmu_addr_vram0() +
+ (mmu.hdma_dst_address - 0x8000),
+ &mmu.memory[mmu.hdma_src_address],
+ to_transfer);
+
+ /* reset to_transfer var */
+ mmu.hdma_to_transfer = 0;
+
+ /* move forward src and dst addresses =| */
+ mmu.hdma_src_address += to_transfer;
+ mmu.hdma_dst_address += to_transfer;
+ }
+ else
+ {
+ mmu.hdma_to_transfer = to_transfer;
+
+ /* check if we're already into hblank phase */
+ cycles_hdma();
+ }
+
+ break;
+ }
+ }
+
+ /* finally set memory byte with data */
+ mmu.memory[a] = v;
+
+ /* DMA access */
+ if (a == 0xFF46)
+ {
+ /* calc source address */
+ mmu.dma_address = v * 256;
+
+ /* initialize counter, DMA needs 672 ticks */
+ mmu.dma_next = cycles.cnt + 4; // 168 / 2;
+ }
+ }
+ else
+ mmu.memory[a] = v;
+}
+
+/* write 16 bit block on a memory address */
+void mmu_write_16(uint16_t a, uint16_t v)
+{
+ mmu.memory[a] = (uint8_t) (v & 0x00ff);
+ mmu.memory[a + 1] = (uint8_t) (v >> 8);
+
+ /* 16 bit write = +8 cycles */
+ cycles_step();
+ cycles_step();
+}
+
+
+/* write 16 bit block on a memory address (no cycles affected) */
+void mmu_write_no_cyc(uint16_t a, uint8_t v)
+{
+ mmu.memory[a] = v;
+}
+
+
+
+
+
diff --git a/waterbox/pizza/lib/mmu.h b/waterbox/pizza/lib/mmu.h
new file mode 100644
index 0000000000..e3e3fa2f2a
--- /dev/null
+++ b/waterbox/pizza/lib/mmu.h
@@ -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 .
+
+*/
+
+#ifndef __MMU_HDR__
+#define __MMU_HDR__
+
+#include
+#include
+#include
+
+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
diff --git a/waterbox/pizza/lib/network.c b/waterbox/pizza/lib/network.c
new file mode 100644
index 0000000000..e58aeff818
--- /dev/null
+++ b/waterbox/pizza/lib/network.c
@@ -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 .
+
+*/
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#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));
+}
diff --git a/waterbox/pizza/lib/network.h b/waterbox/pizza/lib/network.h
new file mode 100644
index 0000000000..05904f9d4c
--- /dev/null
+++ b/waterbox/pizza/lib/network.h
@@ -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 .
+
+*/
+
+#ifndef __NETWORK_HDR__
+#define __NETWORK_HDR__
+
+#include
+
+/* 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
diff --git a/waterbox/pizza/lib/serial.c b/waterbox/pizza/lib/serial.c
new file mode 100644
index 0000000000..a8734b4f01
--- /dev/null
+++ b/waterbox/pizza/lib/serial.c
@@ -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 .
+
+*/
+
+#include
+
+#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);
+}
diff --git a/waterbox/pizza/lib/serial.h b/waterbox/pizza/lib/serial.h
new file mode 100644
index 0000000000..6ce30fbddb
--- /dev/null
+++ b/waterbox/pizza/lib/serial.h
@@ -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 .
+
+*/
+
+#ifndef __SERIAL_HDR__
+#define __SERIAL_HDR__
+
+#include
+#include
+
+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
diff --git a/waterbox/pizza/lib/sound.c b/waterbox/pizza/lib/sound.c
new file mode 100644
index 0000000000..324156e3eb
--- /dev/null
+++ b/waterbox/pizza/lib/sound.c
@@ -0,0 +1,1484 @@
+/*
+
+ 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 .
+
+*/
+
+#include "cycles.h"
+#include "global.h"
+#include "gpu.h"
+#include "mmu.h"
+#include "sound.h"
+#include "utils.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+/* semaphore for audio sync */
+pthread_cond_t sound_cond;
+pthread_mutex_t sound_mutex;
+
+/* super variable for audio controller */
+sound_t sound;
+
+/* global for output frequency */
+int sound_output_rate = 48000;
+int sound_output_rate_fifth = 48; // 48000 / 5;
+
+/* internal prototypes */
+size_t sound_available_samples();
+void sound_envelope_step();
+void sound_length_ctrl_step();
+void sound_push_samples(int16_t l, int16_t r);
+void sound_push_sample(int16_t s);
+void sound_read_samples(int len, int16_t *buf);
+void sound_rebuild_wave();
+void sound_sweep_step();
+void sound_term();
+void sound_write_wave(uint16_t a, uint8_t v);
+
+int sound_get_samples()
+{
+ return SOUND_SAMPLES; // sound_output_rate / 10;
+}
+
+void sound_init_pointers()
+{
+ /* point sound structures to their memory areas */
+ sound.nr10 = (nr10_t *) mmu_addr(0xFF10);
+ sound.nr11 = (nr11_t *) mmu_addr(0xFF11);
+ sound.nr12 = (nr12_t *) mmu_addr(0xFF12);
+ sound.nr13 = (nr13_t *) mmu_addr(0xFF13);
+ sound.nr14 = (nr14_t *) mmu_addr(0xFF14);
+
+ sound.nr21 = (nr21_t *) mmu_addr(0xFF16);
+ sound.nr22 = (nr22_t *) mmu_addr(0xFF17);
+ sound.nr23 = (nr23_t *) mmu_addr(0xFF18);
+ sound.nr24 = (nr24_t *) mmu_addr(0xFF19);
+
+ sound.nr30 = (nr30_t *) mmu_addr(0xFF1A);
+ sound.nr31 = (nr31_t *) mmu_addr(0xFF1B);
+ sound.nr32 = (nr32_t *) mmu_addr(0xFF1C);
+ sound.nr33 = (nr33_t *) mmu_addr(0xFF1D);
+ sound.nr34 = (nr34_t *) mmu_addr(0xFF1E);
+
+ sound.nr41 = (nr41_t *) mmu_addr(0xFF20);
+ sound.nr42 = (nr42_t *) mmu_addr(0xFF21);
+ sound.nr43 = (nr43_t *) mmu_addr(0xFF22);
+ sound.nr44 = (nr44_t *) mmu_addr(0xFF23);
+
+ sound.nr50 = mmu_addr(0xFF24);
+ sound.nr51 = mmu_addr(0xFF25);
+ sound.nr52 = mmu_addr(0xFF26);
+
+ sound.wave_table = mmu_addr(0xFF30);
+}
+
+/* init sound states */
+void sound_init()
+{
+ /* reset structure */
+ bzero(&sound, sizeof(sound_t));
+
+ /* point sound structures to their memory areas */
+ sound_init_pointers();
+
+ /* steps length */
+ sound.step_int = 4;
+ sound.step_int1000 = 4000;
+
+ /* buffer stuff */
+ sound.buf_wr = 0;
+ sound.buf_rd = 0;
+ sound.buf_available = 0;
+
+ /* init semaphore for sync */
+ pthread_mutex_init(&sound_mutex, NULL);
+ pthread_cond_init(&sound_cond, NULL);
+
+ /* how many cpu cycles we need to emit a 512hz clock (frame sequencer) */
+ sound.fs_cycles = 4194304 / 512;
+
+ /* how many cpu cycles to generate a single frame seq clock? */
+ sound.fs_cycles_next = sound.fs_cycles;
+
+ /* how many cpu cycles to generate a sample */
+ sound.sample_cycles = (uint_fast32_t) (((double) 4194304 /
+ (double) sound_output_rate) * 1000);
+
+ sound.sample_cycles_next = sound.sample_cycles / 1000;
+ sound.sample_cycles_next_rounded = sound.sample_cycles_next & 0xFFFFFFFC;
+
+ /* init multiplier */
+ sound.frame_multiplier = 1;
+
+ /* no, i'm not empty */
+ sound.buf_empty = 0;
+}
+
+void sound_set_speed(char dbl)
+{
+ return;
+
+ if (dbl)
+ {
+ sound.step_int = 2;
+ sound.step_int1000 = 2000;
+ }
+ else
+ {
+ sound.step_int = 4;
+ sound.step_int1000 = 4000;
+ }
+}
+
+void sound_change_emulation_speed()
+{
+ if (global_emulation_speed == GLOBAL_EMULATION_SPEED_HALF)
+ sound.frame_multiplier = 2;
+ else if (global_emulation_speed == GLOBAL_EMULATION_SPEED_QUARTER)
+ sound.frame_multiplier = 4;
+ else
+ sound.frame_multiplier = 1;
+}
+
+/* update sound internal state given CPU T-states */
+void sound_step_fs()
+{
+ /* rotate from 0 to 7 */
+ sound.fs_cycles_idx = (sound.fs_cycles_idx + 1) & 0x07;
+
+ /* reset fs cycles counter */
+ sound.fs_cycles_next = cycles.cnt +
+ (sound.fs_cycles << global_cpu_double_speed);
+
+ /* length controller works at 256hz */
+ if ((sound.fs_cycles_idx & 0x01) == 0)
+ sound_length_ctrl_step();
+
+ /* sweep works at 128hz */
+ if (sound.fs_cycles_idx == 2 || sound.fs_cycles_idx == 6)
+ sound_sweep_step();
+
+ /* envelope works at 64hz */
+ if (sound.fs_cycles_idx == 7)
+ sound_envelope_step();
+}
+
+/* update all channels */
+void sound_step_ch1()
+{
+ /* recalc current samples */
+ if ((sound.channel_one.duty >> sound.channel_one.duty_idx) & 0x01)
+ sound.channel_one.sample = sound.channel_one.volume;
+ else
+ sound.channel_one.sample = -sound.channel_one.volume;
+
+ /* step to the next duty value */
+ sound.channel_one.duty_idx =
+ (sound.channel_one.duty_idx + 1) & 0x07;
+
+ /* go back */
+ sound.channel_one.duty_cycles_next += sound.channel_one.duty_cycles;
+}
+
+void sound_step_ch2()
+{
+ /* recalc current samples */
+ if ((sound.channel_two.duty >> sound.channel_two.duty_idx) & 0x01)
+ sound.channel_two.sample = sound.channel_two.volume;
+ else
+ sound.channel_two.sample = -sound.channel_two.volume;
+
+ /* step to the next duty value */
+ sound.channel_two.duty_idx =
+ (sound.channel_two.duty_idx + 1) & 0x07;
+
+ /* go back */
+ sound.channel_two.duty_cycles_next += sound.channel_two.duty_cycles;
+}
+
+void sound_step_ch3()
+{
+ /* switch to the next wave sample */
+ sound.channel_three.index = (sound.channel_three.index + 1) & 0x1F;
+
+ /* set the new current sample */
+ sound.channel_three.sample =
+ sound.channel_three.wave[sound.channel_three.index];
+
+ /* reload new period */
+ uint_fast16_t freq = sound.nr33->frequency_lsb |
+ (sound.nr34->frequency_msb << 8);
+
+ /* qty of cpu ticks needed for a wave sample change */
+ sound.channel_three.cycles = ((2048 - freq) * 2) << global_cpu_double_speed;
+ sound.channel_three.cycles_next += sound.channel_three.cycles;
+}
+
+void sound_step_ch4()
+{
+ /* update LSFR */
+ if (sound.nr43->shift < 14)
+ {
+ /* shift register one bit right */
+ uint16_t s = sound.channel_four.reg >> 1;
+
+ /* xor current register and the shifted version */
+ /* and extract bit zero */
+ uint16_t x = (sound.channel_four.reg ^ s) & 1;
+
+ /* update register */
+ sound.channel_four.reg = s | x << 14;
+
+ /* if width is set... */
+ if (sound.nr43->width)
+ sound.channel_four.reg =
+ (sound.channel_four.reg & 0xBF) | x << 6;
+ }
+
+ /* update sample */
+ if (sound.channel_four.reg & 0x01)
+ sound.channel_four.sample = -sound.channel_four.volume;
+ else
+ sound.channel_four.sample = sound.channel_four.volume;
+
+ /* qty of cpu ticks needed for a wave sample change */
+ sound.channel_four.cycles_next += sound.channel_four.period_lfsr;
+}
+
+void sound_step_sample()
+{
+ uint_fast32_t zum = sound.sample_cycles + sound.sample_cycles_remainder;
+
+ sound.sample_cycles_next += ((zum / 1000) << global_cpu_double_speed);
+ sound.sample_cycles_next_rounded =
+ sound.sample_cycles_next & 0xFFFFFFFC;
+ sound.sample_cycles_remainder = zum % 1000;
+
+ /* update output frame counter */
+ sound.frame_counter++;
+
+ /* is it the case to push samples? */
+ if (((global_emulation_speed == GLOBAL_EMULATION_SPEED_DOUBLE &&
+ (sound.frame_counter & 0x0001) != 0) ||
+ (global_emulation_speed == GLOBAL_EMULATION_SPEED_4X &&
+ (sound.frame_counter & 0x0003) != 0)))
+ return;
+
+ /* DAC turned off? */
+ if (sound.nr30->dac == 0 &&
+ sound.channel_one.active == 0 &&
+ sound.channel_two.active == 0 &&
+ sound.channel_four.active == 0)
+ {
+ sound_push_samples(0, 0);
+ return;
+ }
+
+ int16_t sample_left = 0;
+ int16_t sample_right = 0;
+ int16_t sample = 0;
+
+ /* time to generate a sample! sum all the fields */
+ if (sound.channel_one.active) // && sound.channel_one.sample)
+ {
+ /* to the right? */
+ if (sound.nr51->ch1_to_so1)
+ sample_right += sound.channel_one.sample;
+
+ /* to the left? */
+ if (sound.nr51->ch1_to_so2)
+ sample_left += sound.channel_one.sample;
+ }
+
+ if (sound.channel_two.active) // && sound.channel_two.sample)
+ {
+ /* to the right? */
+ if (sound.nr51->ch2_to_so1)
+ sample_right += sound.channel_two.sample;
+
+ /* to the left? */
+ if (sound.nr51->ch2_to_so2)
+ sample_left += sound.channel_two.sample;
+ }
+
+ if (sound.channel_three.active)
+ {
+ uint8_t shift = (sound.nr32->volume_code == 0 ?
+ 4 : sound.nr32->volume_code - 1);
+
+ /* volume is zero in any case */
+ if (shift == 4)
+ sample = 0;
+ else
+ {
+ /* apply volume change */
+ uint8_t idx = sound.channel_three.index;
+ uint16_t s;
+
+ /* extract current sample */
+ if ((idx & 0x01) == 0)
+ s = (sound.wave_table[idx >> 1] & 0xf0) >> 4;
+ else
+ s = sound.wave_table[idx >> 1] & 0x0f;
+
+ /* transform it into signed 16 bit sample */
+ sample = ((s * 0x222) >> shift);
+ }
+
+
+ /* not silence? */
+ if (sample != 0)
+ {
+ /* to the right? */
+ if (sound.nr51->ch3_to_so1)
+ sample_right += sample;
+
+ /* to the left? */
+ if (sound.nr51->ch3_to_so2)
+ sample_left += sample;
+ }
+ }
+
+ if (sound.channel_four.active)
+ {
+ /* to the right? */
+ if (sound.nr51->ch4_to_so1)
+ sample_right += sound.channel_four.sample;
+
+ /* to the left? */
+ if (sound.nr51->ch4_to_so2)
+ sample_left += sound.channel_four.sample;
+ }
+
+ int i;
+
+ for (i=0; ilength_enable,
+ &sound.channel_one.length,
+ &sound.channel_one.active);
+
+ sound_length_ctrl_step_ch(sound.nr24->length_enable,
+ &sound.channel_two.length,
+ &sound.channel_two.active);
+
+ sound_length_ctrl_step_ch(sound.nr34->length_enable,
+ &sound.channel_three.length,
+ &sound.channel_three.active);
+
+ sound_length_ctrl_step_ch(sound.nr44->length_enable,
+ &sound.channel_four.length,
+ &sound.channel_four.active);
+}
+
+void sound_read_buffer(void *userdata, uint8_t *stream, int snd_len)
+{
+ /* requester snd_len is expressed in byte, */
+ /* so divided by to to obtain wanted 16 bits samples */
+ sound_read_samples(snd_len / 2, (int16_t *) stream);
+}
+
+void sound_push_samples(int16_t l, int16_t r)
+{
+ /* store them in tmp buffer */
+ sound.buf_tmp[sound.buf_tmp_wr++] = l;
+ sound.buf_tmp[sound.buf_tmp_wr++] = r;
+
+ if (sound.buf_tmp_wr == SOUND_BUF_TMP_SZ)
+ {
+ unsigned int i;
+
+ /* since we're accessing a shared buffer, lock it */
+ pthread_mutex_lock(&sound_mutex);
+
+ /* put them in circular shared buffer */
+ for (i=0; i sound.buf_wr)
+ return sound.buf_wr + SOUND_BUF_SZ - sound.buf_rd;
+
+ return sound.buf_wr - sound.buf_rd;
+}
+
+/* read a block of data from circular buffer */
+void sound_read_samples(int to_read, int16_t *buf)
+{
+ /* lock the buffer */
+ pthread_mutex_lock(&sound_mutex);
+
+ /* am i shutting down? exit */
+ if (global_quit)
+ {
+ pthread_mutex_unlock(&sound_mutex);
+ return;
+ }
+
+ /* not enough samples? read what we got */
+ if (sound.buf_available < to_read)
+ {
+ /* stop until we got enough samples */
+ sound.buf_empty = 1;
+
+ while (sound.buf_empty && !global_quit)
+ pthread_cond_wait(&sound_cond, &sound_mutex);
+ }
+
+ if (sound.buf_rd + to_read >= SOUND_BUF_SZ)
+ {
+ /* overlaps the end of the buffer? copy in 2 phases */
+ size_t first_block = SOUND_BUF_SZ - sound.buf_rd;
+
+ memcpy(buf, &sound.buf[sound.buf_rd], first_block * 2);
+
+ memcpy(&buf[first_block], sound.buf, (to_read - first_block) * 2);
+
+ /* set the new read index */
+ sound.buf_rd = to_read - first_block;
+ }
+ else
+ {
+ /* a single memcpy is enough */
+ memcpy(buf, &sound.buf[sound.buf_rd], to_read * 2);
+
+ /* update read index */
+ sound.buf_rd += to_read;
+ }
+
+ /* update avaiable samples */
+ sound.buf_available -= to_read;
+
+ /* unlock the buffer */
+ pthread_mutex_unlock(&sound_mutex);
+}
+
+/* calc the new frequency by sweep module */
+uint_fast32_t sound_sweep_calc()
+{
+ uint_fast32_t new_freq;
+
+ /* time to update frequency */
+ uint_fast32_t diff =
+ sound.channel_one.sweep_shadow_frequency >>
+ sound.nr10->shift;
+
+ /* the calculated diff must be summed or subtracted to frequency */
+ if (sound.nr10->negate)
+ {
+ new_freq = sound.channel_one.sweep_shadow_frequency - diff;
+ sound.channel_one.sweep_neg = 1;
+ }
+ else
+ new_freq = sound.channel_one.sweep_shadow_frequency + diff;
+
+ /* if freq > 2047, turn off the channel */
+ if (new_freq > 2047)
+ sound.channel_one.active = 0;
+
+ return new_freq;
+}
+
+/* set channel one new frequency */
+void sound_set_frequency(uint_fast32_t new_freq)
+{
+ /* too high? */
+ if (new_freq > 2047)
+ {
+ sound.channel_one.active = 0;
+ return;
+ }
+
+ /* update with the new frequency */
+ sound.channel_one.frequency = new_freq;
+
+ /* update them also into memory */
+ sound.nr13->frequency_lsb = (uint8_t) (new_freq & 0x000000ff);
+ sound.nr14->frequency_msb = (uint8_t) ((new_freq >> 8) & 0x00000007);
+
+ /* update the duty cycles */
+ sound.channel_one.duty_cycles =
+ ((2048 - new_freq) * 4) << global_cpu_double_speed;
+
+ /* and reset them */
+ sound.channel_one.duty_cycles_next =
+ cycles.cnt + sound.channel_one.duty_cycles;
+}
+
+/* step of frequency sweep at 128hz */
+void sound_sweep_step()
+{
+ uint_fast32_t new_freq;
+
+ if (sound.channel_one.active &&
+ sound.channel_one.sweep_active)
+ {
+ /* make it rotate from 0 to 8 */
+ sound.channel_one.sweep_cnt++;
+
+ /* enough cycles? */
+ if (sound.channel_one.sweep_cnt == sound.channel_one.sweep_next)
+ {
+ /* reload the next step - 0 is treated as 8 */
+ sound.channel_one.sweep_next =
+ sound.nr10->sweep_period ?
+ sound.nr10->sweep_period : 8;
+
+ /* reset sweep counter */
+ sound.channel_one.sweep_cnt = 0;
+
+ /* period must be > 0 if new freq gotta be updated */
+ if (sound.nr10->sweep_period == 0)
+ return;
+
+ /* calc new frequency */
+ new_freq = sound_sweep_calc();
+
+ /* set it only if < 2048 and shift != 0 */
+ if (sound.nr10->shift &&
+ new_freq < 2048)
+ {
+ /* copy new_freq into shadow register */
+ sound.channel_one.sweep_shadow_frequency = new_freq;
+
+ /* update all the stuff related to new frequency */
+ sound_set_frequency(new_freq);
+
+ /* update freq again (but only in shadow register) */
+ sound_sweep_calc();
+ }
+ }
+ }
+}
+
+/* step of envelope at 64hz */
+void sound_envelope_step()
+{
+ if (sound.channel_one.active && sound.nr12->period)
+ {
+ /* update counter */
+ sound.channel_one.envelope_cnt++;
+
+ /* if counter reaches period, update volume */
+ if (sound.channel_one.envelope_cnt == sound.nr12->period)
+ {
+ if (sound.nr12->add)
+ {
+ if (sound.channel_one.volume < (14 * 0x111))
+ sound.channel_one.volume += 0x111;
+ }
+ else
+ {
+ if (sound.channel_one.volume >= 0x111)
+ sound.channel_one.volume -= 0x111;
+ }
+
+ /* reset counter */
+ sound.channel_one.envelope_cnt = 0;
+ }
+ }
+
+ if (sound.channel_two.active && sound.nr22->period)
+ {
+ /* update counter */
+ sound.channel_two.envelope_cnt++;
+
+ /* if counter reaches period, update volume */
+ if (sound.channel_two.envelope_cnt == sound.nr22->period)
+ {
+ if (sound.nr22->add)
+ {
+ if (sound.channel_two.volume < (14 * 0x111))
+ sound.channel_two.volume += 0x111;
+ }
+ else
+ {
+ if (sound.channel_two.volume >= 0x111)
+ sound.channel_two.volume -= 0x111;
+ }
+
+ /* reset counter */
+ sound.channel_two.envelope_cnt = 0;
+ }
+ }
+
+ if (sound.channel_four.active && sound.nr42->period)
+ {
+ /* update counter */
+ sound.channel_four.envelope_cnt++;
+
+ /* if counter reaches period, update volume */
+ if (sound.channel_four.envelope_cnt == sound.nr42->period)
+ {
+ if (sound.nr42->add)
+ {
+ if (sound.channel_four.volume < (14 * 0x111))
+ sound.channel_four.volume += 0x111;
+ }
+ else
+ {
+ if (sound.channel_four.volume > 0x111)
+ sound.channel_four.volume -= 0x111;
+ }
+
+ /* reset counter */
+ sound.channel_four.envelope_cnt = 0;
+ }
+ }
+}
+
+uint8_t sound_read_reg(uint16_t a, uint8_t v)
+{
+ switch (a)
+ {
+ /* NR1X */
+ case 0xFF10: return v | 0x80;
+ case 0xFF11: return v | 0x3F;
+ case 0xFF12: return v;
+ case 0xFF13: return v | 0xFF;
+ case 0xFF14: return v | 0xBF;
+ /* NR2X */
+ case 0xFF15: return v | 0xFF;
+ case 0xFF16: return v | 0x3F;
+ case 0xFF17: return v;
+ case 0xFF18: return v | 0xFF;
+ case 0xFF19: return v | 0xBF;
+ /* NR3X */
+ case 0xFF1A: return v | 0x7F;
+ case 0xFF1B: return v | 0xFF;
+ case 0xFF1C: return v | 0x9F;
+ case 0xFF1D: return v | 0xFF;
+ case 0xFF1E: return v | 0xBF;
+ /* NR4X */
+ case 0xFF1F: return v | 0xFF;
+ case 0xFF20: return v | 0xFF;
+ case 0xFF21: return v;
+ case 0xFF22: return v;
+ case 0xFF23: return v | 0xBF;
+ /* NR5X */
+ case 0xFF24: return v;
+ case 0xFF25: return v;
+ case 0xFF26:
+ if (sound.nr52->power)
+ return 0xf0 |
+ sound.channel_one.active |
+ (sound.channel_two.active << 1) |
+ (sound.channel_three.active << 2) |
+ (sound.channel_four.active << 3);
+ else
+ return 0x70;
+ case 0xFF27:
+ case 0xFF28:
+ case 0xFF29:
+ case 0xFF2A:
+ case 0xFF2B:
+ case 0xFF2C:
+ case 0xFF2D:
+ case 0xFF2E:
+ case 0xFF2F: return 0xFF;
+ case 0xFF30:
+ case 0xFF31:
+ case 0xFF32:
+ case 0xFF33:
+ case 0xFF34:
+ case 0xFF35:
+ case 0xFF36:
+ case 0xFF37:
+ case 0xFF38:
+ case 0xFF39:
+ case 0xFF3A:
+ case 0xFF3B:
+ case 0xFF3C:
+ case 0xFF3D:
+ case 0xFF3E:
+ case 0xFF3F:
+ if (sound.channel_three.active)
+ {
+/* if (!global_cgb && sound.channel_three.ram_access != 0)
+ {
+ printf("RAM ACCESSO NON ZERO %u - CNT %d NEXT %d\n",
+ sound.channel_three.ram_access, cycles.cnt, sound.channel_three.ram_access_next);
+ return 0xFF;
+ }*/
+ if (!global_cgb &&
+ cycles.cnt < sound.channel_three.ram_access_next)
+ return 0xFF;
+
+ return sound.wave_table[sound.channel_three.index >> 1];
+ }
+
+ default: return v;
+ }
+}
+
+void sound_set_output_rate(int freq)
+{
+ sound_output_rate = freq;
+ sound_output_rate_fifth = freq / 5;
+
+ double cpu_base_freq = 4194304;
+
+ /* calc cycles needed to generate a sample */
+ sound.sample_cycles = (uint_fast32_t) (((double) cpu_base_freq /
+ (double) sound_output_rate) * 1000);
+
+ sound.sample_cycles_next = sound.sample_cycles / 1000;
+ sound.sample_cycles_next_rounded = sound.sample_cycles_next & 0xFFFFFFFC;
+}
+
+void sound_write_reg(uint16_t a, uint8_t v)
+{
+ /* when turned off, only write to NR52 (0xFF26) is legit */
+ if (!sound.nr52->power && a != 0xFF26)
+ {
+ /* CGB mode doesnt allow any write on register during power off */
+ if (global_cgb)
+ return;
+
+ /* in DMG mode, update length is legit while no power */
+ switch (a)
+ {
+ case 0xFF11: sound.channel_one.length = 64 - (v & 0x3f); return;
+ case 0xFF16: sound.channel_two.length = 64 - (v & 0x3f); return;
+ case 0xFF1B: sound.channel_three.length = 256 - v; return;
+ case 0xFF20: sound.channel_four.length = 64 - (v & 0x3f); return;
+ default: return;
+ }
+ }
+
+ /* wave write */
+ if (a >= 0xFF30 && a <= 0xFF3F)
+ return sound_write_wave(a, v);
+
+ /* save old value */
+ uint8_t old = *((uint8_t *) mmu_addr(a));
+
+ /* confirm write on memory */
+ *((uint8_t *) mmu_addr(a)) = v;
+
+ switch (a)
+ {
+ case 0xFF10:
+
+ if (!sound.nr10->negate && sound.channel_one.sweep_neg)
+ sound.channel_one.active = 0;
+
+ break;
+
+ case 0xFF11:
+
+ /* set length as 64 - length_load */
+ sound.channel_one.length = 64 - sound.nr11->length_load;
+
+ /* update duty type */
+ switch (sound.nr11->duty)
+ {
+ /* 12.5 % */
+ case 0x00: sound.channel_one.duty = 0x80;
+ break;
+
+ /* 25% */
+ case 0x01: sound.channel_one.duty = 0x81;
+ break;
+
+ /* 50% */
+ case 0x02: sound.channel_one.duty = 0xE1;
+ break;
+
+ /* 75% */
+ case 0x03: sound.channel_one.duty = 0x7E;
+ break;
+ }
+
+ break;
+
+ case 0xFF12:
+
+ /* volume 0 = turn off the DAC = turn off channeru */
+ if (sound.nr12->volume == 0 &&
+ sound.nr12->add == 0)
+ sound.channel_one.active = 0;
+
+ break;
+
+ case 0xFF13:
+
+ /* update frequncy */
+ sound.channel_one.frequency = sound.nr13->frequency_lsb |
+ (sound.nr14->frequency_msb << 8);
+
+ /* update duty cycles */
+ sound.channel_one.duty_cycles =
+ ((2048 - sound.channel_one.frequency) * 4)
+ << global_cpu_double_speed;
+
+ break;
+
+ case 0xFF14:
+
+ /* length counter turned on */
+ if (sound.nr14->length_enable)
+ {
+ nr14_t *old_nr14 = (nr14_t *) &old;
+
+ /* give an extra length clock if */
+ /* 1) we switched from off to on the len counter */
+ /* 2) we are in the first half of len clock */
+ /* 3) actual length is not zero */
+ if ((old_nr14->length_enable == 0) &&
+ ((sound.fs_cycles_idx & 0x01) == 0x00) &&
+ (sound.channel_one.length != 0))
+ sound_length_ctrl_step_ch(sound.nr14->length_enable,
+ &sound.channel_one.length,
+ &sound.channel_one.active);
+ }
+
+ /* always update frequency, even if it's not a trigger */
+ sound.channel_one.frequency = sound.nr13->frequency_lsb |
+ (sound.nr14->frequency_msb << 8);
+
+ /* qty of cpu ticks needed for a duty change */
+ /* (1/8 of wave cycle) */
+ sound.channel_one.duty_cycles =
+ ((2048 - sound.channel_one.frequency) * 4)
+ << global_cpu_double_speed;
+
+ if (v & 0x80)
+ {
+ /* if we switch from OFF to ON, reset duty idx */
+ if (sound.channel_two.active == 0)
+ sound.channel_two.duty_idx = 0;
+
+ /* setting internal modules data with stuff taken from memory */
+ sound.channel_one.active = 1;
+ sound.channel_one.duty_cycles_next =
+ cycles.cnt + sound.channel_one.duty_cycles;
+
+ /* set the 8 phase of a duty cycle by setting 8 bits */
+ switch (sound.nr11->duty)
+ {
+ /* 12.5 % */
+ case 0x00: sound.channel_one.duty = 0x80;
+ break;
+
+ /* 25% */
+ case 0x01: sound.channel_one.duty = 0x81;
+ break;
+
+ /* 50% */
+ case 0x02: sound.channel_one.duty = 0xE1;
+ break;
+
+ /* 75% */
+ case 0x03: sound.channel_one.duty = 0x7E;
+ break;
+ }
+
+ /* calc length */
+ if (sound.channel_one.length == 0)
+ sound.channel_one.length = 64;
+
+ /* base volume */
+ sound.channel_one.volume =
+ sound.nr12->volume * 0x111;
+
+ /* reset envelope counter */
+ sound.channel_one.envelope_cnt = 0;
+
+ /* save current freq into sweep shadow register */
+ sound.channel_one.sweep_shadow_frequency =
+ sound.channel_one.frequency;
+
+ /* reset sweep timer */
+ sound.channel_one.sweep_cnt = 0;
+
+ /* reset sweep neg bool */
+ sound.channel_one.sweep_neg = 0;
+
+ /* reload the next step */
+ sound.channel_one.sweep_next = sound.nr10->sweep_period ?
+ sound.nr10->sweep_period : 8;
+
+ /* set sweep as active if period != 0 or shift != 0 */
+ if (sound.nr10->sweep_period != 0 ||
+ sound.nr10->shift != 0)
+ sound.channel_one.sweep_active = 1;
+ else
+ sound.channel_one.sweep_active = 0;
+
+ /* if shift is != 0, calc the new frequency */
+ if (sound.nr10->shift != 0)
+ {
+ uint32_t new_freq = sound_sweep_calc();
+
+ /* update all the stuff related to new frequency */
+ sound_set_frequency(new_freq);
+ }
+
+ /* if DAC is off, turn off the channel */
+ if (sound.nr12->add == 0 &&
+ sound.nr12->volume == 0)
+ sound.channel_one.active = 0;
+
+ /* extra length clock if length == 64 */
+ /* and FS is in the fist half */
+ if ((sound.fs_cycles_idx & 0x01) == 0x00 &&
+ sound.channel_one.length == 64)
+ sound_length_ctrl_step_ch(sound.nr14->length_enable,
+ &sound.channel_one.length,
+ &sound.channel_one.active);
+ }
+
+
+ break;
+
+ case 0xFF16:
+
+ sound.channel_two.length = 64 - sound.nr21->length_load;
+
+ /* update duty type */
+ switch (sound.nr21->duty)
+ {
+ /* 12.5 % */
+ case 0x00: sound.channel_two.duty = 0x80;
+ break;
+
+ /* 25% */
+ case 0x01: sound.channel_two.duty = 0x81;
+ break;
+
+ /* 50% */
+ case 0x02: sound.channel_two.duty = 0xE1;
+ break;
+
+ /* 75% */
+ case 0x03: sound.channel_two.duty = 0x7E;
+ break;
+ }
+
+ break;
+
+ case 0xFF17:
+
+ /* volume 0 = turn off the DAC = turn off channeru */
+ if (sound.nr22->volume == 0 &&
+ sound.nr22->add == 0)
+ sound.channel_two.active = 0;
+
+ break;
+
+ case 0xFF18:
+
+ /* update frequncy */
+ sound.channel_two.frequency = (sound.nr23->frequency_lsb |
+ (sound.nr24->frequency_msb << 8));
+
+ /* update duty cycles */
+ sound.channel_two.duty_cycles =
+ ((2048 - sound.channel_two.frequency) * 4)
+ << global_cpu_double_speed;
+
+ break;
+
+ case 0xFF19:
+
+ /* length counter turned on */
+ if (sound.nr24->length_enable)
+ {
+ nr24_t *old_nr24 = (nr24_t *) &old;
+
+ /* give an extra length clock if */
+ /* 1) we switched from off to on the len counter */
+ /* 2) we are in the first half of len clock */
+ /* 3) actual length is not zero */
+ if ((old_nr24->length_enable == 0) &&
+ ((sound.fs_cycles_idx & 0x01) == 0x00) &&
+ (sound.channel_two.length != 0))
+ sound_length_ctrl_step_ch(sound.nr24->length_enable,
+ &sound.channel_two.length,
+ &sound.channel_two.active);
+ }
+
+ /* always update frequency, even if it's not a trigger */
+ sound.channel_two.frequency = sound.nr23->frequency_lsb |
+ (sound.nr24->frequency_msb << 8);
+
+ /* qty of cpu ticks needed for a duty change */
+ /* (1/8 of wave cycle) */
+ sound.channel_two.duty_cycles =
+ ((2048 - sound.channel_two.frequency) * 4)
+ << global_cpu_double_speed;
+
+ if (v & 0x80)
+ {
+ /* if we switch from OFF to ON, reset duty idx */
+ if (sound.channel_two.active == 0)
+ sound.channel_two.duty_idx = 0;
+
+ /* setting internal modules data with stuff taken from memory */
+ sound.channel_two.active = 1;
+ sound.channel_two.duty_cycles_next =
+ cycles.cnt + sound.channel_two.duty_cycles;
+
+ /* set the 8 phase of a duty cycle by setting 8 bits */
+ switch (sound.nr21->duty)
+ {
+ /* 12.5 % */
+ case 0x00: sound.channel_two.duty = 0x80;
+ break;
+
+ /* 25% */
+ case 0x01: sound.channel_two.duty = 0x81;
+ break;
+
+ /* 50% */
+ case 0x02: sound.channel_two.duty = 0xE1;
+ break;
+
+ /* 75% */
+ case 0x03: sound.channel_two.duty = 0x7E;
+ break;
+ }
+
+ /* calc length */
+ if (sound.channel_two.length == 0)
+ sound.channel_two.length = 64;
+
+ /* base volume */
+ sound.channel_two.volume =
+ sound.nr22->volume * 0x111;
+
+ /* reset envelope counter */
+ sound.channel_two.envelope_cnt = 0;
+
+ /* if DAC is off, turn off the channel */
+ if (sound.nr22->add == 0 &&
+ sound.nr22->volume == 0)
+ sound.channel_two.active = 0;
+
+ /* extra length clock if length == 64 */
+ /* and FS is in the fist half */
+ if ((sound.fs_cycles_idx & 0x01) == 0x00 &&
+ sound.channel_two.length == 64)
+ sound_length_ctrl_step_ch(sound.nr24->length_enable,
+ &sound.channel_two.length,
+ &sound.channel_two.active);
+ }
+
+ break;
+
+ case 0xFF1A:
+
+ /* if DAC is off, disable the channel */
+ if (sound.nr30->dac == 0)
+ sound.channel_three.active = 0;
+
+ break;
+
+ case 0xFF1B:
+
+ sound.channel_three.length =
+ 256 - sound.nr31->length_load;
+
+ break;
+
+ case 0xFF1C:
+
+ break;
+
+ case 0xFF1E:
+
+ /* length counter turned on */
+ if (sound.nr34->length_enable)
+ {
+ nr34_t *old_nr34 = (nr34_t *) &old;
+
+ /* give an extra length clock if */
+ /* 1) we switched from off to on the len counter */
+ /* 2) we are in the first half of len clock */
+ /* 3) actual length is not zero */
+ if ((old_nr34->length_enable == 0) &&
+ ((sound.fs_cycles_idx & 0x01) == 0x00) &&
+ (sound.channel_three.length != 0))
+ sound_length_ctrl_step_ch(sound.nr34->length_enable,
+ &sound.channel_three.length,
+ &sound.channel_three.active);
+ }
+
+ if (v & 0x80)
+ {
+ uint16_t freq = sound.nr33->frequency_lsb |
+ (sound.nr34->frequency_msb << 8);
+
+ /* setting internal modules data with stuff taken from memory */
+ sound.channel_three.active = 1;
+
+ uint_fast32_t old_cycles = sound.channel_three.cycles;
+
+ /* qty of cpu ticks needed for a wave sample change */
+ sound.channel_three.cycles =
+ (((2048 - freq) * 2) + 6) << global_cpu_double_speed;
+
+
+ /* treat obscure behaviours.... */
+ if (!global_cgb &&
+ cycles.cnt + 8 == sound.channel_three.cycles_next +
+ sound.channel_three.cycles -
+ old_cycles)
+ {
+ uint8_t next =
+ ((sound.channel_three.index + 1) & 0x1F) >> 1;
+
+ if (next < 4)
+ sound.wave_table[0] = sound.wave_table[next];
+ else
+ memcpy(sound.wave_table,
+ &sound.wave_table[next & 0xfc], 4);
+ }
+
+ /* init wave table index */
+ sound.channel_three.index = 0;
+ sound.channel_three.cycles_next =
+ cycles.cnt + sound.channel_three.cycles;
+
+ /* calc length */
+ if (sound.channel_three.length == 0)
+ sound.channel_three.length = 256;
+
+ /* if DAC is off, disable the channel */
+ if (sound.nr30->dac == 0)
+ sound.channel_three.active = 0;
+
+ /* extra length clock if length == 256 */
+ /* and FS is in the fist half */
+ if ((sound.fs_cycles_idx & 0x01) == 0x00 &&
+ sound.channel_three.length == 256)
+ sound_length_ctrl_step_ch(sound.nr34->length_enable,
+ &sound.channel_three.length,
+ &sound.channel_three.active);
+
+ /* i accessed to the wave RAM... */
+ sound.channel_three.ram_access = sound.channel_three.cycles;
+
+ if (sound.channel_three.cycles % 4 == 0)
+ sound.channel_three.ram_access_next =
+ cycles.cnt + sound.channel_three.cycles;
+ else
+ sound.channel_three.ram_access_next = -1;
+
+/* printf("RAM ACCESS RICARICATO %u - CNT %d CYCLES %d \n",
+ sound.channel_three.ram_access,
+ cycles.cnt, sound.channel_three.cycles);*/
+ }
+ break;
+
+ case 0xFF20:
+
+ sound.channel_four.length = 64 - sound.nr41->length_load;
+
+ break;
+
+ case 0xFF21:
+
+ /* highest 5 bits cleared = turn off the DAC = turn off channeru */
+ if (sound.nr42->volume == 0 &&
+ sound.nr42->add == 0)
+ sound.channel_four.active = 0;
+
+ break;
+
+ case 0xFF23:
+
+ /* length counter turned on */
+ if (sound.nr44->length_enable)
+ {
+ nr44_t *old_nr44 = (nr44_t *) &old;
+
+ /* give an extra length clock if */
+ /* 1) we switched from off to on the len counter */
+ /* 2) we are in the first half of len clock */
+ /* 3) actual length is not zero */
+ if ((old_nr44->length_enable == 0) &&
+ ((sound.fs_cycles_idx & 0x01) == 0x00) &&
+ (sound.channel_four.length != 0))
+ sound_length_ctrl_step_ch(sound.nr44->length_enable,
+ &sound.channel_four.length,
+ &sound.channel_four.active);
+ }
+
+ if (v & 0x80)
+ {
+ /* setting internal modules data with stuff taken from memory */
+ sound.channel_four.active = 1;
+
+ /* calc length */
+ if (sound.channel_four.length == 0)
+ sound.channel_four.length = 64;
+
+ uint16_t divisor;
+
+ /* calc LFSR period */
+ switch (sound.nr43->divisor)
+ {
+ case 0: divisor = 8; break;
+ case 1: divisor = 16; break;
+ case 2: divisor = 32; break;
+ case 3: divisor = 48; break;
+ case 4: divisor = 64; break;
+ case 5: divisor = 80; break;
+ case 6: divisor = 96; break;
+ case 7: divisor = 112; break;
+ }
+
+ /* calc LFSR period */
+ sound.channel_four.period_lfsr = divisor << sound.nr43->shift;
+ sound.channel_four.cycles_next =
+ cycles.cnt + sound.channel_four.period_lfsr;
+
+ /* init reg to all bits to 1 */
+ sound.channel_four.reg = 0x7FFF;
+
+ /* base volume */
+ sound.channel_four.volume =
+ sound.nr42->volume * 0x111;
+
+ /* reset envelope counter */
+ sound.channel_four.envelope_cnt = 0;
+
+ /* if DAC is off, turn off the channel */
+ if (sound.nr42->add == 0 &&
+ sound.nr42->volume == 0)
+ sound.channel_four.active = 0;
+
+ /* extra length clock if length == 64 */
+ /* and FS is in the fist half */
+ if ((sound.fs_cycles_idx & 0x01) == 0x00 &&
+ sound.channel_four.length == 64)
+ sound_length_ctrl_step_ch(sound.nr44->length_enable,
+ &sound.channel_four.length,
+ &sound.channel_four.active);
+ }
+
+ break;
+
+ case 0xFF26:
+
+ if (v & 0x80)
+ {
+ /* power from off to on! */
+ if (!(old & 0x80))
+ {
+ /* reset frame sequencer so the next step will be zero */
+ sound.fs_cycles_idx = 7;
+
+ /* reset wave index */
+ sound.channel_three.index = 0;
+
+ /* wave samples are resetted */
+ bzero(sound.wave_table, 16);
+ }
+ }
+ else
+ {
+ /* power off */
+
+ /* clear all the sound memory */
+ bzero(mmu_addr(0xFF10), 22);
+
+ if (global_cgb)
+ {
+ sound.nr41->length_load = 0;
+ sound.channel_four.length = 0;
+ }
+
+ /* turn off every channeru */
+ sound.channel_one.active = 0;
+ sound.channel_two.active = 0;
+ sound.channel_three.active = 0;
+ sound.channel_four.active = 0;
+ }
+
+ }
+}
+
+void sound_write_wave(uint16_t a, uint8_t v)
+{
+ if (sound.channel_three.active)
+ {
+// if (!global_cgb && sound.channel_three.ram_access != 0)
+// return;
+ if (!global_cgb && cycles.cnt < sound.channel_three.ram_access_next)
+ return;
+
+ sound.wave_table[sound.channel_three.index >> 1] = v;
+
+ return;
+ }
+
+ sound.wave_table[a - 0xFF30] = v;
+}
+
+void sound_rebuild_wave()
+{
+ int sample;
+ uint8_t shift = (sound.nr32->volume_code == 0 ?
+ 4 : sound.nr32->volume_code - 1);
+ int i;
+
+ int min = 0;
+ int max = 0;
+
+ /* fill wave buffer */
+ for (i=0; i<16; i++)
+ {
+ /* read higher nibble */
+ sample = (sound.wave_table[i] & 0xf0) >> 4;
+
+ /* apply volume change */
+ sample >>= shift;
+
+ /* convert to signed int 16 */
+ sample = (sample * 0x1111) - 0x8000;
+
+ if (sample < min)
+ min = sample;
+ if (sample > max)
+ max = sample;
+
+ /* save it into rendered wave table */
+ sound.channel_three.wave[i * 2] = (int16_t) sample;
+
+ /* do the same with lowest nibble */
+ sample = (sound.wave_table[i] & 0x0f);
+
+ /* apply volume change */
+ sample >>= shift;
+
+ /* convert to signed int 16 */
+ sample = (sample * 0x1111) - 0x8000;
+
+ /* save it into rendered wave table */
+ sound.channel_three.wave[(i * 2) + 1] = (int16_t) sample;
+
+ if (sample < min)
+ min = sample;
+ if (sample > max)
+ max = sample;
+ }
+
+ /* set the new current sample */
+ sound.channel_three.sample =
+ sound.channel_three.wave[sound.channel_three.index];
+
+}
+
+void sound_term()
+{
+ if (sound.buf_empty)
+ {
+ sound.buf_empty = 0;
+ pthread_cond_signal(&sound_cond);
+ }
+}
+
+void sound_save_stat(FILE *fp)
+{
+ fwrite(&sound, 1, sizeof(sound_t), fp);
+}
+
+void sound_restore_stat(FILE *fp)
+{
+ fread(&sound, 1, sizeof(sound_t), fp);
+
+ sound_init_pointers();
+}
diff --git a/waterbox/pizza/lib/sound.h b/waterbox/pizza/lib/sound.h
new file mode 100644
index 0000000000..1ca9b4c0ef
--- /dev/null
+++ b/waterbox/pizza/lib/sound.h
@@ -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 .
+
+*/
+
+#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
diff --git a/waterbox/pizza/lib/testone.h b/waterbox/pizza/lib/testone.h
new file mode 100644
index 0000000000..1e9a8fc8ba
--- /dev/null
+++ b/waterbox/pizza/lib/testone.h
@@ -0,0 +1,8 @@
+//
+// Created by DVDN on 14/07/2016.
+//
+
+#ifndef PIZZABOY_TESTONE_H
+#define PIZZABOY_TESTONE_H
+
+#endif //PIZZABOY_TESTONE_H
diff --git a/waterbox/pizza/lib/timer.c b/waterbox/pizza/lib/timer.c
new file mode 100644
index 0000000000..a46c224bda
--- /dev/null
+++ b/waterbox/pizza/lib/timer.c
@@ -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 .
+
+*/
+
+#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;
+}
+
+
diff --git a/waterbox/pizza/lib/timer.h b/waterbox/pizza/lib/timer.h
new file mode 100644
index 0000000000..acdf8561c8
--- /dev/null
+++ b/waterbox/pizza/lib/timer.h
@@ -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 .
+
+*/
+
+#ifndef __TIMER_HDR__
+#define __TIMER_HDR__
+
+#include
+
+/* 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
diff --git a/waterbox/pizza/lib/utils.c b/waterbox/pizza/lib/utils.c
new file mode 100644
index 0000000000..7bb91e5f5b
--- /dev/null
+++ b/waterbox/pizza/lib/utils.c
@@ -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 .
+
+*/
+
+#ifdef __ANDROID__
+#include
+#else
+#include
+#endif
+
+#include
+#include
+#include
+
+#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);
+}
+
diff --git a/waterbox/pizza/lib/utils.h b/waterbox/pizza/lib/utils.h
new file mode 100644
index 0000000000..a5fb171e98
--- /dev/null
+++ b/waterbox/pizza/lib/utils.h
@@ -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 .
+
+*/
+
+#ifndef __UTILS_HDR__
+#define __UTILS_HDR__
+
+#include
+
+/* 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
diff --git a/waterbox/pizza/lib/z80_gameboy.h b/waterbox/pizza/lib/z80_gameboy.h
new file mode 100644
index 0000000000..db8c78c66d
--- /dev/null
+++ b/waterbox/pizza/lib/z80_gameboy.h
@@ -0,0 +1,2480 @@
+/*
+
+ 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 .
+
+*/
+
+#include
+#include
+#include
+#include
+#include
+#include "mmu.h"
+#include "z80_gameboy_regs.h"
+
+/* main struct describing CPU state */
+
+typedef struct z80_state_s
+{
+ uint8_t spare;
+ uint8_t a;
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+ uint8_t c;
+ uint8_t b;
+#else
+ uint8_t b;
+ uint8_t c;
+#endif
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+ uint8_t e;
+ uint8_t d;
+#else
+ uint8_t d;
+ uint8_t e;
+#endif
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+ uint8_t l;
+ uint8_t h;
+#else
+ uint8_t h;
+ uint8_t l;
+#endif
+
+ uint16_t sp;
+ uint16_t pc;
+
+ /* shortcuts */
+ uint16_t *bc;
+ uint16_t *de;
+ uint16_t *hl;
+ uint8_t *f;
+
+ uint32_t spare4;
+ uint32_t skip_cycle;
+
+ z80_flags_t flags;
+ uint8_t int_enable;
+
+ /* latest T-state */
+ uint16_t spare3;
+
+ /* total cycles */
+ uint64_t cycles;
+
+} z80_state_t;
+
+#define Z80_MAX_MEMORY 65536
+
+
+/* state of the Z80 CPU */
+z80_state_t state;
+
+/* precomputed flags masks */
+uint8_t zc[1 << 9];
+uint8_t z[1 << 9];
+
+/* macro to access addresses passed as parameters */
+#define ADDR mmu_read_16(state.pc + 1)
+#define NN mmu_read_16(state.pc + 2)
+
+/* dummy value for 0x06 regs resulution table */
+uint8_t dummy;
+
+/* Registers table */
+uint8_t **regs_dst;
+uint8_t **regs_src;
+
+#define FLAG_MASK_Z (1 << FLAG_OFFSET_Z)
+#define FLAG_MASK_AC (1 << FLAG_OFFSET_AC)
+#define FLAG_MASK_N (1 << FLAG_OFFSET_N)
+#define FLAG_MASK_CY (1 << FLAG_OFFSET_CY)
+
+
+/********************************/
+/* */
+/* FLAGS OPS */
+/* */
+/********************************/
+
+/* calc flags SZ with 16 bit param */
+void static inline z80_set_flags_z(unsigned int v)
+{
+ state.flags.z = (v & 0xff) == 0;
+}
+
+/* calc flags SZC with 16 bit param */
+void static inline z80_set_flags_zc(unsigned int v)
+{
+ state.flags.z = (v & 0xff) == 0;
+ state.flags.cy = (v > 0xff);
+}
+
+/* calc flags SZC with 32 bit result */
+void static inline z80_set_flags_zc_16(unsigned int v)
+{
+ state.flags.z = (v & 0xffff) == 0;
+ state.flags.cy = (v > 0xffff);
+}
+
+/* calc AC for given operands */
+void static inline z80_set_flags_ac(uint8_t a, uint8_t b,
+ unsigned int r)
+{
+ /* calc xor for AC and overflow */
+ unsigned int c = (a ^ b ^ r);
+
+ /* set AC */
+ state.flags.ac = ((c & 0x10) != 0);
+
+ return;
+}
+
+/* calc AC and overflow flag given operands (16 bit flavour) */
+void static inline z80_set_flags_ac_16(unsigned int a,
+ unsigned int b,
+ unsigned int r)
+{
+ /* calc xor for AC and overflow */
+ unsigned int c = (a ^ b ^ r);
+
+ /* set AC */
+ state.flags.ac = ((c & 0x01000) != 0);
+
+ return;
+}
+
+/* calc AC flag given operands and result */
+char static inline z80_calc_ac(uint8_t a, uint8_t b, unsigned int r)
+{
+ /* calc xor for AC and overflow */
+ unsigned int c = a ^ b ^ r;
+
+ /* AC */
+ if (c & 0x10)
+ return 1;
+
+ return 0;
+}
+
+/* calculate flags mask array */
+void static z80_calc_flags_mask_array()
+{
+ z80_flags_t f;
+ unsigned int i;
+
+ // bzero(sz53pc, sizeof(sz53pc));
+
+ /* create a mask for bit 5 and 3 and its reverse */
+/* f.spare = 0= 1; f.z = 1; f.u5 = 0; f.ac = 1; f.u3 = 0; f.p = 1; f.n = 1; f.cy = 1;
+ u53_mask = *((uint8_t *) &f);
+ r53_mask = ~(u53_mask); */
+
+ bzero(&f, 1);
+
+ for (i=0; i<512; i++)
+ {
+ f.z = ((i & 0xff) == 0);
+ f.cy = (i > 0xff);
+
+ zc[i] = *((uint8_t *) &f);
+
+ f.cy = 0;
+
+ z[i] = *((uint8_t *) &f);
+
+ /* no CY and parity */
+/* f.cy = 0;
+ f.p = parity[i & 0xff];
+ sz53p[i] = *((uint8_t *) &f);
+*/
+ /* similar but with no u3 and u5 */
+/* f.u3 = 0;
+ f.u5 = 0;
+ szp[i] = *((uint8_t *) &f);
+*/
+ /* similar but with carry and no p */
+/* f.cy = (i > 0xff);
+ f.p = 0;
+ szc[i] = *((uint8_t *) &f); */
+ }
+}
+
+
+/********************************/
+/* */
+/* INSTRUCTIONS SEGMENT */
+/* ordered by name */
+/* */
+/********************************/
+
+
+/* add A register, b parameter and Carry flag, then calculate flags */
+void static inline z80_adc(uint8_t b)
+{
+ /* calc result */
+ unsigned int result = state.a + b + state.flags.cy;
+
+ /* set flags - SZ5H3V0C */
+ *state.f = zc[result & 0x1ff];
+
+ /* set AC and overflow flags */
+ z80_set_flags_ac(state.a, b, result);
+
+ /* save result into A register */
+ state.a = (uint8_t) result;
+
+ return;
+}
+
+/* add a and b parameters (both 16 bits) and the carry, thencalculate flags */
+unsigned int static inline z80_adc_16(unsigned int a, unsigned int b)
+{
+ /* calc result */
+ unsigned int result = a + b + state.flags.cy;
+
+ /* set them - SZ5H3V0C */
+ z80_set_flags_zc_16(result);
+ state.flags.n = 0;
+
+ /* get only high byte */
+ // unsigned int r16 = (result >> 8);
+
+ /* set AC and overflow flags */
+ z80_set_flags_ac_16(a, b, result);
+
+ return result;
+}
+
+/* add A register and b parameter and calculate flags */
+void static inline z80_add(uint8_t b)
+{
+ /* calc result */
+ unsigned int result = state.a + b;
+
+ /* set them - SZ5H3P0C */
+ *state.f = zc[result & 0x1ff];
+
+ /* set AC and overflow flags - given AC and V set to 0 */
+ z80_set_flags_ac(state.a, b, result);
+
+ /* save result into A register */
+ state.a = result;
+
+ return;
+}
+
+/* add a and b parameters (both 16 bits), then calculate flags */
+unsigned int static inline z80_add_16(unsigned int a, unsigned int b)
+{
+ /* calc result */
+ unsigned int result = a + b;
+
+ /* get only high byte */
+ // uint8_t r16 = (result >> 8);
+
+ /* not a subtraction */
+ state.flags.n = 0;
+
+ /* calc xor for AC */
+ z80_set_flags_ac(a, b, result);
+
+ /* set CY */
+ state.flags.cy = (result > 0xffff);
+
+ return result;
+}
+
+/* b AND A register and calculate flags */
+void static inline z80_ana(uint8_t b)
+{
+ /* calc result */
+ uint8_t result = state.a & b;
+
+ /* set them */
+ *state.f = zc[result] | FLAG_MASK_AC;
+
+ /* save result into A register */
+ state.a = result;
+
+ return;
+}
+
+/* BIT instruction, test pos-th bit and set flags */
+void static inline z80_bit(uint8_t *v, uint8_t pos, uint8_t muffa)
+{
+ uint8_t r = *v & (0x01 << pos);
+
+ /* set flags AC,Z, N = 0 */
+ state.flags.n = 0;
+ state.flags.ac = 1;
+ state.flags.z = (r == 0) ;
+
+ return;
+}
+
+/* push the current PC on the stack and move PC to the function addr */
+int static inline z80_call(unsigned int addr)
+{
+ /* move to the next instruction */
+ state.pc += 3;
+
+ /* add 4 more cycles */
+ cycles_step();
+
+ /* save it into stack */
+ mmu_write_16(state.sp - 2, state.pc);
+
+ /* update stack pointer */
+ state.sp -= 2;
+
+ /* move PC to the called function address */
+ state.pc = addr;
+
+ return 0;
+}
+
+/* compare b parameter against A register and calculate flags */
+void static inline z80_cmp(uint8_t b)
+{
+ /* calc result */
+ unsigned int result = state.a - b;
+
+ /* set flags - SZ5H3PN* */
+ *state.f = zc[result & 0x1ff] |
+ FLAG_MASK_N;
+
+ /* set AC and overflow flags */
+ z80_set_flags_ac(state.a, b, result);
+
+ return;
+}
+
+/* compare b parameter against A register and calculate flags */
+void static inline z80_cpid(uint8_t b, int8_t add)
+{
+ /* calc result */
+ unsigned int result = state.a - b;
+
+ /* calc AC */
+ state.flags.ac = z80_calc_ac(state.a, b, result);
+
+ /* increase (add = +1) or decrease (add = -1) HL */
+ *state.hl += add;
+
+ /* decrease BC */
+ *state.bc = *state.bc - 1;
+
+ /* calc n as result - half carry flag */
+ // unsigned int n = result - state.flags.ac;
+
+ /* set flags - SZ5H3P1* */
+ state.flags.z = (result & 0xff) == 0;
+
+// z80_set_flags_z(result);
+
+ /* cmp = subtraction */
+ state.flags.n = 1;
+
+ /* set P if BC != 0 */
+ // state.flags.p = (*state.bc != 0);
+
+ /* flag 3 and 5 are taken from (result - ac) and not the result */
+ /* and u5 is taken exceptionally from the bit 1 */
+// state.flags.u5 = (n & 0x0002) != 0;
+// state.flags.u3 = (n & 0x0008) != 0;
+
+ return;
+}
+
+/* DAA instruction... what else? */
+void static inline z80_daa()
+{
+ unsigned int a = state.a;
+ uint8_t al = state.a & 0x0f;
+
+ if (state.flags.n)
+ {
+ if (state.flags.ac)
+ a = (a - 6) & 0xFF;
+
+ if (state.flags.cy)
+ a -= 0x60;
+ }
+ else
+ {
+ if (al > 9 || state.flags.ac)
+ {
+ state.flags.ac = (al > 9);
+ a += 6;
+ }
+
+ if (state.flags.cy || ((a & 0x1f0) > 0x90))
+ a += 0x60;
+ }
+
+ if (a & 0x0100) state.flags.cy = 1;
+
+ /* set computer A value */
+ state.a = a & 0xff;
+
+ state.flags.ac = 0;
+ state.flags.z = (state.a == 0);
+
+ return;
+}
+
+/* DAA instruction... what else? */
+void static inline z80_daa_ignore_n()
+{
+ unsigned int a = state.a;
+ uint8_t al = state.a & 0x0f;
+
+ if (al > 9 || state.flags.ac)
+ {
+ state.flags.ac = (al > 9);
+ a += 6;
+ }
+
+ if (state.flags.cy || ((a & 0x1f0) > 0x90))
+ a += 0x60;
+
+ if (a & 0x0100) state.flags.cy = 1;
+
+ /* set computer A value */
+ state.a = a & 0xff;
+
+ /* reset H flag */
+ state.flags.z = (state.a == 0x00);
+ state.flags.ac = 0;
+
+ /* and its flags */
+ // z80_set_flags_sz53p(state.a);
+
+ return;
+}
+
+/* add a and b parameters (both 16 bits) and the carry, thencalculate flags */
+unsigned int static inline dad_16(unsigned int a, unsigned int b)
+{
+ /* calc result */
+ unsigned int result = a + b;
+
+ /* reset n */
+ state.flags.n = 0;
+
+ /* calc xor for AC and overflow */
+ unsigned int c = a ^ b ^ result;
+
+ /* set AC */
+ state.flags.ac = ((c & 0x1000) != 0);
+
+ /* set CY */
+ state.flags.cy = (result > 0xffff);
+
+ return result;
+}
+
+/* dec the operand and return result increased by one */
+uint8_t static inline z80_dcr(uint8_t b)
+{
+ unsigned int result = b - 1;
+
+ /* set flags - SZ5H3V1* */
+ z80_set_flags_z(result);
+
+ /* it's a subtraction */
+ state.flags.n = 1;
+
+ /* set overflow and AC */
+ z80_set_flags_ac(b, 1, result);
+
+// state.flags.ac = 0;
+
+ return result;
+}
+
+/* inc the operand and return result increased by one */
+uint8_t static inline z80_inr(uint8_t b)
+{
+ unsigned int result = b + 1;
+
+ /* set flags - SZ5H3V1* */
+ z80_set_flags_z(result);
+
+ /* it's not a subtraction */
+ state.flags.n = 0;
+
+ /* set overflow and AC */
+ z80_set_flags_ac(1, b, result);
+
+ return result;
+}
+
+/* same as call, but save on the stack the current PC instead of next instr */
+int static inline z80_intr(unsigned int addr)
+{
+ /* push the current PC into stack */
+ mmu_write_16(state.sp - 2, state.pc);
+
+ cycles_step();
+
+ /* update stack pointer */
+ state.sp -= 2;
+
+ /* move PC to the called function address */
+ state.pc = addr;
+
+ return 0;
+}
+
+/* copy (HL) in (DE) and decrease HL, DE and BC */
+void static inline z80_ldd()
+{
+ uint8_t byte;
+
+ /* copy! */
+ mmu_move(*state.de, *state.hl);
+
+ /* get last moved byte and sum A */
+ byte = mmu_read(*state.de);
+ byte += state.a;
+
+ /* decrease HL, DE and BC */
+ *state.hl = *state.hl - 1;
+ *state.de = *state.de - 1;
+ *state.bc = *state.bc - 1;
+
+ /* reset flags - preserve ZC */
+ *state.f &= FLAG_MASK_Z |
+ FLAG_MASK_CY;
+
+ return;
+}
+
+/* copy (HL) in (DE) and increase HL and DE. BC is decreased */
+void static inline z80_ldi()
+{
+ uint8_t byte;
+
+ /* copy! */
+ mmu_move(*state.de, *state.hl);
+
+ /* get last moved byte and sum A */
+ byte = mmu_read(*state.de);
+ byte += state.a;
+
+ /* u5 flag is bit 1 of last moved byte + A (WTF?) */
+ // state.flags.u5 = (byte & 0x02) >> 1;
+
+ /* u3 flag is bit 3 of last moved byte + A (WTF?) */
+ // state.flags.u3 = (byte & 0x08) >> 3;
+
+ /* decrease HL, DE and BC */
+ *state.hl = *state.hl + 1;
+ *state.de = *state.de + 1;
+ *state.bc = *state.bc - 1;
+
+ /* reset negative, half carry and parity flags */
+ state.flags.n = 0;
+ state.flags.ac = 0;
+ // state.flags.p = (*state.bc != 0);
+
+ return;
+}
+
+/* negate register A */
+void static inline z80_neg()
+{
+ /* calc result */
+ unsigned int result = 0 - state.a;
+
+ /* set flags - SZ5H3V1C */
+ *state.f = zc[result & 0x1ff] | FLAG_MASK_N;
+
+ /* set AC and overflow */
+ z80_set_flags_ac(0, state.a, result);
+
+ /* save result into A register */
+ state.a = (uint8_t) result;
+
+ return;
+}
+
+/* OR b parameter and A register and calculate flags */
+void static inline z80_ora(uint8_t b)
+{
+ state.a |= b;
+
+ /* set them SZ503P0C */
+ *state.f = zc[state.a];
+
+ return;
+}
+
+/* RES instruction, put a 0 on pos-th bit and set flags */
+uint8_t static inline z80_res(uint8_t *v, uint8_t pos)
+{
+ *v &= ~(0x01 << pos);
+
+ return *v;
+}
+
+/* pop the return address from the stack and move PC to that address */
+int static inline z80_ret()
+{
+ state.pc = mmu_read_16(state.sp);
+ state.sp += 2;
+
+ /* add 4 cycles */
+ cycles_step();
+
+ return 0;
+}
+
+/* RL (Rotate Left) instruction */
+uint8_t static inline z80_rl(uint8_t *v, char with_carry)
+{
+ uint8_t carry;
+
+ /* apply RLC to the memory pointed byte */
+ carry = (*v & 0x80) >> 7;
+ *v = *v << 1;
+
+ if (with_carry)
+ *v |= carry;
+ else
+ *v |= state.flags.cy;
+
+ /* set flags - SZ503P0C */
+ *state.f = 0; // sz53p[*v];
+
+ state.flags.z = (*v == 0);
+ state.flags.cy = carry;
+
+ return *v;
+}
+
+/* RLA instruction */
+uint8_t static inline z80_rla(uint8_t *v, char with_carry)
+{
+ uint8_t carry;
+
+ /* apply RLA to the memory pointed byte */
+ carry = (*v & 0x80) >> 7;
+ *v = *v << 1;
+
+ if (with_carry)
+ *v |= carry;
+ else
+ *v |= state.flags.cy;
+
+ /* reset flags */
+ *state.f = 0;
+
+ /* just set carry */
+ state.flags.cy = carry;
+
+ return *v;
+}
+
+/* RLD instruction */
+void static inline z80_rld()
+{
+ uint8_t hl = mmu_read(*state.hl);
+
+ /* save lowest A 4 bits */
+ uint8_t al = state.a & 0x0f;
+
+ /* A lowest bits are overwritten by (HL) highest ones */
+ state.a &= 0xf0;
+ state.a |= (hl >> 4);
+
+ /* (HL) highest bits are overwritten by (HL) lowest ones */
+ hl <<= 4;
+
+ /* finally, (HL) lowest bits are overwritten by A lowest */
+ hl &= 0xf0;
+ hl |= al;
+
+ /* set (HL) with his new value ((HL) low | A low) */
+ mmu_write(*state.hl, hl);
+
+ /* reset flags - preserve CY */
+ *state.f &= FLAG_MASK_CY;
+
+ /* set flags - SZ503P0* */
+ *state.f |= z[state.a];
+
+ return;
+}
+
+/* RR instruction */
+uint8_t static inline z80_rr(uint8_t *v, char with_carry)
+{
+ uint8_t carry;
+
+ /* apply RRC to the memory pointed byte */
+ carry = *v & 0x01;
+ *v = (*v >> 1);
+
+ /* 7th bit taken from old bit 0 or from CY */
+ if (with_carry)
+ *v |= (carry << 7);
+ else
+ *v |= (state.flags.cy << 7);
+
+ /* set flags - SZ503P0C */
+ *state.f = z[*v];
+
+ state.flags.cy = carry;
+
+ return *v;
+}
+
+/* RRA instruction */
+uint8_t static inline z80_rra(uint8_t *v, char with_carry)
+{
+ uint8_t carry;
+
+ /* apply RRC to the memory pointed byte */
+ carry = *v & 0x01;
+ *v = (*v >> 1);
+
+ /* 7th bit taken from old bit 0 or from CY */
+ if (with_carry)
+ *v |= (carry << 7);
+ else
+ *v |= (state.flags.cy << 7);
+
+ /* reset flags */
+ *state.f = 0;
+
+ state.flags.cy = carry;
+
+// state.flags.n = 0;
+// state.flags.ac = 0;
+
+ /* copy bit 3 and 5 of the result */
+ // state.flags.u3 = ((*v & 0x08) != 0);
+ // state.flags.u5 = ((*v & 0x20) != 0);
+
+ return *v;
+}
+
+/* RRD instruction */
+void static inline z80_rrd()
+{
+ uint8_t hl = mmu_read(*state.hl);
+
+ /* save lowest (HL) 4 bits */
+ uint8_t hll = hl & 0x0f;
+
+ /* (HL) lowest bits are overwritten by (HL) highest ones */
+ hl >>= 4;
+
+ /* (HL) highest bits are overwritten by A lowest ones */
+ hl |= ((state.a & 0x0f) << 4);
+
+ /* set (HL) with his new value (A low | (HL) high) */
+ mmu_write(*state.hl, hl);
+
+ /* finally, A lowest bits are overwritten by (HL) lowest */
+ state.a &= 0xf0;
+ state.a |= hll;
+
+ /* reset flags - preserve CY */
+ *state.f &= FLAG_MASK_CY;
+
+ /* set flags - SZ503P0* */
+ *state.f |= z[state.a];
+
+ return;
+}
+
+/* subtract b parameter and Carry from A register and calculate flags */
+void static inline z80_sbc(uint8_t b)
+{
+ /* calc result */
+ unsigned int result = state.a - b - state.flags.cy;
+
+ /* set flags - ZC and N = 1 */
+ *state.f = zc[result & 0x1ff] | FLAG_MASK_N;
+
+ /* set AC */
+ z80_set_flags_ac(state.a, b, result);
+
+ /* save result into A register */
+ state.a = (uint8_t) result;
+
+ return;
+}
+
+/* subtract a and b parameters (both 16 bits) and the carry, then calculate flags */
+unsigned int static inline z80_sbc_16(unsigned int a, unsigned int b)
+{
+ /* calc result */
+ unsigned int result = a - b - state.flags.cy;
+
+ /* set flags - SZ5H3V1C */
+ z80_set_flags_zc_16(result);
+ state.flags.n = 1;
+
+ /* get only high byte */
+ // unsigned int r16 = (result >> 8);
+
+ /* set AC and overflow flags */
+ z80_set_flags_ac_16(a, b, result);
+
+ return result;
+}
+
+/* SET instruction, put a 1 on pos-th bit and set flags */
+uint8_t static inline z80_set(uint8_t *v, uint8_t pos)
+{
+ *v |= (0x01 << pos);
+
+ return *v;
+}
+
+/* SL instruction (SLA = v * 2, SLL = v * 2 + 1) */
+uint8_t static inline z80_sl(uint8_t *v, char one_insertion)
+{
+ /* move pointed value to local (gives an huge boost in perf!) */
+ uint8_t l = *v;
+
+ /* apply SL to the memory pointed byte */
+ uint8_t cy = (l & 0x80) != 0;
+ l = (l << 1) | one_insertion;
+
+ /* set flags - SZ503P0C */
+ *state.f = z[l];
+
+ state.flags.cy = cy;
+
+ /* re-assign local value */
+ *v = l;
+
+ return l;
+}
+
+/* SR instruction (SRA = preserve 8th bit, SRL = discard 8th bit) */
+uint8_t static inline z80_sr(uint8_t *v, char preserve)
+{
+ uint8_t bit = 0;
+
+ /* save the bit 0 */
+ uint8_t cy = (*v & 0x01);
+
+ /* apply SL to the memory pointed byte */
+ if (preserve)
+ bit = *v & 0x80;
+
+ /* move 1 pos right and restore highest bit (in case of SRA) */
+ *v = (*v >> 1) | bit;
+
+ /* set flags - SZ503P0C */
+ *state.f = z[*v];
+
+ state.flags.cy = cy;
+
+ return *v;
+}
+
+/* subtract b parameter from A register and calculate flags */
+void static inline z80_sub(uint8_t b)
+{
+ /* calc result */
+ unsigned int result = state.a - b;
+
+ /* set them - SZ5H3V1C */
+ *state.f = zc[result & 0x1ff] | FLAG_MASK_N;
+
+ /* set AC and overflow flags */
+ z80_set_flags_ac(state.a, b, result);
+
+ /* save result into A register */
+ state.a = (uint8_t) result;
+
+ return;
+}
+
+/* xor b parameter and A register and calculate flags */
+void static inline z80_xra(uint8_t b)
+{
+ /* calc result */
+ state.a ^= b;
+
+ /* set them SZ503P00 */
+ *state.f = z[state.a];
+
+ return;
+}
+
+
+
+/********************************/
+/* */
+/* INSTRUCTIONS BRANCHES */
+/* */
+/********************************/
+
+
+/* Z80 extended OPs */
+int static inline z80_ext_cb_execute()
+{
+ uint8_t byte = 1;
+ int b = 2;
+
+ /* CB family (ROT, BIT, RES, SET) */
+ uint8_t cbfam;
+
+ /* CB operation */
+ uint8_t cbop;
+
+ /* choosen register */
+ uint8_t reg;
+
+ /* get CB code */
+ uint8_t code = mmu_read(state.pc + 1);
+
+ /* extract family */
+ cbfam = code >> 6;
+
+ /* extract involved register */
+ reg = code & 0x07;
+
+ /* if reg == 0x06, refresh the pointer */
+ // if (reg == 0x06 && code != 0x36)
+ // {
+ /* add 4 more cycles for reading data from memory */
+ // cycles_step();
+
+ // regs_src[0x06] = mmu_addr(*state.hl);
+ // }
+
+ switch (cbfam)
+ {
+ /* Rotate Family */
+ case 0x00: cbop = code & 0xf8;
+
+ switch(cbop)
+ {
+ /* RLC REG */
+ case 0x00: if (reg == 0x06)
+ {
+ byte = mmu_read(*state.hl);
+ mmu_write(*state.hl, z80_rl(&byte, 1));
+ }
+ else
+ z80_rl(regs_src[reg], 1);
+ break;
+
+ /* RRC REG */
+ case 0x08: if (reg == 0x06)
+ {
+ byte = mmu_read(*state.hl);
+ mmu_write(*state.hl, z80_rr(&byte, 1));
+ }
+ else
+ z80_rr(regs_src[reg], 1);
+
+ break;
+
+ /* RL REG */
+ case 0x10: if (reg == 0x06)
+ {
+ byte = mmu_read(*state.hl);
+ mmu_write(*state.hl, z80_rl(&byte, 0));
+ }
+ else
+ z80_rl(regs_src[reg], 0);
+
+ break;
+
+ /* RR REG */
+ case 0x18: if (reg == 0x06)
+ {
+ byte = mmu_read(*state.hl);
+ mmu_write(*state.hl, z80_rr(&byte, 0));
+ }
+ else
+ z80_rr(regs_src[reg], 0);
+
+ break;
+
+ /* SLA REG */
+ case 0x20: if (reg == 0x06)
+ {
+ byte = mmu_read(*state.hl);
+ mmu_write(*state.hl, z80_sl(&byte, 0));
+ }
+ else
+ z80_sl(regs_src[reg], 0);
+
+ break;
+
+ /* SRA REG */
+ case 0x28: if (reg == 0x06)
+ {
+ byte = mmu_read(*state.hl);
+ mmu_write(*state.hl, z80_sr(&byte, 1));
+ }
+ else
+ z80_sr(regs_src[reg], 1);
+
+ break;
+
+ /* SWAP */
+ case 0x30:
+ switch (code & 0x37)
+ {
+ /* SWAP B */
+ case 0x30: byte = state.b;
+ state.b = ((byte & 0xf0) >> 4) |
+ ((byte & 0x0f) << 4);
+ break;
+
+ /* SWAP C */
+ case 0x31: byte = state.c;
+ state.c = ((byte & 0xf0) >> 4) |
+ ((byte & 0x0f) << 4);
+ break;
+
+ /* SWAP D */
+ case 0x32: byte = state.d;
+ state.d = ((byte & 0xf0) >> 4) |
+ ((byte & 0x0f) << 4);
+ break;
+
+ /* SWAP E */
+ case 0x33: byte = state.e;
+ state.e = ((byte & 0xf0) >> 4) |
+ ((byte & 0x0f) << 4);
+ break;
+
+ /* SWAP H */
+ case 0x34: byte = state.h;
+ state.h = ((byte & 0xf0) >> 4) |
+ ((byte & 0x0f) << 4);
+ break;
+
+ /* SWAP L */
+ case 0x35: byte = state.l;
+ state.l = ((byte & 0xf0) >> 4) |
+ ((byte & 0x0f) << 4);
+ break;
+
+ /* SWAP *HL */
+ case 0x36: byte = mmu_read(*state.hl);
+ mmu_write(*state.hl,
+ ((byte & 0xf0) >> 4) |
+ ((byte & 0x0f) << 4));
+
+ break;
+
+ /* SWAP A */
+ case 0x37:
+ byte = state.a;
+ state.a = ((byte & 0xf0) >> 4) |
+ ((byte & 0x0f) << 4);
+ break;
+
+ }
+
+ /* swap functions set Z flags */
+ state.flags.z = (byte == 0x00);
+
+ /* reset all the others */
+ state.flags.ac = 0;
+ state.flags.cy = 0;
+ state.flags.n = 0;
+
+ break;
+
+ /* SRL REG */
+ case 0x38: if (reg == 0x06)
+ {
+ byte = mmu_read(*state.hl);
+ mmu_write(*state.hl, z80_sr(&byte, 0));
+ }
+ else
+ z80_sr(regs_src[reg], 0);
+
+ break;
+ }
+
+ /* accessing HL needs more T-cycles */
+ //if (reg == 0x06 && code != 0x36)
+ // cycles_step();
+
+ /* accessing HL needs more T-cycles */
+// if (reg == 0x06)
+// cycles_step();
+
+ break;
+
+ /* BIT Family */
+ case 0x01: if (reg == 0x06)
+ {
+ byte = mmu_read(*state.hl);
+ z80_bit(&byte, (code >> 3) & 0x07,
+ (uint8_t) *state.hl);
+ }
+ else
+ z80_bit(regs_src[reg], (code >> 3) & 0x07,
+ *regs_src[reg]);
+ break;
+
+ /* RES Family */
+ case 0x02: if (reg == 0x06)
+ {
+ byte = mmu_read(*state.hl);
+ mmu_write(*state.hl, z80_res(&byte, (code >> 3) & 0x07));
+ }
+ else
+ z80_res(regs_src[reg], (code >> 3) & 0x07);
+
+ break;
+
+ /* SET Family */
+ case 0x03: if (reg == 0x06)
+ {
+ byte = mmu_read(*state.hl);
+ mmu_write(*state.hl, z80_set(&byte, (code >> 3) & 0x07));
+ }
+ else
+ z80_set(regs_src[reg], (code >> 3) & 0x07);
+
+ break;
+
+// default: printf("Unimplemented CB family: %02x\n",
+// cbfam);
+
+ }
+
+ return b;
+}
+
+
+/* really execute the OP. Could be ran by normal execution or *
+ * because an interrupt occours */
+int static inline z80_execute(unsigned char code)
+{
+ int b = 1;
+ uint8_t *p;
+ uint8_t byte = 1;
+ uint8_t byte2 = 1;
+ unsigned int result;
+ uint_fast16_t addr;
+
+ switch (code)
+ {
+ /* NOP */
+ case 0x00: break;
+
+ /* LXI B */
+ case 0x01: *state.bc = ADDR;
+ b = 3;
+ break;
+
+ /* STAX B */
+ case 0x02: mmu_write(*state.bc, state.a);
+ break;
+
+ /* INX B */
+ case 0x03: (*state.bc)++;
+ cycles_step();
+ break;
+
+ /* INR B */
+ case 0x04: state.b = z80_inr(state.b);
+ break;
+
+ /* DCR B */
+ case 0x05: state.b = z80_dcr(state.b);
+ break;
+
+ /* MVI B */
+ case 0x06: state.b = mmu_read(state.pc + 1);
+ b = 2;
+ break;
+
+ /* RLCA */
+ case 0x07: z80_rla(&state.a, 1);
+ break;
+
+ /* LD (NN),SP */
+ case 0x08: mmu_write_16(ADDR, state.sp);
+ b = 3;
+ break;
+
+ /* DAD B */
+ case 0x09: *state.hl = dad_16(*state.hl, *state.bc);
+
+ /* needs 4 more cycles */
+ cycles_step();
+
+ break;
+
+ /* LDAX B */
+ case 0x0A: state.a = mmu_read(*state.bc);
+ break;
+
+ /* DCX B */
+ case 0x0B: (*state.bc)--;
+ cycles_step();
+ break;
+
+ /* INR C */
+ case 0x0C: state.c = z80_inr(state.c);
+ break;
+
+ /* DCR C */
+ case 0x0D: state.c = z80_dcr(state.c);
+ break;
+
+ /* MVI C */
+ case 0x0E: state.c = mmu_read(state.pc + 1);
+ b = 2;
+ break;
+
+ /* RRC */
+ case 0x0F: z80_rra(&state.a, 1);
+ break;
+
+ /* STOP */
+ case 0x10: b = 2;
+ break;
+
+ /* LXI D */
+ case 0x11: *state.de = ADDR;
+ b = 3;
+ break;
+
+ /* STAX D */
+ case 0x12: mmu_write(*state.de, state.a);
+ break;
+
+ /* INX D */
+ case 0x13: (*state.de)++;
+ cycles_step();
+ break;
+
+ /* INR D */
+ case 0x14: state.d = z80_inr(state.d);
+ break;
+
+ /* DCR D */
+ case 0x15: state.d = z80_dcr(state.d);
+ break;
+
+ /* MVI D */
+ case 0x16: state.d = mmu_read(state.pc + 1);
+ b = 2;
+ break;
+
+ /* RLA */
+ case 0x17: z80_rla(&state.a, 0);
+ break;
+
+ /* JR */
+ case 0x18: cycles_step();
+ state.pc += (int8_t) mmu_read(state.pc + 1);
+ b = 2;
+ break;
+
+ /* DAD D */
+ case 0x19: *state.hl = dad_16(*state.hl, *state.de);
+
+ /* needs 4 more cycles */
+ cycles_step();
+
+ break;
+
+ /* LDAX D */
+ case 0x1A: state.a = mmu_read(*state.de);
+ break;
+
+ /* DCX D */
+ case 0x1B: (*state.de)--;
+ cycles_step();
+ break;
+
+ /* INR E */
+ case 0x1C: state.e = z80_inr(state.e);
+ break;
+
+ /* DCR E */
+ case 0x1D: state.e = z80_dcr(state.e);
+ break;
+
+ /* MVI E */
+ case 0x1E: state.e = mmu_read(state.pc + 1);
+ b = 2;
+ break;
+
+ /* RRA */
+ case 0x1F: z80_rra(&state.a, 0);
+ break;
+
+ /* JRNZ */
+ case 0x20: cycles_step();
+
+ if (!state.flags.z)
+ state.pc += (int8_t) mmu_read(state.pc + 1);
+
+ b = 2;
+ break;
+
+ /* LXI H */
+ case 0x21: *state.hl = ADDR;
+ b = 3;
+ break;
+
+ /* LDI (HL), A */
+ case 0x22: mmu_write(*state.hl, state.a);
+ (*state.hl)++;
+ break;
+
+ /* INX H */
+ case 0x23: (*state.hl)++;
+ cycles_step();
+ break;
+
+ /* INR H */
+ case 0x24: state.h = z80_inr(state.h);
+ break;
+
+ /* DCR H */
+ case 0x25: state.h = z80_dcr(state.h);
+ break;
+
+ /* MVI H */
+ case 0x26: state.h = mmu_read(state.pc + 1);
+ b = 2;
+ break;
+
+ /* DAA */
+ case 0x27: z80_daa();
+ break;
+
+ /* JRZ */
+ case 0x28: cycles_step();
+ if (state.flags.z)
+ state.pc += (int8_t) mmu_read(state.pc + 1);
+
+ b = 2;
+ break;
+
+ /* DAD H */
+ case 0x29: *state.hl = dad_16(*state.hl, *state.hl);
+
+ /* needs 4 more cycles */
+ cycles_step();
+
+ break;
+
+ /* LDI A,(HL) */
+ case 0x2A: state.a = mmu_read(*state.hl);
+ (*state.hl)++;
+ break;
+
+ /* DCX H */
+ case 0x2B: (*state.hl)--;
+ cycles_step();
+ break;
+
+ /* INR L */
+ case 0x2C: state.l = z80_inr(state.l);
+ break;
+
+ /* DCR L */
+ case 0x2D: state.l = z80_dcr(state.l);
+ break;
+
+ /* MVI L */
+ case 0x2E: state.l = mmu_read(state.pc + 1);
+ b = 2;
+ break;
+
+ /* CMA A */
+ case 0x2F: state.a = ~state.a;
+ state.flags.ac = 1;
+ state.flags.n = 1;
+ break;
+
+ /* JRNC */
+ case 0x30: cycles_step();
+
+ if (!state.flags.cy)
+ state.pc += (int8_t) mmu_read(state.pc + 1);
+
+ b = 2;
+ break;
+
+ /* LXI SP */
+ case 0x31: state.sp = ADDR;
+ b = 3;
+ break;
+
+ /* LDD (HL), A */
+ case 0x32: mmu_write(*state.hl, state.a);
+ (*state.hl)--;
+ break;
+
+ /* INX SP */
+ case 0x33: state.sp++;
+ cycles_step();
+ break;
+
+ /* INR M */
+ case 0x34: mmu_write(*state.hl, z80_inr(mmu_read(*state.hl)));
+ break;
+
+ /* DCR M */
+ case 0x35: mmu_write(*state.hl, z80_dcr(mmu_read(*state.hl)));
+ break;
+
+ /* MVI M */
+ case 0x36: mmu_move(*state.hl, state.pc + 1);
+ b = 2;
+ break;
+
+ /* STC */
+ case 0x37: state.flags.cy = 1;
+ state.flags.ac = 0;
+ state.flags.n = 0;
+ break;
+
+ /* JRC */
+ case 0x38: cycles_step();
+ if (state.flags.cy)
+ state.pc += (int8_t) mmu_read(state.pc + 1);
+
+ b = 2;
+ break;
+
+ /* DAD SP */
+ case 0x39: *state.hl = dad_16(*state.hl, state.sp);
+
+ /* needs 4 more cycles */
+ cycles_step();
+
+ break;
+
+ /* LDD A,(HL) */
+ case 0x3A: state.a = mmu_read(*state.hl);
+ (*state.hl)--;
+ break;
+
+ /* DCX SP */
+ case 0x3B: state.sp--;
+ cycles_step();
+ break;
+
+ /* INR A */
+ case 0x3C: state.a = z80_inr(state.a);
+ break;
+
+ /* DCR A */
+ case 0x3D: state.a = z80_dcr(state.a);
+ break;
+
+ /* MVI A */
+ case 0x3E: state.a = mmu_read(state.pc + 1);
+ b = 2;
+ break;
+
+ /* CCF */
+ case 0x3F: state.flags.ac = 0;
+ state.flags.cy = !state.flags.cy;
+ state.flags.n = 0;
+
+ break;
+
+ /* MOV B,B */
+ case 0x40: state.b = state.b;
+ break;
+
+ /* MOV B,C */
+ case 0x41: state.b = state.c;
+ break;
+
+ /* MOV B,D */
+ case 0x42: state.b = state.d;
+ break;
+
+ /* MOV B,E */
+ case 0x43: state.b = state.e;
+ break;
+
+ /* MOV B,H */
+ case 0x44: state.b = state.h;
+ break;
+
+ /* MOV B,L */
+ case 0x45: state.b = state.l;
+ break;
+
+ /* MOV B,M */
+ case 0x46: state.b = mmu_read(*state.hl);
+ break;
+
+ /* MOV B,A */
+ case 0x47: state.b = state.a;
+ break;
+
+ /* MOV C,B */
+ case 0x48: state.c = state.b;
+ break;
+
+ /* MOV C,C */
+ case 0x49: state.c = state.c;
+ break;
+
+ /* MOV C,D */
+ case 0x4A: state.c = state.d;
+ break;
+
+ /* MOV C,E */
+ case 0x4B: state.c = state.e;
+ break;
+
+ /* MOV C,H */
+ case 0x4C: state.c = state.h;
+ break;
+
+ /* MOV C,L */
+ case 0x4D: state.c = state.l;
+ break;
+
+ /* MOV C,M */
+ case 0x4E: state.c = mmu_read(*state.hl);
+ break;
+
+ /* MOV C,A */
+ case 0x4F: state.c = state.a;
+ break;
+
+ /* MOV D,B */
+ case 0x50: state.d = state.b;
+ break;
+
+ /* MOV D,C */
+ case 0x51: state.d = state.c;
+ break;
+
+ /* MOV D,D */
+ case 0x52: state.d = state.d;
+ break;
+
+ /* MOV D,E */
+ case 0x53: state.d = state.e;
+ break;
+
+ /* MOV D,H */
+ case 0x54: state.d = state.h;
+ break;
+
+ /* MOV D,L */
+ case 0x55: state.d = state.l;
+ break;
+
+ /* MOV D,M */
+ case 0x56: state.d = mmu_read(*state.hl);
+ break;
+
+ /* MOV D,A */
+ case 0x57: state.d = state.a;
+ break;
+
+ /* MOV E,B */
+ case 0x58: state.e = state.b;
+ break;
+
+ /* MOV E,C */
+ case 0x59: state.e = state.c;
+ break;
+
+ /* MOV E,D */
+ case 0x5A: state.e = state.d;
+ break;
+
+ /* MOV E,E */
+ case 0x5B: state.e = state.e;
+ break;
+
+ /* MOV E,H */
+ case 0x5C: state.e = state.h;
+ break;
+
+ /* MOV E,L */
+ case 0x5D: state.e = state.l;
+ break;
+
+ /* MOV E,M */
+ case 0x5E: state.e = mmu_read(*state.hl);
+ break;
+
+ /* MOV E,A */
+ case 0x5F: state.e = state.a;
+ break;
+
+ /* MOV H,B */
+ case 0x60: state.h = state.b;
+ break;
+
+ /* MOV H,C */
+ case 0x61: state.h = state.c;
+ break;
+
+ /* MOV H,D */
+ case 0x62: state.h = state.d;
+ break;
+
+ /* MOV H,E */
+ case 0x63: state.h = state.e;
+ break;
+
+ /* MOV H,H */
+ case 0x64: state.h = state.h;
+ break;
+
+ /* MOV H,L */
+ case 0x65: state.h = state.l;
+ break;
+
+ /* MOV H,M */
+ case 0x66: state.h = mmu_read(*state.hl);
+ break;
+
+ /* MOV H,A */
+ case 0x67: state.h = state.a;
+ break;
+
+ /* MOV L,B */
+ case 0x68: state.l = state.b;
+ break;
+
+ /* MOV L,C */
+ case 0x69: state.l = state.c;
+ break;
+
+ /* MOV L,D */
+ case 0x6A: state.l = state.d;
+ break;
+
+ /* MOV L,E */
+ case 0x6B: state.l = state.e;
+ break;
+
+ /* MOV L,H */
+ case 0x6C: state.l = state.h;
+ break;
+
+ /* MOV L,L */
+ case 0x6D: state.l = state.l;
+ break;
+
+ /* MOV L,M */
+ case 0x6E: state.l = mmu_read(*state.hl);
+ break;
+
+ /* MOV L,A */
+ case 0x6F: state.l = state.a;
+ break;
+
+ /* MOV M,B */
+ case 0x70: mmu_write(*state.hl, state.b);
+ break;
+
+ /* MOV M,C */
+ case 0x71: mmu_write(*state.hl, state.c);
+ break;
+
+ /* MOV M,D */
+ case 0x72: mmu_write(*state.hl, state.d);
+ break;
+
+ /* MOV M,E */
+ case 0x73: mmu_write(*state.hl, state.e);
+ break;
+
+ /* MOV M,H */
+ case 0x74: mmu_write(*state.hl, state.h);
+ break;
+
+ /* MOV M,L */
+ case 0x75: mmu_write(*state.hl, state.l);
+ break;
+
+ /* HLT */
+ case 0x76: return 1;
+
+ /* MOV M,A */
+ case 0x77: mmu_write(*state.hl, state.a);
+ break;
+
+ /* MOV A,B */
+ case 0x78: state.a = state.b;
+ break;
+
+ /* MOV A,C */
+ case 0x79: state.a = state.c;
+ break;
+
+ /* MOV A,D */
+ case 0x7A: state.a = state.d;
+ break;
+
+ /* MOV A,E */
+ case 0x7B: state.a = state.e;
+ break;
+
+ /* MOV A,H */
+ case 0x7C: state.a = state.h;
+ break;
+
+ /* MOV A,L */
+ case 0x7D: state.a = state.l;
+ break;
+
+ /* MOV A,M */
+ case 0x7E: state.a = mmu_read(*state.hl);
+ break;
+
+ /* MOV A,A */
+ case 0x7F: state.a = state.a;
+ break;
+
+ /* ADD B */
+ case 0x80: z80_add(state.b);
+ break;
+
+ /* ADD C */
+ case 0x81: z80_add(state.c);
+ break;
+
+ /* ADD D */
+ case 0x82: z80_add(state.d);
+ break;
+
+ /* ADD E */
+ case 0x83: z80_add(state.e);
+ break;
+
+ /* ADD H */
+ case 0x84: z80_add(state.h);
+ break;
+
+ /* ADD L */
+ case 0x85: z80_add(state.l);
+ break;
+
+ /* ADD M */
+ case 0x86: z80_add(mmu_read(*state.hl));
+ break;
+
+ /* ADD A */
+ case 0x87: z80_add(state.a);
+ break;
+
+ /* ADC B */
+ case 0x88: z80_adc(state.b);
+ break;
+
+ /* ADC C */
+ case 0x89: z80_adc(state.c);
+ break;
+
+ /* ADC D */
+ case 0x8A: z80_adc(state.d);
+ break;
+
+ /* ADC E */
+ case 0x8B: z80_adc(state.e);
+ break;
+
+ /* ADC H */
+ case 0x8C: z80_adc(state.h);
+ break;
+
+ /* ADC L */
+ case 0x8D: z80_adc(state.l);
+ break;
+
+ /* ADC M */
+ case 0x8E: z80_adc(mmu_read(*state.hl));
+ break;
+
+ /* ADC A */
+ case 0x8F: z80_adc(state.a);
+ break;
+
+ /* SUB B */
+ case 0x90: z80_sub(state.b);
+ break;
+
+ /* SUB C */
+ case 0x91: z80_sub(state.c);
+ break;
+
+ /* SUB D */
+ case 0x92: z80_sub(state.d);
+ break;
+
+ /* SUB E */
+ case 0x93: z80_sub(state.e);
+ break;
+
+ /* SUB H */
+ case 0x94: z80_sub(state.h);
+ break;
+
+ /* SUB L */
+ case 0x95: z80_sub(state.l);
+ break;
+
+ /* SUB M */
+ case 0x96: z80_sub(mmu_read(*state.hl));
+ break;
+
+ /* SUB A */
+ case 0x97: z80_sub(state.a);
+ break;
+
+ /* SBC B */
+ case 0x98: z80_sbc(state.b);
+ break;
+
+ /* SBC C */
+ case 0x99: z80_sbc(state.c);
+ break;
+
+ /* SBC D */
+ case 0x9a: z80_sbc(state.d);
+ break;
+
+ /* SBC E */
+ case 0x9b: z80_sbc(state.e);
+ break;
+
+ /* SBC H */
+ case 0x9c: z80_sbc(state.h);
+ break;
+
+ /* SBC L */
+ case 0x9d: z80_sbc(state.l);
+ break;
+
+ /* SBC M */
+ case 0x9E: z80_sbc(mmu_read(*state.hl));
+ break;
+
+ /* SBC A */
+ case 0x9f: z80_sbc(state.a);
+ break;
+
+ /* ANA B */
+ case 0xA0: z80_ana(state.b);
+ break;
+
+ /* ANA C */
+ case 0xA1: z80_ana(state.c);
+ break;
+
+ /* ANA D */
+ case 0xA2: z80_ana(state.d);
+ break;
+
+ /* ANA E */
+ case 0xA3: z80_ana(state.e);
+ break;
+
+ /* ANA H */
+ case 0xA4: z80_ana(state.h);
+ break;
+
+ /* ANA L */
+ case 0xA5: z80_ana(state.l);
+ break;
+
+ /* ANA M */
+ case 0xA6: z80_ana(mmu_read(*state.hl));
+ break;
+
+ /* ANA A */
+ case 0xA7: z80_ana(state.a);
+ break;
+
+ /* XRA B */
+ case 0xA8: z80_xra(state.b);
+ break;
+
+ /* XRA C */
+ case 0xA9: z80_xra(state.c);
+ break;
+
+ /* XRA D */
+ case 0xAA: z80_xra(state.d);
+ break;
+
+ /* XRA E */
+ case 0xAB: z80_xra(state.e);
+ break;
+
+ /* XRA H */
+ case 0xAC: z80_xra(state.h);
+ break;
+
+ /* XRA L */
+ case 0xAD: z80_xra(state.l);
+ break;
+
+ /* XRA M */
+ case 0xAE: z80_xra(mmu_read(*state.hl));
+ break;
+
+ /* XRA A */
+ case 0xAF: z80_xra(state.a);
+ break;
+
+ /* ORA B */
+ case 0xB0: z80_ora(state.b);
+ break;
+
+ /* ORA C */
+ case 0xB1: z80_ora(state.c);
+ break;
+
+ /* ORA D */
+ case 0xB2: z80_ora(state.d);
+ break;
+
+ /* ORA E */
+ case 0xB3: z80_ora(state.e);
+ break;
+
+ /* ORA H */
+ case 0xB4: z80_ora(state.h);
+ break;
+
+ /* ORA L */
+ case 0xB5: z80_ora(state.l);
+ break;
+
+ /* ORA M */
+ case 0xB6: z80_ora(mmu_read(*state.hl));
+ break;
+
+ /* ORA A */
+ case 0xB7: z80_ora(state.a);
+ break;
+
+ /* CMP B */
+ case 0xB8: z80_cmp(state.b);
+ break;
+
+ /* CMP C */
+ case 0xB9: z80_cmp(state.c);
+ break;
+
+ /* CMP D */
+ case 0xBA: z80_cmp(state.d);
+ break;
+
+ /* CMP E */
+ case 0xBB: z80_cmp(state.e);
+ break;
+
+ /* CMP H */
+ case 0xBC: z80_cmp(state.h);
+ break;
+
+ /* CMP L */
+ case 0xBD: z80_cmp(state.l);
+ break;
+
+ /* CMP M */
+ case 0xBE: z80_cmp(mmu_read(*state.hl));
+ break;
+
+ /* CMP A */
+ case 0xBF: z80_cmp(state.a);
+ break;
+
+ /* RNZ */
+ case 0xC0: cycles_step();
+
+ if (state.flags.z == 0)
+ return z80_ret();
+
+ break;
+
+ /* POP B */
+ case 0xC1: *state.bc = mmu_read_16(state.sp);
+ state.sp += 2;
+ break;
+
+ /* JNZ addr */
+ case 0xC2: /* this will add 8 cycles */
+ addr = ADDR;
+
+ if (state.flags.z == 0)
+ {
+ /* add 4 more cycles */
+ cycles_step();
+
+ state.pc = addr;
+ return 0;
+ }
+
+ b = 3;
+ break;
+
+ /* JMP addr */
+ case 0xC3: state.pc = ADDR;
+
+ /* add 4 cycles */
+ cycles_step();
+
+ return 0;
+
+ /* CNZ */
+ case 0xC4: addr = ADDR;
+
+ if (state.flags.z == 0)
+ return z80_call(addr);
+
+ b = 3;
+ break;
+
+ /* PUSH B */
+ case 0xC5: cycles_step();
+ mmu_write_16(state.sp - 2, *state.bc);
+ state.sp -= 2;
+ break;
+
+ /* ADI */
+ case 0xC6: z80_add(mmu_read(state.pc + 1));
+ b = 2;
+ break;
+
+ /* RST 0 */
+ case 0xC7: state.pc++;
+ return z80_intr(0x0008 * 0);
+
+ /* RZ */
+ case 0xC8: cycles_step();
+
+ if (state.flags.z)
+ return z80_ret();
+
+ break;
+
+ /* RET */
+ case 0xC9: return z80_ret();
+
+ /* JZ */
+ case 0xCA: /* add 8 cycles */
+ addr = ADDR;
+
+ if (state.flags.z)
+ {
+ /* add 4 more cycles */
+ cycles_step();
+
+ state.pc = addr;
+ return 0;
+ }
+
+ b = 3;
+ break;
+
+ /* CB */
+ case 0xCB: b = z80_ext_cb_execute();
+ break;
+
+ /* CZ */
+ case 0xCC: addr = ADDR;
+
+ if (state.flags.z)
+ return z80_call(addr);
+
+ b = 3;
+ break;
+
+ /* CALL addr */
+ case 0xCD: return z80_call(ADDR);
+
+ /* ACI */
+ case 0xCE: z80_adc(mmu_read(state.pc + 1));
+ b = 2;
+ break;
+
+ /* RST 1 */
+ case 0xCF: state.pc++;
+ return z80_intr(0x0008 * 1);
+
+ /* RNC */
+ case 0xD0: cycles_step();
+
+ if (state.flags.cy == 0)
+ return z80_ret();
+
+ break;
+
+ /* POP D */
+ case 0xD1: *state.de = mmu_read_16(state.sp);
+ state.sp += 2;
+ break;
+
+ /* JNC */
+ case 0xD2: /* add 8 cycles */
+ addr = ADDR;
+
+ if (state.flags.cy == 0)
+ {
+ /* add 4 more cycles */
+ cycles_step();
+
+ state.pc = addr;
+ return 0;
+ }
+
+ b = 3;
+ break;
+
+ /* not present */
+ case 0xD3: // b = 2;
+ break;
+
+ /* CNC */
+ case 0xD4: addr = ADDR;
+
+ if (state.flags.cy == 0)
+ return z80_call(addr);
+
+ b = 3;
+ break;
+
+ /* PUSH D */
+ case 0xD5: cycles_step();
+ mmu_write_16(state.sp - 2, *state.de);
+ state.sp -= 2;
+ break;
+
+ /* SUI */
+ case 0xD6: z80_sub(mmu_read(state.pc + 1));
+ b = 2;
+ break;
+
+ /* RST 2 */
+ case 0xD7: state.pc++;
+ return z80_intr(0x0008 * 2);
+
+ /* RC */
+ case 0xD8: cycles_step();
+
+ if (state.flags.cy)
+ return z80_ret();
+
+ break;
+
+ /* RETI */
+ case 0xD9: state.int_enable = 1;
+ return z80_ret();
+ break;
+
+ /* JC */
+ case 0xDA: /* add 8 cycles */
+ addr = ADDR;
+
+ if (state.flags.cy)
+ {
+ /* add 4 more cycles */
+ cycles_step();
+
+ state.pc = addr;
+ return 0;
+ }
+
+ b = 3;
+ break;
+
+ /* not present */
+ case 0xDB: break;
+
+ /* CC */
+ case 0xDC: addr = ADDR;
+
+ if (state.flags.cy)
+ return z80_call(addr);
+
+ b = 3;
+ break;
+
+ /* SBI */
+ case 0xDE: z80_sbc(mmu_read(state.pc + 1));
+ b = 2;
+ break;
+
+ /* RST 3 */
+ case 0xDF: state.pc++;
+ return z80_intr(0x0008 * 3);
+
+ /* LD (FF00+N),A */
+ case 0xE0: mmu_write(0xFF00 + mmu_read(state.pc + 1), state.a);
+ b = 2;
+ break;
+
+ /* POP H */
+ case 0xE1: *state.hl = mmu_read_16(state.sp);
+ state.sp += 2;
+ break;
+
+ /* LD (FF00+C),A */
+ case 0xE2: mmu_write(0xFF00 + state.c, state.a);
+ break;
+
+ /* not present on Gameboy Z80 */
+ case 0xE3:
+ case 0xE4: break;
+
+ /* PUSH H */
+ case 0xE5: cycles_step();
+ mmu_write_16(state.sp - 2, *state.hl);
+ state.sp -= 2;
+ break;
+
+ /* ANI */
+ case 0xE6: z80_ana(mmu_read(state.pc + 1));
+ b = 2;
+ break;
+
+ /* RST 4 */
+ case 0xE7: state.pc++;
+ return z80_intr(0x0008 * 4);
+
+ /* ADD SP,dd */
+ case 0xE8: byte = mmu_read(state.pc + 1);
+ byte2 = (uint8_t) (state.sp & 0x00ff);
+ result = byte2 + byte;
+
+ state.flags.z = 0;
+ state.flags.n = 0;
+
+ state.flags.cy = (result > 0xff);
+
+ /* add 8 cycles */
+ cycles_step();
+ cycles_step();
+
+ /* calc xor for AC */
+ z80_set_flags_ac(byte2, byte, result);
+
+ /* set sp */
+ state.sp += (int8_t) byte; // result & 0xffff;
+
+ b = 2;
+ break;
+
+ /* PCHL */
+ case 0xE9: state.pc = *state.hl;
+ return 0;
+
+ /* LD (NN),A */
+ case 0xEA: mmu_write(ADDR, state.a);
+ b = 3;
+ break;
+
+ /* not present on Gameboy Z80 */
+ case 0xEB:
+ case 0xEC:
+ case 0xED: break;
+
+ /* XRI */
+ case 0xEE: z80_xra(mmu_read(state.pc + 1));
+ b = 2;
+ break;
+
+ /* RST 5 */
+ case 0xEF: state.pc++;
+ return z80_intr(0x0008 * 5);
+
+ /* LD A,(FF00+N) */
+ case 0xF0: state.a = mmu_read(0xFF00 + mmu_read(state.pc + 1));
+ b = 2;
+ break;
+
+ /* POP PSW */
+ case 0xF1: p = (uint8_t *) &state.flags;
+ *p = (mmu_read(state.sp) & 0xf0);
+ state.a = mmu_read(state.sp + 1);
+
+ state.sp += 2;
+ break;
+
+ /* LD A,(FF00+C) */
+ case 0xF2: state.a = mmu_read(0xFF00 + state.c);
+ break;
+
+ /* DI */
+ case 0xF3: state.int_enable = 0;
+ break;
+
+ /* not present on Gameboy Z80 */
+ case 0xF4: break;
+
+ /* PUSH PSW */
+ case 0xF5: p = (uint8_t *) &state.flags;
+
+ cycles_step();
+
+ mmu_write(state.sp - 1, state.a);
+ mmu_write(state.sp - 2, *p);
+ state.sp -= 2;
+ break;
+
+ /* ORI */
+ case 0xF6: z80_ora(mmu_read(state.pc + 1));
+ b = 2;
+ break;
+
+ /* RST 6 */
+ case 0xF7: state.pc++;
+ return z80_intr(0x0008 * 6);
+
+ /* LD HL,SP+dd */
+ case 0xF8: byte = mmu_read(state.pc + 1);
+ byte2 = (uint8_t) (state.sp & 0x00ff);
+ result = byte2 + byte;
+
+ state.flags.z = 0;
+ state.flags.n = 0;
+
+ state.flags.cy = (result > 0xff);
+
+ /* add 4 cycles */
+ cycles_step();
+
+ /* calc xor for AC */
+ z80_set_flags_ac(byte2, byte, result);
+
+ /* set sp */
+ *state.hl = state.sp + (int8_t) byte; // result & 0xffff;
+
+ b = 2;
+ break;
+
+ /* SPHL */
+ case 0xF9: cycles_step();
+ state.sp = *state.hl;
+ break;
+
+ /* LD A, (NN) */
+ case 0xFA: state.a = mmu_read(ADDR);
+ b = 3;
+ break;
+
+ /* EI */
+ case 0xFB: state.int_enable = 1;
+ break;
+
+ /* not present on Gameboy Z80 */
+ case 0xFC:
+ case 0xFD: break;
+
+ /* CPI */
+ case 0xFE: z80_cmp(mmu_read(state.pc + 1));
+ b = 2;
+ break;
+
+ /* RST 7 */
+ case 0xFF: state.pc++;
+ return z80_intr(0x0008 * 7);
+
+ default: return 1;
+ }
+
+ /* make the PC points to the next instruction */
+ state.pc += b;
+
+ return 0;
+}
+
+/* init registers, flags and state.memory of Gameboy Z80 CPU */
+z80_state_t static *z80_init()
+{
+ /* wipe all the structs */
+ bzero(&state, sizeof(z80_state_t));
+
+/* 16 bit values just point to the first reg of the pairs */
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+ state.hl = (uint16_t *) &state.l;
+ state.bc = (uint16_t *) &state.c;
+ state.de = (uint16_t *) &state.e;
+#else
+ state.hl = (uint16_t *) &state.h;
+ state.bc = (uint16_t *) &state.b;
+ state.de = (uint16_t *) &state.d;
+#endif
+
+ state.sp = 0xffff;
+ state.a = 0xff;
+
+ state.b = 0x7f;
+ state.c = 0xbc;
+ state.d = 0x00;
+ state.e = 0x00;
+ state.h = 0x34;
+ state.l = 0xc0;
+
+ regs_dst = malloc(8 * sizeof(uint8_t *));
+
+ regs_dst[0x00] = &state.b;
+ regs_dst[0x01] = &state.c;
+ regs_dst[0x02] = &state.d;
+ regs_dst[0x03] = &state.e;
+ regs_dst[0x04] = &state.h;
+ regs_dst[0x05] = &state.l;
+ regs_dst[0x06] = &dummy;
+ regs_dst[0x07] = &state.a;
+
+ regs_src = malloc(8 * sizeof(uint8_t *));
+
+ regs_src[0x00] = &state.b;
+ regs_src[0x01] = &state.c;
+ regs_src[0x02] = &state.d;
+ regs_src[0x03] = &state.e;
+ regs_src[0x04] = &state.h;
+ regs_src[0x05] = &state.l;
+ regs_src[0x06] = mmu_addr(*state.hl);
+ regs_src[0x07] = &state.a;
+
+ state.flags.cy = 1;
+ state.flags.n = 1;
+ state.flags.ac = 1;
+ state.flags.z = 1;
+
+ /* flags shortcut */
+ state.f = (uint8_t *) &state.flags;
+
+ /* flags mask array */
+ z80_calc_flags_mask_array();
+
+ return &state;
+}
diff --git a/waterbox/pizza/lib/z80_gameboy_regs.h b/waterbox/pizza/lib/z80_gameboy_regs.h
new file mode 100644
index 0000000000..6b403005f1
--- /dev/null
+++ b/waterbox/pizza/lib/z80_gameboy_regs.h
@@ -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 .
+
+*/
+
+
+#ifndef Z80_REGS_H
+#define Z80_REGS_H
+
+#include
+
+/* 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
diff --git a/waterbox/pizza/pizza.c b/waterbox/pizza/pizza.c
new file mode 100644
index 0000000000..83ab83b7e2
--- /dev/null
+++ b/waterbox/pizza/pizza.c
@@ -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 .
+
+*/
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#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 (; pxw, 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;
+}