From ea3286219909f51d5e4855867aedb209223bacdc Mon Sep 17 00:00:00 2001 From: nattthebear Date: Mon, 19 Jun 2017 19:52:27 -0400 Subject: [PATCH] pizza: some basic ATTR stuff now working --- .../Consoles/Nintendo/Gameboy/Pizza.cs | 2 + waterbox/pizza/lib/gpu.c | 6 + waterbox/pizza/lib/sgb.c | 468 +++++++++++++++++- waterbox/pizza/lib/sgb.h | 4 + waterbox/pizza/pizza.c | 33 +- 5 files changed, 497 insertions(+), 16 deletions(-) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Pizza.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Pizza.cs index f5d7f84e8a..a2e01fe724 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Pizza.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Pizza.cs @@ -53,6 +53,8 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.Gameboy if (_sgb) VsyncNumerator = TICKSPERSECOND_SGB; + + Console.WriteLine("Pizza Initialized: CGB {0} SGB {1}", IsCGBMode(), IsSGBMode()); } /// diff --git a/waterbox/pizza/lib/gpu.c b/waterbox/pizza/lib/gpu.c index 3126324ea7..8f4ee2c848 100644 --- a/waterbox/pizza/lib/gpu.c +++ b/waterbox/pizza/lib/gpu.c @@ -29,6 +29,7 @@ #include "interrupt.h" #include "mmu.h" #include "utils.h" +#include "sgb.h" /* Gameboy OAM 4 bytes data */ typedef struct gpu_oam_s @@ -183,6 +184,11 @@ void gpu_draw_frame() (gpu.frame_counter & 0x0003) != 0)) return;*/ + if (global_sgb) + { + sgb_take_frame(gpu.frame_buffer); + } + /* call the callback */ if (gpu_frame_ready_cb) (*gpu_frame_ready_cb)(); diff --git a/waterbox/pizza/lib/sgb.c b/waterbox/pizza/lib/sgb.c index c41d66a3f0..993c56b2fd 100644 --- a/waterbox/pizza/lib/sgb.c +++ b/waterbox/pizza/lib/sgb.c @@ -25,6 +25,28 @@ typedef struct // palettes uint32_t palette[8][16]; + uint32_t auxpalette[512][4]; + + // frame data + uint8_t frame[160 * 144]; // the most recent obtained full frame + uint8_t frozenframe[160 * 144]; // the most recent saved full frame (MASK_EN) + uint8_t attr[20 * 18]; // current attr map for the GB screen + uint8_t auxattr[45][20 * 18]; // 45 attr files + + // MASK_EN + uint8_t waiting_mask; // true if waiting to capture a mask + uint8_t active_mask; // true if mask is currently being used + + // transfers + uint32_t waiting_transfer; +#define TRN_NONE 0 +#define TRN_SOUND 1 +#define TRN_PAL 2 +#define TRN_CHR_LOW 3 +#define TRN_CHR_HI 4 +#define TRN_PCT 5 +#define TRN_ATTR 6 + int32_t transfer_countdown; // number of frames until transfer. not entirely accurate } sgb_t; static sgb_t sgb; @@ -34,13 +56,33 @@ static uint32_t makecol(uint16_t c) return c >> 7 & 0xf8 | c >> 12 & 0x07 | c << 6 & 0xf800 | c << 1 & 0x0700 | c << 19 & 0xf80000 | c << 14 & 0x070000 | 0xff000000; } +static void cmd_trn(uint32_t which) +{ + if ((sgb.command[0] & 7) == 1) + { + if (sgb.waiting_transfer == TRN_NONE) + { + sgb.waiting_transfer = which; + sgb.transfer_countdown = 4; + } + else + { + utils_log("SGB: TRN already queued!"); + } + } + else + { + utils_log("SGB: cmd_trn bad length"); + } +} + static void cmd_pal(int a, int b) { if ((sgb.command[0] & 7) == 1) { uint32_t c[7]; for (int i = 0; i < 7; i++) - c[i] = makecol(sgb.command[1 + i] | sgb.command[2 + i] << 8); + c[i] = makecol(sgb.command[i * 2 + 1] | sgb.command[i * 2 + 2] << 8); sgb.palette[0][0] = c[0]; sgb.palette[a][1] = c[1]; sgb.palette[a][2] = c[2]; @@ -55,6 +97,193 @@ static void cmd_pal(int a, int b) } } +static void cmd_pal_set(void) +{ + if ((sgb.command[0] & 7) == 1) + { + for (int i = 0; i < 4; i++) + { + int p = sgb.command[i * 2 + 1] | sgb.command[i * 2 + 2] << 8 & 0x100; + for (int j = 0; j < 4; j++) + sgb.palette[i][j] = sgb.auxpalette[p][j]; + } + if (sgb.command[9] & 0x80) // change attribute + { + int attr = sgb.command[9] & 0x3f; + if (attr >= 45) + attr = 44; + memcpy(sgb.attr, sgb.auxattr[attr], sizeof(sgb.attr)); + } + if (sgb.command[9] & 0x40) // cancel mask + { + sgb.waiting_mask = 0; + sgb.active_mask = 0; + } + } + else + { + utils_log("SGB: cmd_pal bad length"); + } +} + +static void cmd_attr_blk() +{ +} + +static void cmd_attr_lin() +{ + int nset = sgb.command[1]; + if (nset <= 0 || nset >= 111) + { + utils_log("SGB: cmd_attr_lin bad nset"); + return; + } + int npacket = (nset + 17) / 16; + if ((sgb.command[0] & 7) != npacket) + { + utils_log("SGB: cmd_attr_lin bad length"); + return; + } + for (int i = 0; i < nset; i++) + { + uint8_t v = sgb.command[i + 2]; + int line = v & 31; + int a = v >> 5 & 3; + if (v & 0x80) // horizontal + { + if (line > 17) + line = 17; + memset(sgb.attr + line * 20, a, 20); + } + else // vertical + { + if (line > 19) + line = 19; + uint8_t *dst = sgb.attr + line; + for (int i = 0; i < 18; i++, dst += 20) + dst[0] = a; + } + } +} + +static void cmd_attr_div() +{ + if ((sgb.command[0] & 7) == 1) + { + uint8_t v = sgb.command[1]; + + int c = v & 3; + int a = v >> 2 & 3; + int b = v >> 4 & 3; + + int pos = sgb.command[2]; + uint8_t *dst = sgb.attr; + if (v & 0x40) // horizontal + { + if (pos > 17) + pos = 17; + int i; + for (i = 0; i < pos; i++, dst += 20) + memset(dst, a, 20); + memset(dst, b, 20); + i++, dst += 20; + for (; i < 18; i++, dst += 20) + memset(dst, c, 20); + } + else // vertical + { + if (pos > 19) + pos = 19; + for (int j = 0; j < 18; j++) + { + int i; + for (i = 0; i < pos; i++) + *dst++ = a; + *dst++ = b; + i++; + for (; i < 20; i++) + *dst++ = c; + } + } + } + else + { + utils_log("SGB: cmd_attr_div bad length"); + } +} + +static void cmd_attr_chr() +{ + int x = sgb.command[1]; + int y = sgb.command[2]; + int n = sgb.command[3] | sgb.command[4] << 8; + if (n > 360) + { + utils_log("SGB: cmd_attr_chr bad n"); + return; + } + int npacket = (n + 87) / 64; + if ((sgb.command[0] & 7) != npacket) + { + utils_log("SGB: cmd_attr_chr bad length"); + return; + } + uint8_t *dst = sgb.attr; + if (x > 19) + x = 19; + if (y > 17) + y = 17; + int vertical = sgb.command[5]; + for (int i = 0; i < 360; i++) + { + uint8_t v = i / 4 + 6; + int a = v >> (2 * (3 - (i & 3))) & 3; + dst[y * 20 + x] = a; + if (vertical) + { + y++; + if (y == 18) + { + y = 0; + x++; + if (x == 20) + return; + } + } + else + { + x++; + if (x == 20) + { + x = 0; + y++; + if (y == 18) + return; + } + } + } +} + +static void cmd_attr_set() +{ + if ((sgb.command[0] & 7) == 1) + { + int attr = sgb.command[1] & 0x3f; + if (attr >= 45) + attr = 44; + memcpy(sgb.attr, sgb.auxattr[attr], sizeof(sgb.attr)); + if (sgb.command[1] & 0x40) + { + sgb.waiting_mask = 0; + sgb.active_mask = 0; + } + } + else + { + utils_log("SGB: cmd_attr_set bad length"); + } +} + static void cmd_mlt_req(void) { if ((sgb.command[0] & 7) == 1) @@ -83,6 +312,33 @@ static void cmd_mlt_req(void) } } +static void cmd_mask(void) +{ + if ((sgb.command[0] & 7) == 1) + { + switch (sgb.command[1] & 3) + { + case 0: + sgb.waiting_mask = 0; + sgb.active_mask = 0; + break; + case 1: + sgb.waiting_mask = 1; + break; + case 2: + case 3: + sgb.waiting_mask = 0; + sgb.active_mask = 1; + memset(sgb.frozenframe, 0, sizeof(sgb.frozenframe)); + break; + } + } + else + { + utils_log("SGB: cmd_mask bad length"); + } +} + static void do_command(void) { const int command = sgb.command[0] >> 3; @@ -108,6 +364,36 @@ static void do_command(void) utils_log("SGB: PAL12"); cmd_pal(1, 2); break; + case 0x0a: // PAL_SET + utils_log("SGB: PAL_SET"); + cmd_pal_set(); + break; + + case 0x04: // ATTR_BLK + utils_log("SGB: ATTR_BLK"); + cmd_attr_blk(); + break; + case 0x05: // ATTR_LIN + utils_log("SGB: ATTR_LIN"); + cmd_attr_lin(); + break; + case 0x06: // ATTR_DIV + utils_log("SGB: ATTR_DIV"); + cmd_attr_div(); + break; + case 0x07: // ATTR_CHR + utils_log("SGB: ATTR_CHR"); + cmd_attr_chr(); + break; + case 0x16: // ATTR_SET + utils_log("SGB: ATTR_SET"); + cmd_attr_set(); + break; + + case 0x17: // MASK_EN + utils_log("SGB: MASK_EN"); + cmd_mask(); + break; // unknown functions case 0x0c: // ATRC_EN @@ -125,10 +411,13 @@ static void do_command(void) break; // unimplementable functions - case 0x10: // DATA_TRN - // TODO: Is it possible for this to write data to + case 0x0f: // DATA_SND + // TODO: Is it possible for this (and DATA_TRN) to write data to // memory areas used for the attribute file, etc? // If so, do games do this? + utils_log("SGB: DATA_SND!!"); + break; + case 0x10: // DATA_TRN utils_log("SGB: DATA_TRN!!"); break; case 0x12: // JUMP @@ -140,6 +429,28 @@ static void do_command(void) utils_log("SGB: MLT_REQ"); cmd_mlt_req(); break; + + // all vram transfers + case 0x09: // SOU_TRN + utils_log("SGB: SOU_TRN"); + cmd_trn(TRN_SOUND); + break; + case 0x0b: // PAL_TRN + utils_log("SGB: PAL_TRN"); + cmd_trn(TRN_PAL); + break; + case 0x13: // CHR_TRN + utils_log("SGB: CHR_TRN"); + cmd_trn(sgb.command[1] & 1 ? TRN_CHR_HI : TRN_CHR_LOW); + break; + case 0x14: // PCT_TRN + utils_log("SGB: PCT_TRN"); + cmd_trn(TRN_PCT); + break; + case 0x15: // ATTR_TRN + utils_log("SGB: ATTR_TRN"); + cmd_trn(TRN_ATTR); + break; } } @@ -176,7 +487,7 @@ void sgb_write_ff00(uint8_t val, uint64_t time) { val &= 0x30; - utils_log("ZZ: %02x, %llu", val, time); + //utils_log("ZZ: %02x, %llu", val, time); const int p14_fell = (val & 0x10) < (sgb.last_write_value & 0x10); const int p15_fell = (val & 0x20) < (sgb.last_write_value & 0x20); const int p14_rose = (val & 0x10) > (sgb.last_write_value & 0x10); @@ -224,7 +535,7 @@ void sgb_write_ff00(uint8_t val, uint64_t time) sgb.joypad_has_been_read = 0; sgb.joypad_index++; sgb.joypad_index &= sgb.num_joypads - 1; - utils_log("SGB: joypad index to %u", sgb.joypad_index); + //utils_log("SGB: joypad index to %u", sgb.joypad_index); } else { @@ -252,7 +563,7 @@ uint8_t sgb_read_ff00(uint64_t time) if (!p14 && !p15) { - utils_log("SGB: SCAN%u", ji); + //utils_log("SGB: SCAN%u", ji); // scan id return ret | (15 - ji); } @@ -264,7 +575,7 @@ uint8_t sgb_read_ff00(uint64_t time) ret |= j >> 4; if (p15) ret |= j & 0x0f; - utils_log("SGB: READ%u %02x", ji, ret ^ 0x0f); + //utils_log("SGB: READ%u %02x", ji, ret ^ 0x0f); return ret ^ 0x0f; } } @@ -276,3 +587,146 @@ void sgb_set_controller_data(const uint8_t *buttons) { memcpy(sgb.joypad_data, buttons, sizeof(sgb.joypad_data)); } + +static void trn_pal(const uint8_t *data) +{ + const uint16_t *src = (const uint16_t *)data; + uint32_t *dst = (uint32_t *)sgb.auxpalette; + for (int i = 0; i < 2048; i++) + dst[i] = makecol(src[i]); +} + +static void trn_attr(const uint8_t *data) +{ + uint8_t *dst = (uint8_t *)sgb.auxattr; + for (int n = 0; n < 45 * 90; n++) + { + uint8_t s = *data++; + *dst++ = s >> 6 & 3; + *dst++ = s >> 4 & 3; + *dst++ = s >> 2 & 3; + *dst++ = s >> 0 & 3; + } +} + +#include "mmu.h" +static void do_vram_transfer(void) +{ + uint8_t vram[4096]; + for (int tilenum = 0; tilenum < 256; tilenum++) + { + const int ty = tilenum / 20; + const int tx = tilenum % 20; + const uint8_t *src = sgb.frame + ty * 8 * 160 + tx * 8; + uint8_t *dst = vram + 16 * tilenum; + for (int j = 0; j < 8; j++) + { + uint32_t a = 0, b = 0; + a |= (src[7] & 1) << 0; + a |= (src[6] & 1) << 1; + a |= (src[5] & 1) << 2; + a |= (src[4] & 1) << 3; + a |= (src[3] & 1) << 4; + a |= (src[2] & 1) << 5; + a |= (src[1] & 1) << 6; + a |= (src[0] & 1) << 7; + + b |= (src[7] & 2) >> 1; + b |= (src[6] & 2) << 0; + b |= (src[5] & 2) << 1; + b |= (src[4] & 2) << 2; + b |= (src[3] & 2) << 3; + b |= (src[2] & 2) << 4; + b |= (src[1] & 2) << 5; + b |= (src[0] & 2) << 6; + *dst++ = a; + *dst++ = b; + src += 160; + } + } + + switch (sgb.waiting_transfer) + { + case TRN_SOUND: + break; + case TRN_PAL: + trn_pal(vram); + break; + case TRN_CHR_LOW: + break; + case TRN_CHR_HI: + break; + case TRN_PCT: + break; + case TRN_ATTR: + trn_attr(vram); + break; + } +} + +// 160x144 32bpp pixel data +// assumed to contain exact pixel values 00, 55, aa, ff +void sgb_take_frame(uint32_t *vbuff) +{ + for (int i = 0; i < 160 * 144; i++) + { + sgb.frame[i] = 3 - (vbuff[i] >> 6 & 3); // 0, 1, 2, or 3 for each pixel + } + if (sgb.waiting_transfer != TRN_NONE) + { + if (!--sgb.transfer_countdown) + { + do_vram_transfer(); + sgb.waiting_transfer = TRN_NONE; + } + } + if (sgb.waiting_mask) + { + memcpy(sgb.frozenframe, sgb.frame, sizeof(sgb.frame)); + sgb.waiting_mask = 0; + sgb.active_mask = 1; + } +} + +static void sgb_render_frame_gb(uint32_t *vbuff) +{ + /*sgb.palette[0][0] = 0xff000000; + sgb.palette[0][1] = 0xff550055; + sgb.palette[0][2] = 0xffaa00aa; + sgb.palette[0][3] = 0xffff00ff; + + sgb.palette[1][0] = 0xff00003f; + sgb.palette[1][1] = 0xff00007f; + sgb.palette[1][2] = 0xff0000bf; + sgb.palette[1][3] = 0xff0000ff; + + sgb.palette[2][0] = 0xff003f00; + sgb.palette[2][1] = 0xff007f00; + sgb.palette[2][2] = 0xff00bf00; + sgb.palette[2][3] = 0xff00ff00; + + sgb.palette[3][0] = 0xff3f0000; + sgb.palette[3][1] = 0xff7f0000; + sgb.palette[3][2] = 0xffbf0000; + sgb.palette[3][3] = 0xffff0000;*/ + + const uint8_t *attr = sgb.attr; + const uint8_t *src = sgb.active_mask ? sgb.frozenframe : sgb.frame; + uint32_t *dst = vbuff + ((224 - 144) / 2 * 256 + (256 - 160) / 2); + + for (int j = 0; j < 144; j++) + { + const uint8_t *attr_line = attr + j / 8 * 20; + for (int i = 0; i < 160; i++) + { + const int attr_index = i / 8; + *dst++ = sgb.palette[attr_line[attr_index]][*src++]; + } + dst += 256 - 160; + } +} + +void sgb_render_frame(uint32_t *vbuff) +{ + sgb_render_frame_gb(vbuff); +} diff --git a/waterbox/pizza/lib/sgb.h b/waterbox/pizza/lib/sgb.h index 5db61497c9..99a92fc654 100644 --- a/waterbox/pizza/lib/sgb.h +++ b/waterbox/pizza/lib/sgb.h @@ -8,3 +8,7 @@ uint8_t sgb_read_ff00(uint64_t time); void sgb_set_controller_data(const uint8_t* buttons); void sgb_init(void); + +void sgb_take_frame(uint32_t* vbuff); + +void sgb_render_frame(uint32_t* vbuff); diff --git a/waterbox/pizza/pizza.c b/waterbox/pizza/pizza.c index 165c92ee1e..ee4b1f67d0 100644 --- a/waterbox/pizza/pizza.c +++ b/waterbox/pizza/pizza.c @@ -86,8 +86,8 @@ EXPORT int Init(const void *rom, int romlen, int sgb) typedef struct { - uint32_t* VideoBuffer; - int16_t* SoundBuffer; + uint32_t *VideoBuffer; + int16_t *SoundBuffer; int64_t Cycles; int32_t Width; int32_t Height; @@ -96,13 +96,13 @@ typedef struct uint32_t Keys; } MyFrameInfo; -static uint32_t* current_vbuff; +static uint32_t *current_vbuff; static uint64_t overflow; -EXPORT void FrameAdvance(MyFrameInfo* frame) +EXPORT void FrameAdvance(MyFrameInfo *frame) { if (global_sgb) - sgb_set_controller_data((uint8_t*)&frame->Keys); + sgb_set_controller_data((uint8_t *)&frame->Keys); else input_set_keys(frame->Keys); current_vbuff = frame->VideoBuffer; @@ -115,8 +115,16 @@ EXPORT void FrameAdvance(MyFrameInfo* frame) overflow = cycles.sampleclock - target; frame->Samples = sound_output_read(frame->SoundBuffer); - frame->Width = 160; - frame->Height = 144; + if (global_sgb) + { + frame->Width = 256; + frame->Height = 224; + } + else + { + frame->Width = 160; + frame->Height = 144; + } current_vbuff = NULL; } @@ -130,7 +138,7 @@ EXPORT void SetInputCallback(void (*callback)(void)) // TODO } -EXPORT void GetMemoryAreas(MemoryArea* m) +EXPORT void GetMemoryAreas(MemoryArea *m) { m[0].Data = mmu.memory; m[0].Name = "Fake System Bus"; @@ -140,7 +148,14 @@ EXPORT void GetMemoryAreas(MemoryArea* m) void frame_cb() { - memcpy(current_vbuff, gpu.frame_buffer, sizeof(gpu.frame_buffer)); + if (global_sgb) + { + sgb_render_frame(current_vbuff); + } + else + { + memcpy(current_vbuff, gpu.frame_buffer, sizeof(gpu.frame_buffer)); + } } void connected_cb()