1 // Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
3 #include "kss_scc_apu.h"
5 /* Copyright (C) 2006-2008 Shay Green. This module is free software; you
6 can redistribute it and/or modify it under the terms of the GNU Lesser
7 General Public License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version. This
9 module is distributed in the hope that it will be useful, but WITHOUT ANY
10 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
12 details. You should have received a copy of the GNU Lesser General Public
13 License along with this module; if not, write to the Free Software Foundation,
14 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
16 #include "blargg_source.h"
18 // Tones above this frequency are treated as disabled tone at half volume.
19 // Power of two is more efficient (avoids division).
20 extern int const inaudible_freq
;
22 int const wave_size
= 0x20;
24 static void set_output( struct Scc_Apu
* this, struct Blip_Buffer
* buf
)
27 for ( i
= 0; i
< scc_osc_count
; ++i
)
28 Scc_set_output( this, i
, buf
);
31 void Scc_volume( struct Scc_Apu
* this, double v
)
33 Synth_volume( &this->synth
, 0.43 / scc_osc_count
/ scc_amp_range
* v
);
36 void Scc_reset( struct Scc_Apu
* this )
41 for ( i
= scc_osc_count
; --i
>= 0; )
42 memset( &this->oscs
[i
], 0, offsetof (struct scc_osc_t
,output
) );
44 memset( this->regs
, 0, sizeof this->regs
);
47 void Scc_init( struct Scc_Apu
* this )
49 Synth_init( &this->synth
);
51 set_output( this, NULL
);
52 Scc_volume( this, 1.0 );
56 static void run_until( struct Scc_Apu
* this, blip_time_t end_time
)
59 for ( index
= 0; index
< scc_osc_count
; index
++ )
61 struct scc_osc_t
* osc
= &this->oscs
[index
];
63 struct Blip_Buffer
* const output
= osc
->output
;
67 blip_time_t period
= (this->regs
[0xA0 + index
* 2 + 1] & 0x0F) * 0x100 +
68 this->regs
[0xA0 + index
* 2] + 1;
70 if ( this->regs
[0xAF] & (1 << index
) )
72 blip_time_t inaudible_period
= (unsigned) (Blip_clock_rate( output
) +
73 inaudible_freq
* 32) / (unsigned) (inaudible_freq
* 16);
74 if ( period
> inaudible_period
)
75 volume
= (this->regs
[0xAA + index
] & 0x0F) * (scc_amp_range
/ 256 / 15);
78 int8_t const* wave
= (int8_t*) this->regs
+ index
* wave_size
;
79 /*if ( index == osc_count - 1 )
80 wave -= wave_size; // last two oscs share same wave RAM*/
83 int delta
= wave
[osc
->phase
] * volume
- osc
->last_amp
;
86 osc
->last_amp
+= delta
;
87 Blip_set_modified( output
);
88 Synth_offset( &this->synth
, this->last_time
, delta
, output
);
92 blip_time_t time
= this->last_time
+ osc
->delay
;
93 if ( time
< end_time
)
95 int phase
= osc
->phase
;
99 int count
= (end_time
- time
+ period
- 1) / period
;
100 phase
+= count
; // will be masked below
101 time
+= count
* period
;
105 int last_wave
= wave
[phase
];
106 phase
= (phase
+ 1) & (wave_size
- 1); // pre-advance for optimal inner loop
109 int delta
= wave
[phase
] - last_wave
;
110 phase
= (phase
+ 1) & (wave_size
- 1);
114 Synth_offset_inline( &this->synth
, time
, delta
* volume
, output
);
118 while ( time
< end_time
);
120 osc
->last_amp
= last_wave
* volume
;
121 Blip_set_modified( output
);
122 phase
--; // undo pre-advance
124 osc
->phase
= phase
& (wave_size
- 1);
126 osc
->delay
= time
- end_time
;
128 this->last_time
= end_time
;
131 void Scc_write( struct Scc_Apu
* this, blip_time_t time
, int addr
, int data
)
133 //assert( (unsigned) addr < reg_count );
134 assert( ( addr
>= 0x9800 && addr
<= 0x988F ) || ( addr
>= 0xB800 && addr
<= 0xB8AF ) );
135 run_until( this, time
);
138 if ( ( unsigned ) addr
< 0x90 )
140 if ( ( unsigned ) addr
< 0x60 )
141 this->regs
[addr
] = data
;
142 else if ( ( unsigned ) addr
< 0x80 )
144 this->regs
[addr
] = this->regs
[addr
+ 0x20] = data
;
146 else if ( ( unsigned ) addr
< 0x90 )
148 this->regs
[addr
+ 0x20] = data
;
153 addr
-= 0xB800 - 0x9800;
154 if ( ( unsigned ) addr
< 0xB0 )
155 this->regs
[addr
] = data
;
159 void Scc_end_frame( struct Scc_Apu
* this, blip_time_t end_time
)
161 if ( end_time
> this->last_time
)
162 run_until( this, end_time
);
164 this->last_time
-= end_time
;
165 assert( this->last_time
>= 0 );