cscg22-gearboy

CSCG 2022 Challenge 'Gearboy'
git clone https://git.sinitax.com/sinitax/cscg22-gearboy
Log | Files | Refs | sfeed.txt

Gb_Oscs.h (4650B)


      1// Private oscillators used by Gb_Apu
      2
      3// Gb_Snd_Emu 0.2.0
      4#ifndef GB_OSCS_H
      5#define GB_OSCS_H
      6
      7#include "blargg_common.h"
      8#include "Blip_Buffer.h"
      9
     10#ifndef GB_APU_OVERCLOCK
     11	#define GB_APU_OVERCLOCK 1
     12#endif
     13
     14#if GB_APU_OVERCLOCK & (GB_APU_OVERCLOCK - 1)
     15	#error "GB_APU_OVERCLOCK must be a power of 2"
     16#endif
     17
     18class Gb_Osc {
     19protected:
     20
     21	// 11-bit frequency in NRx3 and NRx4
     22	int frequency() const { return (regs [4] & 7) * 0x100 + regs [3]; }
     23
     24	void update_amp( blip_time_t, int new_amp );
     25	int write_trig( int frame_phase, int max_len, int old_data );
     26public:
     27
     28	enum { clk_mul  = GB_APU_OVERCLOCK };
     29	enum { dac_bias = 7 };
     30
     31	Blip_Buffer*    outputs [4];// NULL, right, left, center
     32	Blip_Buffer*    output;     // where to output sound
     33	BOOST::uint8_t* regs;       // osc's 5 registers
     34	int             mode;       // mode_dmg, mode_cgb, mode_agb
     35	int             dac_off_amp;// amplitude when DAC is off
     36	int             last_amp;   // current amplitude in Blip_Buffer
     37	typedef Blip_Synth<blip_good_quality,1> Good_Synth;
     38	typedef Blip_Synth<blip_med_quality ,1> Med_Synth;
     39	Good_Synth const* good_synth;
     40	Med_Synth  const* med_synth;
     41
     42	int         delay;      // clocks until frequency timer expires
     43	int         length_ctr; // length counter
     44	unsigned    phase;      // waveform phase (or equivalent)
     45	bool        enabled;    // internal enabled flag
     46
     47	void clock_length();
     48	void reset();
     49};
     50
     51class Gb_Env : public Gb_Osc {
     52public:
     53	Gb_Env() : env_delay(0), env_enabled(false) {}
     54	int  env_delay;
     55	int  volume;
     56	bool env_enabled;
     57
     58	void clock_envelope();
     59	bool write_register( int frame_phase, int reg, int old_data, int data );
     60
     61	void reset()
     62	{
     63		env_delay = 0;
     64		volume    = 0;
     65		Gb_Osc::reset();
     66	}
     67protected:
     68	// Non-zero if DAC is enabled
     69	int dac_enabled() const { return regs [2] & 0xF8; }
     70private:
     71	void zombie_volume( int old, int data );
     72	int reload_env_timer();
     73};
     74
     75class Gb_Square : public Gb_Env {
     76public:
     77	bool write_register( int frame_phase, int reg, int old_data, int data );
     78	void run( blip_time_t, blip_time_t );
     79
     80	void reset()
     81	{
     82		Gb_Env::reset();
     83		delay = 0x40000000; // TODO: something less hacky (never clocked until first trigger)
     84	}
     85private:
     86	// Frequency timer period
     87	int period() const { return (2048 - frequency()) * (4 * clk_mul); }
     88};
     89
     90class Gb_Sweep_Square : public Gb_Square {
     91public:
     92	int  sweep_freq;
     93	int  sweep_delay;
     94	bool sweep_enabled;
     95	bool sweep_neg;
     96
     97	void clock_sweep();
     98	void write_register( int frame_phase, int reg, int old_data, int data );
     99
    100	void reset()
    101	{
    102		sweep_freq    = 0;
    103		sweep_delay   = 0;
    104		sweep_enabled = false;
    105		sweep_neg     = false;
    106		Gb_Square::reset();
    107	}
    108private:
    109	enum { period_mask = 0x70 };
    110	enum { shift_mask  = 0x07 };
    111
    112	void calc_sweep( bool update );
    113	void reload_sweep_timer();
    114};
    115
    116class Gb_Noise : public Gb_Env {
    117public:
    118
    119	int divider; // noise has more complex frequency divider setup
    120
    121	void run( blip_time_t, blip_time_t );
    122	void write_register( int frame_phase, int reg, int old_data, int data );
    123
    124	void reset()
    125	{
    126		divider = 0;
    127		Gb_Env::reset();
    128		delay = 4 * clk_mul; // TODO: remove?
    129	}
    130private:
    131	enum { period2_mask = 0x1FFFF };
    132
    133	int period2_index() const { return regs [3] >> 4; }
    134	int period2( int base = 8 ) const { return base << period2_index(); }
    135	unsigned lfsr_mask() const { return (regs [3] & 0x08) ? ~0x4040 : ~0x4000; }
    136};
    137
    138class Gb_Wave : public Gb_Osc {
    139public:
    140	int sample_buf; // last wave RAM byte read (hardware has this as well)
    141
    142	void write_register( int frame_phase, int reg, int old_data, int data );
    143	void run( blip_time_t, blip_time_t );
    144
    145	// Reads/writes wave RAM
    146	int read( unsigned addr ) const;
    147	void write( unsigned addr, int data );
    148
    149	void reset()
    150	{
    151		sample_buf = 0;
    152		Gb_Osc::reset();
    153	}
    154
    155private:
    156	enum { bank40_mask = 0x40 };
    157	enum { bank_size   = 32 };
    158
    159	int agb_mask;               // 0xFF if AGB features enabled, 0 otherwise
    160	BOOST::uint8_t* wave_ram;   // 32 bytes (64 nybbles), stored in APU
    161
    162	friend class Gb_Apu;
    163
    164	// Frequency timer period
    165	int period() const { return (2048 - frequency()) * (2 * clk_mul); }
    166
    167	// Non-zero if DAC is enabled
    168	int dac_enabled() const { return regs [0] & 0x80; }
    169
    170	void corrupt_wave();
    171
    172	BOOST::uint8_t* wave_bank() const { return &wave_ram [(~regs [0] & bank40_mask) >> 2 & agb_mask]; }
    173
    174	// Wave index that would be accessed, or -1 if no access would occur
    175	int access( unsigned addr ) const;
    176};
    177
    178inline int Gb_Wave::read( unsigned addr ) const
    179{
    180	int index = access( addr );
    181	return (index < 0 ? 0xFF : wave_bank() [index]);
    182}
    183
    184inline void Gb_Wave::write( unsigned addr, int data )
    185{
    186	int index = access( addr );
    187	if ( index >= 0 )
    188		wave_bank() [index] = data;;
    189}
    190
    191#endif