gbsplay-0.0.93/0000755000175000017500000000000012566424552012251 5ustar mitchmitchgbsplay-0.0.93/gbhw.c0000644000175000017500000004211112566424552013343 0ustar mitchmitch/* * gbsplay is a Gameboy sound player * * 2003-2006,2008 (C) by Tobias Diedrich * Christian Garbs * Licensed under GNU GPL. */ #include #include #include #include #include #include #include "gbcpu.h" #include "gbhw.h" #include "impulsegen.h" static uint8_t *rom; static uint8_t intram[0x2000]; static uint8_t extram[0x2000]; static uint8_t ioregs[0x80]; static uint8_t hiram[0x80]; static long rombank = 1; static long lastbank; static const char dutylookup[4] = { 1, 2, 4, 6 }; struct gbhw_channel gbhw_ch[4]; static long lminval, lmaxval, rminval, rmaxval; #define MASTER_VOL_MIN 0 #define MASTER_VOL_MAX (256*256) static long master_volume; static long master_fade; static long master_dstvol; static const long vblanktc = 70256; /* ~59.7 Hz (vblankctr)*/ static long vblankctr = 70256; static long timertc = 70256; static long timerctr = 70256; static const long msec_cycles = GBHW_CLOCK/1000; static long sum_cycles; static long pause_output = 0; static gbhw_callback_fn callback; static /*@null@*/ /*@dependent@*/ void *callbackpriv; static /*@null@*/ /*@dependent@*/ struct gbhw_buffer *soundbuf = NULL; /* externally visible output buffer */ static /*@null@*/ /*@only@*/ struct gbhw_buffer *impbuf = NULL; /* internal impulse output buffer */ static gbhw_iocallback_fn iocallback; static /*@null@*/ /*@dependent@*/ void *iocallback_priv; #define TAP1_15 0x4000; #define TAP2_15 0x2000; #define TAP1_7 0x0040; #define TAP2_7 0x0020; static uint32_t tap1 = TAP1_15; static uint32_t tap2 = TAP2_15; static uint32_t lfsr = 0xffffffff; #define SOUND_DIV_MULT 0x10000LL static long long sound_div_tc = 0; static const long main_div_tc = 32; static long main_div; static const long sweep_div_tc = 256; static long sweep_div; static long ch3pos; static long impulse_n_shift = 7; static long impulse_w_shift = 5; static double impulse_cutoff = 1.0; static short *base_impulse = NULL; #define IMPULSE_WIDTH (1 << impulse_w_shift) #define IMPULSE_N (1 << impulse_n_shift) #define IMPULSE_N_MASK (IMPULSE_N - 1) static regparm uint32_t rom_get(uint32_t addr) { // DPRINTF("rom_get(%04x)\n", addr); return rom[addr & 0x3fff]; } static regparm uint32_t rombank_get(uint32_t addr) { // DPRINTF("rombank_get(%04x)\n", addr); return rom[(addr & 0x3fff) + 0x4000*rombank]; } static regparm uint32_t io_get(uint32_t addr) { if (addr >= 0xff80 && addr <= 0xfffe) { return hiram[addr & 0x7f]; } if (addr >= 0xff10 && addr <= 0xff3f) { return ioregs[addr & 0x7f]; } if (addr == 0xff00) return 0; if (addr == 0xffff) return ioregs[0x7f]; fprintf(stderr, "ioread from 0x%04x unimplemented.\n", (unsigned int)addr); DPRINTF("io_get(%04x)\n", addr); return 0xff; } static regparm uint32_t intram_get(uint32_t addr) { // DPRINTF("intram_get(%04x)\n", addr); return intram[addr & 0x1fff]; } static regparm uint32_t extram_get(uint32_t addr) { // DPRINTF("extram_get(%04x)\n", addr); return extram[addr & 0x1fff]; } static regparm void rom_put(uint32_t addr, uint8_t val) { if (addr >= 0x2000 && addr <= 0x3fff) { val &= 0x1f; rombank = val + (val == 0); if (rombank > lastbank) { fprintf(stderr, "Bank %ld out of range (0-%ld)!\n", rombank, lastbank); rombank = lastbank; } } } static regparm void io_put(uint32_t addr, uint8_t val) { long chn = (addr - 0xff10)/5; iocallback(sum_cycles, addr, val, iocallback_priv); if (addr >= 0xff80 && addr <= 0xfffe) { hiram[addr & 0x7f] = val; return; } ioregs[addr & 0x7f] = val; DPRINTF(" ([0x%04x]=%02x) ", addr, val); switch (addr) { case 0xff06: case 0xff07: timertc = (256-ioregs[0x06]) * (16 << (((ioregs[0x07]+3) & 3) << 1)); if ((ioregs[0x07] & 0xf0) == 0x80) timertc /= 2; // printf("Callback rate set to %2.2fHz.\n", GBHW_CLOCK/(float)timertc); break; case 0xff10: gbhw_ch[0].sweep_ctr = gbhw_ch[0].sweep_tc = ((val >> 4) & 7); gbhw_ch[0].sweep_dir = (val >> 3) & 1; gbhw_ch[0].sweep_shift = val & 7; break; case 0xff11: case 0xff16: case 0xff20: { long duty_ctr = val >> 6; long len = val & 0x3f; gbhw_ch[chn].duty_ctr = dutylookup[duty_ctr]; gbhw_ch[chn].duty_tc = gbhw_ch[chn].div_tc*gbhw_ch[chn].duty_ctr/8; gbhw_ch[chn].len = (64 - len)*2; break; } case 0xff12: case 0xff17: case 0xff21: { long vol = val >> 4; long envdir = (val >> 3) & 1; long envspd = val & 7; gbhw_ch[chn].volume = vol; gbhw_ch[chn].env_dir = envdir; gbhw_ch[chn].env_ctr = gbhw_ch[chn].env_tc = envspd*8; } break; case 0xff13: case 0xff14: case 0xff18: case 0xff19: case 0xff1d: case 0xff1e: { long div = ioregs[0x13 + 5*chn]; div |= ((long)ioregs[0x14 + 5*chn] & 7) << 8; gbhw_ch[chn].div_tc = 2048 - div; gbhw_ch[chn].duty_tc = gbhw_ch[chn].div_tc*gbhw_ch[chn].duty_ctr/8; if (addr == 0xff13 || addr == 0xff18 || addr == 0xff1d) break; } gbhw_ch[chn].len_enable = (ioregs[0x14 + 5*chn] & 0x40) > 0; // printf(" ch%ld: vol=%02d envd=%ld envspd=%ld duty_ctr=%ld len=%03d len_en=%ld key=%04d gate=%ld%ld\n", chn, gbhw_ch[chn].volume, gbhw_ch[chn].env_dir, gbhw_ch[chn].env_tc, gbhw_ch[chn].duty_ctr, gbhw_ch[chn].len, gbhw_ch[chn].len_enable, gbhw_ch[chn].div_tc, gbhw_ch[chn].leftgate, gbhw_ch[chn].rightgate); break; case 0xff15: break; case 0xff1a: gbhw_ch[2].master = (ioregs[0x1a] & 0x80) > 0; break; case 0xff1b: gbhw_ch[2].len = (256 - val)*2; break; case 0xff1c: { long vol = (ioregs[0x1c] >> 5) & 3; gbhw_ch[2].volume = vol; break; } case 0xff1f: break; case 0xff22: case 0xff23: { long div = ioregs[0x22]; long shift = div >> 4; long rate = div & 7; gbhw_ch[3].div_ctr = 0; gbhw_ch[3].div_tc = 1 << shift; if (div & 8) { tap1 = TAP1_7; tap2 = TAP2_7; } else { tap1 = TAP1_15; tap2 = TAP2_15; } lfsr |= 1; /* Make sure lfsr is not 0 */ if (rate) gbhw_ch[3].div_tc *= rate; else gbhw_ch[3].div_tc /= 2; if (addr == 0xff22) break; // printf(" ch4: vol=%02d envd=%ld envspd=%ld duty_ctr=%ld len=%03d len_en=%ld key=%04d gate=%ld%ld\n", gbhw_ch[3].volume, gbhw_ch[3].env_dir, gbhw_ch[3].env_ctr, gbhw_ch[3].duty_ctr, gbhw_ch[3].len, gbhw_ch[3].len_enable, gbhw_ch[3].div_tc, gbhw_ch[3].leftgate, gbhw_ch[3].rightgate); } gbhw_ch[chn].len_enable = (ioregs[0x23] & 0x40) > 0; break; case 0xff25: gbhw_ch[0].leftgate = (val & 0x10) > 0; gbhw_ch[0].rightgate = (val & 0x01) > 0; gbhw_ch[1].leftgate = (val & 0x20) > 0; gbhw_ch[1].rightgate = (val & 0x02) > 0; gbhw_ch[2].leftgate = (val & 0x40) > 0; gbhw_ch[2].rightgate = (val & 0x04) > 0; gbhw_ch[3].leftgate = (val & 0x80) > 0; gbhw_ch[3].rightgate = (val & 0x08) > 0; break; case 0xff26: ioregs[0x26] = 0x80; break; case 0xff00: case 0xff24: case 0xff27: case 0xff28: case 0xff29: case 0xff2a: case 0xff2b: case 0xff2c: case 0xff2d: case 0xff2e: case 0xff2f: 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: case 0xffff: break; default: fprintf(stderr, "iowrite to 0x%04x unimplemented (val=%02x).\n", addr, val); break; } } static regparm void intram_put(uint32_t addr, uint8_t val) { intram[addr & 0x1fff] = val; } static regparm void extram_put(uint32_t addr, uint8_t val) { extram[addr & 0x1fff] = val; } static regparm void gb_sound_sweep(void) { long i; if (gbhw_ch[0].sweep_tc) { gbhw_ch[0].sweep_ctr--; if (gbhw_ch[0].sweep_ctr < 0) { long val = gbhw_ch[0].div_tc >> gbhw_ch[0].sweep_shift; gbhw_ch[0].sweep_ctr = gbhw_ch[0].sweep_tc; if (gbhw_ch[0].sweep_dir) { if (gbhw_ch[0].div_tc < 2048 - val) gbhw_ch[0].div_tc += val; } else { if (gbhw_ch[0].div_tc > val) gbhw_ch[0].div_tc -= val; } gbhw_ch[0].duty_tc = gbhw_ch[0].div_tc*gbhw_ch[0].duty_ctr/8; } } for (i=0; i<4; i++) { if (gbhw_ch[i].len > 0 && gbhw_ch[i].len_enable) { gbhw_ch[i].len--; if (gbhw_ch[i].len == 0) { gbhw_ch[i].volume = 0; gbhw_ch[i].env_tc = 0; } } if (gbhw_ch[i].env_tc) { gbhw_ch[i].env_ctr--; if (gbhw_ch[i].env_ctr <=0) { gbhw_ch[i].env_ctr = gbhw_ch[i].env_tc; if (!gbhw_ch[i].env_dir) { if (gbhw_ch[i].volume > 0) gbhw_ch[i].volume--; } else { if (gbhw_ch[i].volume < 15) gbhw_ch[i].volume++; } } } } if (master_fade) { master_volume += master_fade; if ((master_fade > 0 && master_volume >= master_dstvol) || (master_fade < 0 && master_volume <= master_dstvol)) { master_fade = 0; master_volume = master_dstvol; } } } regparm void gbhw_master_fade(long speed, long dstvol) { if (dstvol < MASTER_VOL_MIN) dstvol = MASTER_VOL_MIN; if (dstvol > MASTER_VOL_MAX) dstvol = MASTER_VOL_MAX; master_dstvol = dstvol; if (dstvol > master_volume) master_fade = speed; else master_fade = -speed; } #define GET_NIBBLE(p, n) ({ \ long index = ((n) >> 1) & 0xf; \ long shift = (~(n) & 1) << 2; \ (((p)[index] >> shift) & 0xf); }) static regparm void gb_flush_buffer(void) { long i; long overlap; long l_smpl, r_smpl; assert(soundbuf != NULL); assert(impbuf != NULL); /* integrate buffer */ l_smpl = soundbuf->l_lvl; r_smpl = soundbuf->r_lvl; for (i=0; isamples; i++) { l_smpl = l_smpl + impbuf->data[i*2 ]; r_smpl = r_smpl + impbuf->data[i*2+1]; soundbuf->data[i*2 ] = l_smpl * master_volume / MASTER_VOL_MAX; soundbuf->data[i*2+1] = r_smpl * master_volume / MASTER_VOL_MAX; if (l_smpl > lmaxval) lmaxval = l_smpl; if (l_smpl < lminval) lminval = l_smpl; if (r_smpl > rmaxval) rmaxval = r_smpl; if (r_smpl < rminval) rminval = r_smpl; } soundbuf->pos = soundbuf->samples; soundbuf->l_lvl = l_smpl; soundbuf->r_lvl = r_smpl; if (callback != NULL) callback(soundbuf, callbackpriv); overlap = impbuf->samples - soundbuf->samples; memmove(impbuf->data, impbuf->data+(2*soundbuf->samples), 4*overlap); memset(impbuf->data + 2*overlap, 0, impbuf->bytes - 4*overlap); assert(impbuf->bytes == impbuf->samples*4); assert(soundbuf->bytes == soundbuf->samples*4); memset(soundbuf->data, 0, soundbuf->bytes); soundbuf->pos = 0; impbuf->cycles -= (sound_div_tc * soundbuf->samples) / SOUND_DIV_MULT; } static regparm void gb_change_level(long l_ofs, long r_ofs) { long pos; long imp_idx; long imp_l = -IMPULSE_WIDTH/2; long imp_r = IMPULSE_WIDTH/2; long i; short *ptr = base_impulse; assert(impbuf != NULL); pos = (long)(impbuf->cycles * SOUND_DIV_MULT / sound_div_tc); imp_idx = (long)((impbuf->cycles << impulse_n_shift)*SOUND_DIV_MULT / sound_div_tc) & IMPULSE_N_MASK; assert(pos + imp_r < impbuf->samples); assert(pos + imp_l >= 0); ptr += imp_idx * IMPULSE_WIDTH; for (i=imp_l; idata[bufi*2 ] += ptr[impi] * l_ofs; impbuf->data[bufi*2+1] += ptr[impi] * r_ofs; } impbuf->l_lvl += l_ofs*256; impbuf->r_lvl += r_ofs*256; } static regparm void gb_sound(long cycles) { long i, j; long l_lvl = 0, r_lvl = 0; static long old_l = 0, old_r = 0; assert(impbuf != NULL); for (j=0; jcycles++; if (impbuf->cycles*SOUND_DIV_MULT >= sound_div_tc*(impbuf->samples - IMPULSE_WIDTH/2)) gb_flush_buffer(); if (gbhw_ch[2].master) { gbhw_ch[2].div_ctr--; if (gbhw_ch[2].div_ctr <= 0) { long pos = ch3pos++; long val = GET_NIBBLE(&ioregs[0x30], pos); long old_l = gbhw_ch[2].l_lvl; long old_r = gbhw_ch[2].r_lvl; long l_diff, r_diff; gbhw_ch[2].div_ctr = gbhw_ch[2].div_tc*2; if (gbhw_ch[2].volume) { val = val >> (gbhw_ch[2].volume-1); } else val = 0; val = val*2; if (gbhw_ch[2].volume && !gbhw_ch[2].mute) { if (gbhw_ch[2].leftgate) gbhw_ch[2].l_lvl = val; if (gbhw_ch[2].rightgate) gbhw_ch[2].r_lvl = val; } l_diff = gbhw_ch[2].l_lvl - old_l; r_diff = gbhw_ch[2].r_lvl - old_r; gb_change_level(l_diff, r_diff); } } if (main_div > main_div_tc) { main_div -= main_div_tc; for (i=0; i<2; i++) if (gbhw_ch[i].master) { long val = gbhw_ch[i].volume; if (gbhw_ch[i].div_ctr > gbhw_ch[i].duty_tc) { val = -val; } if (!gbhw_ch[i].mute) { if (gbhw_ch[i].leftgate) gbhw_ch[i].l_lvl = val; if (gbhw_ch[i].rightgate) gbhw_ch[i].r_lvl = val; } gbhw_ch[i].div_ctr--; if (gbhw_ch[i].div_ctr <= 0) { gbhw_ch[i].div_ctr = gbhw_ch[i].div_tc; } } for (i=0; i<2; i++) { l_lvl += gbhw_ch[i].l_lvl; r_lvl += gbhw_ch[i].r_lvl; } if (gbhw_ch[3].master) { // long val = gbhw_ch[3].volume * (((lfsr >> 13) & 2)-1); // long val = gbhw_ch[3].volume * ((random() & 2)-1); static long val; if (!gbhw_ch[3].mute) { if (gbhw_ch[3].leftgate) gbhw_ch[3].l_lvl = val; if (gbhw_ch[3].rightgate) gbhw_ch[3].r_lvl = val; } gbhw_ch[3].div_ctr--; if (gbhw_ch[3].div_ctr <= 0) { gbhw_ch[3].div_ctr = gbhw_ch[3].div_tc; lfsr = (lfsr << 1) | (((lfsr & tap1) > 0) ^ ((lfsr & tap2) > 0)); val = gbhw_ch[3].volume * ((lfsr & 2)-1); } } l_lvl += gbhw_ch[3].l_lvl; r_lvl += gbhw_ch[3].r_lvl; if (l_lvl != old_l || r_lvl != old_r) { gb_change_level(l_lvl - old_l, r_lvl - old_r); old_l = l_lvl; old_r = r_lvl; } sweep_div += 1; if (sweep_div >= sweep_div_tc) { sweep_div = 0; gb_sound_sweep(); } } } } regparm void gbhw_setcallback(gbhw_callback_fn fn, void *priv) { callback = fn; callbackpriv = priv; } regparm void gbhw_setiocallback(gbhw_iocallback_fn fn, void *priv) { iocallback = fn; iocallback_priv = priv; } static regparm void gbhw_impbuf_reset(struct gbhw_buffer *impbuf) { assert(sound_div_tc != 0); impbuf->cycles = (long)(sound_div_tc * IMPULSE_WIDTH/2 / SOUND_DIV_MULT); impbuf->l_lvl = 0; impbuf->r_lvl = 0; memset(impbuf->data, 0, impbuf->bytes); } regparm void gbhw_setbuffer(struct gbhw_buffer *buffer) { soundbuf = buffer; soundbuf->samples = soundbuf->bytes / 4; if (impbuf) free(impbuf); impbuf = malloc(sizeof(*impbuf) + (soundbuf->samples + IMPULSE_WIDTH + 1) * 4); if (impbuf == NULL) { fprintf(stderr, "%s", _("Memory allocation failed!\n")); return; } memset(impbuf, 0, sizeof(*impbuf)); impbuf->data = (void*)(impbuf+1); impbuf->samples = soundbuf->samples + IMPULSE_WIDTH + 1; impbuf->bytes = impbuf->samples * 4; gbhw_impbuf_reset(impbuf); } regparm void gbhw_setrate(long rate) { sound_div_tc = GBHW_CLOCK*SOUND_DIV_MULT/rate; } regparm void gbhw_getminmax(int16_t *lmin, int16_t *lmax, int16_t *rmin, int16_t *rmax) { if (lminval == INT_MAX) return; *lmin = lminval; *lmax = lmaxval; *rmin = rminval; *rmax = rmaxval; lminval = rminval = INT_MAX; lmaxval = rmaxval = INT_MIN; } /* * Initialize Gameboy hardware emulation. * The size should be a multiple of 0x4000, * so we don't need range checking in rom_get and * rombank_get. */ regparm void gbhw_init(uint8_t *rombuf, uint32_t size) { long i; int mute_tmp[4]; for (i=0; i<4; i++) mute_tmp[i] = gbhw_ch[i].mute; if (impbuf) gbhw_impbuf_reset(impbuf); rom = rombuf; lastbank = ((size + 0x3fff) / 0x4000) - 1; rombank = 1; master_volume = MASTER_VOL_MAX; master_fade = 0; if (soundbuf) { soundbuf->pos = 0; soundbuf->l_lvl = 0; soundbuf->r_lvl = 0; } lminval = rminval = INT_MAX; lmaxval = rmaxval = INT_MIN; for (i=0; i<4; i++) { gbhw_ch[i].duty_ctr = 4; gbhw_ch[i].div_tc = 1; gbhw_ch[i].master = 1; gbhw_ch[i].mute = mute_tmp[i]; } memset(extram, 0, sizeof(extram)); memset(intram, 0, sizeof(intram)); memset(hiram, 0, sizeof(hiram)); memset(ioregs, 0, sizeof(ioregs)); sum_cycles = 0; gbcpu_init(); gbcpu_addmem(0x00, 0x3f, rom_put, rom_get); gbcpu_addmem(0x40, 0x7f, rom_put, rombank_get); gbcpu_addmem(0xa0, 0xbf, extram_put, extram_get); gbcpu_addmem(0xc0, 0xfe, intram_put, intram_get); gbcpu_addmem(0xff, 0xff, io_put, io_get); if (base_impulse) free(base_impulse); base_impulse = gen_impulsetab(impulse_w_shift, impulse_n_shift, impulse_cutoff); } /** * @param time_to_work emulated time in milliseconds * @return elapsed cpu cycles */ regparm long gbhw_step(long time_to_work) { long cycles_total = 0; if (pause_output) { (void)usleep(time_to_work*1000); return 0; } time_to_work *= msec_cycles; while (cycles_total < time_to_work) { long maxcycles = time_to_work - cycles_total; long cycles = 0; if (vblankctr > 0 && vblankctr < maxcycles) maxcycles = vblankctr; if (timerctr > 0 && timerctr < maxcycles) maxcycles = timerctr; while (cycles < maxcycles) { long step = gbcpu_step(); if (step < 0) return step; cycles += step; sum_cycles += step; gb_sound(step); } if (vblankctr > 0) vblankctr -= cycles; if (vblankctr <= 0 && gbcpu_if && (ioregs[0x7f] & 1)) { vblankctr += vblanktc; gbcpu_intr(0x40); } if (timerctr > 0) timerctr -= cycles; if (timerctr <= 0 && gbcpu_if && (ioregs[0x7f] & 4)) { timerctr += timertc; gbcpu_intr(0x48); } cycles_total += cycles; } return cycles_total; } regparm void gbhw_pause(long new_pause) { pause_output = new_pause != 0; } gbsplay-0.0.93/common.h0000644000175000017500000000377512566424552013726 0ustar mitchmitch/* * gbsplay is a Gameboy sound player * * 2003-2005 (C) by Tobias Diedrich * Licensed under GNU GPL. */ #ifndef _COMMON_H_ #define _COMMON_H_ /*@-onlytrans@*/ #include "config.h" #include #include #include #ifndef true #define true (0==0) #define false (!true) #endif #define TEXTDOMAIN "gbsplay" #define N_(x) x #if USE_REGPARM == 1 # define regparm __attribute__((regparm(3))) #else # define regparm #endif #if USE_I18N == 1 # include # include # if GBS_PRESERVE_TEXTDOMAIN == 1 static /*@dependent@*/ inline char* _(const char *msgid) { char *olddomain = textdomain(NULL); char *olddir = bindtextdomain(olddomain, NULL); char *res; bindtextdomain(TEXTDOMAIN, LOCALE_PREFIX); res = dgettext(TEXTDOMAIN, msgid); bindtextdomain(olddomain, olddir); return res; } # else static inline /*@dependent@*/ char* _(const char *msgid) { return gettext(msgid); } static inline void i18n_init(void) { if (setlocale(LC_ALL, "") == NULL) { fprintf(stderr, "setlocale() failed\n"); } if (bindtextdomain(TEXTDOMAIN, LOCALE_PREFIX) == NULL) { fprintf(stderr, "bindtextdomain() failed: %s\n", strerror(errno)); } if (textdomain(TEXTDOMAIN) == NULL) { fprintf(stderr, "textdomain() failed: %s\n", strerror(errno)); } } # endif #else # define _(x) (x) static inline void i18n_init(void) {} #endif #include #ifndef BYTE_ORDER # define BIG_ENDIAN 1 # define LITTLE_ENDIAN 2 # ifdef _BIG_ENDIAN # define BYTE_ORDER BIG_ENDIAN # endif # ifdef _LITTLE_ENDIAN # define BYTE_ORDER LITTLE_ENDIAN # endif #endif /* BYTE_ORDER */ #if !BYTE_ORDER || !BIG_ENDIAN || !LITTLE_ENDIAN # error endian defines missing #endif #if BYTE_ORDER == BIG_ENDIAN static inline long is_le_machine() { return false; } static inline long is_be_machine() { return true; } #else static inline long is_le_machine() { return true; } static inline long is_be_machine() { return false; } #endif /*@=onlytrans@*/ #endif gbsplay-0.0.93/impulsegen.h0000644000175000017500000000047012566424552014573 0ustar mitchmitch/* * gbsplay is a Gameboy sound player * * 2003-2006 (C) by Tobias Diedrich * Licensed under GNU GPL. */ #ifndef _IMPULSEGEN_H_ #define _IMPULSEGEN_H_ #define IMPULSE_HEIGHT 256.0 short *gen_impulsetab(long w_shift, long n_shift, double cutoff); #endif /* _IMPULSEGEN_H_ */ gbsplay-0.0.93/gbcpu.c0000644000175000017500000012451212566424552013522 0ustar mitchmitch/* * gbsplay is a Gameboy sound player * * 2003-2005 (C) by Tobias Diedrich * Licensed under GNU GPL. */ #include #include #include #include #include #include #include "gbcpu.h" #if DEBUG == 1 static const char regnames[12] = "BCDEHLFASPPC"; static const char *regnamech16[6] = { "BC", "DE", "HL", "FA", "SP", "PC" }; static const char *conds[4] = { "NZ", "Z", "NC", "C" }; #endif struct opinfo; typedef void regparm (*ex_fn)(uint32_t op, const struct opinfo *oi); struct opinfo { #if DEBUG == 1 || defined(S_SPLINT_S) char *name; #endif ex_fn fn; }; gbcpu_regs_u gbcpu_regs; long gbcpu_halted; long gbcpu_stopped; long gbcpu_if; static regparm uint32_t none_get(/*@unused@*/ uint32_t addr) { return 0xff; } static regparm void none_put(/*@unused@*/ uint32_t addr, /*@unused@*/ uint8_t val) { } static gbcpu_get_fn getlookup[256] = { &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get, &none_get }; static gbcpu_put_fn putlookup[256] = { &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put, &none_put }; static inline regparm uint32_t mem_get(uint32_t addr) { gbcpu_get_fn fn = getlookup[addr >> 8]; return fn(addr); } static inline regparm void mem_put(uint32_t addr, uint32_t val) { gbcpu_put_fn fn = putlookup[addr >> 8]; fn(addr, val); } static regparm void push(uint32_t val) { uint32_t sp = REGS16_R(gbcpu_regs, SP) - 2; REGS16_W(gbcpu_regs, SP, sp); mem_put(sp, val & 0xff); mem_put(sp+1, val >> 8); } static regparm uint32_t pop(void) { uint32_t res; uint32_t sp = REGS16_R(gbcpu_regs, SP); res = mem_get(sp); res += mem_get(sp+1) << 8; REGS16_W(gbcpu_regs, SP, sp + 2); return res; } static regparm uint32_t get_imm8(void) { uint32_t pc = REGS16_R(gbcpu_regs, PC); uint32_t res; REGS16_W(gbcpu_regs, PC, pc + 1); res = mem_get(pc); DPRINTF("%02x", res); return res; } static regparm uint32_t get_imm16(void) { uint32_t pc = REGS16_R(gbcpu_regs, PC); uint32_t res; REGS16_W(gbcpu_regs, PC, pc + 2); res = mem_get(pc) + (mem_get(pc+1) << 8); DPRINTF("%04x", res); return res; } static inline void print_reg(long i) { if (i == 6) DPRINTF("[HL]"); /* indirect memory access by [HL] */ else DPRINTF("%c", regnames[i]); } static regparm uint32_t get_reg(long i) { if (i == 6) /* indirect memory access by [HL] */ return mem_get(REGS16_R(gbcpu_regs, HL)); return REGS8_R(gbcpu_regs, i); } static regparm void put_reg(long i, uint32_t val) { if (i == 6) /* indirect memory access by [HL] */ mem_put(REGS16_R(gbcpu_regs, HL), val); else REGS8_W(gbcpu_regs, i, val); } static regparm void op_unknown(uint32_t op, /*@unused@*/ const struct opinfo *oi) { fprintf(stderr, "\n\nUnknown opcode %02x.\n", (unsigned char)op); gbcpu_stopped = 1; } static regparm void op_set(uint32_t op) { long reg = op & 7; unsigned long bit = (op >> 3) & 7; DPRINTF("\tSET %ld, ", bit); print_reg(reg); put_reg(reg, get_reg(reg) | (1 << bit)); } static regparm void op_res(uint32_t op) { long reg = op & 7; unsigned long bit = (op >> 3) & 7; DPRINTF("\tRES %ld, ", bit); print_reg(reg); put_reg(reg, get_reg(reg) & ~(1 << bit)); } static regparm void op_bit(uint32_t op) { long reg = op & 7; unsigned long bit = (op >> 3) & 7; DPRINTF("\tBIT %ld, ", bit); print_reg(reg); gbcpu_regs.rn.f &= ~NF; gbcpu_regs.rn.f |= HF | ZF; gbcpu_regs.rn.f ^= ((get_reg(reg) << 8) >> (bit+1)) & ZF; } static regparm void op_rl(uint32_t op, const struct opinfo *oi) { long reg = op & 7; uint8_t res, val; DPRINTF(" %s ", oi->name); print_reg(reg); res = val = get_reg(reg); res = res << 1; res |= (gbcpu_regs.rn.f & CF) >> 4; gbcpu_regs.rn.f = (val >> 7) << 4; if (res == 0) gbcpu_regs.rn.f |= ZF; put_reg(reg, res); } static regparm void op_rla(/*@unused@*/ uint32_t op, const struct opinfo *oi) { uint8_t res; DPRINTF(" %s", oi->name); res = gbcpu_regs.rn.a; res = res << 1; res |= (gbcpu_regs.rn.f & CF) >> 4; gbcpu_regs.rn.f = (gbcpu_regs.rn.a >> 7) << 4; if (res == 0) gbcpu_regs.rn.f |= ZF; gbcpu_regs.rn.a = res; } static regparm void op_rlc(uint32_t op, const struct opinfo *oi) { long reg = op & 7; uint8_t res, val; DPRINTF(" %s ", oi->name); print_reg(reg); res = val = get_reg(reg); res = res << 1; res |= val >> 7; gbcpu_regs.rn.f = (val >> 7) << 4; if (res == 0) gbcpu_regs.rn.f |= ZF; put_reg(reg, res); } static regparm void op_rlca(/*@unused@*/ uint32_t op, const struct opinfo *oi) { uint8_t res; DPRINTF(" %s", oi->name); res = gbcpu_regs.rn.a; res = res << 1; res |= gbcpu_regs.rn.a >> 7; gbcpu_regs.rn.f = (gbcpu_regs.rn.a >> 7) << 4; if (res == 0) gbcpu_regs.rn.f |= ZF; gbcpu_regs.rn.a = res; } static regparm void op_sla(uint32_t op, const struct opinfo *oi) { long reg = op & 7; uint8_t res, val; DPRINTF(" %s ", oi->name); print_reg(reg); res = val = get_reg(reg); res = res << 1; gbcpu_regs.rn.f = (val >> 7) << 4; if (res == 0) gbcpu_regs.rn.f |= ZF; put_reg(reg, res); } static regparm void op_rr(uint32_t op, const struct opinfo *oi) { long reg = op & 7; uint8_t res, val; DPRINTF(" %s ", oi->name); print_reg(reg); res = val = get_reg(reg); res = res >> 1; res |= (gbcpu_regs.rn.f & CF) << 3; gbcpu_regs.rn.f = (val & 1) << 4; if (res == 0) gbcpu_regs.rn.f |= ZF; put_reg(reg, res); } static regparm void op_rra(/*@unused@*/ uint32_t op, const struct opinfo *oi) { uint8_t res; DPRINTF(" %s", oi->name); res = gbcpu_regs.rn.a; res = res >> 1; res |= (gbcpu_regs.rn.f & CF) << 3; gbcpu_regs.rn.f = (gbcpu_regs.rn.a & 1) << 4; if (res == 0) gbcpu_regs.rn.f |= ZF; gbcpu_regs.rn.a = res; } static regparm void op_rrc(uint32_t op, const struct opinfo *oi) { long reg = op & 7; uint8_t res, val; DPRINTF(" %s ", oi->name); print_reg(reg); res = val = get_reg(reg); res = res >> 1; res |= val << 7; gbcpu_regs.rn.f = (val & 1) << 4; if (res == 0) gbcpu_regs.rn.f |= ZF; put_reg(reg, res); } static regparm void op_rrca(/*@unused@*/ uint32_t op, const struct opinfo *oi) { uint8_t res; DPRINTF(" %s", oi->name); res = gbcpu_regs.rn.a; res = res >> 1; res |= gbcpu_regs.rn.a << 7; gbcpu_regs.rn.f = (gbcpu_regs.rn.a & 1) << 4; if (res == 0) gbcpu_regs.rn.f |= ZF; gbcpu_regs.rn.a = res; } static regparm void op_sra(uint32_t op, const struct opinfo *oi) { long reg = op & 7; uint8_t res, val; DPRINTF(" %s ", oi->name); print_reg(reg); res = val = get_reg(reg); res = res >> 1; res |= val & 0x80; gbcpu_regs.rn.f = (val & 1) << 4; if (res == 0) gbcpu_regs.rn.f |= ZF; put_reg(reg, res); } static regparm void op_srl(uint32_t op, const struct opinfo *oi) { long reg = op & 7; uint8_t res, val; DPRINTF(" %s ", oi->name); print_reg(reg); res = val = get_reg(reg); res = res >> 1; gbcpu_regs.rn.f = (val & 1) << 4; if (res == 0) gbcpu_regs.rn.f |= ZF; put_reg(reg, res); } static regparm void op_swap(uint32_t op, const struct opinfo *oi) { long reg = op & 7; uint32_t res; uint32_t val; DPRINTF(" %s ", oi->name); print_reg(reg); val = get_reg(reg); res = (val >> 4) | (val << 4); gbcpu_regs.rn.f = 0; if (res == 0) gbcpu_regs.rn.f |= ZF; put_reg(reg, res); } static const struct opinfo cbops[8] = { OPINFO("\tRLC", &op_rlc), /* opcode cb00-cb07 */ OPINFO("\tRRC", &op_rrc), /* opcode cb08-cb0f */ OPINFO("\tRL", &op_rl), /* opcode cb10-cb17 */ OPINFO("\tRR", &op_rr), /* opcode cb18-cb1f */ OPINFO("\tSLA", &op_sla), /* opcode cb20-cb27 */ OPINFO("\tSRA", &op_sra), /* opcode cb28-cb2f */ OPINFO("\tSWAP", &op_swap), /* opcode cb30-cb37 */ OPINFO("\tSRL", &op_srl), /* opcode cb38-cb3f */ }; static regparm void op_cbprefix(uint32_t op, /*@unused@*/ const struct opinfo *oi) { uint16_t pc = REGS16_R(gbcpu_regs, PC); REGS16_W(gbcpu_regs, PC, pc + 1); op = mem_get(pc); switch (op >> 6) { case 0: cbops[(op >> 3) & 7].fn(op, &cbops[(op >> 3) & 7]); return; case 1: op_bit(op); return; case 2: op_res(op); return; case 3: op_set(op); return; } fprintf(stderr, "\n\nUnknown CB subopcode %02x.\n", (unsigned char)op); gbcpu_stopped = 1; } static regparm void op_ld(uint32_t op, const struct opinfo *oi) { long src = op & 7; long dst = (op >> 3) & 7; DPRINTF(" %s ", oi->name); print_reg(dst); DPRINTF(", "); print_reg(src); put_reg(dst, get_reg(src)); } static regparm void op_ld_imm(/*@unused@*/ uint32_t op, const struct opinfo *oi) { long ofs = get_imm16(); DPRINTF(" %s A, [0x%04lx]", oi->name, ofs); gbcpu_regs.rn.a = mem_get(ofs); } static regparm void op_ld_ind16_a(/*@unused@*/ uint32_t op, const struct opinfo *oi) { long ofs = get_imm16(); DPRINTF(" %s [0x%04lx], A", oi->name, ofs); mem_put(ofs, gbcpu_regs.rn.a); } static regparm void op_ld_ind16_sp(/*@unused@*/ uint32_t op, const struct opinfo *oi) { long ofs = get_imm16(); long sp = REGS16_R(gbcpu_regs, SP); DPRINTF(" %s [0x%04lx], SP", oi->name, ofs); mem_put(ofs, sp & 0xff); mem_put(ofs+1, sp >> 8); } static regparm void op_ld_hlsp(/*@unused@*/ uint32_t op, const struct opinfo *oi) { int8_t ofs = get_imm8(); uint16_t old = REGS16_R(gbcpu_regs, SP); uint16_t new = old + ofs; if (ofs>0) DPRINTF(" %s HL, SP+0x%02x", oi->name, ofs); else DPRINTF(" %s HL, SP-0x%02x", oi->name, -ofs); REGS16_W(gbcpu_regs, HL, new); gbcpu_regs.rn.f = 0; if (old > new) gbcpu_regs.rn.f |= CF; if ((old & 0xfff) > (new & 0xfff)) gbcpu_regs.rn.f |= HF; } static regparm void op_ld_sphl(/*@unused@*/ uint32_t op, const struct opinfo *oi) { DPRINTF(" %s SP, HL", oi->name); REGS16_W(gbcpu_regs, SP, REGS16_R(gbcpu_regs, HL)); } static regparm void op_ld_reg16_imm(uint32_t op, const struct opinfo *oi) { long val = get_imm16(); long reg = (op >> 4) & 3; reg += reg > 2; /* skip over FA */ DPRINTF(" %s %s, 0x%04lx", oi->name, regnamech16[reg], val); REGS16_W(gbcpu_regs, reg, val); } static regparm void op_ld_reg16_a(uint32_t op, const struct opinfo *oi) { long reg = (op >> 4) & 3; uint16_t r; reg -= reg > 2; if (op & 8) { DPRINTF(" %s A, [%s]", oi->name, regnamech16[reg]); gbcpu_regs.rn.a = mem_get(r = REGS16_R(gbcpu_regs, reg)); } else { DPRINTF(" %s [%s], A", oi->name, regnamech16[reg]); mem_put(r = REGS16_R(gbcpu_regs, reg), gbcpu_regs.rn.a); } if (reg == 2) { r += (((op & 0x10) == 0) << 1)-1; REGS16_W(gbcpu_regs, reg, r); } } static regparm void op_ld_reg8_imm(uint32_t op, const struct opinfo *oi) { long val = get_imm8(); long reg = (op >> 3) & 7; DPRINTF(" %s ", oi->name); print_reg(reg); put_reg(reg, val); DPRINTF(", 0x%02lx", val); } static regparm void op_ldh(uint32_t op, const struct opinfo *oi) { long ofs = op & 2 ? 0 : get_imm8(); if (op & 0x10) { DPRINTF(" %s A, ", oi->name); if ((op & 2) == 0) { DPRINTF("[%02lx]", ofs); } else { ofs = gbcpu_regs.rn.c; DPRINTF("[C]"); } gbcpu_regs.rn.a = mem_get(0xff00 + ofs); } else { if ((op & 2) == 0) { DPRINTF(" %s [%02lx], A", oi->name, ofs); } else { ofs = gbcpu_regs.rn.c; DPRINTF(" %s [C], A", oi->name); } mem_put(0xff00 + ofs, gbcpu_regs.rn.a); } } static regparm void op_inc(uint32_t op, const struct opinfo *oi) { long reg = (op >> 3) & 7; uint8_t res; uint8_t old; DPRINTF(" %s ", oi->name); print_reg(reg); old = res = get_reg(reg); res++; put_reg(reg, res); gbcpu_regs.rn.f &= ~(NF | ZF | HF); if (res == 0) gbcpu_regs.rn.f |= ZF; if ((old & 15) > (res & 15)) gbcpu_regs.rn.f |= HF; } static regparm void op_inc16(uint32_t op, const struct opinfo *oi) { long reg = (op >> 4) & 3; uint16_t res = REGS16_R(gbcpu_regs, reg); DPRINTF(" %s %s\t", oi->name, regnamech16[reg]); res++; REGS16_W(gbcpu_regs, reg, res); } static regparm void op_dec(uint32_t op, const struct opinfo *oi) { long reg = (op >> 3) & 7; uint8_t res; uint8_t old; DPRINTF(" %s ", oi->name); print_reg(reg); old = res = get_reg(reg); res--; put_reg(reg, res); gbcpu_regs.rn.f |= NF; gbcpu_regs.rn.f &= ~(ZF | HF); if (res == 0) gbcpu_regs.rn.f |= ZF; if ((old & 15) > (res & 15)) gbcpu_regs.rn.f |= HF; } static regparm void op_dec16(uint32_t op, const struct opinfo *oi) { long reg = (op >> 4) & 3; uint16_t res = REGS16_R(gbcpu_regs, reg); DPRINTF(" %s %s", oi->name, regnamech16[reg]); res--; REGS16_W(gbcpu_regs, reg, res); } static regparm void op_add_sp_imm(/*@unused@*/ uint32_t op, const struct opinfo *oi) { int8_t imm = get_imm8(); uint16_t old = REGS16_R(gbcpu_regs, SP); uint16_t new = old; DPRINTF(" %s SP, %02x", oi->name, imm); new += imm; REGS16_W(gbcpu_regs, SP, new); gbcpu_regs.rn.f = 0; if (old > new) gbcpu_regs.rn.f |= CF; if ((old & 0xfff) > (new & 0xfff)) gbcpu_regs.rn.f |= HF; } static regparm void op_add(uint32_t op, const struct opinfo *oi) { uint8_t old = gbcpu_regs.rn.a; uint8_t new; DPRINTF(" %s A, ", oi->name); print_reg(op & 7); gbcpu_regs.rn.a += get_reg(op & 7); new = gbcpu_regs.rn.a; gbcpu_regs.rn.f = 0; if (old > new) gbcpu_regs.rn.f |= CF; if ((old & 15) > (new & 15)) gbcpu_regs.rn.f |= HF; if (new == 0) gbcpu_regs.rn.f |= ZF; } static regparm void op_add_imm(/*@unused@*/ uint32_t op, const struct opinfo *oi) { uint8_t imm = get_imm8(); uint8_t old = gbcpu_regs.rn.a; uint8_t new = old; DPRINTF(" %s A, $0x%02x", oi->name, imm); new += imm; gbcpu_regs.rn.a = new; gbcpu_regs.rn.f = 0; if (old > new) gbcpu_regs.rn.f |= CF; if ((old & 15) > (new & 15)) gbcpu_regs.rn.f |= HF; if (new == 0) gbcpu_regs.rn.f |= ZF; } static regparm void op_add_hl(uint32_t op, const struct opinfo *oi) { long reg = (op >> 4) & 3; uint16_t old = REGS16_R(gbcpu_regs, HL); uint16_t new = old; reg += reg > 2; DPRINTF(" %s HL, %s", oi->name, regnamech16[reg]); new += REGS16_R(gbcpu_regs, reg); REGS16_W(gbcpu_regs, HL, new); gbcpu_regs.rn.f &= ~(NF | CF | HF); if (old > new) gbcpu_regs.rn.f |= CF; if ((old & 0xfff) > (new & 0xfff)) gbcpu_regs.rn.f |= HF; } static regparm void op_adc(uint32_t op, const struct opinfo *oi) { uint8_t old = gbcpu_regs.rn.a; uint8_t new; DPRINTF(" %s A, ", oi->name); print_reg(op & 7); gbcpu_regs.rn.a += get_reg(op & 7); gbcpu_regs.rn.a += (gbcpu_regs.rn.f & CF) > 0; gbcpu_regs.rn.f &= ~NF; new = gbcpu_regs.rn.a; if (old > new) gbcpu_regs.rn.f |= CF; else gbcpu_regs.rn.f &= ~CF; if ((old & 15) > (new & 15)) gbcpu_regs.rn.f |= HF; else gbcpu_regs.rn.f &= ~HF; if (new == 0) gbcpu_regs.rn.f |= ZF; else gbcpu_regs.rn.f &= ~ZF; } static regparm void op_adc_imm(/*@unused@*/ uint32_t op, const struct opinfo *oi) { uint8_t imm = get_imm8(); uint8_t old = gbcpu_regs.rn.a; uint8_t new = old; DPRINTF(" %s A, $0x%02x", oi->name, imm); new += imm; new += (gbcpu_regs.rn.f & CF) > 0; gbcpu_regs.rn.f &= ~NF; gbcpu_regs.rn.a = new; if (old > new) gbcpu_regs.rn.f |= CF; else gbcpu_regs.rn.f &= ~CF; if ((old & 15) > (new & 15)) gbcpu_regs.rn.f |= HF; else gbcpu_regs.rn.f &= ~HF; if (new == 0) gbcpu_regs.rn.f |= ZF; else gbcpu_regs.rn.f &= ~ZF; } static regparm void op_cp(uint32_t op, const struct opinfo *oi) { uint8_t old = gbcpu_regs.rn.a; uint8_t new = old; DPRINTF(" %s A, ", oi->name); print_reg(op & 7); new -= get_reg(op & 7); gbcpu_regs.rn.f = NF; if (old < new) gbcpu_regs.rn.f |= CF; if ((old & 15) < (new & 15)) gbcpu_regs.rn.f |= HF; if (new == 0) gbcpu_regs.rn.f |= ZF; } static regparm void op_cp_imm(/*@unused@*/ uint32_t op, const struct opinfo *oi) { uint8_t imm = get_imm8(); uint8_t old = gbcpu_regs.rn.a; uint8_t new = old; DPRINTF(" %s A, $0x%02x", oi->name, imm); new -= imm; gbcpu_regs.rn.f = NF; if (old < new) gbcpu_regs.rn.f |= CF; if ((old & 15) < (new & 15)) gbcpu_regs.rn.f |= HF; if (new == 0) gbcpu_regs.rn.f |= ZF; } static regparm void op_sub(uint32_t op, const struct opinfo *oi) { uint8_t old = gbcpu_regs.rn.a; uint8_t new; DPRINTF(" %s A, ", oi->name); print_reg(op & 7); gbcpu_regs.rn.a -= get_reg(op & 7); new = gbcpu_regs.rn.a; gbcpu_regs.rn.f = NF; if (old < new) gbcpu_regs.rn.f |= CF; if ((old & 15) < (new & 15)) gbcpu_regs.rn.f |= HF; if (new == 0) gbcpu_regs.rn.f |= ZF; } static regparm void op_sub_imm(/*@unused@*/ uint32_t op, const struct opinfo *oi) { uint8_t imm = get_imm8(); uint8_t old = gbcpu_regs.rn.a; uint8_t new = old; DPRINTF(" %s A, $0x%02x", oi->name, imm); new -= imm; gbcpu_regs.rn.a = new; gbcpu_regs.rn.f = NF; if (old < new) gbcpu_regs.rn.f |= CF; if ((old & 15) < (new & 15)) gbcpu_regs.rn.f |= HF; if (new == 0) gbcpu_regs.rn.f |= ZF; } static regparm void op_sbc(uint32_t op, const struct opinfo *oi) { uint8_t old = gbcpu_regs.rn.a; uint8_t new; DPRINTF(" %s A, ", oi->name); print_reg(op & 7); gbcpu_regs.rn.a -= get_reg(op & 7); gbcpu_regs.rn.a -= (gbcpu_regs.rn.f & CF) > 0; new = gbcpu_regs.rn.a; gbcpu_regs.rn.f = NF; if (old < new) gbcpu_regs.rn.f |= CF; if ((old & 15) < (new & 15)) gbcpu_regs.rn.f |= HF; if (new == 0) gbcpu_regs.rn.f |= ZF; } static regparm void op_sbc_imm(/*@unused@*/ uint32_t op, const struct opinfo *oi) { uint8_t imm = get_imm8(); uint8_t old = gbcpu_regs.rn.a; uint8_t new = old; DPRINTF(" %s A, $0x%02x", oi->name, imm); new -= imm; new -= (gbcpu_regs.rn.f & CF) > 0; gbcpu_regs.rn.a = new; gbcpu_regs.rn.f = NF; if (old < new) gbcpu_regs.rn.f |= CF; if ((old & 15) < (new & 15)) gbcpu_regs.rn.f |= HF; if (new == 0) gbcpu_regs.rn.f |= ZF; } static regparm void op_and(uint32_t op, const struct opinfo *oi) { DPRINTF(" %s A, ", oi->name); print_reg(op & 7); gbcpu_regs.rn.a &= get_reg(op & 7); gbcpu_regs.rn.f = HF; if (gbcpu_regs.rn.a == 0) gbcpu_regs.rn.f |= ZF; } static regparm void op_and_imm(/*@unused@*/ uint32_t op, const struct opinfo *oi) { uint8_t imm = get_imm8(); DPRINTF(" %s A, $0x%02x", oi->name, imm); gbcpu_regs.rn.a &= imm; gbcpu_regs.rn.f = HF; if (gbcpu_regs.rn.a == 0) gbcpu_regs.rn.f |= ZF; } static regparm void op_or(uint32_t op, const struct opinfo *oi) { DPRINTF(" %s A, ", oi->name); print_reg(op & 7); gbcpu_regs.rn.a |= get_reg(op & 7); gbcpu_regs.rn.f = 0; if (gbcpu_regs.rn.a == 0) gbcpu_regs.rn.f |= ZF; } static regparm void op_or_imm(/*@unused@*/ uint32_t op, const struct opinfo *oi) { uint8_t imm = get_imm8(); DPRINTF(" %s A, $0x%02x", oi->name, imm); gbcpu_regs.rn.a |= imm; gbcpu_regs.rn.f = 0; if (gbcpu_regs.rn.a == 0) gbcpu_regs.rn.f |= ZF; } static regparm void op_xor(uint32_t op, const struct opinfo *oi) { DPRINTF(" %s A, ", oi->name); print_reg(op & 7); gbcpu_regs.rn.a ^= get_reg(op & 7); gbcpu_regs.rn.f = 0; if (gbcpu_regs.rn.a == 0) gbcpu_regs.rn.f |= ZF; } static regparm void op_xor_imm(/*@unused@*/ uint32_t op, const struct opinfo *oi) { uint8_t imm = get_imm8(); DPRINTF(" %s A, $0x%02x", oi->name, imm); gbcpu_regs.rn.a ^= imm; gbcpu_regs.rn.f = 0; if (gbcpu_regs.rn.a == 0) gbcpu_regs.rn.f |= ZF; } static regparm void op_push(uint32_t op, const struct opinfo *oi) { long reg = op >> 4 & 3; push(REGS16_R(gbcpu_regs, reg)); DPRINTF(" %s %s\t", oi->name, regnamech16[reg]); } static regparm void op_pop(uint32_t op, const struct opinfo *oi) { long reg = op >> 4 & 3; REGS16_W(gbcpu_regs, reg, pop()); DPRINTF(" %s %s\t", oi->name, regnamech16[reg]); } static regparm void op_cpl(/*@unused@*/ uint32_t op, const struct opinfo *oi) { DPRINTF(" %s", oi->name); gbcpu_regs.rn.a = ~gbcpu_regs.rn.a; gbcpu_regs.rn.f |= NF | HF; } static regparm void op_ccf(/*@unused@*/ uint32_t op, const struct opinfo *oi) { DPRINTF(" %s", oi->name); gbcpu_regs.rn.f ^= CF; gbcpu_regs.rn.f &= ~(NF | HF); } static regparm void op_scf(/*@unused@*/ uint32_t op, const struct opinfo *oi) { DPRINTF(" %s", oi->name); gbcpu_regs.rn.f |= CF; gbcpu_regs.rn.f &= ~(NF | HF); } static regparm void op_call(/*@unused@*/ uint32_t op, const struct opinfo *oi) { uint16_t ofs = get_imm16(); DPRINTF(" %s 0x%04x", oi->name, ofs); push(REGS16_R(gbcpu_regs, PC)); REGS16_W(gbcpu_regs, PC, ofs); } static regparm void op_call_cond(uint32_t op, const struct opinfo *oi) { uint16_t ofs = get_imm16(); long cond = (op >> 3) & 3; DPRINTF(" %s %s 0x%04x", oi->name, conds[cond], ofs); switch (cond) { case 0: if ((gbcpu_regs.rn.f & ZF) != 0) return; break; case 1: if ((gbcpu_regs.rn.f & ZF) == 0) return; break; case 2: if ((gbcpu_regs.rn.f & CF) != 0) return; break; case 3: if ((gbcpu_regs.rn.f & CF) == 0) return; break; } push(REGS16_R(gbcpu_regs, PC)); REGS16_W(gbcpu_regs, PC, ofs); } static regparm void op_ret(/*@unused@*/ uint32_t op, const struct opinfo *oi) { REGS16_W(gbcpu_regs, PC, pop()); DPRINTF(" %s", oi->name); } static regparm void op_reti(/*@unused@*/ uint32_t op, const struct opinfo *oi) { REGS16_W(gbcpu_regs, PC, pop()); DPRINTF(" %s", oi->name); } static regparm void op_ret_cond(uint32_t op, const struct opinfo *oi) { long cond = (op >> 3) & 3; DPRINTF(" %s %s", oi->name, conds[cond]); switch (cond) { case 0: if ((gbcpu_regs.rn.f & ZF) != 0) return; break; case 1: if ((gbcpu_regs.rn.f & ZF) == 0) return; break; case 2: if ((gbcpu_regs.rn.f & CF) != 0) return; break; case 3: if ((gbcpu_regs.rn.f & CF) == 0) return; break; } REGS16_W(gbcpu_regs, PC, pop()); } static regparm void op_halt(/*@unused@*/ uint32_t op, const struct opinfo *oi) { gbcpu_halted = 1; DPRINTF(" %s", oi->name); } static regparm void op_stop(/*@unused@*/ uint32_t op, const struct opinfo *oi) { DPRINTF(" %s", oi->name); } static regparm void op_di(/*@unused@*/ uint32_t op, const struct opinfo *oi) { gbcpu_if = 0; DPRINTF(" %s", oi->name); } static regparm void op_ei(/*@unused@*/ uint32_t op, const struct opinfo *oi) { gbcpu_if = 1; DPRINTF(" %s", oi->name); } static regparm void op_jr(/*@unused@*/ uint32_t op, const struct opinfo *oi) { int16_t ofs = (int8_t) get_imm8(); if (ofs < 0) DPRINTF(" %s $-0x%02x", oi->name, -ofs); else DPRINTF(" %s $+0x%02x", oi->name, ofs); REGS16_W(gbcpu_regs, PC, REGS16_R(gbcpu_regs, PC) + ofs); } static regparm void op_jr_cond(uint32_t op, const struct opinfo *oi) { int16_t ofs = (int8_t) get_imm8(); long cond = (op >> 3) & 3; if (ofs < 0) DPRINTF(" %s %s $-0x%02x", oi->name, conds[cond], -ofs); else DPRINTF(" %s %s $+0x%02x", oi->name, conds[cond], ofs); switch (cond) { case 0: if ((gbcpu_regs.rn.f & ZF) != 0) return; break; case 1: if ((gbcpu_regs.rn.f & ZF) == 0) return; break; case 2: if ((gbcpu_regs.rn.f & CF) != 0) return; break; case 3: if ((gbcpu_regs.rn.f & CF) == 0) return; break; } REGS16_W(gbcpu_regs, PC, REGS16_R(gbcpu_regs, PC) + ofs); } static regparm void op_jp(/*@unused@*/ uint32_t op, const struct opinfo *oi) { uint16_t ofs = get_imm16(); DPRINTF(" %s 0x%04x", oi->name, ofs); REGS16_W(gbcpu_regs, PC, ofs); } static regparm void op_jp_hl(/*@unused@*/ uint32_t op, const struct opinfo *oi) { DPRINTF(" %s HL", oi->name); REGS16_W(gbcpu_regs, PC, REGS16_R(gbcpu_regs, HL)); } static regparm void op_jp_cond(uint32_t op, const struct opinfo *oi) { uint16_t ofs = get_imm16(); long cond = (op >> 3) & 3; DPRINTF(" %s %s 0x%04x", oi->name, conds[cond], ofs); switch (cond) { case 0: if ((gbcpu_regs.rn.f & ZF) != 0) return; break; case 1: if ((gbcpu_regs.rn.f & ZF) == 0) return; break; case 2: if ((gbcpu_regs.rn.f & CF) != 0) return; break; case 3: if ((gbcpu_regs.rn.f & CF) == 0) return; break; } REGS16_W(gbcpu_regs, PC, ofs); } static regparm void op_rst(uint32_t op, const struct opinfo *oi) { int16_t ofs = op & 0x38; DPRINTF(" %s 0x%02x", oi->name, ofs); push(REGS16_R(gbcpu_regs, PC)); REGS16_W(gbcpu_regs, PC, ofs); } static regparm void op_nop(/*@unused@*/ uint32_t op, const struct opinfo *oi) { DPRINTF(" %s", oi->name); } static const struct opinfo ops[256] = { OPINFO("\tNOP", &op_nop), /* opcode 00 */ OPINFO("\tLD", &op_ld_reg16_imm), /* opcode 01 */ OPINFO("\tLD", &op_ld_reg16_a), /* opcode 02 */ OPINFO("\tINC", &op_inc16), /* opcode 03 */ OPINFO("\tINC", &op_inc), /* opcode 04 */ OPINFO("\tDEC", &op_dec), /* opcode 05 */ OPINFO("\tLD", &op_ld_reg8_imm), /* opcode 06 */ OPINFO("\tRLCA", &op_rlca), /* opcode 07 */ OPINFO("\tLD", &op_ld_ind16_sp), /* opcode 08 */ OPINFO("\tADD", &op_add_hl), /* opcode 09 */ OPINFO("\tLD", &op_ld_reg16_a), /* opcode 0a */ OPINFO("\tDEC", &op_dec16), /* opcode 0b */ OPINFO("\tINC", &op_inc), /* opcode 0c */ OPINFO("\tDEC", &op_dec), /* opcode 0d */ OPINFO("\tLD", &op_ld_reg8_imm), /* opcode 0e */ OPINFO("\tRRCA", &op_rrca), /* opcode 0f */ OPINFO("\tSTOP", &op_stop), /* opcode 10 */ OPINFO("\tLD", &op_ld_reg16_imm), /* opcode 11 */ OPINFO("\tLD", &op_ld_reg16_a), /* opcode 12 */ OPINFO("\tINC", &op_inc16), /* opcode 13 */ OPINFO("\tINC", &op_inc), /* opcode 14 */ OPINFO("\tDEC", &op_dec), /* opcode 15 */ OPINFO("\tLD", &op_ld_reg8_imm), /* opcode 16 */ OPINFO("\tRLA", &op_rla), /* opcode 17 */ OPINFO("\tJR", &op_jr), /* opcode 18 */ OPINFO("\tADD", &op_add_hl), /* opcode 19 */ OPINFO("\tLD", &op_ld_reg16_a), /* opcode 1a */ OPINFO("\tDEC", &op_dec16), /* opcode 1b */ OPINFO("\tINC", &op_inc), /* opcode 1c */ OPINFO("\tDEC", &op_dec), /* opcode 1d */ OPINFO("\tLD", &op_ld_reg8_imm), /* opcode 1e */ OPINFO("\tRRA", &op_rra), /* opcode 1f */ OPINFO("\tJR", &op_jr_cond), /* opcode 20 */ OPINFO("\tLD", &op_ld_reg16_imm), /* opcode 21 */ OPINFO("\tLDI", &op_ld_reg16_a), /* opcode 22 */ OPINFO("\tINC", &op_inc16), /* opcode 23 */ OPINFO("\tINC", &op_inc), /* opcode 24 */ OPINFO("\tDEC", &op_dec), /* opcode 25 */ OPINFO("\tLD", &op_ld_reg8_imm), /* opcode 26 */ OPINFO("\tUNKN", &op_unknown), /* opcode 27 */ OPINFO("\tJR", &op_jr_cond), /* opcode 28 */ OPINFO("\tADD", &op_add_hl), /* opcode 29 */ OPINFO("\tLDI", &op_ld_reg16_a), /* opcode 2a */ OPINFO("\tDEC", &op_dec16), /* opcode 2b */ OPINFO("\tINC", &op_inc), /* opcode 2c */ OPINFO("\tDEC", &op_dec), /* opcode 2d */ OPINFO("\tLD", &op_ld_reg8_imm), /* opcode 2e */ OPINFO("\tCPL", &op_cpl), /* opcode 2f */ OPINFO("\tJR", &op_jr_cond), /* opcode 30 */ OPINFO("\tLD", &op_ld_reg16_imm), /* opcode 31 */ OPINFO("\tLDD", &op_ld_reg16_a), /* opcode 32 */ OPINFO("\tINC", &op_inc16), /* opcode 33 */ OPINFO("\tINC", &op_inc), /* opcode 34 */ OPINFO("\tDEC", &op_dec), /* opcode 35 */ OPINFO("\tLD", &op_ld_reg8_imm), /* opcode 36 */ OPINFO("\tSCF", &op_scf), /* opcode 37 */ OPINFO("\tJR", &op_jr_cond), /* opcode 38 */ OPINFO("\tADD", &op_add_hl), /* opcode 39 */ OPINFO("\tLDD", &op_ld_reg16_a), /* opcode 3a */ OPINFO("\tDEC", &op_dec16), /* opcode 3b */ OPINFO("\tINC", &op_inc), /* opcode 3c */ OPINFO("\tDEC", &op_dec), /* opcode 3d */ OPINFO("\tLD", &op_ld_reg8_imm), /* opcode 3e */ OPINFO("\tCCF", &op_ccf), /* opcode 3f */ OPINFO("\tLD", &op_ld), /* opcode 40 */ OPINFO("\tLD", &op_ld), /* opcode 41 */ OPINFO("\tLD", &op_ld), /* opcode 42 */ OPINFO("\tLD", &op_ld), /* opcode 43 */ OPINFO("\tLD", &op_ld), /* opcode 44 */ OPINFO("\tLD", &op_ld), /* opcode 45 */ OPINFO("\tLD", &op_ld), /* opcode 46 */ OPINFO("\tLD", &op_ld), /* opcode 47 */ OPINFO("\tLD", &op_ld), /* opcode 48 */ OPINFO("\tLD", &op_ld), /* opcode 49 */ OPINFO("\tLD", &op_ld), /* opcode 4a */ OPINFO("\tLD", &op_ld), /* opcode 4b */ OPINFO("\tLD", &op_ld), /* opcode 4c */ OPINFO("\tLD", &op_ld), /* opcode 4d */ OPINFO("\tLD", &op_ld), /* opcode 4e */ OPINFO("\tLD", &op_ld), /* opcode 4f */ OPINFO("\tLD", &op_ld), /* opcode 50 */ OPINFO("\tLD", &op_ld), /* opcode 51 */ OPINFO("\tLD", &op_ld), /* opcode 52 */ OPINFO("\tLD", &op_ld), /* opcode 53 */ OPINFO("\tLD", &op_ld), /* opcode 54 */ OPINFO("\tLD", &op_ld), /* opcode 55 */ OPINFO("\tLD", &op_ld), /* opcode 56 */ OPINFO("\tLD", &op_ld), /* opcode 57 */ OPINFO("\tLD", &op_ld), /* opcode 58 */ OPINFO("\tLD", &op_ld), /* opcode 59 */ OPINFO("\tLD", &op_ld), /* opcode 5a */ OPINFO("\tLD", &op_ld), /* opcode 5b */ OPINFO("\tLD", &op_ld), /* opcode 5c */ OPINFO("\tLD", &op_ld), /* opcode 5d */ OPINFO("\tLD", &op_ld), /* opcode 5e */ OPINFO("\tLD", &op_ld), /* opcode 5f */ OPINFO("\tLD", &op_ld), /* opcode 60 */ OPINFO("\tLD", &op_ld), /* opcode 61 */ OPINFO("\tLD", &op_ld), /* opcode 62 */ OPINFO("\tLD", &op_ld), /* opcode 63 */ OPINFO("\tLD", &op_ld), /* opcode 64 */ OPINFO("\tLD", &op_ld), /* opcode 65 */ OPINFO("\tLD", &op_ld), /* opcode 66 */ OPINFO("\tLD", &op_ld), /* opcode 67 */ OPINFO("\tLD", &op_ld), /* opcode 68 */ OPINFO("\tLD", &op_ld), /* opcode 69 */ OPINFO("\tLD", &op_ld), /* opcode 6a */ OPINFO("\tLD", &op_ld), /* opcode 6b */ OPINFO("\tLD", &op_ld), /* opcode 6c */ OPINFO("\tLD", &op_ld), /* opcode 6d */ OPINFO("\tLD", &op_ld), /* opcode 6e */ OPINFO("\tLD", &op_ld), /* opcode 6f */ OPINFO("\tLD", &op_ld), /* opcode 70 */ OPINFO("\tLD", &op_ld), /* opcode 71 */ OPINFO("\tLD", &op_ld), /* opcode 72 */ OPINFO("\tLD", &op_ld), /* opcode 73 */ OPINFO("\tLD", &op_ld), /* opcode 74 */ OPINFO("\tLD", &op_ld), /* opcode 75 */ OPINFO("\tHALT", &op_halt), /* opcode 76 */ OPINFO("\tLD", &op_ld), /* opcode 77 */ OPINFO("\tLD", &op_ld), /* opcode 78 */ OPINFO("\tLD", &op_ld), /* opcode 79 */ OPINFO("\tLD", &op_ld), /* opcode 7a */ OPINFO("\tLD", &op_ld), /* opcode 7b */ OPINFO("\tLD", &op_ld), /* opcode 7c */ OPINFO("\tLD", &op_ld), /* opcode 7d */ OPINFO("\tLD", &op_ld), /* opcode 7e */ OPINFO("\tLD", &op_ld), /* opcode 7f */ OPINFO("\tADD", &op_add), /* opcode 80 */ OPINFO("\tADD", &op_add), /* opcode 81 */ OPINFO("\tADD", &op_add), /* opcode 82 */ OPINFO("\tADD", &op_add), /* opcode 83 */ OPINFO("\tADD", &op_add), /* opcode 84 */ OPINFO("\tADD", &op_add), /* opcode 85 */ OPINFO("\tADD", &op_add), /* opcode 86 */ OPINFO("\tADD", &op_add), /* opcode 87 */ OPINFO("\tADC", &op_adc), /* opcode 88 */ OPINFO("\tADC", &op_adc), /* opcode 89 */ OPINFO("\tADC", &op_adc), /* opcode 8a */ OPINFO("\tADC", &op_adc), /* opcode 8b */ OPINFO("\tADC", &op_adc), /* opcode 8c */ OPINFO("\tADC", &op_adc), /* opcode 8d */ OPINFO("\tADC", &op_adc), /* opcode 8e */ OPINFO("\tADC", &op_adc), /* opcode 8f */ OPINFO("\tSUB", &op_sub), /* opcode 90 */ OPINFO("\tSUB", &op_sub), /* opcode 91 */ OPINFO("\tSUB", &op_sub), /* opcode 92 */ OPINFO("\tSUB", &op_sub), /* opcode 93 */ OPINFO("\tSUB", &op_sub), /* opcode 94 */ OPINFO("\tSUB", &op_sub), /* opcode 95 */ OPINFO("\tSUB", &op_sub), /* opcode 96 */ OPINFO("\tSUB", &op_sub), /* opcode 97 */ OPINFO("\tSBC", &op_sbc), /* opcode 98 */ OPINFO("\tSBC", &op_sbc), /* opcode 99 */ OPINFO("\tSBC", &op_sbc), /* opcode 9a */ OPINFO("\tSBC", &op_sbc), /* opcode 9b */ OPINFO("\tSBC", &op_sbc), /* opcode 9c */ OPINFO("\tSBC", &op_sbc), /* opcode 9d */ OPINFO("\tSBC", &op_sbc), /* opcode 9e */ OPINFO("\tSBC", &op_sbc), /* opcode 9f */ OPINFO("\tAND", &op_and), /* opcode a0 */ OPINFO("\tAND", &op_and), /* opcode a1 */ OPINFO("\tAND", &op_and), /* opcode a2 */ OPINFO("\tAND", &op_and), /* opcode a3 */ OPINFO("\tAND", &op_and), /* opcode a4 */ OPINFO("\tAND", &op_and), /* opcode a5 */ OPINFO("\tAND", &op_and), /* opcode a6 */ OPINFO("\tAND", &op_and), /* opcode a7 */ OPINFO("\tXOR", &op_xor), /* opcode a8 */ OPINFO("\tXOR", &op_xor), /* opcode a9 */ OPINFO("\tXOR", &op_xor), /* opcode aa */ OPINFO("\tXOR", &op_xor), /* opcode ab */ OPINFO("\tXOR", &op_xor), /* opcode ac */ OPINFO("\tXOR", &op_xor), /* opcode ad */ OPINFO("\tXOR", &op_xor), /* opcode ae */ OPINFO("\tXOR", &op_xor), /* opcode af */ OPINFO("\tOR", &op_or), /* opcode b0 */ OPINFO("\tOR", &op_or), /* opcode b1 */ OPINFO("\tOR", &op_or), /* opcode b2 */ OPINFO("\tOR", &op_or), /* opcode b3 */ OPINFO("\tOR", &op_or), /* opcode b4 */ OPINFO("\tOR", &op_or), /* opcode b5 */ OPINFO("\tOR", &op_or), /* opcode b6 */ OPINFO("\tOR", &op_or), /* opcode b7 */ OPINFO("\tCP", &op_cp), /* opcode b8 */ OPINFO("\tCP", &op_cp), /* opcode b9 */ OPINFO("\tCP", &op_cp), /* opcode ba */ OPINFO("\tCP", &op_cp), /* opcode bb */ OPINFO("\tCP", &op_cp), /* opcode bc */ OPINFO("\tCP", &op_cp), /* opcode bd */ OPINFO("\tCP", &op_cp), /* opcode be */ OPINFO("\tUNKN", &op_unknown), /* opcode bf */ OPINFO("\tRET", &op_ret_cond), /* opcode c0 */ OPINFO("\tPOP", &op_pop), /* opcode c1 */ OPINFO("\tJP", &op_jp_cond), /* opcode c2 */ OPINFO("\tJP", &op_jp), /* opcode c3 */ OPINFO("\tCALL", &op_call_cond), /* opcode c4 */ OPINFO("\tPUSH", &op_push), /* opcode c5 */ OPINFO("\tADD", &op_add_imm), /* opcode c6 */ OPINFO("\tRST", &op_rst), /* opcode c7 */ OPINFO("\tRET", &op_ret_cond), /* opcode c8 */ OPINFO("\tRET", &op_ret), /* opcode c9 */ OPINFO("\tJP", &op_jp_cond), /* opcode ca */ OPINFO("\tCBPREFIX", &op_cbprefix), /* opcode cb */ OPINFO("\tCALL", &op_call_cond), /* opcode cc */ OPINFO("\tCALL", &op_call), /* opcode cd */ OPINFO("\tADC", &op_adc_imm), /* opcode ce */ OPINFO("\tRST", &op_rst), /* opcode cf */ OPINFO("\tRET", &op_ret_cond), /* opcode d0 */ OPINFO("\tPOP", &op_pop), /* opcode d1 */ OPINFO("\tJP", &op_jp_cond), /* opcode d2 */ OPINFO("\tUNKN", &op_unknown), /* opcode d3 */ OPINFO("\tCALL", &op_call_cond), /* opcode d4 */ OPINFO("\tPUSH", &op_push), /* opcode d5 */ OPINFO("\tSUB", &op_sub_imm), /* opcode d6 */ OPINFO("\tRST", &op_rst), /* opcode d7 */ OPINFO("\tRET", &op_ret_cond), /* opcode d8 */ OPINFO("\tRETI", &op_reti), /* opcode d9 */ OPINFO("\tJP", &op_jp_cond), /* opcode da */ OPINFO("\tUNKN", &op_unknown), /* opcode db */ OPINFO("\tCALL", &op_call_cond), /* opcode dc */ OPINFO("\tUNKN", &op_unknown), /* opcode dd */ OPINFO("\tSBC", &op_sbc_imm), /* opcode de */ OPINFO("\tRST", &op_rst), /* opcode df */ OPINFO("\tLDH", &op_ldh), /* opcode e0 */ OPINFO("\tPOP", &op_pop), /* opcode e1 */ OPINFO("\tLDH", &op_ldh), /* opcode e2 */ OPINFO("\tUNKN", &op_unknown), /* opcode e3 */ OPINFO("\tUNKN", &op_unknown), /* opcode e4 */ OPINFO("\tPUSH", &op_push), /* opcode e5 */ OPINFO("\tAND", &op_and_imm), /* opcode e6 */ OPINFO("\tRST", &op_rst), /* opcode e7 */ OPINFO("\tADD", &op_add_sp_imm), /* opcode e8 */ OPINFO("\tJP", &op_jp_hl), /* opcode e9 */ OPINFO("\tLD", &op_ld_ind16_a), /* opcode ea */ OPINFO("\tUNKN", &op_unknown), /* opcode eb */ OPINFO("\tUNKN", &op_unknown), /* opcode ec */ OPINFO("\tUNKN", &op_unknown), /* opcode ed */ OPINFO("\tXOR", &op_xor_imm), /* opcode ee */ OPINFO("\tRST", &op_rst), /* opcode ef */ OPINFO("\tLDH", &op_ldh), /* opcode f0 */ OPINFO("\tPOP", &op_pop), /* opcode f1 */ OPINFO("\tLDH", &op_ldh), /* opcode f2 */ OPINFO("\tDI", &op_di), /* opcode f3 */ OPINFO("\tUNKN", &op_unknown), /* opcode f4 */ OPINFO("\tPUSH", &op_push), /* opcode f5 */ OPINFO("\tOR", &op_or_imm), /* opcode f6 */ OPINFO("\tRST", &op_rst), /* opcode f7 */ OPINFO("\tLD", &op_ld_hlsp), /* opcode f8 */ OPINFO("\tLD", &op_ld_sphl), /* opcode f9 */ OPINFO("\tLD", &op_ld_imm), /* opcode fa */ OPINFO("\tEI", &op_ei), /* opcode fb */ OPINFO("\tUNKN", &op_unknown), /* opcode fc */ OPINFO("\tUNKN", &op_unknown), /* opcode fd */ OPINFO("\tCP", &op_cp_imm), /* opcode fe */ OPINFO("\tRST", &op_rst), /* opcode ff */ }; #if DEBUG == 1 static gbcpu_regs_u oldregs; static regparm void dump_regs(void) { long i; DPRINTF("; "); for (i=0; i<8; i++) { DPRINTF("%c=%02x ", regnames[i], REGS8_R(gbcpu_regs, i)); } for (i=5; i<6; i++) { DPRINTF("%s=%04x ", regnamech16[i], REGS16_R(gbcpu_regs, i)); } DPRINTF("\n"); oldregs = gbcpu_regs; } static regparm void show_reg_diffs(void) { long i; DPRINTF("\t\t; "); for (i=0; i<3; i++) { if (REGS16_R(gbcpu_regs, i) != REGS16_R(oldregs, i)) { DPRINTF("%s=%04x ", regnamech16[i], REGS16_R(gbcpu_regs, i)); REGS16_W(oldregs, i, REGS16_R(gbcpu_regs, i)); } } for (i=6; i<8; i++) { if (REGS8_R(gbcpu_regs, i) != REGS8_R(oldregs, i)) { if (i == 6) { /* Flags */ if (gbcpu_regs.rn.f & ZF) DPRINTF("Z"); else DPRINTF("z"); if (gbcpu_regs.rn.f & NF) DPRINTF("N"); else DPRINTF("n"); if (gbcpu_regs.rn.f & HF) DPRINTF("H"); else DPRINTF("h"); if (gbcpu_regs.rn.f & CF) DPRINTF("C"); else DPRINTF("c"); DPRINTF(" "); } else { DPRINTF("%c=%02x ", regnames[i], REGS8_R(gbcpu_regs,i)); } REGS8_W(oldregs, i, REGS8_R(gbcpu_regs, i)); } } for (i=4; i<5; i++) { if (REGS16_R(gbcpu_regs, i) != REGS16_R(oldregs, i)) { DPRINTF("%s=%04x ", regnamech16[i], REGS16_R(gbcpu_regs, i)); REGS16_W(oldregs, i, REGS16_R(gbcpu_regs, i)); } } DPRINTF("\n"); } #endif regparm void gbcpu_addmem(uint32_t start, uint32_t end, gbcpu_put_fn putfn, gbcpu_get_fn getfn) { uint32_t i; for (i=start; i<=end; i++) { putlookup[i] = putfn; getlookup[i] = getfn; } } regparm void gbcpu_init(void) { memset(&gbcpu_regs, 0, sizeof(gbcpu_regs)); gbcpu_halted = 0; gbcpu_stopped = 0; gbcpu_if = 0; DEB(dump_regs()); } regparm void gbcpu_intr(long vec) { gbcpu_halted = 0; push(REGS16_R(gbcpu_regs, PC)); REGS16_W(gbcpu_regs, PC, vec); } regparm long gbcpu_step(void) { uint8_t op; if (!gbcpu_halted) { op = mem_get(gbcpu_regs.rn.pc++); DPRINTF("%04x: %02x", gbcpu_regs.rn.pc - 1, op); ops[op].fn(op, &ops[op]); DEB(show_reg_diffs()); return 1; } if (gbcpu_halted == 1 && gbcpu_if == 0) { fprintf(stderr, "CPU locked up (halt with interrupts disabled).\n"); gbcpu_stopped = 1; } if (gbcpu_stopped) return -1; return 16; } gbsplay-0.0.93/plugout_alsa.c0000644000175000017500000000656212566424552015125 0ustar mitchmitch/* * gbsplay is a Gameboy sound player * * 2006 (C) by Tobias Diedrich * * Licensed under GNU GPL. */ #include "common.h" #include #include #include #include #include #include #include #include "plugout.h" /* Handle for the PCM device */ snd_pcm_t *pcm_handle; #if BYTE_ORDER == LITTLE_ENDIAN #define SND_PCM_FORMAT_S16_NE SND_PCM_FORMAT_S16_LE #else #define SND_PCM_FORMAT_S16_NE SND_PCM_FORMAT_S16_BE #endif static long regparm alsa_open(enum plugout_endian endian, long rate) { const char *pcm_name = "default"; int fmt, err; unsigned exact_rate; snd_pcm_hw_params_t *hwparams; switch (endian) { case PLUGOUT_ENDIAN_BIG: fmt = SND_PCM_FORMAT_S16_BE; break; case PLUGOUT_ENDIAN_LITTLE: fmt = SND_PCM_FORMAT_S16_LE; break; default: case PLUGOUT_ENDIAN_NATIVE: fmt = SND_PCM_FORMAT_S16_NE; break; } snd_pcm_hw_params_alloca(&hwparams); if ((err = snd_pcm_open(&pcm_handle, pcm_name, SND_PCM_STREAM_PLAYBACK, 0)) < 0) { fprintf(stderr, _("Could not open ALSA PCM device '%s': %s\n"), pcm_name, snd_strerror(err)); return -1; } if ((err = snd_pcm_hw_params_any(pcm_handle, hwparams)) < 0) { fprintf(stderr, _("snd_pcm_hw_params_any failed: %s\n"), snd_strerror(err)); return -1; } if ((err = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { fprintf(stderr, _("snd_pcm_hw_params_set_access failed: %s\n"), snd_strerror(err)); return -1; } if ((err = snd_pcm_hw_params_set_format(pcm_handle, hwparams, fmt)) < 0) { fprintf(stderr, _("snd_pcm_hw_params_set_format failed: %s\n"), snd_strerror(err)); return -1; } exact_rate = rate; if ((err = snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &exact_rate, 0)) < 0) { fprintf(stderr, _("snd_pcm_hw_params_set_rate_near failed: %s\n"), snd_strerror(err)); return -1; } if (rate != exact_rate) { fprintf(stderr, _("Requested rate %ldHz, got %dHz.\n"), rate, exact_rate); } if ((err = snd_pcm_hw_params_set_channels(pcm_handle, hwparams, 2)) < 0) { fprintf(stderr, _("snd_pcm_hw_params_set_channels failed: %s\n"), snd_strerror(err)); return -1; } if ((err = snd_pcm_hw_params_set_periods(pcm_handle, hwparams, 4, 0)) < 0) { fprintf(stderr, _("snd_pcm_hw_params_set_periods failed: %s\n"), snd_strerror(err)); } if ((err = snd_pcm_hw_params_set_buffer_size(pcm_handle, hwparams, 8192)) < 0) { fprintf(stderr, _("snd_pcm_hw_params_set_buffer_size failed: %s\n"), snd_strerror(err)); } if ((err = snd_pcm_hw_params(pcm_handle, hwparams)) < 0) { fprintf(stderr, _("snd_pcm_hw_params failed: %s\n"), snd_strerror(err)); return -1; } return 0; } static ssize_t regparm alsa_write(const void *buf, size_t count) { snd_pcm_sframes_t retval; do { retval = snd_pcm_writei(pcm_handle, buf, count / 4); if (retval != -ESTRPIPE) break; /* resume from suspend */ while (snd_pcm_resume(pcm_handle) == -EAGAIN) sleep(1); } while (1); if (retval < 0) { fprintf(stderr, _("snd_pcm_writei failed: %s\n"), snd_strerror(retval)); snd_pcm_prepare(pcm_handle); } return retval; } static void regparm alsa_close() { snd_pcm_drop(pcm_handle); snd_pcm_close(pcm_handle); } const struct output_plugin plugout_alsa = { .name = "alsa", .description = "ALSA sound driver", .open = alsa_open, .write = alsa_write, .close = alsa_close, }; gbsplay-0.0.93/plugout_stdout.h0000644000175000017500000000063212566424552015524 0ustar mitchmitch/* * gbsplay is a Gameboy sound player * * 2004 (C) by Christian Garbs * Licensed under GNU GPL. * * header file for STDOUT file writer output plugin */ #include "common.h" #ifndef _PLUGOUT_STDOUT_H_ #define _PLUGOUT_STDOUT_H_ void regparm stdout_open (long endian, long rate); ssize_t regparm stdout_write(const void *buf, size_t count); void regparm stdout_close(); #endif gbsplay-0.0.93/CODINGSTYLE0000644000175000017500000000063312566424552013762 0ustar mitchmitchThe preferred coding style for this project is the Linux coding style, with one exception, namely breaking of long lines, which should be done so that the users tab width preference does not matter: void foo(void) { if (bar) printf("Lorem ipsum dolor sit amet, consectetuer adipiscing " "elit, sed diam nonummy nibh euismod tincidunt ut " "laoreet dolore magna aliquam erat volutpat."); } gbsplay-0.0.93/gbhw.h0000644000175000017500000000303012566424552013345 0ustar mitchmitch/* * gbsplay is a Gameboy sound player * * 2003-2005 (C) by Tobias Diedrich * Licensed under GNU GPL. */ #ifndef _GBHW_H_ #define _GBHW_H_ #include #include "common.h" #define GBHW_CLOCK 4194304 struct gbhw_buffer { /*@dependent@*/ int16_t *data; long pos; long l_lvl; long r_lvl; long bytes; long samples; long cycles; }; struct gbhw_channel { long mute; long master; long leftgate; long rightgate; long l_lvl; long r_lvl; long volume; long env_dir; long env_tc; long env_ctr; long sweep_dir; long sweep_tc; long sweep_ctr; long sweep_shift; long len; long len_enable; long div_tc; long div_ctr; long duty_tc; long duty_ctr; }; extern struct gbhw_channel gbhw_ch[4]; typedef regparm void (*gbhw_callback_fn)(/*@temp@*/ struct gbhw_buffer *buf, /*@temp@*/ void *priv); typedef regparm void (*gbhw_iocallback_fn)(long cycles, uint32_t addr, uint8_t valu, /*@temp@*/ void *priv); regparm void gbhw_setcallback(/*@dependent@*/ gbhw_callback_fn fn, /*@dependent@*/ void *priv); regparm void gbhw_setiocallback(/*@dependent@*/ gbhw_iocallback_fn fn, /*@dependent@*/ void *priv); regparm void gbhw_setrate(long rate); regparm void gbhw_setbuffer(/*@dependent@*/ struct gbhw_buffer *buffer); regparm void gbhw_init(uint8_t *rombuf, uint32_t size); regparm void gbhw_pause(long new_pause); regparm void gbhw_master_fade(long speed, long dstvol); regparm void gbhw_getminmax(int16_t *lmin, int16_t *lmax, int16_t *rmin, int16_t *rmax); regparm long gbhw_step(long time_to_work); #endif gbsplay-0.0.93/plugout_nas.c0000644000175000017500000001336712566424552014767 0ustar mitchmitch/* * gbsplay is a Gameboy sound player * * 2004-2005 (C) by Christian Garbs * Tobias Diedrich * Licensed under GNU GPL. * * NAS sound output plugin * * Based on the libaudiooss nas backend, which was largely rewritten * by me (Tobias Diedrich), I'd dare to say the only function left from * the xmms code is nas_find_device, but I did not check that. :-) */ /*@-nullpass@*/ /*@-onlytrans@*/ /*@-uniondef@*/ #include "common.h" #include #include #include #include #include