2017-10-14 07:03:19 +02:00
# include <inttypes.h>
2017-10-16 06:19:18 +02:00
# include <stdarg.h>
2017-09-25 04:31:20 -04:00
# include <stdio.h>
# include <stdint.h>
# include <string.h>
2016-06-26 00:34:39 +02:00
# include <stdlib.h>
2017-09-25 04:31:20 -04:00
# include <wchar.h>
2020-04-04 12:45:47 +02:00
# define _USE_MATH_DEFINES
2016-06-26 00:34:39 +02:00
# include <math.h>
2017-12-10 02:53:10 -05:00
# define HAVE_STDARG_H
2020-03-29 14:24:42 +02:00
# include <86box/86box.h>
# include <86box/device.h>
# include <86box/io.h>
# include <86box/mem.h>
# include <86box/rom.h>
# include <86box/timer.h>
# include <86box/sound.h>
# include <86box/snd_emu8k.h>
2017-09-25 04:31:20 -04:00
2017-11-24 02:23:00 -05:00
2017-08-21 04:19:34 +02:00
# if !defined FILTER_INITIAL && !defined FILTER_MOOG && !defined FILTER_CONSTANT
2017-10-14 07:03:19 +02:00
//#define FILTER_INITIAL
2017-08-21 04:19:34 +02:00
# define FILTER_MOOG
2017-10-14 07:03:19 +02:00
//#define FILTER_CONSTANT
2017-08-21 04:19:34 +02:00
# endif
2017-07-24 12:04:39 +02:00
2017-08-21 04:19:34 +02:00
# if !defined RESAMPLER_LINEAR && !defined RESAMPLER_CUBIC
2017-10-14 07:03:19 +02:00
//#define RESAMPLER_LINEAR
2017-08-21 04:19:34 +02:00
# define RESAMPLER_CUBIC
# endif
2017-07-24 12:04:39 +02:00
2017-10-14 07:03:19 +02:00
//#define EMU8K_DEBUG_REGISTERS
2017-09-01 01:01:53 +02:00
char * PORT_NAMES [ ] [ 8 ] =
{
/* Data 0 ( 0x620/0x622) */
{ " AWE_CPF " ,
" AWE_PTRX " ,
" AWE_CVCF " ,
" AWE_VTFT " ,
" Unk-620-4 " ,
" Unk-620-5 " ,
" AWE_PSST " ,
" AWE_CSL " ,
} ,
/* Data 1 0xA20 */
{ " AWE_CCCA " ,
0 ,
/*
" AWE_HWCF4 "
" AWE_HWCF5 "
" AWE_HWCF6 "
" AWE_HWCF7 "
" AWE_SMALR "
" AWE_SMARR "
" AWE_SMALW "
" AWE_SMARW "
" AWE_SMLD "
" AWE_SMRD "
" AWE_WC "
" AWE_HWCF1 "
" AWE_HWCF2 "
" AWE_HWCF3 "
*/
2017-10-14 07:03:19 +02:00
0 , //"AWE_INIT1",
0 , //"AWE_INIT3",
2017-09-01 01:01:53 +02:00
" AWE_ENVVOL " ,
" AWE_DCYSUSV " ,
" AWE_ENVVAL " ,
" AWE_DCYSUS " ,
} ,
/* Data 2 0xA22 */
{ " AWE_CCCA " ,
0 ,
2017-10-14 07:03:19 +02:00
0 , //"AWE_INIT2",
0 , //"AWE_INIT4",
2017-09-01 01:01:53 +02:00
" AWE_ATKHLDV " ,
" AWE_LFO1VAL " ,
" AWE_ATKHLD " ,
" AWE_LFO2VAL " ,
} ,
/* Data 3 0xE20 */
{ " AWE_IP " ,
" AWE_IFATN " ,
" AWE_PEFE " ,
" AWE_FMMOD " ,
" AWE_TREMFRQ " ,
" AWE_FM2FRQ2 " ,
0 ,
0 ,
} ,
2017-07-24 12:04:39 +02:00
} ;
2016-06-26 00:34:39 +02:00
enum
{
ENV_STOPPED = 0 ,
2017-07-24 12:04:39 +02:00
ENV_DELAY = 1 ,
ENV_ATTACK = 2 ,
ENV_HOLD = 3 ,
2017-10-14 07:03:19 +02:00
//ENV_DECAY = 4,
2017-07-24 12:04:39 +02:00
ENV_SUSTAIN = 5 ,
2017-10-14 07:03:19 +02:00
//ENV_RELEASE = 6,
2017-07-24 12:04:39 +02:00
ENV_RAMP_DOWN = 7 ,
ENV_RAMP_UP = 8
2016-06-26 00:34:39 +02:00
} ;
2017-07-24 12:04:39 +02:00
static int random_helper = 0 ;
int dmareadbit = 0 ;
int dmawritebit = 0 ;
/* cubic and linear tables resolution. Note: higher than 10 does not improve the result. */
# define CUBIC_RESOLUTION_LOG 10
# define CUBIC_RESOLUTION (1<<CUBIC_RESOLUTION_LOG)
2017-08-21 04:19:34 +02:00
/* cubic_table coefficients. */
static float cubic_table [ CUBIC_RESOLUTION * 4 ] ;
2017-07-24 12:04:39 +02:00
/* conversion from current pitch to linear frequency change (in 32.32 fixed point). */
2016-06-26 00:34:39 +02:00
static int64_t freqtable [ 65536 ] ;
2017-07-24 12:04:39 +02:00
/* Conversion from initial attenuation to 16 bit unsigned lineal amplitude (currently only a way to update volume target register) */
static int32_t attentable [ 256 ] ;
2017-09-01 01:01:53 +02:00
/* Conversion from envelope dbs (once rigth shifted) (0 = 0dBFS, 65535 = -96dbFS and silence ) to 16 bit unsigned lineal amplitude,
* to convert to current volume . ( 0 to 65536 ) */
2017-07-24 12:04:39 +02:00
static int32_t env_vol_db_to_vol_target [ 65537 ] ;
2017-09-01 01:01:53 +02:00
/* Same as above, but to convert amplitude (once rigth shifted) (0 to 65536) to db (0 = 0dBFS, 65535 = -96dbFS and silence ).
* it is needed so that the delay , attack and hold phase can be added to initial attenuation and tremolo */
2017-07-24 12:04:39 +02:00
static int32_t env_vol_amplitude_to_db [ 65537 ] ;
2017-09-01 01:01:53 +02:00
/* Conversion from envelope herts (once right shifted) to octave . it is needed so that the delay, attack and hold phase can be
* added to initial pitch , lfos pitch , initial filter and lfo filter */
2017-07-24 12:04:39 +02:00
static int32_t env_mod_hertz_to_octave [ 65537 ] ;
/* Conversion from envelope amount to time in samples. */
static int32_t env_attack_to_samples [ 128 ] ;
/* This table has been generated using the following formula:
2017-09-01 01:01:53 +02:00
* Get the amount of dBs that have to be added each sample to reach 96 dBs in the amount
* of time determined by the encoded value " i " .
* float d = 1.0 / ( ( env_decay_to_millis [ i ] / 96.0 ) * 44.1 ) ;
* int result = round ( d * 21845 ) ;
* The multiplication by 21845 gives a minimum value of 1 , and a maximum accumulated value of 1 < < 21
* The accumulated value has to be converted to amplitude , and that can be done with the
* env_vol_db_to_vol_target and shifting by 8
* In other words , the unit of the table is the 1 / 21845 th of a dB per sample frame , to be added or
* substracted to the accumulating value_db of the envelope . */
2017-08-21 04:19:34 +02:00
static int32_t env_decay_to_dbs_or_oct [ 128 ] =
{
0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , 13 , 14 , 15 ,
16 , 17 , 18 , 19 , 20 , 20 , 21 , 22 , 23 , 24 , 25 , 27 , 28 , 29 , 30 , 32 ,
33 , 34 , 36 , 38 , 39 , 41 , 43 , 45 , 49 , 51 , 53 , 55 , 58 , 60 , 63 , 66 ,
69 , 72 , 75 , 78 , 82 , 85 , 89 , 93 , 97 , 102 , 106 , 111 , 116 , 121 , 126 , 132 ,
138 , 144 , 150 , 157 , 164 , 171 , 179 , 186 , 195 , 203 , 212 , 222 , 232 , 243 , 253 , 264 ,
276 , 288 , 301 , 315 , 328 , 342 , 358 , 374 , 390 , 406 , 425 , 444 , 466 , 485 , 506 , 528 ,
553 , 580 , 602 , 634 , 660 , 689 , 721 , 755 , 780 , 820 , 849 , 897 , 932 , 970 , 1012 , 1057 ,
1106 , 1160 , 1219 , 1285 , 1321 , 1399 , 1441 , 1534 , 1585 , 1640 , 1698 , 1829 , 1902 , 1981 , 2068 , 2162
2017-07-24 12:04:39 +02:00
} ;
/* The table "env_decay_to_millis" is based on the table "decay_time_tbl" found in the freebsd/linux
2017-09-01 01:01:53 +02:00
* AWE32 driver .
* I tried calculating it using the instructions in awe32p10 from Judge Dredd , but the formula there
* is wrong .
*
2017-07-24 12:04:39 +02:00
static int32_t env_decay_to_millis [ 128 ] = {
0 , 45120 , 22614 , 15990 , 11307 , 9508 , 7995 , 6723 , 5653 , 5184 , 4754 , 4359 , 3997 , 3665 , 3361 , 3082 ,
2828 , 2765 , 2648 , 2535 , 2428 , 2325 , 2226 , 2132 , 2042 , 1955 , 1872 , 1793 , 1717 , 1644 , 1574 , 1507 ,
1443 , 1382 , 1324 , 1267 , 1214 , 1162 , 1113 , 1066 , 978 , 936 , 897 , 859 , 822 , 787 , 754 , 722 ,
691 , 662 , 634 , 607 , 581 , 557 , 533 , 510 , 489 , 468 , 448 , 429 , 411 , 393 , 377 , 361 ,
345 , 331 , 317 , 303 , 290 , 278 , 266 , 255 , 244 , 234 , 224 , 214 , 205 , 196 , 188 , 180 ,
172 , 165 , 158 , 151 , 145 , 139 , 133 , 127 , 122 , 117 , 112 , 107 , 102 , 98 , 94 , 90 ,
86 , 82 , 79 , 75 , 72 , 69 , 66 , 63 , 61 , 58 , 56 , 53 , 51 , 49 , 47 , 45 ,
43 , 41 , 39 , 37 , 36 , 34 , 33 , 31 , 30 , 29 , 28 , 26 , 25 , 24 , 23 , 22 ,
} ;
*/
/* Table represeting the LFO waveform (signed 16bits with 32768 max int. >> 15 to move back to +/-1 range). */
static int32_t lfotable [ 65536 ] ;
/* Table to transform the speed parameter to emu8k_mem_internal_t range. */
static int64_t lfofreqtospeed [ 256 ] ;
2017-08-21 04:19:34 +02:00
/* LFO used for the chorus. a sine wave.(signed 16bits with 32768 max int. >> 15 to move back to +/-1 range). */
static double chortable [ 65536 ] ;
static const int REV_BUFSIZE_STEP = 242 ;
2017-07-24 12:04:39 +02:00
2017-09-01 01:01:53 +02:00
/* These lines come from the awe32faq, describing the NRPN control for the initial filter
* where it describes a linear increment filter instead of an octave - incremented one .
* NRPN LSB 21 ( Initial Filter Cutoff )
* Range : [ 0 , 127 ]
* Unit : 62 Hz
* Filter cutoff from 100 Hz to 8000 Hz
* This table comes from the awe32faq , describing the NRPN control for the filter Q .
* I don ' t know if is meant to be interpreted as the actual measured output of the
* filter or what . Especially , I don ' t understand the " low " and " high " ranges .
* What is otherwise documented is that the Q ranges from 0 dB to 24 dB and the attenuation
* is half of the Q ( i . e . for 12 dB Q , attenuate the input signal with - 6 dB )
Coeff Low Fc ( Hz ) Low Q ( dB ) High Fc ( kHz ) High Q ( dB ) DC Attenuation ( dB )
* 0 92 5 Flat Flat - 0.0
* 1 93 6 8.5 0.5 - 0.5
* 2 94 8 8.3 1 - 1.2
* 3 95 10 8.2 2 - 1.8
* 4 96 11 8.1 3 - 2.5
* 5 97 13 8.0 4 - 3.3
* 6 98 14 7.9 5 - 4.1
* 7 99 16 7.8 6 - 5.5
* 8 100 17 7.7 7 - 6.0
* 9 100 19 7.5 9 - 6.6
* 10 100 20 7.4 10 - 7.2
* 11 100 22 7.3 11 - 7.9
* 12 100 23 7.2 13 - 8.5
* 13 100 25 7.1 15 - 9.3
* 14 100 26 7.1 16 - 10.1
* 15 100 28 7.0 18 - 11.0
*
* Attenuation as above , codified in amplitude . */
2017-08-21 04:19:34 +02:00
static int32_t filter_atten [ 16 ] =
{
2017-07-24 12:04:39 +02:00
65536 , 61869 , 57079 , 53269 , 49145 , 44820 , 40877 , 34792 , 32845 , 30653 , 28607 ,
26392 , 24630 , 22463 , 20487 , 18470
} ;
2017-09-01 01:01:53 +02:00
/*Coefficients for the filters for a defined Q and cutoff.*/
2017-07-24 12:04:39 +02:00
static int32_t filt_coeffs [ 16 ] [ 256 ] [ 3 ] ;
2016-06-26 00:34:39 +02:00
2017-07-24 12:04:39 +02:00
# define READ16_SWITCH(addr, var) switch ((addr) & 2) \
2016-06-26 00:34:39 +02:00
{ \
case 0 : ret = ( var ) & 0xffff ; break ; \
case 2 : ret = ( ( var ) > > 16 ) & 0xffff ; break ; \
}
2017-07-24 12:04:39 +02:00
# define WRITE16_SWITCH(addr, var, val) switch ((addr) & 2) \
2016-06-26 00:34:39 +02:00
{ \
case 0 : var = ( var & 0xffff0000 ) | ( val ) ; break ; \
case 2 : var = ( var & 0x0000ffff ) | ( ( val ) < < 16 ) ; break ; \
}
2017-07-24 12:04:39 +02:00
# ifdef EMU8K_DEBUG_REGISTERS
uint32_t dw_value = 0 ;
uint32_t last_read = 0 ;
uint32_t last_write = 0 ;
uint32_t rep_count_r = 0 ;
uint32_t rep_count_w = 0 ;
# define READ16(addr, var) READ16_SWITCH(addr, var) \
2017-09-01 01:01:53 +02:00
{ \
2017-07-24 12:04:39 +02:00
const char * name = 0 ; \
2017-10-14 07:03:19 +02:00
switch ( addr & 0xF02 ) \
2017-09-01 01:01:53 +02:00
{ \
2017-10-14 07:03:19 +02:00
case 0x600 : case 0x602 : \
2017-07-24 12:04:39 +02:00
name = PORT_NAMES [ 0 ] [ emu8k - > cur_reg ] ; \
break ; \
2017-10-14 07:03:19 +02:00
case 0xA00 : \
2017-07-24 12:04:39 +02:00
name = PORT_NAMES [ 1 ] [ emu8k - > cur_reg ] ; \
break ; \
2017-10-14 07:03:19 +02:00
case 0xA02 : \
2017-07-24 12:04:39 +02:00
name = PORT_NAMES [ 2 ] [ emu8k - > cur_reg ] ; \
break ; \
} \
2017-09-01 01:01:53 +02:00
if ( name = = 0 ) \
{ \
2017-10-16 06:19:18 +02:00
/*emu8k_log("EMU8K READ %04X-%02X(%d): %04X\n",addr,(emu8k->cur_reg)<<5|emu8k->cur_voice, emu8k->cur_voice,ret);*/ \
2017-09-01 01:01:53 +02:00
} \
else \
{ \
2017-10-16 06:19:18 +02:00
emu8k_log ( " EMU8K READ %s(%d) (%d): %04X \n " , name , ( addr & 0x2 ) , emu8k - > cur_voice , ret ) ; \
2017-07-24 12:04:39 +02:00
} \
}
# define WRITE16(addr, var, val) WRITE16_SWITCH(addr, var, val) \
2017-09-01 01:01:53 +02:00
{ \
2017-07-24 12:04:39 +02:00
const char * name = 0 ; \
2017-10-14 07:03:19 +02:00
switch ( addr & 0xF02 ) \
2017-09-01 01:01:53 +02:00
{ \
2017-10-14 07:03:19 +02:00
case 0x600 : case 0x602 : \
2017-07-24 12:04:39 +02:00
name = PORT_NAMES [ 0 ] [ emu8k - > cur_reg ] ; \
break ; \
2017-10-14 07:03:19 +02:00
case 0xA00 : \
2017-07-24 12:04:39 +02:00
name = PORT_NAMES [ 1 ] [ emu8k - > cur_reg ] ; \
break ; \
2017-10-14 07:03:19 +02:00
case 0xA02 : \
2017-07-24 12:04:39 +02:00
name = PORT_NAMES [ 2 ] [ emu8k - > cur_reg ] ; \
break ; \
} \
2017-09-01 01:01:53 +02:00
if ( name = = 0 ) \
{ \
2017-10-16 06:19:18 +02:00
/*emu8k_log("EMU8K WRITE %04X-%02X(%d): %04X\n",addr,(emu8k->cur_reg)<<5|emu8k->cur_voice,emu8k->cur_voice, val);*/ \
2017-09-01 01:01:53 +02:00
} \
else \
{ \
2017-10-16 06:19:18 +02:00
emu8k_log ( " EMU8K WRITE %s(%d) (%d): %04X \n " , name , ( addr & 0x2 ) , emu8k - > cur_voice , val ) ; \
2017-07-24 12:04:39 +02:00
} \
}
# else
# define READ16(addr, var) READ16_SWITCH(addr, var)
# define WRITE16(addr, var, val) WRITE16_SWITCH(addr, var, val)
2017-10-14 07:03:19 +02:00
# endif //EMU8K_DEBUG_REGISTERS
2017-07-24 12:04:39 +02:00
2017-10-16 06:19:18 +02:00
# ifdef ENABLE_EMU8K_LOG
int emu8k_do_log = ENABLE_EMU8K_LOG ;
2017-11-24 02:23:00 -05:00
static void
2017-12-10 02:53:10 -05:00
emu8k_log ( const char * fmt , . . . )
2017-10-16 06:19:18 +02:00
{
2017-12-05 23:35:35 +01:00
va_list ap ;
2017-11-24 02:23:00 -05:00
if ( emu8k_do_log ) {
2017-12-10 02:53:10 -05:00
va_start ( ap , fmt ) ;
pclog_ex ( fmt , ap ) ;
2017-11-24 02:23:00 -05:00
va_end ( ap ) ;
}
2017-10-16 06:19:18 +02:00
}
2018-10-19 00:39:32 +02:00
# else
# define emu8k_log(fmt, ...)
# endif
2017-10-16 06:19:18 +02:00
2017-11-24 02:23:00 -05:00
2017-07-24 12:04:39 +02:00
static inline int16_t EMU8K_READ ( emu8k_t * emu8k , uint32_t addr )
2016-06-26 00:34:39 +02:00
{
2017-07-24 12:04:39 +02:00
const register emu8k_mem_pointers_t addrmem = { { addr } } ;
return emu8k - > ram_pointers [ addrmem . hb_address ] [ addrmem . lw_address ] ;
2016-06-26 00:34:39 +02:00
}
2017-10-27 20:54:31 -04:00
# if NOTUSED
2017-07-24 12:04:39 +02:00
static inline int16_t EMU8K_READ_INTERP_LINEAR ( emu8k_t * emu8k , uint32_t int_addr , uint16_t fract )
2016-06-26 00:34:39 +02:00
{
2017-07-24 12:04:39 +02:00
/* The interpolation in AWE32 used a so-called patented 3-point interpolation
2017-09-01 01:01:53 +02:00
* ( I guess some sort of spline having one point before and one point after ) .
* Also , it has the consequence that the playback is delayed by one sample .
* I simulate the " one sample later " than the address with addr + 1 and addr + 2
* instead of + 0 and + 1 */
2017-07-24 12:04:39 +02:00
int16_t dat1 = EMU8K_READ ( emu8k , int_addr + 1 ) ;
int32_t dat2 = EMU8K_READ ( emu8k , int_addr + 2 ) ;
dat1 + = ( ( dat2 - ( int32_t ) dat1 ) * fract ) > > 16 ;
return dat1 ;
2016-06-26 00:34:39 +02:00
}
2017-10-27 20:54:31 -04:00
# endif
2016-06-26 00:34:39 +02:00
2017-09-01 01:01:53 +02:00
static inline int32_t EMU8K_READ_INTERP_CUBIC ( emu8k_t * emu8k , uint32_t int_addr , uint16_t fract )
2016-06-26 00:34:39 +02:00
{
2017-08-21 04:19:34 +02:00
/*Since there are four floats in the table for each fraction, the position is 16byte aligned. */
fract > > = 16 - CUBIC_RESOLUTION_LOG ;
fract < < = 2 ;
/* TODO: I still have to verify how this works, but I think that
* the card could use two oscillators ( usually 31 and 32 ) where it would
* be writing the OPL3 output , and to which , chorus and reverb could be applied to get
* those effects for OPL3 sounds . */
2017-10-14 07:03:19 +02:00
// if ((addr & EMU8K_FM_MEM_ADDRESS) == EMU8K_FM_MEM_ADDRESS) {}
2017-07-24 12:04:39 +02:00
/* This is cubic interpolation.
2017-08-21 04:19:34 +02:00
* Not the same than 3 - point interpolation , but a better approximation than linear
* interpolation .
* Also , it takes into account the " Note that the actual audio location is the point
* 1 word higher than this value due to interpolation offset " .
* That ' s why the pointers are 0 , 1 , 2 , 3 and not - 1 , 0 , 1 , 2 */
2017-07-24 12:04:39 +02:00
int32_t dat2 = EMU8K_READ ( emu8k , int_addr + 1 ) ;
2017-08-21 04:19:34 +02:00
const float * table = & cubic_table [ fract ] ;
const int32_t dat1 = EMU8K_READ ( emu8k , int_addr ) ;
const int32_t dat3 = EMU8K_READ ( emu8k , int_addr + 2 ) ;
const int32_t dat4 = EMU8K_READ ( emu8k , int_addr + 3 ) ;
/* Note: I've ended using float for the table values to avoid some cases of integer overflow. */
dat2 = dat1 * table [ 0 ] + dat2 * table [ 1 ] + dat3 * table [ 2 ] + dat4 * table [ 3 ] ;
2017-07-24 12:04:39 +02:00
return dat2 ;
}
2017-06-22 23:51:57 +02:00
2017-07-24 12:04:39 +02:00
static inline void EMU8K_WRITE ( emu8k_t * emu8k , uint32_t addr , uint16_t val )
{
2017-06-22 23:51:57 +02:00
addr & = EMU8K_MEM_ADDRESS_MASK ;
2017-07-24 12:04:39 +02:00
if ( ! emu8k - > ram | | addr < EMU8K_RAM_MEM_START | | addr > = EMU8K_FM_MEM_ADDRESS )
return ;
/* It looks like if an application writes to a memory part outside of the available
2017-09-01 01:01:53 +02:00
* amount on the card , it wraps , and opencubicplayer uses that to detect the amount
* of memory , as opposed to simply check at the address that it has just tried to write . */
while ( addr > = emu8k - > ram_end_addr )
2017-06-22 23:51:57 +02:00
addr - = emu8k - > ram_end_addr - EMU8K_RAM_MEM_START ;
2017-09-01 01:01:53 +02:00
2017-06-22 23:51:57 +02:00
emu8k - > ram [ addr - EMU8K_RAM_MEM_START ] = val ;
2016-06-26 00:34:39 +02:00
}
2017-06-22 23:51:57 +02:00
2017-05-05 01:49:42 +02:00
uint16_t emu8k_inw ( uint16_t addr , void * p )
2016-06-26 00:34:39 +02:00
{
emu8k_t * emu8k = ( emu8k_t * ) p ;
2017-05-05 01:49:42 +02:00
uint16_t ret = 0xffff ;
2017-09-01 01:01:53 +02:00
2017-07-24 12:04:39 +02:00
# ifdef EMU8K_DEBUG_REGISTERS
2017-08-21 04:19:34 +02:00
if ( addr = = 0xE22 )
{
2017-10-16 06:19:18 +02:00
emu8k_log ( " EMU8K READ POINTER: %d \n " ,
2017-08-21 04:19:34 +02:00
( ( 0x80 | ( ( random_helper + 1 ) & 0x1F ) ) < < 8 ) | ( emu8k - > cur_reg < < 5 ) | emu8k - > cur_voice ) ;
}
else if ( ( addr & 0xF00 ) = = 0x600 )
{
/* These are automatically reported by READ16 */
if ( rep_count_r > 1 )
{
2017-10-16 06:19:18 +02:00
emu8k_log ( " EMU8K ...... for %d times \n " , rep_count_r ) ;
2017-07-24 12:04:39 +02:00
rep_count_r = 0 ;
}
2017-08-21 04:19:34 +02:00
last_read = 0 ;
2017-07-24 12:04:39 +02:00
}
2017-08-21 04:19:34 +02:00
else if ( ( addr & 0xF00 ) = = 0xA00 & & emu8k - > cur_reg = = 0 )
{
/* These are automatically reported by READ16 */
if ( rep_count_r > 1 )
{
2017-10-16 06:19:18 +02:00
emu8k_log ( " EMU8K ...... for %d times \n " , rep_count_r ) ;
2017-07-24 12:04:39 +02:00
rep_count_r = 0 ;
}
2017-08-21 04:19:34 +02:00
last_read = 0 ;
2017-07-24 12:04:39 +02:00
}
2017-08-21 04:19:34 +02:00
else if ( ( addr & 0xF00 ) = = 0xA00 & & emu8k - > cur_reg = = 1 )
{
uint32_t tmpz = ( ( addr & 0xF00 ) < < 16 ) | ( emu8k - > cur_reg < < 5 ) ;
if ( tmpz ! = last_read )
{
if ( rep_count_r > 1 )
{
2017-10-16 06:19:18 +02:00
emu8k_log ( " EMU8K ...... for %d times \n " , rep_count_r ) ;
2017-08-21 04:19:34 +02:00
rep_count_r = 0 ;
}
last_read = tmpz ;
2017-10-16 06:19:18 +02:00
emu8k_log ( " EMU8K READ RAM I/O or configuration or clock \n " ) ;
2017-08-21 04:19:34 +02:00
}
2017-10-16 06:19:18 +02:00
//emu8k_log("EMU8K READ %04X-%02X(%d/%d)\n",addr,(emu8k->cur_reg)<<5|emu8k->cur_voice, emu8k->cur_reg, emu8k->cur_voice);
2017-08-21 04:19:34 +02:00
}
else if ( ( addr & 0xF00 ) = = 0xA00 & & ( emu8k - > cur_reg = = 2 | | emu8k - > cur_reg = = 3 ) )
{
uint32_t tmpz = ( ( addr & 0xF00 ) < < 16 ) ;
if ( tmpz ! = last_read )
{
if ( rep_count_r > 1 )
{
2017-10-16 06:19:18 +02:00
emu8k_log ( " EMU8K ...... for %d times \n " , rep_count_r ) ;
2017-08-21 04:19:34 +02:00
rep_count_r = 0 ;
}
last_read = tmpz ;
2017-10-16 06:19:18 +02:00
emu8k_log ( " EMU8K READ INIT \n " ) ;
2017-08-21 04:19:34 +02:00
}
2017-10-16 06:19:18 +02:00
//emu8k_log("EMU8K READ %04X-%02X(%d/%d)\n",addr,(emu8k->cur_reg)<<5|emu8k->cur_voice, emu8k->cur_reg, emu8k->cur_voice);
2017-08-21 04:19:34 +02:00
}
else
{
2017-07-24 12:04:39 +02:00
uint32_t tmpz = ( addr < < 16 ) | ( emu8k - > cur_reg < < 5 ) | emu8k - > cur_voice ;
2017-08-21 04:19:34 +02:00
if ( tmpz ! = last_read )
{
char * name = 0 ;
uint16_t val = 0xBAAD ;
if ( addr = = 0xA20 )
2017-07-24 12:04:39 +02:00
{
2017-08-21 04:19:34 +02:00
name = PORT_NAMES [ 1 ] [ emu8k - > cur_reg ] ;
switch ( emu8k - > cur_reg )
{
case 2 : val = emu8k - > init1 [ emu8k - > cur_voice ] ; break ;
case 3 : val = emu8k - > init3 [ emu8k - > cur_voice ] ; break ;
case 4 : val = emu8k - > voice [ emu8k - > cur_voice ] . envvol ; break ;
case 5 : val = emu8k - > voice [ emu8k - > cur_voice ] . dcysusv ; break ;
case 6 : val = emu8k - > voice [ emu8k - > cur_voice ] . envval ; break ;
case 7 : val = emu8k - > voice [ emu8k - > cur_voice ] . dcysus ; break ;
2017-09-01 01:01:53 +02:00
}
2017-07-24 12:04:39 +02:00
}
2017-08-21 04:19:34 +02:00
else if ( addr = = 0xA22 )
2017-07-24 12:04:39 +02:00
{
2017-08-21 04:19:34 +02:00
name = PORT_NAMES [ 2 ] [ emu8k - > cur_reg ] ;
switch ( emu8k - > cur_reg )
{
case 2 : val = emu8k - > init2 [ emu8k - > cur_voice ] ; break ;
case 3 : val = emu8k - > init4 [ emu8k - > cur_voice ] ; break ;
case 4 : val = emu8k - > voice [ emu8k - > cur_voice ] . atkhldv ; break ;
case 5 : val = emu8k - > voice [ emu8k - > cur_voice ] . lfo1val ; break ;
case 6 : val = emu8k - > voice [ emu8k - > cur_voice ] . atkhld ; break ;
case 7 : val = emu8k - > voice [ emu8k - > cur_voice ] . lfo2val ; break ;
}
2017-07-24 12:04:39 +02:00
}
2017-08-21 04:19:34 +02:00
else if ( addr = = 0xE20 )
2017-07-24 12:04:39 +02:00
{
2017-08-21 04:19:34 +02:00
name = PORT_NAMES [ 3 ] [ emu8k - > cur_reg ] ;
switch ( emu8k - > cur_reg )
{
case 0 : val = emu8k - > voice [ emu8k - > cur_voice ] . ip ; break ;
case 1 : val = emu8k - > voice [ emu8k - > cur_voice ] . ifatn ; break ;
case 2 : val = emu8k - > voice [ emu8k - > cur_voice ] . pefe ; break ;
case 3 : val = emu8k - > voice [ emu8k - > cur_voice ] . fmmod ; break ;
case 4 : val = emu8k - > voice [ emu8k - > cur_voice ] . tremfrq ; break ;
case 5 : val = emu8k - > voice [ emu8k - > cur_voice ] . fm2frq2 ; break ;
case 6 : val = 0xffff ; break ;
case 7 : val = 0x1c | ( ( emu8k - > id & 0x0002 ) ? 0xff02 : 0 ) ; break ;
}
2017-07-24 12:04:39 +02:00
}
2017-08-21 04:19:34 +02:00
if ( rep_count_r > 1 )
{
2017-10-16 06:19:18 +02:00
emu8k_log ( " EMU8K ...... for %d times \n " , rep_count_r ) ;
2017-07-24 12:04:39 +02:00
}
2017-08-21 04:19:34 +02:00
if ( name = = 0 )
{
2017-10-16 06:19:18 +02:00
emu8k_log ( " EMU8K READ %04X-%02X(%d/%d): %04X \n " , addr , ( emu8k - > cur_reg ) < < 5 | emu8k - > cur_voice , emu8k - > cur_reg , emu8k - > cur_voice , val ) ;
2017-08-21 04:19:34 +02:00
}
else
{
2017-10-16 06:19:18 +02:00
emu8k_log ( " EMU8K READ %s (%d): %04X \n " , name , emu8k - > cur_voice , val ) ;
2017-07-24 12:04:39 +02:00
}
rep_count_r = 0 ;
last_read = tmpz ;
}
rep_count_r + + ;
}
2017-10-14 07:03:19 +02:00
# endif // EMU8K_DEBUG_REGISTERS
2017-07-24 12:04:39 +02:00
switch ( addr & 0xF02 )
2016-06-26 00:34:39 +02:00
{
2017-09-01 01:01:53 +02:00
case 0x600 : case 0x602 : /*Data0. also known as BLASTER+0x400 and EMU+0x000 */
2016-06-26 00:34:39 +02:00
switch ( emu8k - > cur_reg )
{
case 0 :
2017-07-24 12:04:39 +02:00
READ16 ( addr , emu8k - > voice [ emu8k - > cur_voice ] . cpf ) ;
return ret ;
2016-06-26 00:34:39 +02:00
case 1 :
READ16 ( addr , emu8k - > voice [ emu8k - > cur_voice ] . ptrx ) ;
return ret ;
case 2 :
READ16 ( addr , emu8k - > voice [ emu8k - > cur_voice ] . cvcf ) ;
return ret ;
case 3 :
READ16 ( addr , emu8k - > voice [ emu8k - > cur_voice ] . vtft ) ;
return ret ;
2017-07-24 12:04:39 +02:00
case 4 :
READ16 ( addr , emu8k - > voice [ emu8k - > cur_voice ] . unknown_data0_4 ) ;
return ret ;
case 5 :
READ16 ( addr , emu8k - > voice [ emu8k - > cur_voice ] . unknown_data0_5 ) ;
return ret ;
2016-06-26 00:34:39 +02:00
case 6 :
READ16 ( addr , emu8k - > voice [ emu8k - > cur_voice ] . psst ) ;
return ret ;
case 7 :
2017-06-22 23:51:57 +02:00
READ16 ( addr , emu8k - > voice [ emu8k - > cur_voice ] . csl ) ;
2016-06-26 00:34:39 +02:00
return ret ;
}
break ;
2017-09-01 01:01:53 +02:00
case 0xA00 : /*Data1. also known as BLASTER+0x800 and EMU+0x400 */
2016-06-26 00:34:39 +02:00
switch ( emu8k - > cur_reg )
{
case 0 :
2017-07-24 12:04:39 +02:00
READ16 ( addr , emu8k - > voice [ emu8k - > cur_voice ] . ccca ) ;
return ret ;
2016-06-26 00:34:39 +02:00
case 1 :
switch ( emu8k - > cur_voice )
{
2017-07-24 12:04:39 +02:00
case 9 :
READ16 ( addr , emu8k - > hwcf4 ) ;
return ret ;
case 10 :
READ16 ( addr , emu8k - > hwcf5 ) ;
return ret ;
2017-09-01 01:01:53 +02:00
/* Actually, these two might be command words rather than registers, or some LFO position/buffer reset.*/
2017-07-24 12:04:39 +02:00
case 13 :
READ16 ( addr , emu8k - > hwcf6 ) ;
return ret ;
case 14 :
READ16 ( addr , emu8k - > hwcf7 ) ;
return ret ;
2016-06-26 00:34:39 +02:00
case 20 :
READ16 ( addr , emu8k - > smalr ) ;
return ret ;
case 21 :
READ16 ( addr , emu8k - > smarr ) ;
return ret ;
case 22 :
READ16 ( addr , emu8k - > smalw ) ;
return ret ;
case 23 :
READ16 ( addr , emu8k - > smarw ) ;
return ret ;
case 26 :
{
uint16_t val = emu8k - > smld_buffer ;
emu8k - > smld_buffer = EMU8K_READ ( emu8k , emu8k - > smalr ) ;
2017-07-24 12:04:39 +02:00
emu8k - > smalr = ( emu8k - > smalr + 1 ) & EMU8K_MEM_ADDRESS_MASK ;
2016-06-26 00:34:39 +02:00
return val ;
}
2017-07-24 12:04:39 +02:00
2016-06-26 00:34:39 +02:00
/*The EMU8000 PGM describes the return values of these registers as 'a VLSI error'*/
case 29 : /*Configuration Word 1*/
return ( emu8k - > hwcf1 & 0xfe ) | ( emu8k - > hwcf3 & 0x01 ) ;
case 30 : /*Configuration Word 2*/
2017-08-21 04:19:34 +02:00
return ( ( emu8k - > hwcf2 > > 4 ) & 0x0e ) | ( emu8k - > hwcf1 & 0x01 ) | ( ( emu8k - > hwcf3 & 0x02 ) ? 0x10 : 0 ) | ( ( emu8k - > hwcf3 & 0x04 ) ? 0x40 : 0 )
| ( ( emu8k - > hwcf3 & 0x08 ) ? 0x20 : 0 ) | ( ( emu8k - > hwcf3 & 0x10 ) ? 0x80 : 0 ) ;
2016-06-26 00:34:39 +02:00
case 31 : /*Configuration Word 3*/
return emu8k - > hwcf2 & 0x1f ;
}
break ;
2017-07-24 12:04:39 +02:00
case 2 :
return emu8k - > init1 [ emu8k - > cur_voice ] ;
2016-06-26 00:34:39 +02:00
2017-07-24 12:04:39 +02:00
case 3 :
return emu8k - > init3 [ emu8k - > cur_voice ] ;
case 4 :
return emu8k - > voice [ emu8k - > cur_voice ] . envvol ;
2016-06-26 00:34:39 +02:00
case 5 :
return emu8k - > voice [ emu8k - > cur_voice ] . dcysusv ;
2017-07-24 12:04:39 +02:00
case 6 :
return emu8k - > voice [ emu8k - > cur_voice ] . envval ;
2016-06-26 00:34:39 +02:00
case 7 :
return emu8k - > voice [ emu8k - > cur_voice ] . dcysus ;
}
break ;
2017-09-01 01:01:53 +02:00
case 0xA02 : /*Data2. also known as BLASTER+0x802 and EMU+0x402 */
2016-06-26 00:34:39 +02:00
switch ( emu8k - > cur_reg )
{
case 0 :
2017-07-24 12:04:39 +02:00
READ16 ( addr , emu8k - > voice [ emu8k - > cur_voice ] . ccca ) ;
return ret ;
2016-06-26 00:34:39 +02:00
case 1 :
switch ( emu8k - > cur_voice )
{
2017-07-24 12:04:39 +02:00
case 9 :
READ16 ( addr , emu8k - > hwcf4 ) ;
return ret ;
case 10 :
READ16 ( addr , emu8k - > hwcf5 ) ;
return ret ;
/* Actually, these two might be command words rather than registers, or some LFO position/buffer reset. */
case 13 :
READ16 ( addr , emu8k - > hwcf6 ) ;
return ret ;
case 14 :
READ16 ( addr , emu8k - > hwcf7 ) ;
return ret ;
/* Simulating empty/full bits by unsetting it once read. */
2016-06-26 00:34:39 +02:00
case 20 :
2017-07-24 12:04:39 +02:00
READ16 ( addr , emu8k - > smalr | dmareadbit ) ;
/* xor with itself to set to zero faster. */
dmareadbit ^ = dmareadbit ;
2016-06-26 00:34:39 +02:00
return ret ;
case 21 :
2017-07-24 12:04:39 +02:00
READ16 ( addr , emu8k - > smarr | dmareadbit ) ;
2017-09-01 01:01:53 +02:00
/* xor with itself to set to zero faster.*/
2017-07-24 12:04:39 +02:00
dmareadbit ^ = dmareadbit ;
2016-06-26 00:34:39 +02:00
return ret ;
case 22 :
2017-07-24 12:04:39 +02:00
READ16 ( addr , emu8k - > smalw | dmawritebit ) ;
2017-09-01 01:01:53 +02:00
/*xor with itself to set to zero faster.*/
2017-07-24 12:04:39 +02:00
dmawritebit ^ = dmawritebit ;
2016-06-26 00:34:39 +02:00
return ret ;
case 23 :
2017-07-24 12:04:39 +02:00
READ16 ( addr , emu8k - > smarw | dmawritebit ) ;
2017-09-01 01:01:53 +02:00
/*xor with itself to set to zero faster.*/
2017-07-24 12:04:39 +02:00
dmawritebit ^ = dmawritebit ;
2016-06-26 00:34:39 +02:00
return ret ;
case 26 :
{
uint16_t val = emu8k - > smrd_buffer ;
emu8k - > smrd_buffer = EMU8K_READ ( emu8k , emu8k - > smarr ) ;
2017-07-24 12:04:39 +02:00
emu8k - > smarr = ( emu8k - > smarr + 1 ) & EMU8K_MEM_ADDRESS_MASK ;
2016-06-26 00:34:39 +02:00
return val ;
}
2017-07-24 12:04:39 +02:00
/*TODO: We need to improve the precision of this clock, since
it is used by programs to wait . Not critical , but should help reduce
the amount of calls and wait time */
case 27 : /*Sample Counter ( 44Khz clock) */
2016-06-26 00:34:39 +02:00
return emu8k - > wc ;
}
break ;
2017-07-24 12:04:39 +02:00
case 2 :
return emu8k - > init2 [ emu8k - > cur_voice ] ;
case 3 :
return emu8k - > init4 [ emu8k - > cur_voice ] ;
2016-06-26 00:34:39 +02:00
case 4 :
return emu8k - > voice [ emu8k - > cur_voice ] . atkhldv ;
2017-07-24 12:04:39 +02:00
case 5 :
return emu8k - > voice [ emu8k - > cur_voice ] . lfo1val ;
case 6 :
return emu8k - > voice [ emu8k - > cur_voice ] . atkhld ;
case 7 :
return emu8k - > voice [ emu8k - > cur_voice ] . lfo2val ;
2016-06-26 00:34:39 +02:00
}
break ;
2017-09-01 01:01:53 +02:00
case 0xE00 : /*Data3. also known as BLASTER+0xC00 and EMU+0x800 */
2016-06-26 00:34:39 +02:00
switch ( emu8k - > cur_reg )
{
case 0 :
return emu8k - > voice [ emu8k - > cur_voice ] . ip ;
case 1 :
return emu8k - > voice [ emu8k - > cur_voice ] . ifatn ;
case 2 :
return emu8k - > voice [ emu8k - > cur_voice ] . pefe ;
case 3 :
return emu8k - > voice [ emu8k - > cur_voice ] . fmmod ;
case 4 :
return emu8k - > voice [ emu8k - > cur_voice ] . tremfrq ;
case 5 :
return emu8k - > voice [ emu8k - > cur_voice ] . fm2frq2 ;
case 6 :
return 0xffff ;
case 7 : /*ID?*/
return 0x1c | ( ( emu8k - > id & 0x0002 ) ? 0xff02 : 0 ) ;
}
break ;
2017-07-24 12:04:39 +02:00
2017-09-01 01:01:53 +02:00
case 0xE02 : /* Pointer. also known as BLASTER+0xC02 and EMU+0x802 */
2017-06-22 23:51:57 +02:00
/* LS five bits = channel number, next 3 bits = register number
2017-09-01 01:01:53 +02:00
* and MS 8 bits = VLSI test register .
* Impulse tracker tests the non variability of the LS byte that it has set , and the variability
* of the MS byte to determine that it really is an AWE32 .
* cubic player has a similar code , where it waits until value & 0x1000 is nonzero , and then waits again until it changes to zero . */
2017-07-24 12:04:39 +02:00
random_helper = ( random_helper + 1 ) & 0x1F ;
return ( ( 0x80 | random_helper ) < < 8 ) | ( emu8k - > cur_reg < < 5 ) | emu8k - > cur_voice ;
2016-06-26 00:34:39 +02:00
}
2017-10-16 06:19:18 +02:00
emu8k_log ( " EMU8K READ : Unknown register read: %04X-%02X(%d/%d) \n " , addr , ( emu8k - > cur_reg < < 5 ) | emu8k - > cur_voice , emu8k - > cur_reg , emu8k - > cur_voice ) ;
2016-06-26 00:34:39 +02:00
return 0xffff ;
}
2017-05-05 01:49:42 +02:00
void emu8k_outw ( uint16_t addr , uint16_t val , void * p )
2016-06-26 00:34:39 +02:00
{
emu8k_t * emu8k = ( emu8k_t * ) p ;
2017-09-01 01:01:53 +02:00
/*TODO: I would like to not call this here, but i found it was needed or else cubic player would not finish opening (take a looot more of time than usual).
* Basically , being here means that the audio is generated in the emulation thread , instead of the audio thread . */
2016-06-26 00:34:39 +02:00
emu8k_update ( emu8k ) ;
2017-07-24 12:04:39 +02:00
# ifdef EMU8K_DEBUG_REGISTERS
2017-08-21 04:19:34 +02:00
if ( addr = = 0xE22 )
{
2017-10-16 06:19:18 +02:00
//emu8k_log("EMU8K WRITE POINTER: %d\n", val);
2017-08-21 04:19:34 +02:00
}
else if ( ( addr & 0xF00 ) = = 0x600 )
{
/* These are automatically reported by WRITE16 */
if ( rep_count_w > 1 )
{
2017-10-16 06:19:18 +02:00
emu8k_log ( " EMU8K ...... for %d times \n " , rep_count_w ) ;
2017-07-24 12:04:39 +02:00
rep_count_w = 0 ;
}
2017-08-21 04:19:34 +02:00
last_write = 0 ;
2017-07-24 12:04:39 +02:00
}
2017-08-21 04:19:34 +02:00
else if ( ( addr & 0xF00 ) = = 0xA00 & & emu8k - > cur_reg = = 0 )
{
/* These are automatically reported by WRITE16 */
if ( rep_count_w > 1 )
{
2017-10-16 06:19:18 +02:00
emu8k_log ( " EMU8K ...... for %d times \n " , rep_count_w ) ;
2017-07-24 12:04:39 +02:00
rep_count_w = 0 ;
}
2017-08-21 04:19:34 +02:00
last_write = 0 ;
2017-07-24 12:04:39 +02:00
}
2017-08-21 04:19:34 +02:00
else if ( ( addr & 0xF00 ) = = 0xA00 & & emu8k - > cur_reg = = 1 )
{
uint32_t tmpz = ( ( addr & 0xF00 ) < < 16 ) | ( emu8k - > cur_reg < < 5 ) ;
if ( tmpz ! = last_write )
{
if ( rep_count_w > 1 )
{
2017-10-16 06:19:18 +02:00
emu8k_log ( " EMU8K ...... for %d times \n " , rep_count_w ) ;
2017-08-21 04:19:34 +02:00
rep_count_w = 0 ;
}
last_write = tmpz ;
2017-10-16 06:19:18 +02:00
emu8k_log ( " EMU8K WRITE RAM I/O or configuration \n " ) ;
2017-08-21 04:19:34 +02:00
}
2017-10-16 06:19:18 +02:00
//emu8k_log("EMU8K WRITE %04X-%02X(%d/%d): %04X\n",addr,(emu8k->cur_reg)<<5|emu8k->cur_voice,emu8k->cur_reg,emu8k->cur_voice, val);
2017-08-21 04:19:34 +02:00
}
else if ( ( addr & 0xF00 ) = = 0xA00 & & ( emu8k - > cur_reg = = 2 | | emu8k - > cur_reg = = 3 ) )
2017-09-01 01:01:53 +02:00
{
2017-08-21 04:19:34 +02:00
uint32_t tmpz = ( ( addr & 0xF00 ) < < 16 ) ;
if ( tmpz ! = last_write )
{
if ( rep_count_w > 1 )
{
2017-10-16 06:19:18 +02:00
emu8k_log ( " EMU8K ...... for %d times \n " , rep_count_w ) ;
2017-08-21 04:19:34 +02:00
rep_count_w = 0 ;
}
last_write = tmpz ;
2017-10-16 06:19:18 +02:00
emu8k_log ( " EMU8K WRITE INIT \n " ) ;
2017-08-21 04:19:34 +02:00
}
2017-10-16 06:19:18 +02:00
//emu8k_log("EMU8K WRITE %04X-%02X(%d/%d): %04X\n",addr,(emu8k->cur_reg)<<5|emu8k->cur_voice,emu8k->cur_reg,emu8k->cur_voice, val);
2017-08-21 04:19:34 +02:00
}
else if ( addr ! = 0xE22 )
{
2017-07-24 12:04:39 +02:00
uint32_t tmpz = ( addr < < 16 ) | ( emu8k - > cur_reg < < 5 ) | emu8k - > cur_voice ;
2017-10-14 07:03:19 +02:00
//if (tmpz != last_write)
2017-08-21 04:19:34 +02:00
if ( 1 )
{
2017-07-24 12:04:39 +02:00
char * name = 0 ;
2017-08-21 04:19:34 +02:00
if ( addr = = 0xA20 )
{
name = PORT_NAMES [ 1 ] [ emu8k - > cur_reg ] ;
2017-07-24 12:04:39 +02:00
}
2017-08-21 04:19:34 +02:00
else if ( addr = = 0xA22 )
{
name = PORT_NAMES [ 2 ] [ emu8k - > cur_reg ] ;
2017-07-24 12:04:39 +02:00
}
2017-08-21 04:19:34 +02:00
else if ( addr = = 0xE20 )
{
name = PORT_NAMES [ 3 ] [ emu8k - > cur_reg ] ;
2017-07-24 12:04:39 +02:00
}
2017-08-21 04:19:34 +02:00
if ( rep_count_w > 1 )
{
2017-10-16 06:19:18 +02:00
emu8k_log ( " EMU8K ...... for %d times \n " , rep_count_w ) ;
2017-07-24 12:04:39 +02:00
}
2017-08-21 04:19:34 +02:00
if ( name = = 0 )
{
2017-10-16 06:19:18 +02:00
emu8k_log ( " EMU8K WRITE %04X-%02X(%d/%d): %04X \n " , addr , ( emu8k - > cur_reg ) < < 5 | emu8k - > cur_voice , emu8k - > cur_reg , emu8k - > cur_voice , val ) ;
2017-08-21 04:19:34 +02:00
}
else
{
2017-10-16 06:19:18 +02:00
emu8k_log ( " EMU8K WRITE %s (%d): %04X \n " , name , emu8k - > cur_voice , val ) ;
2017-07-24 12:04:39 +02:00
}
rep_count_w = 0 ;
last_write = tmpz ;
}
rep_count_w + + ;
}
2017-10-14 07:03:19 +02:00
# endif //EMU8K_DEBUG_REGISTERS
2017-07-24 12:04:39 +02:00
switch ( addr & 0xF02 )
2016-06-26 00:34:39 +02:00
{
2017-09-01 01:01:53 +02:00
case 0x600 : case 0x602 : /*Data0. also known as BLASTER+0x400 and EMU+0x000 */
2016-06-26 00:34:39 +02:00
switch ( emu8k - > cur_reg )
{
case 0 :
2017-07-24 12:04:39 +02:00
/* The docs says that this value is constantly updating, and it should have no actual effect. Actions should be done over ptrx */
2017-09-01 01:01:53 +02:00
WRITE16 ( addr , emu8k - > voice [ emu8k - > cur_voice ] . cpf , val ) ;
2016-06-26 00:34:39 +02:00
return ;
case 1 :
WRITE16 ( addr , emu8k - > voice [ emu8k - > cur_voice ] . ptrx , val ) ;
return ;
case 2 :
2017-07-24 12:04:39 +02:00
/* The docs says that this value is constantly updating, and it should have no actual effect. Actions should be done over vtft */
2017-08-21 04:19:34 +02:00
WRITE16 ( addr , emu8k - > voice [ emu8k - > cur_voice ] . cvcf , val ) ;
2016-06-26 00:34:39 +02:00
return ;
case 3 :
WRITE16 ( addr , emu8k - > voice [ emu8k - > cur_voice ] . vtft , val ) ;
return ;
2017-07-24 12:04:39 +02:00
case 4 :
WRITE16 ( addr , emu8k - > voice [ emu8k - > cur_voice ] . unknown_data0_4 , val ) ;
return ;
case 5 :
WRITE16 ( addr , emu8k - > voice [ emu8k - > cur_voice ] . unknown_data0_5 , val ) ;
return ;
2016-06-26 00:34:39 +02:00
case 6 :
{
2017-07-24 12:04:39 +02:00
emu8k_voice_t * emu_voice = & emu8k - > voice [ emu8k - > cur_voice ] ;
WRITE16 ( addr , emu_voice - > psst , val ) ;
/* TODO: Should we update only on MSB update, or this could be used as some sort of hack by applications? */
emu_voice - > loop_start . int_address = emu_voice - > psst & EMU8K_MEM_ADDRESS_MASK ;
if ( addr & 2 )
{
emu_voice - > vol_l = emu_voice - > psst_pan ;
emu_voice - > vol_r = 255 - ( emu_voice - > psst_pan ) ;
}
2016-06-26 00:34:39 +02:00
}
return ;
case 7 :
2017-06-22 23:51:57 +02:00
WRITE16 ( addr , emu8k - > voice [ emu8k - > cur_voice ] . csl , val ) ;
/* TODO: Should we update only on MSB update, or this could be used as some sort of hack by applications? */
2017-07-24 12:04:39 +02:00
emu8k - > voice [ emu8k - > cur_voice ] . loop_end . int_address = emu8k - > voice [ emu8k - > cur_voice ] . csl & EMU8K_MEM_ADDRESS_MASK ;
2016-06-26 00:34:39 +02:00
return ;
}
break ;
2017-09-01 01:01:53 +02:00
case 0xA00 : /*Data1. also known as BLASTER+0x800 and EMU+0x400 */
2016-06-26 00:34:39 +02:00
switch ( emu8k - > cur_reg )
{
case 0 :
WRITE16 ( addr , emu8k - > voice [ emu8k - > cur_voice ] . ccca , val ) ;
2017-06-22 23:51:57 +02:00
/* TODO: Should we update only on MSB update, or this could be used as some sort of hack by applications? */
2017-07-24 12:04:39 +02:00
emu8k - > voice [ emu8k - > cur_voice ] . addr . int_address = emu8k - > voice [ emu8k - > cur_voice ] . ccca & EMU8K_MEM_ADDRESS_MASK ;
2016-06-26 00:34:39 +02:00
return ;
case 1 :
switch ( emu8k - > cur_voice )
{
2017-07-24 12:04:39 +02:00
case 9 :
WRITE16 ( addr , emu8k - > hwcf4 , val ) ;
return ;
case 10 :
WRITE16 ( addr , emu8k - > hwcf5 , val ) ;
return ;
/* Actually, these two might be command words rather than registers, or some LFO position/buffer reset. */
case 13 :
WRITE16 ( addr , emu8k - > hwcf6 , val ) ;
return ;
case 14 :
WRITE16 ( addr , emu8k - > hwcf7 , val ) ;
return ;
2016-06-26 00:34:39 +02:00
case 20 :
WRITE16 ( addr , emu8k - > smalr , val ) ;
return ;
case 21 :
WRITE16 ( addr , emu8k - > smarr , val ) ;
return ;
case 22 :
WRITE16 ( addr , emu8k - > smalw , val ) ;
return ;
case 23 :
WRITE16 ( addr , emu8k - > smarw , val ) ;
return ;
case 26 :
EMU8K_WRITE ( emu8k , emu8k - > smalw , val ) ;
2017-07-24 12:04:39 +02:00
emu8k - > smalw = ( emu8k - > smalw + 1 ) & EMU8K_MEM_ADDRESS_MASK ;
return ;
2016-06-26 00:34:39 +02:00
2017-07-24 12:04:39 +02:00
case 29 :
2016-06-26 00:34:39 +02:00
emu8k - > hwcf1 = val ;
return ;
2017-07-24 12:04:39 +02:00
case 30 :
2016-06-26 00:34:39 +02:00
emu8k - > hwcf2 = val ;
return ;
2017-07-24 12:04:39 +02:00
case 31 :
2016-06-26 00:34:39 +02:00
emu8k - > hwcf3 = val ;
return ;
}
break ;
2017-07-24 12:04:39 +02:00
case 2 :
emu8k - > init1 [ emu8k - > cur_voice ] = val ;
2017-08-21 04:19:34 +02:00
/* Skip if in first/second initialization step */
if ( emu8k - > init1 [ 0 ] ! = 0x03FF )
{
switch ( emu8k - > cur_voice )
{
case 0x3 : emu8k - > reverb_engine . out_mix = val & 0xFF ;
break ;
case 0x5 :
{
int c ;
for ( c = 0 ; c < 8 ; c + + )
{
emu8k - > reverb_engine . allpass [ c ] . feedback = ( val & 0xFF ) / ( ( float ) 0xFF ) ;
}
}
break ;
case 0x7 : emu8k - > reverb_engine . link_return_type = ( val = = 0x8474 ) ? 1 : 0 ;
break ;
case 0xF : emu8k - > reverb_engine . reflections [ 0 ] . output_gain = ( ( val & 0xF0 ) > > 4 ) / 15.0 ;
break ;
case 0x17 : emu8k - > reverb_engine . reflections [ 1 ] . output_gain = ( ( val & 0xF0 ) > > 4 ) / 15.0 ;
break ;
case 0x1F : emu8k - > reverb_engine . reflections [ 2 ] . output_gain = ( ( val & 0xF0 ) > > 4 ) / 15.0 ;
break ;
case 0x9 : emu8k - > reverb_engine . reflections [ 0 ] . feedback = ( val & 0xF ) / 15.0 ;
break ;
2017-10-14 07:03:19 +02:00
case 0xB : //emu8k->reverb_engine.reflections[0].feedback_r = (val&0xF)/15.0;
2017-08-21 04:19:34 +02:00
break ;
case 0x11 : emu8k - > reverb_engine . reflections [ 1 ] . feedback = ( val & 0xF ) / 15.0 ;
break ;
2017-10-14 07:03:19 +02:00
case 0x13 : //emu8k->reverb_engine.reflections[1].feedback_r = (val&0xF)/15.0;
2017-08-21 04:19:34 +02:00
break ;
case 0x19 : emu8k - > reverb_engine . reflections [ 2 ] . feedback = ( val & 0xF ) / 15.0 ;
break ;
2017-10-14 07:03:19 +02:00
case 0x1B : //emu8k->reverb_engine.reflections[2].feedback_r = (val&0xF)/15.0;
2017-08-21 04:19:34 +02:00
break ;
}
}
2017-07-24 12:04:39 +02:00
return ;
case 3 :
emu8k - > init3 [ emu8k - > cur_voice ] = val ;
2017-08-21 04:19:34 +02:00
/* Skip if in first/second initialization step */
if ( emu8k - > init1 [ 0 ] ! = 0x03FF )
{
switch ( emu8k - > cur_voice )
{
case 9 :
emu8k - > chorus_engine . feedback = ( val & 0xFF ) ;
break ;
case 12 :
2017-10-14 07:03:19 +02:00
/* Limiting this to a sane value given our buffer. */
emu8k - > chorus_engine . delay_samples_central = ( val & 0x1FFF ) ;
2017-08-21 04:19:34 +02:00
break ;
case 1 : emu8k - > reverb_engine . refl_in_amp = val & 0xFF ;
break ;
2017-10-14 07:03:19 +02:00
case 3 : //emu8k->reverb_engine.refl_in_amp_r = val&0xFF;
2017-08-21 04:19:34 +02:00
break ;
}
2017-07-24 12:04:39 +02:00
}
return ;
case 4 :
emu8k - > voice [ emu8k - > cur_voice ] . envvol = val ;
emu8k - > voice [ emu8k - > cur_voice ] . vol_envelope . delay_samples = ENVVOL_TO_EMU_SAMPLES ( val ) ;
return ;
2016-06-26 00:34:39 +02:00
case 5 :
{
2017-08-21 04:19:34 +02:00
emu8k - > voice [ emu8k - > cur_voice ] . dcysusv = val ;
emu8k_envelope_t * const vol_env = & emu8k - > voice [ emu8k - > cur_voice ] . vol_envelope ;
int old_on = emu8k - > voice [ emu8k - > cur_voice ] . env_engine_on ;
emu8k - > voice [ emu8k - > cur_voice ] . env_engine_on = DCYSUSV_GENERATOR_ENGINE_ON ( val ) ;
2017-07-24 12:04:39 +02:00
2017-08-23 03:50:36 +02:00
if ( emu8k - > voice [ emu8k - > cur_voice ] . env_engine_on & &
2017-09-01 01:01:53 +02:00
old_on ! = emu8k - > voice [ emu8k - > cur_voice ] . env_engine_on )
{
2017-10-14 07:03:19 +02:00
if ( emu8k - > hwcf3 ! = 0x04 )
2017-08-23 03:50:36 +02:00
{
/* This is a hack for some programs like Doom or cubic player 1.7 that don't initialize
the hwcfg and init registers ( doom does not init the card at all . only tests the cfg registers ) */
emu8k - > hwcf3 = 0x04 ;
}
2017-09-01 01:01:53 +02:00
2017-10-14 07:03:19 +02:00
//reset lfos.
2017-08-21 04:19:34 +02:00
emu8k - > voice [ emu8k - > cur_voice ] . lfo1_count . addr = 0 ;
emu8k - > voice [ emu8k - > cur_voice ] . lfo2_count . addr = 0 ;
2017-10-14 07:03:19 +02:00
// Trigger envelopes
2017-08-21 04:19:34 +02:00
if ( ATKHLDV_TRIGGER ( emu8k - > voice [ emu8k - > cur_voice ] . atkhldv ) )
{
vol_env - > value_amp_hz = 0 ;
if ( vol_env - > delay_samples )
{
vol_env - > state = ENV_DELAY ;
}
else if ( vol_env - > attack_amount_amp_hz = = 0 )
{
vol_env - > state = ENV_STOPPED ;
}
else
{
vol_env - > state = ENV_ATTACK ;
/* TODO: Verify if "never attack" means eternal mute,
* or it means skip attack , go to hold " .
if ( vol_env - > attack_amount = = 0 )
{
vol_env - > value = ( 1 < < 21 ) ;
vol_env - > state = ENV_HOLD ;
} */
}
}
if ( ATKHLD_TRIGGER ( emu8k - > voice [ emu8k - > cur_voice ] . atkhld ) )
{
emu8k_envelope_t * const mod_env = & emu8k - > voice [ emu8k - > cur_voice ] . mod_envelope ;
mod_env - > value_amp_hz = 0 ;
mod_env - > value_db_oct = 0 ;
if ( mod_env - > delay_samples )
{
mod_env - > state = ENV_DELAY ;
}
else if ( mod_env - > attack_amount_amp_hz = = 0 )
{
mod_env - > state = ENV_STOPPED ;
}
else
{
mod_env - > state = ENV_ATTACK ;
/* TODO: Verify if "never attack" means eternal start,
* or it means skip attack , go to hold " .
if ( mod_env - > attack_amount = = 0 )
{
mod_env - > value = ( 1 < < 21 ) ;
mod_env - > state = ENV_HOLD ;
} */
}
}
}
/* Converting the input in dBs to envelope value range. */
vol_env - > sustain_value_db_oct = DCYSUSV_SUS_TO_ENV_RANGE ( DCYSUSV_SUSVALUE_GET ( val ) ) ;
vol_env - > ramp_amount_db_oct = env_decay_to_dbs_or_oct [ DCYSUSV_DECAYRELEASE_GET ( val ) ] ;
if ( DCYSUSV_IS_RELEASE ( val ) )
{
if ( vol_env - > state = = ENV_DELAY | | vol_env - > state = = ENV_ATTACK | | vol_env - > state = = ENV_HOLD )
{
vol_env - > value_db_oct = env_vol_amplitude_to_db [ vol_env - > value_amp_hz > > 5 ] < < 5 ;
if ( vol_env - > value_db_oct > ( 1 < < 21 ) )
vol_env - > value_db_oct = 1 < < 21 ;
}
vol_env - > state = ( vol_env - > value_db_oct > = vol_env - > sustain_value_db_oct ) ? ENV_RAMP_DOWN : ENV_RAMP_UP ;
}
2016-06-26 00:34:39 +02:00
}
return ;
2017-07-24 12:04:39 +02:00
case 6 :
emu8k - > voice [ emu8k - > cur_voice ] . envval = val ;
emu8k - > voice [ emu8k - > cur_voice ] . mod_envelope . delay_samples = ENVVAL_TO_EMU_SAMPLES ( val ) ;
return ;
2017-09-01 01:01:53 +02:00
2016-06-26 00:34:39 +02:00
case 7 :
{
2017-10-14 07:03:19 +02:00
//TODO: Look for a bug on delay (first trigger it works, next trigger it doesn't)
2017-08-21 04:19:34 +02:00
emu8k - > voice [ emu8k - > cur_voice ] . dcysus = val ;
emu8k_envelope_t * const mod_env = & emu8k - > voice [ emu8k - > cur_voice ] . mod_envelope ;
/* Converting the input in octaves to envelope value range. */
mod_env - > sustain_value_db_oct = DCYSUS_SUS_TO_ENV_RANGE ( DCYSUS_SUSVALUE_GET ( val ) ) ;
mod_env - > ramp_amount_db_oct = env_decay_to_dbs_or_oct [ DCYSUS_DECAYRELEASE_GET ( val ) ] ;
if ( DCYSUS_IS_RELEASE ( val ) )
{
if ( mod_env - > state = = ENV_DELAY | | mod_env - > state = = ENV_ATTACK | | mod_env - > state = = ENV_HOLD )
{
mod_env - > value_db_oct = env_mod_hertz_to_octave [ mod_env - > value_amp_hz > > 9 ] < < 9 ;
if ( mod_env - > value_db_oct > = ( 1 < < 21 ) )
mod_env - > value_db_oct = ( 1 < < 21 ) - 1 ;
}
mod_env - > state = ( mod_env - > value_db_oct > = mod_env - > sustain_value_db_oct ) ? ENV_RAMP_DOWN : ENV_RAMP_UP ;
}
2016-06-26 00:34:39 +02:00
}
return ;
}
break ;
2017-09-01 01:01:53 +02:00
case 0xA02 : /*Data2. also known as BLASTER+0x802 and EMU+0x402 */
2016-06-26 00:34:39 +02:00
switch ( emu8k - > cur_reg )
{
case 0 :
{
2017-07-24 12:04:39 +02:00
emu8k_voice_t * emu_voice = & emu8k - > voice [ emu8k - > cur_voice ] ;
WRITE16 ( addr , emu_voice - > ccca , val ) ;
emu_voice - > addr . int_address = emu_voice - > ccca & EMU8K_MEM_ADDRESS_MASK ;
uint32_t paramq = CCCA_FILTQ_GET ( emu_voice - > ccca ) ;
emu_voice - > filt_att = filter_atten [ paramq ] ;
emu_voice - > filterq_idx = paramq ;
2016-06-26 00:34:39 +02:00
}
return ;
case 1 :
switch ( emu8k - > cur_voice )
{
2017-07-24 12:04:39 +02:00
case 9 :
WRITE16 ( addr , emu8k - > hwcf4 , val ) ;
2017-08-21 04:19:34 +02:00
/* Skip if in first/second initialization step */
if ( emu8k - > init1 [ 0 ] ! = 0x03FF )
{
/*(1/256th of a 44Khz sample) */
2017-10-14 07:03:19 +02:00
/* clip the value to a reasonable value given our buffer */
int32_t tmp = emu8k - > hwcf4 & 0x1FFFFF ;
emu8k - > chorus_engine . delay_offset_samples_right = ( ( double ) tmp ) / 256.0 ;
2017-08-21 04:19:34 +02:00
}
2016-06-26 00:34:39 +02:00
return ;
2017-07-24 12:04:39 +02:00
case 10 :
2017-08-21 04:19:34 +02:00
WRITE16 ( addr , emu8k - > hwcf5 , val ) ;
/* Skip if in first/second initialization step */
if ( emu8k - > init1 [ 0 ] ! = 0x03FF )
2017-07-24 12:04:39 +02:00
{
2017-08-21 04:19:34 +02:00
/* The scale of this value is unknown. I've taken it as milliHz.
* Another interpretation could be periods . ( and so , Hz = 1 / period ) */
2017-10-14 07:03:19 +02:00
double osc_speed = emu8k - > hwcf5 ; //*1.316;
# if 1 // milliHz
2017-08-21 04:19:34 +02:00
/*milliHz to lfotable samples.*/
osc_speed * = 65.536 / 44100.0 ;
2017-10-14 07:03:19 +02:00
# elif 0 //periods
/* 44.1Khz ticks to lfotable samples.*/
osc_speed = 65.536 / osc_speed ;
# endif
2017-08-21 04:19:34 +02:00
/*left shift 32bits for 32.32 fixed.point*/
osc_speed * = 65536.0 * 65536.0 ;
emu8k - > chorus_engine . lfo_inc . addr = ( uint64_t ) osc_speed ;
2017-07-24 12:04:39 +02:00
}
2016-06-26 00:34:39 +02:00
return ;
2017-09-01 01:01:53 +02:00
/* Actually, these two might be command words rather than registers, or some LFO position/buffer reset.*/
2017-07-24 12:04:39 +02:00
case 13 :
WRITE16 ( addr , emu8k - > hwcf6 , val ) ;
2016-06-26 00:34:39 +02:00
return ;
2017-07-24 12:04:39 +02:00
case 14 :
WRITE16 ( addr , emu8k - > hwcf7 , val ) ;
return ;
2017-09-01 01:01:53 +02:00
case 20 : /*Top 8 bits are for Empty (MT) bit or non-addressable.*/
2017-07-24 12:04:39 +02:00
WRITE16 ( addr , emu8k - > smalr , val & 0xFF ) ;
dmareadbit = 0x8000 ;
return ;
2017-09-01 01:01:53 +02:00
case 21 : /*Top 8 bits are for Empty (MT) bit or non-addressable.*/
2017-07-24 12:04:39 +02:00
WRITE16 ( addr , emu8k - > smarr , val & 0xFF ) ;
dmareadbit = 0x8000 ;
return ;
2017-09-01 01:01:53 +02:00
case 22 : /*Top 8 bits are for full bit or non-addressable.*/
2017-07-24 12:04:39 +02:00
WRITE16 ( addr , emu8k - > smalw , val & 0xFF ) ;
return ;
2017-09-01 01:01:53 +02:00
case 23 : /*Top 8 bits are for full bit or non-addressable.*/
2017-07-24 12:04:39 +02:00
WRITE16 ( addr , emu8k - > smarw , val & 0xFF ) ;
2016-06-26 00:34:39 +02:00
return ;
case 26 :
2017-07-24 12:04:39 +02:00
dmawritebit = 0x8000 ;
2016-06-26 00:34:39 +02:00
EMU8K_WRITE ( emu8k , emu8k - > smarw , val ) ;
emu8k - > smarw + + ;
2017-07-24 12:04:39 +02:00
return ;
2016-06-26 00:34:39 +02:00
}
break ;
2017-07-24 12:04:39 +02:00
case 2 :
emu8k - > init2 [ emu8k - > cur_voice ] = val ;
2017-08-21 04:19:34 +02:00
/* Skip if in first/second initialization step */
if ( emu8k - > init1 [ 0 ] ! = 0x03FF )
{
switch ( emu8k - > cur_voice )
{
case 0x14 :
{
int multip = ( ( val & 0xF00 ) > > 8 ) + 18 ;
emu8k - > reverb_engine . reflections [ 5 ] . bufsize = multip * REV_BUFSIZE_STEP ;
emu8k - > reverb_engine . tailL . bufsize = ( multip + 1 ) * REV_BUFSIZE_STEP ;
if ( emu8k - > reverb_engine . link_return_type = = 0 )
{
emu8k - > reverb_engine . tailR . bufsize = ( multip + 1 ) * REV_BUFSIZE_STEP ;
}
}
break ;
case 0x16 :
if ( emu8k - > reverb_engine . link_return_type = = 1 )
{
int multip = ( ( val & 0xF00 ) > > 8 ) + 18 ;
emu8k - > reverb_engine . tailR . bufsize = ( multip + 1 ) * REV_BUFSIZE_STEP ;
}
break ;
case 0x7 : emu8k - > reverb_engine . reflections [ 3 ] . output_gain = ( ( val & 0xF0 ) > > 4 ) / 15.0 ;
break ;
case 0xf : emu8k - > reverb_engine . reflections [ 4 ] . output_gain = ( ( val & 0xF0 ) > > 4 ) / 15.0 ;
break ;
case 0x17 : emu8k - > reverb_engine . reflections [ 5 ] . output_gain = ( ( val & 0xF0 ) > > 4 ) / 15.0 ;
break ;
case 0x1d :
{
int c ;
for ( c = 0 ; c < 6 ; c + + )
{
emu8k - > reverb_engine . reflections [ c ] . damp1 = ( val & 0xFF ) / 255.0 ;
emu8k - > reverb_engine . reflections [ c ] . damp2 = ( 0xFF - ( val & 0xFF ) ) / 255.0 ;
emu8k - > reverb_engine . reflections [ c ] . filterstore = 0 ;
}
emu8k - > reverb_engine . damper . damp1 = ( val & 0xFF ) / 255.0 ;
emu8k - > reverb_engine . damper . damp2 = ( 0xFF - ( val & 0xFF ) ) / 255.0 ;
emu8k - > reverb_engine . damper . filterstore = 0 ;
}
break ;
case 0x1f : /* filter r */
break ;
case 0x1 : emu8k - > reverb_engine . reflections [ 3 ] . feedback = ( val & 0xF ) / 15.0 ;
break ;
2017-10-14 07:03:19 +02:00
case 0x3 : //emu8k->reverb_engine.reflections[3].feedback_r = (val&0xF)/15.0;
2017-08-21 04:19:34 +02:00
break ;
case 0x9 : emu8k - > reverb_engine . reflections [ 4 ] . feedback = ( val & 0xF ) / 15.0 ;
break ;
2017-10-14 07:03:19 +02:00
case 0xb : //emu8k->reverb_engine.reflections[4].feedback_r = (val&0xF)/15.0;
2017-08-21 04:19:34 +02:00
break ;
case 0x11 : emu8k - > reverb_engine . reflections [ 5 ] . feedback = ( val & 0xF ) / 15.0 ;
break ;
2017-10-14 07:03:19 +02:00
case 0x13 : //emu8k->reverb_engine.reflections[5].feedback_r = (val&0xF)/15.0;
2017-08-21 04:19:34 +02:00
break ;
}
}
2017-07-24 12:04:39 +02:00
return ;
2017-08-21 04:19:34 +02:00
2017-07-24 12:04:39 +02:00
case 3 :
emu8k - > init4 [ emu8k - > cur_voice ] = val ;
2017-08-21 04:19:34 +02:00
/* Skip if in first/second initialization step */
if ( emu8k - > init1 [ 0 ] ! = 0x03FF )
{
switch ( emu8k - > cur_voice )
{
case 0x3 :
{
int32_t samples = ( ( val & 0xFF ) * emu8k - > chorus_engine . delay_samples_central ) > > 8 ;
emu8k - > chorus_engine . lfodepth_multip = samples ;
2017-10-14 07:03:19 +02:00
2017-08-21 04:19:34 +02:00
}
break ;
case 0x1F :
emu8k - > reverb_engine . link_return_amp = val & 0xFF ;
break ;
}
2017-07-24 12:04:39 +02:00
}
return ;
2016-06-26 00:34:39 +02:00
case 4 :
2017-07-24 12:04:39 +02:00
{
2017-08-21 04:19:34 +02:00
emu8k - > voice [ emu8k - > cur_voice ] . atkhldv = val ;
emu8k_envelope_t * const vol_env = & emu8k - > voice [ emu8k - > cur_voice ] . vol_envelope ;
vol_env - > attack_samples = env_attack_to_samples [ ATKHLDV_ATTACK ( val ) ] ;
if ( vol_env - > attack_samples = = 0 )
2017-07-24 12:04:39 +02:00
{
2017-08-21 04:19:34 +02:00
vol_env - > attack_amount_amp_hz = 0 ;
2017-07-24 12:04:39 +02:00
}
2017-08-21 04:19:34 +02:00
else
2017-07-24 12:04:39 +02:00
{
2017-08-21 04:19:34 +02:00
/* Linear amplitude increase each sample. */
vol_env - > attack_amount_amp_hz = ( 1 < < 21 ) / vol_env - > attack_samples ;
2017-07-24 12:04:39 +02:00
}
2017-08-21 04:19:34 +02:00
vol_env - > hold_samples = ATKHLDV_HOLD_TO_EMU_SAMPLES ( val ) ;
if ( ATKHLDV_TRIGGER ( val ) & & emu8k - > voice [ emu8k - > cur_voice ] . env_engine_on )
2017-07-24 12:04:39 +02:00
{
2017-08-21 04:19:34 +02:00
/*TODO: I assume that "envelope trigger" is the same as new note
* ( since changing the IP can be done when modulating pitch too ) */
emu8k - > voice [ emu8k - > cur_voice ] . lfo1_count . addr = 0 ;
emu8k - > voice [ emu8k - > cur_voice ] . lfo2_count . addr = 0 ;
vol_env - > value_amp_hz = 0 ;
if ( vol_env - > delay_samples )
{
vol_env - > state = ENV_DELAY ;
}
else if ( vol_env - > attack_amount_amp_hz = = 0 )
{
vol_env - > state = ENV_STOPPED ;
}
else
2017-07-24 12:04:39 +02:00
{
2017-08-21 04:19:34 +02:00
vol_env - > state = ENV_ATTACK ;
/* TODO: Verify if "never attack" means eternal mute,
* or it means skip attack , go to hold " .
if ( vol_env - > attack_amount = = 0 )
{
vol_env - > value = ( 1 < < 21 ) ;
vol_env - > state = ENV_HOLD ;
} */
}
2017-07-24 12:04:39 +02:00
}
}
return ;
case 5 :
emu8k - > voice [ emu8k - > cur_voice ] . lfo1val = val ;
/* TODO: verify if this is set once, or set every time. */
emu8k - > voice [ emu8k - > cur_voice ] . lfo1_delay_samples = LFOxVAL_TO_EMU_SAMPLES ( val ) ;
2016-06-26 00:34:39 +02:00
return ;
case 6 :
2017-07-24 12:04:39 +02:00
{
2017-08-21 04:19:34 +02:00
emu8k - > voice [ emu8k - > cur_voice ] . atkhld = val ;
emu8k_envelope_t * const mod_env = & emu8k - > voice [ emu8k - > cur_voice ] . mod_envelope ;
mod_env - > attack_samples = env_attack_to_samples [ ATKHLD_ATTACK ( val ) ] ;
if ( mod_env - > attack_samples = = 0 )
2017-07-24 12:04:39 +02:00
{
2017-08-21 04:19:34 +02:00
mod_env - > attack_amount_amp_hz = 0 ;
2017-07-24 12:04:39 +02:00
}
2017-08-21 04:19:34 +02:00
else
2017-07-24 12:04:39 +02:00
{
2017-08-21 04:19:34 +02:00
/* Linear amplitude increase each sample. */
mod_env - > attack_amount_amp_hz = ( 1 < < 21 ) / mod_env - > attack_samples ;
2017-07-24 12:04:39 +02:00
}
2017-08-21 04:19:34 +02:00
mod_env - > hold_samples = ATKHLD_HOLD_TO_EMU_SAMPLES ( val ) ;
if ( ATKHLD_TRIGGER ( val ) & & emu8k - > voice [ emu8k - > cur_voice ] . env_engine_on )
2017-07-24 12:04:39 +02:00
{
2017-08-21 04:19:34 +02:00
mod_env - > value_amp_hz = 0 ;
mod_env - > value_db_oct = 0 ;
if ( mod_env - > delay_samples )
2017-07-24 12:04:39 +02:00
{
2017-08-21 04:19:34 +02:00
mod_env - > state = ENV_DELAY ;
}
else if ( mod_env - > attack_amount_amp_hz = = 0 )
{
mod_env - > state = ENV_STOPPED ;
}
else
{
mod_env - > state = ENV_ATTACK ;
/* TODO: Verify if "never attack" means eternal start,
* or it means skip attack , go to hold " .
if ( mod_env - > attack_amount = = 0 )
{
mod_env - > value = ( 1 < < 21 ) ;
mod_env - > state = ENV_HOLD ;
} */
}
2017-07-24 12:04:39 +02:00
}
}
return ;
case 7 :
emu8k - > voice [ emu8k - > cur_voice ] . lfo2val = val ;
emu8k - > voice [ emu8k - > cur_voice ] . lfo2_delay_samples = LFOxVAL_TO_EMU_SAMPLES ( val ) ;
2016-06-26 00:34:39 +02:00
return ;
}
break ;
2017-09-01 01:01:53 +02:00
case 0xE00 : /*Data3. also known as BLASTER+0xC00 and EMU+0x800 */
2016-06-26 00:34:39 +02:00
switch ( emu8k - > cur_reg )
{
case 0 :
emu8k - > voice [ emu8k - > cur_voice ] . ip = val ;
2017-07-24 12:04:39 +02:00
emu8k - > voice [ emu8k - > cur_voice ] . ptrx_pit_target = freqtable [ val ] > > 18 ;
2016-06-26 00:34:39 +02:00
return ;
case 1 :
2017-07-24 12:04:39 +02:00
{
2017-08-21 04:19:34 +02:00
emu8k_voice_t * const the_voice = & emu8k - > voice [ emu8k - > cur_voice ] ;
if ( ( val & 0xFF ) = = 0 & & the_voice - > cvcf_curr_volume = = 0 & & the_voice - > vtft_vol_target = = 0
& & the_voice - > dcysusv = = 0x80 & & the_voice - > ip = = 0 )
{
2017-10-14 07:03:19 +02:00
// Patch to avoid some clicking noises with Impulse tracker or other software that sets
// different values to 0 to set noteoff, but here, 0 means no attenuation = full volume.
2017-08-21 04:19:34 +02:00
return ;
}
the_voice - > ifatn = val ;
the_voice - > initial_att = ( ( ( int32_t ) the_voice - > ifatn_attenuation < < 21 ) / 0xFF ) ;
the_voice - > vtft_vol_target = attentable [ the_voice - > ifatn_attenuation ] ;
the_voice - > initial_filter = ( ( ( int32_t ) the_voice - > ifatn_init_filter < < 21 ) / 0xFF ) ;
if ( the_voice - > ifatn_init_filter = = 0xFF )
{
the_voice - > vtft_filter_target = 0xFFFF ;
}
else
{
the_voice - > vtft_filter_target = the_voice - > initial_filter > > 5 ;
}
2017-07-24 12:04:39 +02:00
}
2016-06-26 00:34:39 +02:00
return ;
case 2 :
2017-07-24 12:04:39 +02:00
{
emu8k_voice_t * const the_voice = & emu8k - > voice [ emu8k - > cur_voice ] ;
the_voice - > pefe = val ;
2017-08-21 04:19:34 +02:00
int divider = ( the_voice - > pefe_modenv_filter_height < 0 ) ? 0x80 : 0x7F ;
the_voice - > fixed_modenv_filter_height = ( ( int32_t ) the_voice - > pefe_modenv_filter_height ) * 0x4000 / divider ;
divider = ( the_voice - > pefe_modenv_pitch_height < 0 ) ? 0x80 : 0x7F ;
the_voice - > fixed_modenv_pitch_height = ( ( int32_t ) the_voice - > pefe_modenv_pitch_height ) * 0x4000 / divider ;
2017-07-24 12:04:39 +02:00
}
2016-06-26 00:34:39 +02:00
return ;
case 3 :
2017-07-24 12:04:39 +02:00
{
emu8k_voice_t * const the_voice = & emu8k - > voice [ emu8k - > cur_voice ] ;
the_voice - > fmmod = val ;
2017-08-21 04:19:34 +02:00
int divider = ( the_voice - > fmmod_lfo1_filt_mod < 0 ) ? 0x80 : 0x7F ;
the_voice - > fixed_lfo1_filt_mod = ( ( int32_t ) the_voice - > fmmod_lfo1_filt_mod ) * 0x4000 / divider ;
divider = ( the_voice - > fmmod_lfo1_vibrato < 0 ) ? 0x80 : 0x7F ;
the_voice - > fixed_lfo1_vibrato = ( ( int32_t ) the_voice - > fmmod_lfo1_vibrato ) * 0x4000 / divider ;
2017-07-24 12:04:39 +02:00
}
2016-06-26 00:34:39 +02:00
return ;
case 4 :
2017-07-24 12:04:39 +02:00
{
emu8k_voice_t * const the_voice = & emu8k - > voice [ emu8k - > cur_voice ] ;
the_voice - > tremfrq = val ;
the_voice - > lfo1_speed = lfofreqtospeed [ the_voice - > tremfrq_lfo1_freq ] ;
2017-08-21 04:19:34 +02:00
int divider = ( the_voice - > tremfrq_lfo1_tremolo < 0 ) ? 0x80 : 0x7F ;
the_voice - > fixed_lfo1_tremolo = ( ( int32_t ) the_voice - > tremfrq_lfo1_tremolo ) * 0x4000 / divider ;
2017-07-24 12:04:39 +02:00
}
2016-06-26 00:34:39 +02:00
return ;
case 5 :
2017-07-24 12:04:39 +02:00
{
emu8k_voice_t * const the_voice = & emu8k - > voice [ emu8k - > cur_voice ] ;
the_voice - > fm2frq2 = val ;
the_voice - > lfo2_speed = lfofreqtospeed [ the_voice - > fm2frq2_lfo2_freq ] ;
2017-08-21 04:19:34 +02:00
int divider = ( the_voice - > fm2frq2_lfo2_vibrato < 0 ) ? 0x80 : 0x7F ;
the_voice - > fixed_lfo2_vibrato = ( ( int32_t ) the_voice - > fm2frq2_lfo2_vibrato ) * 0x4000 / divider ;
2017-07-24 12:04:39 +02:00
}
2016-06-26 00:34:39 +02:00
return ;
2017-08-21 04:19:34 +02:00
case 7 : /*ID? I believe that this allows applications to know if the emu is in use by another application */
2016-06-26 00:34:39 +02:00
emu8k - > id = val ;
return ;
}
break ;
2017-09-01 01:01:53 +02:00
case 0xE02 : /* Pointer. also known as BLASTER+0xC02 and EMU+0x802 */
2016-06-26 00:34:39 +02:00
emu8k - > cur_voice = ( val & 31 ) ;
emu8k - > cur_reg = ( ( val > > 5 ) & 7 ) ;
return ;
}
2017-10-16 06:19:18 +02:00
emu8k_log ( " EMU8K WRITE: Unknown register write: %04X-%02X(%d/%d): %04X \n " , addr , ( emu8k - > cur_reg ) < < 5 | emu8k - > cur_voice ,
2017-09-01 01:01:53 +02:00
emu8k - > cur_reg , emu8k - > cur_voice , val ) ;
2017-07-24 12:04:39 +02:00
2016-06-26 00:34:39 +02:00
}
2017-05-05 01:49:42 +02:00
uint8_t emu8k_inb ( uint16_t addr , void * p )
2016-06-26 00:34:39 +02:00
{
2017-07-24 12:04:39 +02:00
/* Reading a single byte is a feature that at least Impulse tracker uses,
2017-09-01 01:01:53 +02:00
* but only on detection code and not for odd addresses . */
2016-06-26 00:34:39 +02:00
if ( addr & 1 )
return emu8k_inw ( addr & ~ 1 , p ) > > 1 ;
return emu8k_inw ( addr , p ) & 0xff ;
}
2017-05-05 01:49:42 +02:00
void emu8k_outb ( uint16_t addr , uint8_t val , void * p )
2016-06-26 00:34:39 +02:00
{
2017-09-01 01:01:53 +02:00
/* TODO: AWE32 docs says that you cannot write in bytes, but if
* an app were to use this implementation , the content of the LS Byte would be lost . */
2016-06-26 00:34:39 +02:00
if ( addr & 1 )
emu8k_outw ( addr & ~ 1 , val < < 8 , p ) ;
else
emu8k_outw ( addr , val , p ) ;
}
2017-08-21 04:19:34 +02:00
/* TODO: This is not a correct emulation, just a workalike implementation. */
2017-07-24 12:04:39 +02:00
void emu8k_work_chorus ( int32_t * inbuf , int32_t * outbuf , emu8k_chorus_eng_t * engine , int count )
2016-06-26 00:34:39 +02:00
{
2017-07-24 12:04:39 +02:00
int pos ;
for ( pos = 0 ; pos < count ; pos + + )
2017-08-21 04:19:34 +02:00
{
double lfo_inter1 = chortable [ engine - > lfo_pos . int_address ] ;
2017-10-14 07:03:19 +02:00
// double lfo_inter2 = chortable[(engine->lfo_pos.int_address+1)&0xFFFF];
2017-08-21 04:19:34 +02:00
2017-09-01 01:01:53 +02:00
double offset_lfo = lfo_inter1 ; //= lfo_inter1 + ((lfo_inter2-lfo_inter1)*engine->lfo_pos.fract_address/65536.0);
2017-08-21 04:19:34 +02:00
offset_lfo * = engine - > lfodepth_multip ;
/* Work left */
double readdouble = ( double ) engine - > write - ( double ) engine - > delay_samples_central - offset_lfo ;
int read = ( int32_t ) floor ( readdouble ) ;
int fraction_part = ( readdouble - ( double ) read ) * 65536.0 ;
int next_value = read + 1 ;
if ( read < 0 )
{
2017-10-14 07:03:19 +02:00
read + = EMU8K_LFOCHORUS_SIZE ;
if ( next_value < 0 ) next_value + = EMU8K_LFOCHORUS_SIZE ;
2017-08-21 04:19:34 +02:00
}
2017-10-14 07:03:19 +02:00
else if ( next_value > = EMU8K_LFOCHORUS_SIZE )
2017-08-21 04:19:34 +02:00
{
2017-10-14 07:03:19 +02:00
next_value - = EMU8K_LFOCHORUS_SIZE ;
if ( read > = EMU8K_LFOCHORUS_SIZE ) read - = EMU8K_LFOCHORUS_SIZE ;
2017-08-21 04:19:34 +02:00
}
int32_t dat1 = engine - > chorus_left_buffer [ read ] ;
int32_t dat2 = engine - > chorus_left_buffer [ next_value ] ;
dat1 + = ( ( dat2 - dat1 ) * fraction_part ) > > 16 ;
engine - > chorus_left_buffer [ engine - > write ] = * inbuf + ( ( dat1 * engine - > feedback ) > > 8 ) ;
/* Work right */
readdouble = ( double ) engine - > write - ( double ) engine - > delay_samples_central - engine - > delay_offset_samples_right - offset_lfo ;
read = ( int32_t ) floor ( readdouble ) ;
next_value = read + 1 ;
if ( read < 0 )
{
2017-10-14 07:03:19 +02:00
read + = EMU8K_LFOCHORUS_SIZE ;
if ( next_value < 0 ) next_value + = EMU8K_LFOCHORUS_SIZE ;
2017-08-21 04:19:34 +02:00
}
2017-10-14 07:03:19 +02:00
else if ( next_value > = EMU8K_LFOCHORUS_SIZE )
2017-08-21 04:19:34 +02:00
{
2017-10-14 07:03:19 +02:00
next_value - = EMU8K_LFOCHORUS_SIZE ;
if ( read > = EMU8K_LFOCHORUS_SIZE ) read - = EMU8K_LFOCHORUS_SIZE ;
2017-08-21 04:19:34 +02:00
}
int32_t dat3 = engine - > chorus_right_buffer [ read ] ;
int32_t dat4 = engine - > chorus_right_buffer [ next_value ] ;
dat3 + = ( ( dat4 - dat3 ) * fraction_part ) > > 16 ;
engine - > chorus_right_buffer [ engine - > write ] = * inbuf + ( ( dat3 * engine - > feedback ) > > 8 ) ;
+ + engine - > write ;
2017-10-14 07:03:19 +02:00
engine - > write % = EMU8K_LFOCHORUS_SIZE ;
2017-08-21 04:19:34 +02:00
engine - > lfo_pos . addr + = engine - > lfo_inc . addr ;
engine - > lfo_pos . int_address & = 0xFFFF ;
( * outbuf + + ) + = dat1 ;
( * outbuf + + ) + = dat3 ;
inbuf + + ;
2017-07-24 12:04:39 +02:00
}
2017-08-21 04:19:34 +02:00
2017-07-24 12:04:39 +02:00
}
2017-05-05 01:49:42 +02:00
2017-08-21 04:19:34 +02:00
int32_t emu8k_reverb_comb_work ( emu8k_reverb_combfilter_t * comb , int32_t in )
2017-07-24 12:04:39 +02:00
{
2017-08-21 04:19:34 +02:00
int32_t bufin ;
/* get echo */
int32_t output = comb - > reflection [ comb - > read_pos ] ;
/* apply lowpass */
comb - > filterstore = ( output * comb - > damp2 ) + ( comb - > filterstore * comb - > damp1 ) ;
/* appply feedback */
bufin = in - ( comb - > filterstore * comb - > feedback ) ;
/* store new value in delayed buffer */
comb - > reflection [ comb - > read_pos ] = bufin ;
if ( + + comb - > read_pos > = comb - > bufsize ) comb - > read_pos = 0 ;
return output * comb - > output_gain ;
2017-07-24 12:04:39 +02:00
}
2017-08-21 04:19:34 +02:00
int32_t emu8k_reverb_diffuser_work ( emu8k_reverb_combfilter_t * comb , int32_t in )
{
int32_t bufout = comb - > reflection [ comb - > read_pos ] ;
/*diffuse*/
int32_t bufin = - in + ( bufout * comb - > feedback ) ;
int32_t output = bufout - ( bufin * comb - > feedback ) ;
/* store new value in delayed buffer */
comb - > reflection [ comb - > read_pos ] = bufin ;
if ( + + comb - > read_pos > = comb - > bufsize ) comb - > read_pos = 0 ;
return output ;
}
int32_t emu8k_reverb_tail_work ( emu8k_reverb_combfilter_t * comb , emu8k_reverb_combfilter_t * allpasses , int32_t in )
{
int32_t output = comb - > reflection [ comb - > read_pos ] ;
2017-09-01 01:01:53 +02:00
/* store new value in delayed buffer */
2017-08-21 04:19:34 +02:00
comb - > reflection [ comb - > read_pos ] = in ;
2017-10-14 07:03:19 +02:00
//output = emu8k_reverb_allpass_work(&allpasses[0],output);
2017-08-21 04:19:34 +02:00
output = emu8k_reverb_diffuser_work ( & allpasses [ 1 ] , output ) ;
output = emu8k_reverb_diffuser_work ( & allpasses [ 2 ] , output ) ;
2017-10-14 07:03:19 +02:00
//output = emu8k_reverb_allpass_work(&allpasses[3],output);
2017-08-21 04:19:34 +02:00
if ( + + comb - > read_pos > = comb - > bufsize ) comb - > read_pos = 0 ;
return output ;
}
int32_t emu8k_reverb_damper_work ( emu8k_reverb_combfilter_t * comb , int32_t in )
{
/* apply lowpass */
comb - > filterstore = ( in * comb - > damp2 ) + ( comb - > filterstore * comb - > damp1 ) ;
return comb - > filterstore ;
}
/* TODO: This is not a correct emulation, just a workalike implementation. */
void emu8k_work_reverb ( int32_t * inbuf , int32_t * outbuf , emu8k_reverb_eng_t * engine , int count )
{
int pos ;
if ( engine - > link_return_type )
{
for ( pos = 0 ; pos < count ; pos + + )
{
int32_t dat1 , dat2 , in , in2 ;
in = emu8k_reverb_damper_work ( & engine - > damper , inbuf [ pos ] ) ;
in2 = ( in * engine - > refl_in_amp ) > > 8 ;
dat2 = emu8k_reverb_comb_work ( & engine - > reflections [ 0 ] , in2 ) ;
dat2 + = emu8k_reverb_comb_work ( & engine - > reflections [ 1 ] , in2 ) ;
dat1 = emu8k_reverb_comb_work ( & engine - > reflections [ 2 ] , in2 ) ;
dat2 + = emu8k_reverb_comb_work ( & engine - > reflections [ 3 ] , in2 ) ;
dat1 + = emu8k_reverb_comb_work ( & engine - > reflections [ 4 ] , in2 ) ;
dat2 + = emu8k_reverb_comb_work ( & engine - > reflections [ 5 ] , in2 ) ;
dat1 + = ( emu8k_reverb_tail_work ( & engine - > tailL , & engine - > allpass [ 0 ] , in + dat1 ) * engine - > link_return_amp ) > > 8 ;
dat2 + = ( emu8k_reverb_tail_work ( & engine - > tailR , & engine - > allpass [ 4 ] , in + dat2 ) * engine - > link_return_amp ) > > 8 ;
( * outbuf + + ) + = ( dat1 * engine - > out_mix ) > > 8 ;
( * outbuf + + ) + = ( dat2 * engine - > out_mix ) > > 8 ;
}
}
else
{
for ( pos = 0 ; pos < count ; pos + + )
{
int32_t dat1 , dat2 , in , in2 ;
in = emu8k_reverb_damper_work ( & engine - > damper , inbuf [ pos ] ) ;
in2 = ( in * engine - > refl_in_amp ) > > 8 ;
dat1 = emu8k_reverb_comb_work ( & engine - > reflections [ 0 ] , in2 ) ;
dat1 + = emu8k_reverb_comb_work ( & engine - > reflections [ 1 ] , in2 ) ;
dat1 + = emu8k_reverb_comb_work ( & engine - > reflections [ 2 ] , in2 ) ;
dat1 + = emu8k_reverb_comb_work ( & engine - > reflections [ 3 ] , in2 ) ;
dat1 + = emu8k_reverb_comb_work ( & engine - > reflections [ 4 ] , in2 ) ;
dat1 + = emu8k_reverb_comb_work ( & engine - > reflections [ 5 ] , in2 ) ;
dat2 = dat1 ;
dat1 + = ( emu8k_reverb_tail_work ( & engine - > tailL , & engine - > allpass [ 0 ] , in + dat1 ) * engine - > link_return_amp ) > > 8 ;
dat2 + = ( emu8k_reverb_tail_work ( & engine - > tailR , & engine - > allpass [ 4 ] , in + dat2 ) * engine - > link_return_amp ) > > 8 ;
( * outbuf + + ) + = ( dat1 * engine - > out_mix ) > > 8 ;
( * outbuf + + ) + = ( dat2 * engine - > out_mix ) > > 8 ;
}
}
}
void emu8k_work_eq ( int32_t * inoutbuf , int count )
2017-07-24 12:04:39 +02:00
{
2017-10-14 07:03:19 +02:00
// TODO: Work EQ over buf
2017-07-24 12:04:39 +02:00
}
2017-09-01 01:01:53 +02:00
int32_t emu8k_vol_slide ( emu8k_slide_t * slide , int32_t target )
{
if ( slide - > last < target )
{
2017-08-21 04:19:34 +02:00
slide - > last + = 0x400 ;
if ( slide - > last > target ) slide - > last = target ;
2017-09-01 01:01:53 +02:00
}
else if ( slide - > last > target )
{
2017-08-21 04:19:34 +02:00
slide - > last - = 0x400 ;
if ( slide - > last < target ) slide - > last = target ;
}
return slide - > last ;
}
2017-10-14 07:03:19 +02:00
//int32_t old_pitch[32]={0};
//int32_t old_cut[32]={0};
//int32_t old_vol[32]={0};
2017-07-24 12:04:39 +02:00
void emu8k_update ( emu8k_t * emu8k )
{
2016-06-26 00:34:39 +02:00
int new_pos = ( sound_pos_global * 44100 ) / 48000 ;
2017-07-24 12:04:39 +02:00
if ( emu8k - > pos > = new_pos )
return ;
int32_t * buf ;
emu8k_voice_t * emu_voice ;
int pos ;
int c ;
2017-08-21 04:19:34 +02:00
/* Clean the buffers since we will accumulate into them. */
2017-07-24 12:04:39 +02:00
buf = & emu8k - > buffer [ emu8k - > pos * 2 ] ;
memset ( buf , 0 , 2 * ( new_pos - emu8k - > pos ) * sizeof ( emu8k - > buffer [ 0 ] ) ) ;
memset ( & emu8k - > chorus_in_buffer [ emu8k - > pos ] , 0 , ( new_pos - emu8k - > pos ) * sizeof ( emu8k - > chorus_in_buffer [ 0 ] ) ) ;
2017-08-21 04:19:34 +02:00
memset ( & emu8k - > reverb_in_buffer [ emu8k - > pos ] , 0 , ( new_pos - emu8k - > pos ) * sizeof ( emu8k - > reverb_in_buffer [ 0 ] ) ) ;
2016-06-26 00:34:39 +02:00
2017-08-21 04:19:34 +02:00
/* Voices section */
2017-07-24 12:04:39 +02:00
for ( c = 0 ; c < 32 ; c + + )
{
emu_voice = & emu8k - > voice [ c ] ;
2016-06-26 00:34:39 +02:00
buf = & emu8k - > buffer [ emu8k - > pos * 2 ] ;
2017-07-24 12:04:39 +02:00
for ( pos = emu8k - > pos ; pos < new_pos ; pos + + )
2016-06-26 00:34:39 +02:00
{
2017-07-24 12:04:39 +02:00
int32_t dat ;
2020-11-16 00:01:21 +01:00
if ( emu_voice - > cvcf_curr_volume )
2017-08-21 04:19:34 +02:00
{
2020-11-16 00:01:21 +01:00
/* Waveform oscillator */
# ifdef RESAMPLER_LINEAR
dat = EMU8K_READ_INTERP_LINEAR ( emu8k , emu_voice - > addr . int_address ,
emu_voice - > addr . fract_address ) ;
# elif defined RESAMPLER_CUBIC
dat = EMU8K_READ_INTERP_CUBIC ( emu8k , emu_voice - > addr . int_address ,
emu_voice - > addr . fract_address ) ;
# endif
/* Filter section */
if ( emu_voice - > filterq_idx | | emu_voice - > cvcf_curr_filt_ctoff ! = 0xFFFF )
{
int cutoff = emu_voice - > cvcf_curr_filt_ctoff > > 8 ;
const int64_t coef0 = filt_coeffs [ emu_voice - > filterq_idx ] [ cutoff ] [ 0 ] ;
const int64_t coef1 = filt_coeffs [ emu_voice - > filterq_idx ] [ cutoff ] [ 1 ] ;
const int64_t coef2 = filt_coeffs [ emu_voice - > filterq_idx ] [ cutoff ] [ 2 ] ;
/* clip at twice the range */
# define ClipBuffer(buf) (buf < -16777216) ? -16777216 : (buf > 16777216) ? 16777216 : buf
# ifdef FILTER_INITIAL
# define NOOP(x) (void)x;
NOOP ( coef1 )
/* Apply expected attenuation. (FILTER_MOOG does it implicitly, but this one doesn't).
* Work in 24 bits . */
dat = ( dat * emu_voice - > filt_att ) > > 8 ;
2017-07-24 12:04:39 +02:00
2020-11-16 00:01:21 +01:00
int64_t vhp = ( ( - emu_voice - > filt_buffer [ 0 ] * coef2 ) > > 24 ) - emu_voice - > filt_buffer [ 1 ] - dat ;
emu_voice - > filt_buffer [ 1 ] + = ( emu_voice - > filt_buffer [ 0 ] * coef0 ) > > 24 ;
emu_voice - > filt_buffer [ 0 ] + = ( vhp * coef0 ) > > 24 ;
dat = ( int32_t ) ( emu_voice - > filt_buffer [ 1 ] > > 8 ) ;
if ( dat > 32767 ) { dat = 32767 ; }
else if ( dat < - 32768 ) { dat = - 32768 ; }
2017-07-24 12:04:39 +02:00
2020-11-16 00:01:21 +01:00
# elif defined FILTER_MOOG
2017-07-24 12:04:39 +02:00
2020-11-16 00:01:21 +01:00
/*move to 24bits*/
dat < < = 8 ;
2017-07-24 12:04:39 +02:00
2020-11-16 00:01:21 +01:00
dat - = ( coef2 * emu_voice - > filt_buffer [ 4 ] ) > > 24 ; /*feedback*/
int64_t t1 = emu_voice - > filt_buffer [ 1 ] ;
emu_voice - > filt_buffer [ 1 ] = ( ( dat + emu_voice - > filt_buffer [ 0 ] ) * coef0 - emu_voice - > filt_buffer [ 1 ] * coef1 ) > > 24 ;
emu_voice - > filt_buffer [ 1 ] = ClipBuffer ( emu_voice - > filt_buffer [ 1 ] ) ;
2017-07-24 12:04:39 +02:00
2020-11-16 00:01:21 +01:00
int64_t t2 = emu_voice - > filt_buffer [ 2 ] ;
emu_voice - > filt_buffer [ 2 ] = ( ( emu_voice - > filt_buffer [ 1 ] + t1 ) * coef0 - emu_voice - > filt_buffer [ 2 ] * coef1 ) > > 24 ;
emu_voice - > filt_buffer [ 2 ] = ClipBuffer ( emu_voice - > filt_buffer [ 2 ] ) ;
2017-07-24 12:04:39 +02:00
2020-11-16 00:01:21 +01:00
int64_t t3 = emu_voice - > filt_buffer [ 3 ] ;
emu_voice - > filt_buffer [ 3 ] = ( ( emu_voice - > filt_buffer [ 2 ] + t2 ) * coef0 - emu_voice - > filt_buffer [ 3 ] * coef1 ) > > 24 ;
emu_voice - > filt_buffer [ 3 ] = ClipBuffer ( emu_voice - > filt_buffer [ 3 ] ) ;
2017-07-24 12:04:39 +02:00
2020-11-16 00:01:21 +01:00
emu_voice - > filt_buffer [ 4 ] = ( ( emu_voice - > filt_buffer [ 3 ] + t3 ) * coef0 - emu_voice - > filt_buffer [ 4 ] * coef1 ) > > 24 ;
emu_voice - > filt_buffer [ 4 ] = ClipBuffer ( emu_voice - > filt_buffer [ 4 ] ) ;
2017-07-24 12:04:39 +02:00
2020-11-16 00:01:21 +01:00
emu_voice - > filt_buffer [ 0 ] = ClipBuffer ( dat ) ;
2017-07-24 12:04:39 +02:00
2020-11-16 00:01:21 +01:00
dat = ( int32_t ) ( emu_voice - > filt_buffer [ 4 ] > > 8 ) ;
if ( dat > 32767 ) { dat = 32767 ; }
else if ( dat < - 32768 ) { dat = - 32768 ; }
2017-07-24 12:04:39 +02:00
2020-11-16 00:01:21 +01:00
# elif defined FILTER_CONSTANT
2017-07-24 12:04:39 +02:00
2020-11-16 00:01:21 +01:00
/* Apply expected attenuation. (FILTER_MOOG does it implicitly, but this one is constant gain).
* Also stay at 24 bits . */
dat = ( dat * emu_voice - > filt_att ) > > 8 ;
2017-07-24 12:04:39 +02:00
2020-11-16 00:01:21 +01:00
emu_voice - > filt_buffer [ 0 ] = ( coef1 * emu_voice - > filt_buffer [ 0 ]
+ coef0 * ( dat +
( ( coef2 * ( emu_voice - > filt_buffer [ 0 ] - emu_voice - > filt_buffer [ 1 ] ) ) > > 24 ) )
) > > 24 ;
emu_voice - > filt_buffer [ 1 ] = ( coef1 * emu_voice - > filt_buffer [ 1 ]
+ coef0 * emu_voice - > filt_buffer [ 0 ] ) > > 24 ;
2017-07-24 12:04:39 +02:00
2020-11-16 00:01:21 +01:00
emu_voice - > filt_buffer [ 0 ] = ClipBuffer ( emu_voice - > filt_buffer [ 0 ] ) ;
emu_voice - > filt_buffer [ 1 ] = ClipBuffer ( emu_voice - > filt_buffer [ 1 ] ) ;
2017-07-24 12:04:39 +02:00
2020-11-16 00:01:21 +01:00
dat = ( int32_t ) ( emu_voice - > filt_buffer [ 1 ] > > 8 ) ;
if ( dat > 32767 ) { dat = 32767 ; }
else if ( dat < - 32768 ) { dat = - 32768 ; }
2017-07-24 12:04:39 +02:00
2020-11-16 00:01:21 +01:00
# endif
2017-07-24 12:04:39 +02:00
2016-06-26 00:34:39 +02:00
}
2020-11-16 00:01:21 +01:00
if ( ( emu8k - > hwcf3 & 0x04 ) & & ! CCCA_DMA_ACTIVE ( emu_voice - > ccca ) )
2017-07-24 12:04:39 +02:00
{
2020-11-16 00:01:21 +01:00
/*volume and pan*/
dat = ( dat * emu_voice - > cvcf_curr_volume ) > > 16 ;
( * buf + + ) + = ( dat * emu_voice - > vol_l ) > > 8 ;
( * buf + + ) + = ( dat * emu_voice - > vol_r ) > > 8 ;
/* Effects section */
if ( emu_voice - > ptrx_revb_send > 0 )
{
emu8k - > reverb_in_buffer [ pos ] + = ( dat * emu_voice - > ptrx_revb_send ) > > 8 ;
}
if ( emu_voice - > csl_chor_send > 0 )
{
emu8k - > chorus_in_buffer [ pos ] + = ( dat * emu_voice - > csl_chor_send ) > > 8 ;
}
2017-07-24 12:04:39 +02:00
}
}
2017-08-21 04:19:34 +02:00
if ( emu_voice - > env_engine_on )
{
2017-07-24 12:04:39 +02:00
int32_t attenuation = emu_voice - > initial_att ;
int32_t filtercut = emu_voice - > initial_filter ;
int32_t currentpitch = emu_voice - > ip ;
/* run envelopes */
emu8k_envelope_t * volenv = & emu_voice - > vol_envelope ;
switch ( volenv - > state )
2016-06-26 00:34:39 +02:00
{
2017-07-24 12:04:39 +02:00
case ENV_DELAY :
volenv - > delay_samples - - ;
if ( volenv - > delay_samples < = 0 )
{
volenv - > state = ENV_ATTACK ;
2017-08-21 04:19:34 +02:00
volenv - > delay_samples = 0 ;
2017-07-24 12:04:39 +02:00
}
attenuation = 0x1FFFFF ;
break ;
2016-06-26 00:34:39 +02:00
case ENV_ATTACK :
2017-07-24 12:04:39 +02:00
/* Attack amount is in linear amplitude */
volenv - > value_amp_hz + = volenv - > attack_amount_amp_hz ;
if ( volenv - > value_amp_hz > = ( 1 < < 21 ) )
2016-06-26 00:34:39 +02:00
{
2017-07-24 12:04:39 +02:00
volenv - > value_amp_hz = 1 < < 21 ;
volenv - > value_db_oct = 0 ;
if ( volenv - > hold_samples )
{
volenv - > state = ENV_HOLD ;
}
else
{
/* RAMP_UP since db value is inverted and it is 0 at this point. */
volenv - > state = ENV_RAMP_UP ;
}
2016-06-26 00:34:39 +02:00
}
2017-07-24 12:04:39 +02:00
attenuation + = env_vol_amplitude_to_db [ volenv - > value_amp_hz > > 5 ] < < 5 ;
2016-06-26 00:34:39 +02:00
break ;
2017-07-24 12:04:39 +02:00
case ENV_HOLD :
volenv - > hold_samples - - ;
if ( volenv - > hold_samples < = 0 )
2016-06-26 00:34:39 +02:00
{
2017-07-24 12:04:39 +02:00
volenv - > state = ENV_RAMP_UP ;
2016-06-26 00:34:39 +02:00
}
2017-07-24 12:04:39 +02:00
attenuation + = volenv - > value_db_oct ;
2016-06-26 00:34:39 +02:00
break ;
2017-07-24 12:04:39 +02:00
case ENV_RAMP_DOWN :
/* Decay/release amount is in fraction of dBs and is always positive */
volenv - > value_db_oct - = volenv - > ramp_amount_db_oct ;
if ( volenv - > value_db_oct < = volenv - > sustain_value_db_oct )
2016-06-26 00:34:39 +02:00
{
2017-07-24 12:04:39 +02:00
volenv - > value_db_oct = volenv - > sustain_value_db_oct ;
volenv - > state = ENV_SUSTAIN ;
2016-06-26 00:34:39 +02:00
}
2017-07-24 12:04:39 +02:00
attenuation + = volenv - > value_db_oct ;
2016-06-26 00:34:39 +02:00
break ;
2017-07-24 12:04:39 +02:00
case ENV_RAMP_UP :
/* Decay/release amount is in fraction of dBs and is always positive */
volenv - > value_db_oct + = volenv - > ramp_amount_db_oct ;
if ( volenv - > value_db_oct > = volenv - > sustain_value_db_oct )
{
volenv - > value_db_oct = volenv - > sustain_value_db_oct ;
volenv - > state = ENV_SUSTAIN ;
}
attenuation + = volenv - > value_db_oct ;
break ;
case ENV_SUSTAIN :
attenuation + = volenv - > value_db_oct ;
break ;
case ENV_STOPPED :
attenuation = 0x1FFFFF ;
break ;
}
2016-06-26 00:34:39 +02:00
2017-07-24 12:04:39 +02:00
emu8k_envelope_t * modenv = & emu_voice - > mod_envelope ;
switch ( modenv - > state )
2016-06-26 00:34:39 +02:00
{
2017-07-24 12:04:39 +02:00
case ENV_DELAY :
modenv - > delay_samples - - ;
if ( modenv - > delay_samples < = 0 )
{
modenv - > state = ENV_ATTACK ;
2017-08-21 04:19:34 +02:00
modenv - > delay_samples = 0 ;
2017-07-24 12:04:39 +02:00
}
break ;
2016-06-26 00:34:39 +02:00
case ENV_ATTACK :
2017-07-24 12:04:39 +02:00
/* Attack amount is in linear amplitude */
modenv - > value_amp_hz + = modenv - > attack_amount_amp_hz ;
modenv - > value_db_oct = env_mod_hertz_to_octave [ modenv - > value_amp_hz > > 5 ] < < 5 ;
if ( modenv - > value_amp_hz > = ( 1 < < 21 ) )
2016-06-26 00:34:39 +02:00
{
2017-07-24 12:04:39 +02:00
modenv - > value_amp_hz = 1 < < 21 ;
modenv - > value_db_oct = 1 < < 21 ;
if ( modenv - > hold_samples )
{
modenv - > state = ENV_HOLD ;
}
else
{
modenv - > state = ENV_RAMP_DOWN ;
}
2016-06-26 00:34:39 +02:00
}
break ;
2017-07-24 12:04:39 +02:00
case ENV_HOLD :
modenv - > hold_samples - - ;
if ( modenv - > hold_samples < = 0 )
{
2017-08-21 04:19:34 +02:00
modenv - > state = ENV_RAMP_UP ;
2017-07-24 12:04:39 +02:00
}
break ;
case ENV_RAMP_DOWN :
/* Decay/release amount is in fraction of octave and is always positive */
modenv - > value_db_oct - = modenv - > ramp_amount_db_oct ;
if ( modenv - > value_db_oct < = modenv - > sustain_value_db_oct )
2016-06-26 00:34:39 +02:00
{
2017-07-24 12:04:39 +02:00
modenv - > value_db_oct = modenv - > sustain_value_db_oct ;
modenv - > state = ENV_SUSTAIN ;
2016-06-26 00:34:39 +02:00
}
break ;
2017-07-24 12:04:39 +02:00
case ENV_RAMP_UP :
/* Decay/release amount is in fraction of octave and is always positive */
modenv - > value_db_oct + = modenv - > ramp_amount_db_oct ;
if ( modenv - > value_db_oct > = modenv - > sustain_value_db_oct )
2016-06-26 00:34:39 +02:00
{
2017-07-24 12:04:39 +02:00
modenv - > value_db_oct = modenv - > sustain_value_db_oct ;
modenv - > state = ENV_SUSTAIN ;
2016-06-26 00:34:39 +02:00
}
break ;
}
2017-07-24 12:04:39 +02:00
/* run lfos */
if ( emu_voice - > lfo1_delay_samples )
{
emu_voice - > lfo1_delay_samples - - ;
}
else
{
emu_voice - > lfo1_count . addr + = emu_voice - > lfo1_speed ;
emu_voice - > lfo1_count . int_address & = 0xFFFF ;
}
if ( emu_voice - > lfo2_delay_samples )
{
emu_voice - > lfo2_delay_samples - - ;
}
else
{
emu_voice - > lfo2_count . addr + = emu_voice - > lfo2_speed ;
emu_voice - > lfo2_count . int_address & = 0xFFFF ;
}
2017-08-21 04:19:34 +02:00
if ( emu_voice - > fixed_modenv_pitch_height )
{
/* modenv range 1<<21, pitch height range 1<<14 desired range 0x1000 (+/-one octave) */
currentpitch + = ( ( modenv - > value_db_oct > > 9 ) * emu_voice - > fixed_modenv_pitch_height ) > > 14 ;
2017-07-24 12:04:39 +02:00
}
2017-08-21 04:19:34 +02:00
if ( emu_voice - > fixed_lfo1_vibrato )
{
/* table range 1<<15, pitch mod range 1<<14 desired range 0x1000 (+/-one octave) */
int32_t lfo1_vibrato = ( lfotable [ emu_voice - > lfo1_count . int_address ] * emu_voice - > fixed_lfo1_vibrato ) > > 17 ;
currentpitch + = lfo1_vibrato ;
2017-07-24 12:04:39 +02:00
}
2017-08-21 04:19:34 +02:00
if ( emu_voice - > fixed_lfo2_vibrato )
{
/* table range 1<<15, pitch mod range 1<<14 desired range 0x1000 (+/-one octave) */
int32_t lfo2_vibrato = ( lfotable [ emu_voice - > lfo2_count . int_address ] * emu_voice - > fixed_lfo2_vibrato ) > > 17 ;
currentpitch + = lfo2_vibrato ;
2017-07-24 12:04:39 +02:00
}
2017-08-21 04:19:34 +02:00
if ( emu_voice - > fixed_modenv_filter_height )
{
/* modenv range 1<<21, pitch height range 1<<14 desired range 0x200000 (+/-full filter range) */
filtercut + = ( ( modenv - > value_db_oct > > 9 ) * emu_voice - > fixed_modenv_filter_height ) > > 5 ;
2017-07-24 12:04:39 +02:00
}
2016-06-26 00:34:39 +02:00
2017-08-21 04:19:34 +02:00
if ( emu_voice - > fixed_lfo1_filt_mod )
{
/* table range 1<<15, pitch mod range 1<<14 desired range 0x100000 (+/-three octaves) */
int32_t lfo1_filtmod = ( lfotable [ emu_voice - > lfo1_count . int_address ] * emu_voice - > fixed_lfo1_filt_mod ) > > 9 ;
filtercut + = lfo1_filtmod ;
2017-07-24 12:04:39 +02:00
}
2017-08-21 04:19:34 +02:00
if ( emu_voice - > fixed_lfo1_tremolo )
{
/* table range 1<<15, pitch mod range 1<<14 desired range 0x40000 (+/-12dBs). */
int32_t lfo1_tremolo = ( lfotable [ emu_voice - > lfo1_count . int_address ] * emu_voice - > fixed_lfo1_tremolo ) > > 11 ;
attenuation + = lfo1_tremolo ;
2017-07-24 12:04:39 +02:00
}
if ( currentpitch > 0xFFFF ) currentpitch = 0xFFFF ;
if ( currentpitch < 0 ) currentpitch = 0 ;
if ( attenuation > 0x1FFFFF ) attenuation = 0x1FFFFF ;
if ( attenuation < 0 ) attenuation = 0 ;
if ( filtercut > 0x1FFFFF ) filtercut = 0x1FFFFF ;
if ( filtercut < 0 ) filtercut = 0 ;
emu_voice - > vtft_vol_target = env_vol_db_to_vol_target [ attenuation > > 5 ] ;
emu_voice - > vtft_filter_target = filtercut > > 5 ;
emu_voice - > ptrx_pit_target = freqtable [ currentpitch ] > > 18 ;
}
/*
I ' ve recopilated these sentences to get an idea of how to loop
- Set its PSST register and its CLS register to zero to cause no loops to occur .
- Setting the Loop Start Offset and the Loop End Offset to the same value , will cause the oscillator to loop the entire memory .
2017-08-21 04:19:34 +02:00
- Setting the PlayPosition greater than the Loop End Offset , will cause the oscillator to play in reverse , back to the Loop End Offset .
It ' s pretty neat , but appears to be uncontrollable ( the rate at which the samples are played in reverse ) .
2017-09-01 01:01:53 +02:00
2017-07-24 12:04:39 +02:00
- Note that due to interpolator offset , the actual loop point is one greater than the start address
- Note that due to interpolator offset , the actual loop point will end at an address one greater than the loop address
- Note that the actual audio location is the point 1 word higher than this value due to interpolation offset
- In programs that use the awe , they generally set the loop address as " loopaddress -1 " to compensate for the above .
( Note : I am already using address + 1 in the interpolators so these things are already as they should . )
*/
emu_voice - > addr . addr + = ( ( uint64_t ) emu_voice - > cpf_curr_pitch ) < < 18 ;
if ( emu_voice - > addr . addr > = emu_voice - > loop_end . addr )
{
emu_voice - > addr . int_address - = ( emu_voice - > loop_end . int_address - emu_voice - > loop_start . int_address ) ;
emu_voice - > addr . int_address & = EMU8K_MEM_ADDRESS_MASK ;
2016-06-26 00:34:39 +02:00
}
2017-07-24 12:04:39 +02:00
/* TODO: How and when are the target and current values updated */
emu_voice - > cpf_curr_pitch = emu_voice - > ptrx_pit_target ;
2017-08-21 04:19:34 +02:00
emu_voice - > cvcf_curr_volume = emu8k_vol_slide ( & emu_voice - > volumeslide , emu_voice - > vtft_vol_target ) ;
2017-07-24 12:04:39 +02:00
emu_voice - > cvcf_curr_filt_ctoff = emu_voice - > vtft_filter_target ;
}
2016-06-26 00:34:39 +02:00
2017-07-24 12:04:39 +02:00
/* Update EMU voice registers. */
2017-08-23 03:50:36 +02:00
emu_voice - > ccca = ( ( ( uint32_t ) emu_voice - > ccca_qcontrol ) < < 24 ) | emu_voice - > addr . int_address ;
2017-07-24 12:04:39 +02:00
emu_voice - > cpf_curr_frac_addr = emu_voice - > addr . fract_address ;
2017-10-14 07:03:19 +02:00
//if ( emu_voice->cvcf_curr_volume != old_vol[c]) {
2020-11-16 00:01:21 +01:00
// pclog("EMUVOL (%d):%d\n", c, emu_voice->cvcf_curr_volume);
2017-10-14 07:03:19 +02:00
// old_vol[c]=emu_voice->cvcf_curr_volume;
//}
2020-11-16 00:01:21 +01:00
//pclog("EMUFILT :%d\n", emu_voice->cvcf_curr_filt_ctoff);
2017-07-24 12:04:39 +02:00
}
2017-09-01 01:01:53 +02:00
2017-07-24 12:04:39 +02:00
buf = & emu8k - > buffer [ emu8k - > pos * 2 ] ;
2017-08-21 04:19:34 +02:00
emu8k_work_reverb ( & emu8k - > reverb_in_buffer [ emu8k - > pos ] , buf , & emu8k - > reverb_engine , new_pos - emu8k - > pos ) ;
2017-07-24 12:04:39 +02:00
emu8k_work_chorus ( & emu8k - > chorus_in_buffer [ emu8k - > pos ] , buf , & emu8k - > chorus_engine , new_pos - emu8k - > pos ) ;
2017-08-23 03:50:36 +02:00
emu8k_work_eq ( buf , new_pos - emu8k - > pos ) ;
2017-09-01 01:01:53 +02:00
2017-10-14 07:03:19 +02:00
// Clip signal
2017-07-24 12:04:39 +02:00
for ( pos = emu8k - > pos ; pos < new_pos ; pos + + )
{
if ( buf [ 0 ] < - 32768 )
buf [ 0 ] = - 32768 ;
else if ( buf [ 0 ] > 32767 )
buf [ 0 ] = 32767 ;
if ( buf [ 1 ] < - 32768 )
buf [ 1 ] = - 32768 ;
else if ( buf [ 1 ] > 32767 )
buf [ 1 ] = 32767 ;
2016-06-26 00:34:39 +02:00
2017-07-24 12:04:39 +02:00
buf + = 2 ;
2016-06-26 00:34:39 +02:00
}
2017-07-24 12:04:39 +02:00
/* Update EMU clock. */
emu8k - > wc + = ( new_pos - emu8k - > pos ) ;
emu8k - > pos = new_pos ;
}
/* onboard_ram in kilobytes */
2017-10-14 07:03:19 +02:00
void emu8k_init ( emu8k_t * emu8k , uint16_t emu_addr , int onboard_ram )
2016-06-26 00:34:39 +02:00
{
2017-07-24 12:04:39 +02:00
uint32_t const BLOCK_SIZE_WORDS = 0x10000 ;
2016-06-26 00:34:39 +02:00
FILE * f ;
int c ;
double out ;
2017-07-24 12:04:39 +02:00
2017-09-25 04:31:20 -04:00
f = rom_fopen ( L " roms/sound/awe32.raw " , L " rb " ) ;
2016-06-26 00:34:39 +02:00
if ( ! f )
2017-07-24 12:04:39 +02:00
fatal ( " AWE32.RAW not found \n " ) ;
2016-06-26 00:34:39 +02:00
2017-07-24 12:04:39 +02:00
emu8k - > rom = malloc ( 1024 * 1024 ) ;
2020-01-15 04:58:28 +01:00
if ( fread ( emu8k - > rom , 1 , 1048576 , f ) ! = 1048576 )
fatal ( " emu8k_init(): Error reading data \n " ) ;
2017-07-24 12:04:39 +02:00
fclose ( f ) ;
/*AWE-DUMP creates ROM images offset by 2 bytes, so if we detect this
then correct it */
if ( emu8k - > rom [ 3 ] = = 0x314d & & emu8k - > rom [ 4 ] = = 0x474d )
{
memmove ( & emu8k - > rom [ 0 ] , & emu8k - > rom [ 1 ] , ( 1024 * 1024 ) - 2 ) ;
emu8k - > rom [ 0x7ffff ] = 0 ;
}
emu8k - > empty = malloc ( 2 * BLOCK_SIZE_WORDS ) ;
memset ( emu8k - > empty , 0 , 2 * BLOCK_SIZE_WORDS ) ;
int j = 0 ;
for ( ; j < 0x8 ; j + + )
{
emu8k - > ram_pointers [ j ] = emu8k - > rom + ( j * BLOCK_SIZE_WORDS ) ;
}
for ( ; j < 0x20 ; j + + )
{
emu8k - > ram_pointers [ j ] = emu8k - > empty ;
}
2016-06-26 00:34:39 +02:00
if ( onboard_ram )
{
2017-09-01 01:01:53 +02:00
/*Clip to 28MB, since that's the max that we can address. */
2017-06-22 23:51:57 +02:00
if ( onboard_ram > 0x7000 ) onboard_ram = 0x7000 ;
2016-06-26 00:34:39 +02:00
emu8k - > ram = malloc ( onboard_ram * 1024 ) ;
2017-07-24 12:04:39 +02:00
memset ( emu8k - > ram , 0 , onboard_ram * 1024 ) ;
const int i_end = onboard_ram > > 7 ;
int i = 0 ;
for ( ; i < i_end ; i + + , j + + )
{
emu8k - > ram_pointers [ j ] = emu8k - > ram + ( i * BLOCK_SIZE_WORDS ) ;
}
emu8k - > ram_end_addr = EMU8K_RAM_MEM_START + ( onboard_ram < < 9 ) ;
2017-06-22 23:51:57 +02:00
}
else
{
emu8k - > ram = 0 ;
emu8k - > ram_end_addr = EMU8K_RAM_MEM_START ;
2016-06-26 00:34:39 +02:00
}
2017-07-24 12:04:39 +02:00
for ( ; j < 0x100 ; j + + )
2016-06-26 00:34:39 +02:00
{
2017-07-24 12:04:39 +02:00
emu8k - > ram_pointers [ j ] = emu8k - > empty ;
2016-06-26 00:34:39 +02:00
}
2017-07-24 12:04:39 +02:00
2017-10-14 07:03:19 +02:00
io_sethandler ( emu_addr , 0x0004 , emu8k_inb , emu8k_inw , NULL , emu8k_outb , emu8k_outw , NULL , emu8k ) ;
io_sethandler ( emu_addr + 0x400 , 0x0004 , emu8k_inb , emu8k_inw , NULL , emu8k_outb , emu8k_outw , NULL , emu8k ) ;
io_sethandler ( emu_addr + 0x800 , 0x0004 , emu8k_inb , emu8k_inw , NULL , emu8k_outb , emu8k_outw , NULL , emu8k ) ;
2016-06-26 00:34:39 +02:00
2017-07-24 12:04:39 +02:00
/*Create frequency table. (Convert initial pitch register value to a linear speed change)
* The input is encoded such as 0xe000 is center note ( no pitch shift )
* and from then on , changing up or down 0x1000 ( 4096 ) increments / decrements an octave .
* Note that this is in reference to the 44.1 Khz clock that the channels play at .
* The 65536 * 65536 is in order to left - shift the 32 bit value to a 64 bit value as a 32.32 fixed point .
*/
2016-06-26 00:34:39 +02:00
for ( c = 0 ; c < 0x10000 ; c + + )
{
freqtable [ c ] = ( uint64_t ) ( exp2 ( ( double ) ( c - 0xe000 ) / 4096.0 ) * 65536.0 * 65536.0 ) ;
}
2017-07-24 12:04:39 +02:00
/* Shortcut: minimum pitch equals stopped. I don't really know if this is true, but it's better
2017-09-01 01:01:53 +02:00
* since some programs set the pitch to 0 for unused channels . */
2017-07-24 12:04:39 +02:00
freqtable [ 0 ] = 0 ;
2016-06-26 00:34:39 +02:00
2017-07-24 12:04:39 +02:00
/* starting at 65535 because it is used for "volume target" register conversion. */
out = 65535.0 ;
2016-06-26 00:34:39 +02:00
for ( c = 0 ; c < 256 ; c + + )
{
2017-07-24 12:04:39 +02:00
attentable [ c ] = ( int32_t ) out ;
2016-06-26 00:34:39 +02:00
out / = sqrt ( 1.09018 ) ; /*0.375 dB steps*/
}
2017-07-24 12:04:39 +02:00
/* Shortcut: max attenuation is silent, not -96dB. */
attentable [ 255 ] = 0 ;
2016-06-26 00:34:39 +02:00
2017-07-24 12:04:39 +02:00
/* Note: these two tables have "db" inverted: 0 dB is max volume, 65535 "db" (-96.32dBFS) is silence.
2017-09-01 01:01:53 +02:00
* Important : Using 65535 as max output value because this is intended to be used with the volume target register ! */
2017-07-24 12:04:39 +02:00
out = 65535.0 ;
for ( c = 0 ; c < 0x10000 ; c + + )
{
2017-10-14 07:03:19 +02:00
//double db = -(c*6.0205999/65535.0)*16.0;
//out = powf(10.f,db/20.f) * 65536.0;
2017-07-24 12:04:39 +02:00
env_vol_db_to_vol_target [ c ] = ( int32_t ) out ;
/* calculated from the 65536th root of 65536 */
out / = 1.00016923970 ;
}
/* Shortcut: max attenuation is silent, not -96dB. */
env_vol_db_to_vol_target [ 0x10000 - 1 ] = 0 ;
2017-09-01 01:01:53 +02:00
/* One more position to accept max value being 65536. */
2017-07-24 12:04:39 +02:00
env_vol_db_to_vol_target [ 0x10000 ] = 0 ;
for ( c = 1 ; c < 0x10000 ; c + + )
{
2017-09-01 01:01:53 +02:00
out = - 680.32142884264 * 20.0 * log10 ( ( ( double ) c ) / 65535.0 ) ;
2017-07-24 12:04:39 +02:00
env_vol_amplitude_to_db [ c ] = ( int32_t ) out ;
}
2017-09-01 01:01:53 +02:00
/*Shortcut: max attenuation is silent, not -96dB.*/
2017-07-24 12:04:39 +02:00
env_vol_amplitude_to_db [ 0 ] = 65535 ;
2017-09-01 01:01:53 +02:00
/* One more position to accept max value being 65536. */
2017-07-24 12:04:39 +02:00
env_vol_amplitude_to_db [ 0x10000 ] = 0 ;
2016-06-26 00:34:39 +02:00
2017-07-24 12:04:39 +02:00
for ( c = 1 ; c < 0x10000 ; c + + )
2016-06-26 00:34:39 +02:00
{
2017-09-01 01:01:53 +02:00
out = log2 ( ( ( ( double ) c ) / 0x10000 ) + 1.0 ) * 65536.0 ;
2017-07-24 12:04:39 +02:00
env_mod_hertz_to_octave [ c ] = ( int32_t ) out ;
}
2017-09-01 01:01:53 +02:00
/*No hertz change, no octave change. */
2017-07-24 12:04:39 +02:00
env_mod_hertz_to_octave [ 0 ] = 0 ;
2017-09-01 01:01:53 +02:00
/* One more position to accept max value being 65536. */
2017-07-24 12:04:39 +02:00
env_mod_hertz_to_octave [ 0x10000 ] = 65536 ;
/* This formula comes from vince vu/judge dredd's awe32p10 and corresponds to what the freebsd/linux AWE32 driver has. */
float millis ;
2017-09-01 01:01:53 +02:00
for ( c = 0 ; c < 128 ; c + + )
{
if ( c = = 0 )
millis = 0 ; /* This means never attack. */
else if ( c < 32 )
millis = 11878.0 / c ;
else
millis = 360 * exp ( ( c - 32 ) / ( 16.0 / log ( 1.0 / 2.0 ) ) ) ;
env_attack_to_samples [ c ] = 44.1 * millis ;
/* This is an alternate formula with linear increments, but probably incorrect:
* millis = ( 256 + 4096 * ( 0x7F - c ) ) */
2016-06-26 00:34:39 +02:00
}
2017-07-24 12:04:39 +02:00
/* The LFOs use a triangular waveform starting at zero and going 1/-1/1/-1.
2017-09-01 01:01:53 +02:00
* This table is stored in signed 16 bits precision , with a period of 65536 samples */
2017-07-24 12:04:39 +02:00
for ( c = 0 ; c < 65536 ; c + + )
2016-06-26 00:34:39 +02:00
{
2017-07-24 12:04:39 +02:00
int d = ( c + 16384 ) & 65535 ;
if ( d > = 32768 )
lfotable [ c ] = 32768 + ( ( 32768 - d ) * 2 ) ;
2016-06-26 00:34:39 +02:00
else
2017-07-24 12:04:39 +02:00
lfotable [ c ] = ( d * 2 ) - 32768 ;
2016-06-26 00:34:39 +02:00
}
2017-07-24 12:04:39 +02:00
/* The 65536 * 65536 is in order to left-shift the 32bit value to a 64bit value as a 32.32 fixed point. */
out = 0.01 ;
2016-06-26 00:34:39 +02:00
for ( c = 0 ; c < 256 ; c + + )
{
2017-07-24 12:04:39 +02:00
lfofreqtospeed [ c ] = ( uint64_t ) ( out * 65536.0 / 44100.0 * 65536.0 * 65536.0 ) ;
out + = 0.042 ;
2016-06-26 00:34:39 +02:00
}
2017-08-21 04:19:34 +02:00
for ( c = 0 ; c < 65536 ; c + + )
{
chortable [ c ] = sin ( c * M_PI / 32768.0 ) ;
}
2017-07-24 12:04:39 +02:00
/* Filter coefficients tables. Note: Values are multiplied by *16777216 to left shift 24 bits. (i.e. 8.24 fixed point) */
int qidx ;
for ( qidx = 0 ; qidx < 16 ; qidx + + )
{
out = 125.0 ; /* Start at 125Hz */
for ( c = 0 ; c < 256 ; c + + )
{
# ifdef FILTER_INITIAL
float w0 = sin ( 2.0 * M_PI * out / 44100.0 ) ;
/* The value 102.5f has been selected a bit randomly. Pretends to reach 0.2929 at w0 = 1.0 */
float q = ( qidx / 102.5f ) * ( 1.0 + 1.0 / w0 ) ;
/* Limit max value. Else it would be 470. */
if ( q > 200 ) q = 200 ;
filt_coeffs [ qidx ] [ c ] [ 0 ] = ( int32_t ) ( w0 * 16777216.0 ) ;
filt_coeffs [ qidx ] [ c ] [ 1 ] = 16777216.0 ;
filt_coeffs [ qidx ] [ c ] [ 2 ] = ( int32_t ) ( ( 1.0f / ( 0.7071f + q ) ) * 16777216.0 ) ;
# elif defined FILTER_MOOG
float w0 = sin ( 2.0 * M_PI * out / 44100.0 ) ;
float q_factor = 1.0f - w0 ;
float p = w0 + 0.8f * w0 * q_factor ;
float f = p + p - 1.0f ;
float resonance = ( 1.0 - pow ( 2.0 , - qidx * 24.0 / 90.0 ) ) * 0.8 ;
float q = resonance * ( 1.0f + 0.5f * q_factor * ( w0 + 5.6f * q_factor * q_factor ) ) ;
filt_coeffs [ qidx ] [ c ] [ 0 ] = ( int32_t ) ( p * 16777216.0 ) ;
filt_coeffs [ qidx ] [ c ] [ 1 ] = ( int32_t ) ( f * 16777216.0 ) ;
filt_coeffs [ qidx ] [ c ] [ 2 ] = ( int32_t ) ( q * 16777216.0 ) ;
# elif defined FILTER_CONSTANT
float q = ( 1.0 - pow ( 2.0 , - qidx * 24.0 / 90.0 ) ) * 0.8 ;
float coef0 = sin ( 2.0 * M_PI * out / 44100.0 ) ;
float coef1 = 1.0 - coef0 ;
float coef2 = q * ( 1.0 + 1.0 / coef1 ) ;
filt_coeffs [ qidx ] [ c ] [ 0 ] = ( int32_t ) ( coef0 * 16777216.0 ) ;
filt_coeffs [ qidx ] [ c ] [ 1 ] = ( int32_t ) ( coef1 * 16777216.0 ) ;
filt_coeffs [ qidx ] [ c ] [ 2 ] = ( int32_t ) ( coef2 * 16777216.0 ) ;
2017-10-14 07:03:19 +02:00
# endif //FILTER_TYPE
2017-07-24 12:04:39 +02:00
/* 42.66 divisions per octave (the doc says quarter seminotes which is 48, but then it would be almost an octave less) */
out * = 1.016378315 ;
2017-09-01 01:01:53 +02:00
/* 42 divisions. This moves the max frequency to 8.5Khz.*/
2017-10-14 07:03:19 +02:00
//out *= 1.0166404394;
2017-09-01 01:01:53 +02:00
/* This is a linear increment method, that corresponds to the NRPN table, but contradicts the EMU8KPRM doc: */
2017-10-14 07:03:19 +02:00
//out = 100.0 + (c+1.0)*31.25; //31.25Hz steps */
2017-07-24 12:04:39 +02:00
}
}
2017-08-21 04:19:34 +02:00
/* NOTE! read_pos and buffer content is implicitly initialized to zero by the sb_t structure memset on sb_awe32_init() */
emu8k - > reverb_engine . reflections [ 0 ] . bufsize = 2 * REV_BUFSIZE_STEP ;
emu8k - > reverb_engine . reflections [ 1 ] . bufsize = 4 * REV_BUFSIZE_STEP ;
emu8k - > reverb_engine . reflections [ 2 ] . bufsize = 8 * REV_BUFSIZE_STEP ;
emu8k - > reverb_engine . reflections [ 3 ] . bufsize = 13 * REV_BUFSIZE_STEP ;
emu8k - > reverb_engine . reflections [ 4 ] . bufsize = 19 * REV_BUFSIZE_STEP ;
emu8k - > reverb_engine . reflections [ 5 ] . bufsize = 26 * REV_BUFSIZE_STEP ;
/*This is a bit random.*/
for ( c = 0 ; c < 4 ; c + + )
{
emu8k - > reverb_engine . allpass [ 3 - c ] . feedback = 0.5 ;
emu8k - > reverb_engine . allpass [ 3 - c ] . bufsize = ( 4 * c ) * REV_BUFSIZE_STEP + 55 ;
emu8k - > reverb_engine . allpass [ 7 - c ] . feedback = 0.5 ;
emu8k - > reverb_engine . allpass [ 7 - c ] . bufsize = ( 4 * c ) * REV_BUFSIZE_STEP + 55 ;
}
2017-09-01 01:01:53 +02:00
2017-07-24 12:04:39 +02:00
2017-09-01 01:01:53 +02:00
/* Cubic Resampling ( 4point cubic spline) */
2017-07-24 12:04:39 +02:00
double const resdouble = 1.0 / ( double ) CUBIC_RESOLUTION ;
2017-09-01 01:01:53 +02:00
for ( c = 0 ; c < CUBIC_RESOLUTION ; c + + )
2017-08-21 04:19:34 +02:00
{
2017-09-01 01:01:53 +02:00
double x = ( double ) c * resdouble ;
2017-07-24 12:04:39 +02:00
/* Cubic resolution is made of four table, but I've put them all in one table to optimize memory access. */
2017-09-01 01:01:53 +02:00
cubic_table [ c * 4 ] = ( - 0.5 * x * x * x + x * x - 0.5 * x ) ;
cubic_table [ c * 4 + 1 ] = ( 1.5 * x * x * x - 2.5 * x * x + 1.0 ) ;
cubic_table [ c * 4 + 2 ] = ( - 1.5 * x * x * x + 2.0 * x * x + 0.5 * x ) ;
cubic_table [ c * 4 + 3 ] = ( 0.5 * x * x * x - 0.5 * x * x ) ;
2017-07-24 12:04:39 +02:00
}
2017-10-14 07:03:19 +02:00
/* Even when the documentation says that this has to be written by applications to initialize the card,
* several applications and drivers ( aweman on windows , linux oss driver . . ) read it to detect an AWE card . */
2016-06-26 00:34:39 +02:00
emu8k - > hwcf1 = 0x59 ;
emu8k - > hwcf2 = 0x20 ;
2017-07-24 12:04:39 +02:00
/* Initial state is muted. 0x04 is unmuted. */
emu8k - > hwcf3 = 0x00 ;
2016-06-26 00:34:39 +02:00
}
void emu8k_close ( emu8k_t * emu8k )
{
free ( emu8k - > rom ) ;
free ( emu8k - > ram ) ;
}
2017-07-24 12:04:39 +02:00