From: <sba...@us...> - 2023-07-19 17:46:24
|
This is an automated email from the git hooks/post-receive-user script. sbaldovi pushed a commit to branch patches-142-currah-uspeech in repository fuse. View the commit online: https://sourceforge.net/p/fuse-emulator/fuse/ci/e09eeec1094fefe8e6f75ad901e9d4a22aa466e9/ commit e09eeec1094fefe8e6f75ad901e9d4a22aa466e9 Author: Stuart Brady <sd...@zu...> AuthorDate: Tue Jul 11 19:31:02 2023 +0200 Initial support for Currah uSpeech peripheral (patches #142) --- fuse.c | 2 + infrastructure/startup_manager.h | 1 + machines/machines_periph.c | 1 + man/fuse.1 | 27 +- memory_pages.c | 8 + menu.c | 1 + menu_data.dat | 1 + periph.h | 1 + peripherals/Makefile.am | 2 + peripherals/sound/sp0256.c | 1525 ++++++++++++++++++++++++++++++++++++++ peripherals/sound/sp0256.h | 40 + peripherals/sound/uspeech.c | 236 ++++++ peripherals/sound/uspeech.h | 46 ++ settings.dat | 3 + settings.pl | 1 + sound.c | 36 + sound.h | 1 + ui/options.dat | 1 + z80/coretest.c | 9 + z80/z80_checks.h | 1 + z80/z80_ops.c | 9 + 21 files changed, 1951 insertions(+), 1 deletion(-) diff --git a/fuse.c b/fuse.c index 9f67a7db..5ae8c6cd 100644 --- a/fuse.c +++ b/fuse.c @@ -86,6 +86,7 @@ #include "peripherals/multiface.h" #include "peripherals/printer.h" #include "peripherals/scld.h" +#include "peripherals/sound/uspeech.h" #include "peripherals/speccyboot.h" #include "peripherals/spectranet.h" #include "peripherals/ttx2000s.h" @@ -344,6 +345,7 @@ run_startup_manager( int *argc, char ***argv ) timer_register_startup(); ula_register_startup(); usource_register_startup(); + uspeech_register_startup(); z80_register_startup(); zxatasp_register_startup(); zxcf_register_startup(); diff --git a/infrastructure/startup_manager.h b/infrastructure/startup_manager.h index aecd1168..5dda9526 100644 --- a/infrastructure/startup_manager.h +++ b/infrastructure/startup_manager.h @@ -76,6 +76,7 @@ typedef enum startup_manager_module { STARTUP_MANAGER_MODULE_TIMER, STARTUP_MANAGER_MODULE_ULA, STARTUP_MANAGER_MODULE_USOURCE, + STARTUP_MANAGER_MODULE_USPEECH, STARTUP_MANAGER_MODULE_Z80, STARTUP_MANAGER_MODULE_ZXATASP, STARTUP_MANAGER_MODULE_ZXCF, diff --git a/machines/machines_periph.c b/machines/machines_periph.c index d84d1bcd..2533c409 100644 --- a/machines/machines_periph.c +++ b/machines/machines_periph.c @@ -205,6 +205,7 @@ base_peripherals_48_128( void ) periph_set_present( PERIPH_TYPE_PLUSD, PERIPH_PRESENT_OPTIONAL ); periph_set_present( PERIPH_TYPE_SPECDRUM, PERIPH_PRESENT_OPTIONAL ); periph_set_present( PERIPH_TYPE_USOURCE, PERIPH_PRESENT_OPTIONAL ); + periph_set_present( PERIPH_TYPE_USPEECH, PERIPH_PRESENT_OPTIONAL ); } /* The set of peripherals available on the 48K and similar machines */ diff --git a/man/fuse.1 b/man/fuse.1 index 952aaeb2..2d627f84 100644 --- a/man/fuse.1 +++ b/man/fuse.1 @@ -1276,6 +1276,9 @@ are not. .br .B \-\-rom\-usource .I file +.br +.B \-\-rom\-uspeech +.I file .RS Specify the file to be used for ROM(s) used for each peripheral. The options respectively refer to @@ -1302,7 +1305,9 @@ the SpeccyBoot ROM the TTX2000S ROM .RI ( ttx2000s.rom ), and the \(mcSource ROM -.RI ( usource.rom ). +.RI ( usource.rom ), +the \(mcSpeech ROM +.RI ( uspeech.rom ). .PP The names in brackets denote the defaults. Note that not all these ROMs are supplied with Fuse \(em you must supply your own copies of those which @@ -1919,6 +1924,13 @@ Emulate a \(mcSource interface. Same as the General Peripherals Options dialog's option. .RE .PP +.B \-\-uspeech +.RS +Emulate a \(mcSpeech interface. Same as the Sound Peripherals Options dialog's +.I "\(mcSpeech" +option. +.RE +.PP .B \-V .br .B \-\-version @@ -2669,6 +2681,11 @@ Sets the relative volume of the Covox from a range of 0\(en100%. .RS Sets the relative volume of the SpecDrum from a range of 0\(en100%. .RE +.PP +.I "uSpeech volume" +.RS +Sets the relative volume of the \(mcSpeech from a range of 0\(en100%. +.RE .RE .PP .I "Options, Peripherals, General..." @@ -2866,6 +2883,14 @@ interface. See the World of Spectrum Infoseek web page at for manuals, software and more. This emulation is only available for the 48k, 128k and TC2048 machines. .RE +.PP +.I "\(mcSpeech" +.RS +If this option is selected, Fuse will emulate a Currah \(mcSpeech interface. +See the World of Spectrum Infoseek web page at +.I http://www.worldofspectrum.org/infoseekid.cgi?id=1000081 +for the manual, software and more. +.RE .RE .PP .I "Options, Peripherals, Disk..." diff --git a/memory_pages.c b/memory_pages.c index cafc4574..c2f51c20 100644 --- a/memory_pages.c +++ b/memory_pages.c @@ -42,6 +42,7 @@ #include "memory_pages.h" #include "module.h" #include "peripherals/disk/opus.h" +#include "peripherals/sound/uspeech.h" #include "peripherals/spectranet.h" #include "peripherals/ttx2000s.h" #include "peripherals/ula.h" @@ -404,6 +405,9 @@ readbyte( libspectrum_word address ) if( ttx2000s_paged && address >= 0x2000 ) return ttx2000s_sram_read( address ); + + if( uspeech_available && address == 0x1000 ) + return uspeech_busy(); } return mapping->page[ address & MEMORY_PAGE_SIZE_MASK ]; @@ -516,6 +520,10 @@ writebyte_internal( libspectrum_word address, libspectrum_byte b ) memory_display_dirty( address, b ); memory[ offset ] = b; + } else if( uspeech_available ) { + if( address == 0x1000 || ( address & 0xfffe ) == 0x3000 ) { + uspeech_write( address, b ); + } } } diff --git a/menu.c b/menu.c index 7168ed3b..77608c16 100644 --- a/menu.c +++ b/menu.c @@ -294,6 +294,7 @@ MENU_CALLBACK_WITH_ACTION( menu_options_selectroms_peripheral_select ) case 10: menu_select_peripheral_roms( "SpeccyBoot", 9, 1 ); return; case 11: menu_select_peripheral_roms( "TTX2000S", 10, 1 ); return; case 12: menu_select_peripheral_roms( "uSource", 11, 1 ); return; + case 13: menu_select_peripheral_roms( "uSpeech", 12, 1 ); return; } diff --git a/menu_data.dat b/menu_data.dat index 42bebe10..55f2f02c 100644 --- a/menu_data.dat +++ b/menu_data.dat @@ -126,6 +126,7 @@ Options/Select ROMs/Peripheral ROMs/_Opus Discovery..., Item,, menu_options_sele Options/Select ROMs/Peripheral ROMs/Specc_yBoot..., Item,, menu_options_selectroms_peripheral_select,, 10 Options/Select ROMs/Peripheral ROMs/TT_X2000S..., Item,, menu_options_selectroms_peripheral_select,, 11 Options/Select ROMs/Peripheral ROMs/_uSource..., Item,, menu_options_selectroms_peripheral_select,, 12 +Options/Select ROMs/Peripheral ROMs/_uSpeech..., Item,, menu_options_selectroms_peripheral_select,, 13 Options/_Filter..., Item,,, menu_filter_detail diff --git a/periph.h b/periph.h index 8f82fa28..3aa7327a 100644 --- a/periph.h +++ b/periph.h @@ -76,6 +76,7 @@ typedef enum periph_type { PERIPH_TYPE_ULA_FULL_DECODE,/* Standard ULA responding only to 0xfe */ PERIPH_TYPE_UPD765, /* +3 uPD765 FDC */ PERIPH_TYPE_USOURCE, /* Currah uSource interface */ + PERIPH_TYPE_USPEECH, /* Currah uSpeech interface */ PERIPH_TYPE_ZXATASP, /* ZXATASP IDE interface */ PERIPH_TYPE_ZXCF, /* ZXCF IDE interface */ PERIPH_TYPE_ZXMMC, /* ZXMMC interface */ diff --git a/peripherals/Makefile.am b/peripherals/Makefile.am index cf9e1325..336123a3 100644 --- a/peripherals/Makefile.am +++ b/peripherals/Makefile.am @@ -38,7 +38,9 @@ fuse_SOURCES += \ peripherals/sound/covox.c \ peripherals/sound/fuller.c \ peripherals/sound/melodik.c \ + peripherals/sound/sp0256.c \ peripherals/sound/specdrum.c \ + peripherals/sound/uspeech.c \ peripherals/disk/beta.c \ peripherals/disk/crc.c \ peripherals/disk/didaktik.c \ diff --git a/peripherals/sound/sp0256.c b/peripherals/sound/sp0256.c new file mode 100644 index 00000000..8ad75edd --- /dev/null +++ b/peripherals/sound/sp0256.c @@ -0,0 +1,1525 @@ +/* sp0256.c: SP0256 speech synthesis emulation + Copyright (c) 1998-2000 Jospeh Zbicaik, 2007-2015 Stuart Brady + + $Id$ + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + Author contact information: + + Philip: phi...@sh... + + Stuart: stu...@gm... + +*/ + +/* Based on the SP0256 emulation in jzIntv (an Intellivision emulator), + * where it is used for Intellivoice emulation. + * + * For information about the SP0256: + * * http://en.wikipedia.org/wiki/General_Instrument_SP0256-AL2 + * * http://www.cpcwiki.eu/index.php/SP0256 + */ + +#include "config.h" + +#include <stdio.h> + +#include "libspectrum.h" + +#include <assert.h> + +// from jzintv-config.h +#include <stdarg.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> + +#include "compat.h" +#include "event.h" +#include "machine.h" +#include "sp0256.h" +#include "sound.h" + +/* + * SP0256-based peripherals for the ZX Spectrum seem limited to using the + * interface to the "Address LoaD" (ALD) mechanism on the SP0256, and the + * "Load ReQuest" (LRQ) state in bit 15. When LRQ is 1, the SP0256 is + * ready to receive a new command. A new command address may then be + * written to using the Address LoaD mechanism to trigger the playback of + * a sound. Note that command address register is actually a 1-deep FIFO, + * and so LRQ will go to 1 before the SP0256 is finished speaking. + * + * The exact format of the SP0256 speech data, as well as the overall + * system view from the SP0256's perspective is documented elsewhere. + */ + +#define dfprintf(x) +#define debug_printf(x) +#define jzp_printf(x, ...) + +#define HIGH_QUALITY +#define SCBUF_SIZE (4096) /* Must be power of 2 */ +#define SCBUF_MASK (SCBUF_SIZE - 1) +#define PER_PAUSE (64) /* Equiv timing period for pauses. */ +#define PER_NOISE (64) /* Equiv timing period for noise. */ + +#define FIFO_ADDR (0x1800 << 3) /* SP0256 address of speech FIFO. */ + +uint64_t sp0256_tstates; +uint64_t sp0256_now; + +typedef struct lpc12_t +{ + int rpt, cnt; /* Repeat counter, Period down-counter. */ + uint32_t per, rng; /* Period, Amplitude, Random Number Generator */ + int amp; + int16_t f_coef[6]; /* F0 through F5. */ + int16_t b_coef[6]; /* B0 through B5. */ + int16_t z_data[6][2]; /* Time-delay data for the */ + /* filter stages. */ + uint8_t r[16]; /* The encoded register set. */ + int interp; +} lpc12_t; + + +typedef struct sp0256_t +{ + int silent; /* Flag: SP0256 is silent. */ + + int16_t *scratch; /* Scratch buffer for audio. */ + uint32_t sc_head; /* Head/Tail pointer into scratch circular buf */ + uint32_t sc_tail; /* Head/Tail pointer into scratch circular buf */ + uint64_t sound_current; + + lpc12_t filt; /* 12-pole filter */ + int lrq; /* Load ReQuest. == 0 if we can accept a load */ + int ald; /* Address LoaD. < 0 if no command pending. */ + int pc; /* Microcontroller's PC value. */ + int stack; /* Microcontroller's PC stack. */ + int fifo_sel; /* True when executing from FIFO. */ + int halted; /* True when CPU is halted. */ + uint32_t mode; /* Mode register. */ + uint32_t page; /* Page set by SETPAGE */ + + uint32_t fifo_head; /* FIFO head pointer (where new data goes). */ + uint32_t fifo_tail; /* FIFO tail pointer (where data comes from). */ + uint32_t fifo_bitp; /* FIFO bit-pointer (for partial decles). */ + uint16_t fifo[64]; /* The 64-decle FIFO. */ + + const uint8_t *rom[16]; /* 4K ROM pages. */ +} sp0256_t; + + +/* ======================================================================== */ +/* sp0256_run -- Where the magic happens. Generate voice data for */ +/* our good friend, the SP0256. */ +/* ======================================================================== */ +static uint32_t sp0256_run(sp0256_t *s, uint32_t len); + +/* ======================================================================== */ +/* sp0256_read_lrq -- Handle reads of the SP0256 LRQ line. */ +/* ======================================================================== */ +static uint32_t sp0256_read_lrq(sp0256_t *s); + +/* ======================================================================== */ +/* sp0256_write_ald -- Handle writes to the SP0256's Address Load FIFO. */ +/* ======================================================================== */ +static void sp0256_write_ald(sp0256_t *s, uint32_t data); + +/* ======================================================================== */ +/* sp0256_do_init -- Makes a new SP0256. */ +/* ======================================================================== */ +static int sp0256_do_init( sp0256_t *s ); + +static int factor = 358; + +/* ======================================================================== */ +/* Internal function prototypes. */ +/* ======================================================================== */ +static inline int16_t limit (int16_t s); +static inline uint32_t bitrev(uint32_t val); +static int lpc12_update(lpc12_t *f, int, int16_t *, uint32_t *); +static void lpc12_regdec(lpc12_t *f); +static uint32_t sp0256_getb(sp0256_t *s, int len); +static void sp0256_micro(sp0256_t *s); + +/* ======================================================================== */ +/* qtbl -- Coefficient Quantization Table. This comes from a */ +/* SP0250 data sheet, and should be correct for SP0256. */ +/* ======================================================================== */ +static const int16_t qtbl[128] = { + 0, 9, 17, 25, 33, 41, 49, 57, + 65, 73, 81, 89, 97, 105, 113, 121, + 129, 137, 145, 153, 161, 169, 177, 185, + 193, 201, 209, 217, 225, 233, 241, 249, + 257, 265, 273, 281, 289, 297, 301, 305, + 309, 313, 317, 321, 325, 329, 333, 337, + 341, 345, 349, 353, 357, 361, 365, 369, + 373, 377, 381, 385, 389, 393, 397, 401, + 405, 409, 413, 417, 421, 425, 427, 429, + 431, 433, 435, 437, 439, 441, 443, 445, + 447, 449, 451, 453, 455, 457, 459, 461, + 463, 465, 467, 469, 471, 473, 475, 477, + 479, 481, 482, 483, 484, 485, 486, 487, + 488, 489, 490, 491, 492, 493, 494, 495, + 496, 497, 498, 499, 500, 501, 502, 503, + 504, 505, 506, 507, 508, 509, 510, 511 +}; + +/* ======================================================================== */ +/* limit -- Limiter function for digital sample output. */ +/* ======================================================================== */ +static inline int16_t +limit(int16_t s) +{ +#ifdef HIGH_QUALITY /* Higher quality than the original, but who cares? */ + if (s > 8191) return 8191; + if (s < -8192) return -8192; +#else + if (s > 127) return 127; + if (s < -128) return -128; +#endif + return s; +} + +/* ======================================================================== */ +/* amp_decode -- Decode amplitude register */ +/* ======================================================================== */ +static int +amp_decode(uint8_t a) +{ + /* -------------------------------------------------------------------- */ + /* Amplitude has 3 bits of exponent and 5 bits of mantissa. This */ + /* contradicts USP 4,296,269 but matches the SP0250 Apps Manual. */ + /* -------------------------------------------------------------------- */ + int expn = (a & 0xe0) >> 5; + int mant = (a & 0x1f); + int ampl = mant << expn; + + /* -------------------------------------------------------------------- */ + /* Careful reading of USP 4,296,279, around line 60 in column 14 on */ + /* page 16 of the scan suggests the LSB might be held and injected */ + /* into the output while the exponent gets counted down, although */ + /* this seems dubious. */ + /* -------------------------------------------------------------------- */ +#if 0 + if (mant & 1) + ampl |= (1 << expn) - 1; +#endif + + return ampl; +} + +/* ======================================================================== */ +/* lpc12_update -- Update the 12-pole filter, outputting samples. */ +/* ======================================================================== */ +static int +lpc12_update(lpc12_t *f, int num_samp, int16_t *out, uint32_t *optr) +{ + int i, j; + int16_t samp; + int do_int, bit; + int oidx = *optr; + + /* -------------------------------------------------------------------- */ + /* Iterate up to the desired number of samples. We actually may */ + /* break out early if our repeat count expires. */ + /* -------------------------------------------------------------------- */ + for (i = 0; i < num_samp; i++) { + /* ---------------------------------------------------------------- */ + /* Generate a series of periodic impulses, or random noise. */ + /* ---------------------------------------------------------------- */ + do_int = 0; + samp = 0; + bit = f->rng & 1; + f->rng = (f->rng >> 1) ^ (bit ? 0x4001 : 0); + + if (--f->cnt <= 0) { + if (f->rpt-- <= 0) { /* Stop if we expire the repeat counter */ + f->cnt = f->rpt = 0; + break; + } + + f->cnt = f->per ? f->per : PER_NOISE; + samp = f->amp; + do_int = f->interp; + } + + if (!f->per) + samp = bit ? -f->amp : f->amp; + + /* ---------------------------------------------------------------- */ + /* If we need to, process the interpolation registers. */ + /* ---------------------------------------------------------------- */ + if (do_int) { + f->r[0] += f->r[14]; + f->r[1] += f->r[15]; + + f->amp = amp_decode(f->r[0]); + f->per = f->r[1]; + + do_int = 0; + } + + /* ---------------------------------------------------------------- */ + /* Each 2nd order stage looks like one of these. The App. Manual */ + /* gives the first form, the patent gives the second form. */ + /* They're equivalent except for time delay. I implement the */ + /* first form. (Note: 1/Z == 1 unit of time delay.) */ + /* */ + /* ---->(+)-------->(+)----------+-------> */ + /* ^ ^ | */ + /* | | | */ + /* | | | */ + /* [B] [2*F] | */ + /* ^ ^ | */ + /* | | | */ + /* | | | */ + /* +---[1/Z]<--+---[1/Z]<--+ */ + /* */ + /* */ + /* +---[2*F]<---+ */ + /* | | */ + /* | | */ + /* v | */ + /* ---->(+)-->[1/Z]-->+-->[1/Z]---+------> */ + /* ^ | */ + /* | | */ + /* | | */ + /* +-----------[B]<---------+ */ + /* */ + /* ---------------------------------------------------------------- */ + for (j = 0; j < 6; j++) { + samp += (((int)f->b_coef[j] * (int)f->z_data[j][1]) >> 9); + samp += (((int)f->f_coef[j] * (int)f->z_data[j][0]) >> 8); + + f->z_data[j][1] = f->z_data[j][0]; + f->z_data[j][0] = samp; + } + +#ifdef HIGH_QUALITY /* Higher quality than the original, but who cares? */ + out[oidx++ & SCBUF_MASK] = limit(samp) << 2; +#else + out[oidx++ & SCBUF_MASK] = (limit(samp >> 4) << 8); +#endif + } + + *optr = oidx; + + return i; +} + +/*static int stage_map[6] = { 4, 2, 0, 5, 3, 1 };*/ +/*static int stage_map[6] = { 3, 0, 4, 1, 5, 2 };*/ +/*static int stage_map[6] = { 3, 0, 1, 4, 2, 5 };*/ +static const int stage_map[6] = { 0, 1, 2, 3, 4, 5 }; + +/* ======================================================================== */ +/* lpc12_regdec -- Decode the register set in the filter bank. */ +/* ======================================================================== */ +static void +lpc12_regdec(lpc12_t *f) +{ + int i; + + /* -------------------------------------------------------------------- */ + /* Decode the Amplitude and Period registers. Force cnt to 0 to get */ + /* the initial impulse. (Redundant?) */ + /* -------------------------------------------------------------------- */ + f->amp = amp_decode(f->r[0]); + f->cnt = 0; + f->per = f->r[1]; + + /* -------------------------------------------------------------------- */ + /* Decode the filter coefficients from the quant table. */ + /* -------------------------------------------------------------------- */ + for (i = 0; i < 6; i++) { + #define IQ(x) (((x) & 0x80) ? qtbl[0x7f & -(x)] : -qtbl[(x)]) + + f->b_coef[stage_map[i]] = IQ(f->r[2 + 2*i]); + f->f_coef[stage_map[i]] = IQ(f->r[3 + 2*i]); + } + + /* -------------------------------------------------------------------- */ + /* Set the Interp flag based on whether we have interpolation parms */ + /* -------------------------------------------------------------------- */ + f->interp = f->r[14] || f->r[15]; + + return; +} + +/* ======================================================================== */ +/* sp0256_datafmt -- Data format table for the SP0256's microsequencer */ +/* */ +/* len 4 bits Length of field to extract */ +/* lshift 4 bits Left-shift amount on field */ +/* param 4 bits Parameter number being updated */ +/* delta 1 bit This is a delta-update. (Implies sign-extend) */ +/* field 1 bit This is a field replace. */ +/* clr5 1 bit Clear F5, B5. */ +/* clrall 1 bit Clear all before doing this update */ +/* ======================================================================== */ + +#define CR(l,s,p,d,f,c5,ca) \ + ( \ + (((l) & 15) << 0) | \ + (((s) & 15) << 4) | \ + (((p) & 15) << 8) | \ + (((d) & 1) << 12) | \ + (((f) & 1) << 13) | \ + (((c5) & 1) << 14) | \ + (((ca) & 1) << 15) \ + ) + +#define CR_DELTA CR(0,0,0,1,0,0,0) +#define CR_FIELD CR(0,0,0,0,1,0,0) +#define CR_CLR5 CR(0,0,0,0,0,1,0) +#define CR_CLRL CR(0,0,0,0,0,0,1) +#define CR_LEN(x) ((x) & 15) +#define CR_SHF(x) (((x) >> 4) & 15) +#define CR_PRM(x) (((x) >> 8) & 15) + +enum { AM = 0, PR, B0, F0, B1, F1, B2, F2, B3, F3, B4, F4, B5, F5, IA, IP }; + +static const uint16_t sp0256_datafmt[] = { + /* -------------------------------------------------------------------- */ + /* OPCODE 1111: PAUSE */ + /* -------------------------------------------------------------------- */ + /* 0 */ CR( 0, 0, 0, 0, 0, 0, 0), /* Clear all */ + + /* -------------------------------------------------------------------- */ + /* Opcode 0001: LOADALL */ + /* -------------------------------------------------------------------- */ + /* All modes */ + /* 1 */ CR( 8, 0, AM, 0, 0, 0, 0), /* Amplitude */ + /* 2 */ CR( 8, 0, PR, 0, 0, 0, 0), /* Period */ + /* 3 */ CR( 8, 0, B0, 0, 0, 0, 0), /* B0 */ + /* 4 */ CR( 8, 0, F0, 0, 0, 0, 0), /* F0 */ + /* 5 */ CR( 8, 0, B1, 0, 0, 0, 0), /* B1 */ + /* 6 */ CR( 8, 0, F1, 0, 0, 0, 0), /* F1 */ + /* 7 */ CR( 8, 0, B2, 0, 0, 0, 0), /* B2 */ + /* 8 */ CR( 8, 0, F2, 0, 0, 0, 0), /* F2 */ + /* 9 */ CR( 8, 0, B3, 0, 0, 0, 0), /* B3 */ + /* 10 */ CR( 8, 0, F3, 0, 0, 0, 0), /* F3 */ + /* 11 */ CR( 8, 0, B4, 0, 0, 0, 0), /* B4 */ + /* 12 */ CR( 8, 0, F4, 0, 0, 0, 0), /* F4 */ + /* 13 */ CR( 8, 0, B5, 0, 0, 0, 0), /* B5 */ + /* 14 */ CR( 8, 0, F5, 0, 0, 0, 0), /* F5 */ + /* Mode 01 and 11 only */ + /* 15 */ CR( 8, 0, IA, 0, 0, 0, 0), /* Amp Interp */ + /* 16 */ CR( 8, 0, IP, 0, 0, 0, 0), /* Pit Interp */ + + /* -------------------------------------------------------------------- */ + /* Opcode 0100: LOAD_4 */ + /* -------------------------------------------------------------------- */ + /* Mode 00 and 01 */ + /* 17 */ CR( 6, 2, AM, 0, 0, 0, 1), /* Amplitude */ + /* 18 */ CR( 8, 0, PR, 0, 0, 0, 0), /* Period */ + /* 19 */ CR( 4, 3, B3, 0, 0, 0, 0), /* B3 (S=0) */ + /* 20 */ CR( 6, 2, F3, 0, 0, 0, 0), /* F3 */ + /* 21 */ CR( 7, 1, B4, 0, 0, 0, 0), /* B4 */ + /* 22 */ CR( 6, 2, F4, 0, 0, 0, 0), /* F4 */ + /* Mode 01 only */ + /* 23 */ CR( 8, 0, B5, 0, 0, 0, 0), /* B5 */ + /* 24 */ CR( 8, 0, F5, 0, 0, 0, 0), /* F5 */ + + /* Mode 10 and 11 */ + /* 25 */ CR( 6, 2, AM, 0, 0, 0, 1), /* Amplitude */ + /* 26 */ CR( 8, 0, PR, 0, 0, 0, 0), /* Period */ + /* 27 */ CR( 6, 1, B3, 0, 0, 0, 0), /* B3 (S=0) */ + /* 28 */ CR( 7, 1, F3, 0, 0, 0, 0), /* F3 */ + /* 29 */ CR( 8, 0, B4, 0, 0, 0, 0), /* B4 */ + /* 30 */ CR( 8, 0, F4, 0, 0, 0, 0), /* F4 */ + /* Mode 11 only */ + /* 31 */ CR( 8, 0, B5, 0, 0, 0, 0), /* B5 */ + /* 32 */ CR( 8, 0, F5, 0, 0, 0, 0), /* F5 */ + + /* -------------------------------------------------------------------- */ + /* Opcode 0110: SETMSB_6 */ + /* -------------------------------------------------------------------- */ + /* Mode 00 only */ + /* 33 */ CR( 0, 0, 0, 0, 0, 0, 0), + /* Mode 00 and 01 */ + /* 34 */ CR( 6, 2, AM, 0, 0, 0, 0), /* Amplitude */ + /* 35 */ CR( 6, 2, F3, 0, 1, 0, 0), /* F3 (5 MSBs) */ + /* 36 */ CR( 6, 2, F4, 0, 1, 0, 0), /* F4 (5 MSBs) */ + /* Mode 01 only */ + /* 37 */ CR( 8, 0, F5, 0, 1, 0, 0), /* F5 (5 MSBs) */ + + /* Mode 10 only */ + /* 38 */ CR( 0, 0, 0, 0, 0, 0, 0), + /* Mode 10 and 11 */ + /* 39 */ CR( 6, 2, AM, 0, 0, 0, 0), /* Amplitude */ + /* 40 */ CR( 7, 1, F3, 0, 1, 0, 0), /* F3 (6 MSBs) */ + /* 41 */ CR( 8, 0, F4, 0, 1, 0, 0), /* F4 (6 MSBs) */ + /* Mode 11 only */ + /* 42 */ CR( 8, 0, F5, 0, 1, 0, 0), /* F5 (6 MSBs) */ + + /* 43 */ 0, + /* 44 */ 0, + + /* -------------------------------------------------------------------- */ + /* Opcode 1001: DELTA_9 */ + /* -------------------------------------------------------------------- */ + /* Mode 00 and 01 */ + /* 45 */ CR( 4, 2, AM, 1, 0, 0, 0), /* Amplitude */ + /* 46 */ CR( 5, 0, PR, 1, 0, 0, 0), /* Period */ + /* 47 */ CR( 3, 4, B0, 1, 0, 0, 0), /* B0 4 MSBs */ + /* 48 */ CR( 3, 3, F0, 1, 0, 0, 0), /* F0 5 MSBs */ + /* 49 */ CR( 3, 4, B1, 1, 0, 0, 0), /* B1 4 MSBs */ + /* 50 */ CR( 3, 3, F1, 1, 0, 0, 0), /* F1 5 MSBs */ + /* 51 */ CR( 3, 4, B2, 1, 0, 0, 0), /* B2 4 MSBs */ + /* 52 */ CR( 3, 3, F2, 1, 0, 0, 0), /* F2 5 MSBs */ + /* 53 */ CR( 3, 3, B3, 1, 0, 0, 0), /* B3 5 MSBs */ + /* 54 */ CR( 4, 2, F3, 1, 0, 0, 0), /* F3 6 MSBs */ + /* 55 */ CR( 4, 1, B4, 1, 0, 0, 0), /* B4 7 MSBs */ + /* 56 */ CR( 4, 2, F4, 1, 0, 0, 0), /* F4 6 MSBs */ + /* Mode 01 only */ + /* 57 */ CR( 5, 0, B5, 1, 0, 0, 0), /* B5 8 MSBs */ + /* 58 */ CR( 5, 0, F5, 1, 0, 0, 0), /* F5 8 MSBs */ + + /* Mode 10 and 11 */ + /* 59 */ CR( 4, 2, AM, 1, 0, 0, 0), /* Amplitude */ + /* 60 */ CR( 5, 0, PR, 1, 0, 0, 0), /* Period */ + /* 61 */ CR( 4, 1, B0, 1, 0, 0, 0), /* B0 7 MSBs */ + /* 62 */ CR( 4, 2, F0, 1, 0, 0, 0), /* F0 6 MSBs */ + /* 63 */ CR( 4, 1, B1, 1, 0, 0, 0), /* B1 7 MSBs */ + /* 64 */ CR( 4, 2, F1, 1, 0, 0, 0), /* F1 6 MSBs */ + /* 65 */ CR( 4, 1, B2, 1, 0, 0, 0), /* B2 7 MSBs */ + /* 66 */ CR( 4, 2, F2, 1, 0, 0, 0), /* F2 6 MSBs */ + /* 67 */ CR( 4, 1, B3, 1, 0, 0, 0), /* B3 7 MSBs */ + /* 68 */ CR( 5, 1, F3, 1, 0, 0, 0), /* F3 7 MSBs */ + /* 69 */ CR( 5, 0, B4, 1, 0, 0, 0), /* B4 8 MSBs */ + /* 70 */ CR( 5, 0, F4, 1, 0, 0, 0), /* F4 8 MSBs */ + /* Mode 11 only */ + /* 71 */ CR( 5, 0, B5, 1, 0, 0, 0), /* B5 8 MSBs */ + /* 72 */ CR( 5, 0, F5, 1, 0, 0, 0), /* F5 8 MSBs */ + + /* -------------------------------------------------------------------- */ + /* Opcode 1010: SETMSB_A */ + /* -------------------------------------------------------------------- */ + /* Mode 00 only */ + /* 73 */ CR( 0, 0, 0, 0, 0, 0, 0), + /* Mode 00 and 01 */ + /* 74 */ CR( 6, 2, AM, 0, 0, 0, 0), /* Amplitude */ + /* 75 */ CR( 5, 3, F0, 0, 1, 0, 0), /* F0 (5 MSBs) */ + /* 76 */ CR( 5, 3, F1, 0, 1, 0, 0), /* F1 (5 MSBs) */ + /* 77 */ CR( 5, 3, F2, 0, 1, 0, 0), /* F2 (5 MSBs) */ + + /* Mode 10 only */ + /* 78 */ CR( 0, 0, 0, 0, 0, 0, 0), + /* Mode 10 and 11 */ + /* 79 */ CR( 6, 2, AM, 0, 0, 0, 0), /* Amplitude */ + /* 80 */ CR( 6, 2, F0, 0, 1, 0, 0), /* F0 (6 MSBs) */ + /* 81 */ CR( 6, 2, F1, 0, 1, 0, 0), /* F1 (6 MSBs) */ + /* 82 */ CR( 6, 2, F2, 0, 1, 0, 0), /* F2 (6 MSBs) */ + + /* -------------------------------------------------------------------- */ + /* Opcode 0010: LOAD_2 Mode 00 and 10 */ + /* Opcode 1100: LOAD_C Mode 00 and 10 */ + /* -------------------------------------------------------------------- */ + /* LOAD_2, LOAD_C Mode 00 */ + /* 83 */ CR( 6, 2, AM, 0, 0, 0, 0), /* Amplitude */ + /* 84 */ CR( 8, 0, PR, 0, 0, 0, 0), /* Period */ + /* 85 */ CR( 3, 4, B0, 0, 0, 0, 0), /* B0 (S=0) */ + /* 86 */ CR( 5, 3, F0, 0, 0, 0, 0), /* F0 */ + /* 87 */ CR( 3, 4, B1, 0, 0, 0, 0), /* B1 (S=0) */ + /* 88 */ CR( 5, 3, F1, 0, 0, 0, 0), /* F1 */ + /* 89 */ CR( 3, 4, B2, 0, 0, 0, 0), /* B2 (S=0) */ + /* 90 */ CR( 5, 3, F2, 0, 0, 0, 0), /* F2 */ + /* 91 */ CR( 4, 3, B3, 0, 0, 0, 0), /* B3 (S=0) */ + /* 92 */ CR( 6, 2, F3, 0, 0, 0, 0), /* F3 */ + /* 93 */ CR( 7, 1, B4, 0, 0, 0, 0), /* B4 */ + /* 94 */ CR( 6, 2, F4, 0, 0, 0, 0), /* F4 */ + /* LOAD_2 only */ + /* 95 */ CR( 5, 0, IA, 0, 0, 0, 0), /* Ampl. Intr. */ + /* 96 */ CR( 5, 0, IP, 0, 0, 0, 0), /* Per. Intr. */ + + /* LOAD_2, LOAD_C Mode 10 */ + /* 97 */ CR( 6, 2, AM, 0, 0, 0, 0), /* Amplitude */ + /* 98 */ CR( 8, 0, PR, 0, 0, 0, 0), /* Period */ + /* 99 */ CR( 6, 1, B0, 0, 0, 0, 0), /* B0 (S=0) */ + /* 100 */ CR( 6, 2, F0, 0, 0, 0, 0), /* F0 */ + /* 101 */ CR( 6, 1, B1, 0, 0, 0, 0), /* B1 (S=0) */ + /* 102 */ CR( 6, 2, F1, 0, 0, 0, 0), /* F1 */ + /* 103 */ CR( 6, 1, B2, 0, 0, 0, 0), /* B2 (S=0) */ + /* 104 */ CR( 6, 2, F2, 0, 0, 0, 0), /* F2 */ + /* 105 */ CR( 6, 1, B3, 0, 0, 0, 0), /* B3 (S=0) */ + /* 106 */ CR( 7, 1, F3, 0, 0, 0, 0), /* F3 */ + /* 107 */ CR( 8, 0, B4, 0, 0, 0, 0), /* B4 */ + /* 108 */ CR( 8, 0, F4, 0, 0, 0, 0), /* F4 */ + /* LOAD_2 only */ + /* 109 */ CR( 5, 0, IA, 0, 0, 0, 0), /* Ampl. Intr. */ + /* 110 */ CR( 5, 0, IP, 0, 0, 0, 0), /* Per. Intr. */ + + /* -------------------------------------------------------------------- */ + /* OPCODE 1101: DELTA_D */ + /* -------------------------------------------------------------------- */ + /* Mode 00 and 01 */ + /* 111 */ CR( 4, 2, AM, 1, 0, 0, 1), /* Amplitude */ + /* 112 */ CR( 5, 0, PR, 1, 0, 0, 0), /* Period */ + /* 113 */ CR( 3, 3, B3, 1, 0, 0, 0), /* B3 5 MSBs */ + /* 114 */ CR( 4, 2, F3, 1, 0, 0, 0), /* F3 6 MSBs */ + /* 115 */ CR( 4, 1, B4, 1, 0, 0, 0), /* B4 7 MSBs */ + /* 116 */ CR( 4, 2, F4, 1, 0, 0, 0), /* F4 6 MSBs */ + /* Mode 01 only */ + /* 117 */ CR( 5, 0, B5, 1, 0, 0, 0), /* B5 8 MSBs */ + /* 118 */ CR( 5, 0, F5, 1, 0, 0, 0), /* F5 8 MSBs */ + + /* Mode 10 and 11 */ + /* 119 */ CR( 4, 2, AM, 1, 0, 0, 0), /* Amplitude */ + /* 120 */ CR( 5, 0, PR, 1, 0, 0, 0), /* Period */ + /* 121 */ CR( 4, 1, B3, 1, 0, 0, 0), /* B3 7 MSBs */ + /* 122 */ CR( 5, 1, F3, 1, 0, 0, 0), /* F3 7 MSBs */ + /* 123 */ CR( 5, 0, B4, 1, 0, 0, 0), /* B4 8 MSBs */ + /* 124 */ CR( 5, 0, F4, 1, 0, 0, 0), /* F4 8 MSBs */ + /* Mode 11 only */ + /* 125 */ CR( 5, 0, B5, 1, 0, 0, 0), /* B5 8 MSBs */ + /* 126 */ CR( 5, 0, F5, 1, 0, 0, 0), /* F5 8 MSBs */ + + /* -------------------------------------------------------------------- */ + /* OPCODE 1110: LOAD_E */ + /* -------------------------------------------------------------------- */ + /* 127 */ CR( 6, 2, AM, 0, 0, 0, 0), /* Amplitude */ + /* 128 */ CR( 8, 0, PR, 0, 0, 0, 0), /* Period */ + + /* -------------------------------------------------------------------- */ + /* Opcode 0010: LOAD_2 Mode 01 and 11 */ + /* Opcode 1100: LOAD_C Mode 01 and 11 */ + /* -------------------------------------------------------------------- */ + /* LOAD_2, LOAD_C Mode 01 */ + /* 129 */ CR( 6, 2, AM, 0, 0, 0, 0), /* Amplitude */ + /* 130 */ CR( 8, 0, PR, 0, 0, 0, 0), /* Period */ + /* 131 */ CR( 3, 4, B0, 0, 0, 0, 0), /* B0 (S=0) */ + /* 132 */ CR( 5, 3, F0, 0, 0, 0, 0), /* F0 */ + /* 133 */ CR( 3, 4, B1, 0, 0, 0, 0), /* B1 (S=0) */ + /* 134 */ CR( 5, 3, F1, 0, 0, 0, 0), /* F1 */ + /* 135 */ CR( 3, 4, B2, 0, 0, 0, 0), /* B2 (S=0) */ + /* 136 */ CR( 5, 3, F2, 0, 0, 0, 0), /* F2 */ + /* 137 */ CR( 4, 3, B3, 0, 0, 0, 0), /* B3 (S=0) */ + /* 138 */ CR( 6, 2, F3, 0, 0, 0, 0), /* F3 */ + /* 139 */ CR( 7, 1, B4, 0, 0, 0, 0), /* B4 */ + /* 140 */ CR( 6, 2, F4, 0, 0, 0, 0), /* F4 */ + /* 141 */ CR( 8, 0, B5, 0, 0, 0, 0), /* B5 */ + /* 142 */ CR( 8, 0, F5, 0, 0, 0, 0), /* F5 */ + /* LOAD_2 only */ + /* 143 */ CR( 5, 0, IA, 0, 0, 0, 0), /* Ampl. Intr. */ + /* 144 */ CR( 5, 0, IP, 0, 0, 0, 0), /* Per. Intr. */ + + /* LOAD_2, LOAD_C Mode 11 */ + /* 145 */ CR( 6, 2, AM, 0, 0, 0, 0), /* Amplitude */ + /* 146 */ CR( 8, 0, PR, 0, 0, 0, 0), /* Period */ + /* 147 */ CR( 6, 1, B0, 0, 0, 0, 0), /* B0 (S=0) */ + /* 148 */ CR( 6, 2, F0, 0, 0, 0, 0), /* F0 */ + /* 149 */ CR( 6, 1, B1, 0, 0, 0, 0), /* B1 (S=0) */ + /* 150 */ CR( 6, 2, F1, 0, 0, 0, 0), /* F1 */ + /* 151 */ CR( 6, 1, B2, 0, 0, 0, 0), /* B2 (S=0) */ + /* 152 */ CR( 6, 2, F2, 0, 0, 0, 0), /* F2 */ + /* 153 */ CR( 6, 1, B3, 0, 0, 0, 0), /* B3 (S=0) */ + /* 154 */ CR( 7, 1, F3, 0, 0, 0, 0), /* F3 */ + /* 155 */ CR( 8, 0, B4, 0, 0, 0, 0), /* B4 */ + /* 156 */ CR( 8, 0, F4, 0, 0, 0, 0), /* F4 */ + /* 157 */ CR( 8, 0, B5, 0, 0, 0, 0), /* B5 */ + /* 158 */ CR( 8, 0, F5, 0, 0, 0, 0), /* F5 */ + /* LOAD_2 only */ + /* 159 */ CR( 5, 0, IA, 0, 0, 0, 0), /* Ampl. Intr. */ + /* 160 */ CR( 5, 0, IP, 0, 0, 0, 0), /* Per. Intr. */ + + /* -------------------------------------------------------------------- */ + /* Opcode 0011: SETMSB_3 */ + /* Opcode 0101: SETMSB_5 */ + /* -------------------------------------------------------------------- */ + /* Mode 00 only */ + /* 161 */ CR( 0, 0, 0, 0, 0, 0, 0), + /* Mode 00 and 01 */ + /* 162 */ CR( 6, 2, AM, 0, 0, 0, 0), /* Amplitude */ + /* 163 */ CR( 8, 0, PR, 0, 0, 0, 0), /* Period */ + /* 164 */ CR( 5, 3, F0, 0, 1, 0, 0), /* F0 (5 MSBs) */ + /* 165 */ CR( 5, 3, F1, 0, 1, 0, 0), /* F1 (5 MSBs) */ + /* 166 */ CR( 5, 3, F2, 0, 1, 0, 0), /* F2 (5 MSBs) */ + /* SETMSB_3 only */ + /* 167 */ CR( 5, 0, IA, 0, 0, 0, 0), /* Ampl. Intr. */ + /* 168 */ CR( 5, 0, IP, 0, 0, 0, 0), /* Per. Intr. */ + + /* Mode 10 only */ + /* 169 */ CR( 0, 0, 0, 0, 0, 0, 0), + /* Mode 10 and 11 */ + /* 170 */ CR( 6, 2, AM, 0, 0, 0, 0), /* Amplitude */ + /* 171 */ CR( 8, 0, PR, 0, 0, 0, 0), /* Period */ + /* 172 */ CR( 6, 2, F0, 0, 1, 0, 0), /* F0 (6 MSBs) */ + /* 173 */ CR( 6, 2, F1, 0, 1, 0, 0), /* F1 (6 MSBs) */ + /* 174 */ CR( 6, 2, F2, 0, 1, 0, 0), /* F2 (6 MSBs) */ + /* SETMSB_3 only */ + /* 175 */ CR( 5, 0, IA, 0, 0, 0, 0), /* Ampl. Intr. */ + /* 176 */ CR( 5, 0, IP, 0, 0, 0, 0), /* Per. Intr. */ +}; + + + +static const int16_t sp0256_df_idx[16 * 8] = { + /* OPCODE 0000 */ -1, -1, -1, -1, -1, -1, -1, -1, + /* OPCODE 1000 */ -1, -1, -1, -1, -1, -1, -1, -1, + /* OPCODE 0100 */ 17, 22, 17, 24, 25, 30, 25, 32, + /* OPCODE 1100 */ 83, 94, 129,142, 97, 108, 145,158, + /* OPCODE 0010 */ 83, 96, 129,144, 97, 110, 145,160, + /* OPCODE 1010 */ 73, 77, 74, 77, 78, 82, 79, 82, + /* OPCODE 0110 */ 33, 36, 34, 37, 38, 41, 39, 42, + /* OPCODE 1110 */ 127,128, 127,128, 127,128, 127,128, + /* OPCODE 0001 */ 1, 14, 1, 16, 1, 14, 1, 16, + /* OPCODE 1001 */ 45, 56, 45, 58, 59, 70, 59, 72, + /* OPCODE 0101 */ 161,166, 162,166, 169,174, 170,174, + /* OPCODE 1101 */ 111,116, 111,118, 119,124, 119,126, + /* OPCODE 0011 */ 161,168, 162,168, 169,176, 170,176, + /* OPCODE 1011 */ -1, -1, -1, -1, -1, -1, -1, -1, + /* OPCODE 0111 */ -1, -1, -1, -1, -1, -1, -1, -1, + /* OPCODE 1111 */ 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* ======================================================================== */ +/* bitrev -- Bit-reverse a 32-bit number. */ +/* ======================================================================== */ +static inline uint32_t +bitrev(uint32_t val) +{ + val = ((val & 0xffff0000) >> 16) | ((val & 0x0000ffff) << 16); + val = ((val & 0xff00ff00) >> 8) | ((val & 0x00ff00ff) << 8); + val = ((val & 0xf0f0f0f0) >> 4) | ((val & 0x0f0f0f0f) << 4); + val = ((val & 0xcccccccc) >> 2) | ((val & 0x33333333) << 2); + val = ((val & 0xaaaaaaaa) >> 1) | ((val & 0x55555555) << 1); + + return val; +} + +/* ======================================================================== */ +/* sp0256_getb -- Get up to 8 bits at the current PC. */ +/* ======================================================================== */ +static uint32_t +sp0256_getb(sp0256_t *s, int len) +{ + uint32_t data = 0; + uint32_t d0, d1; + + /* -------------------------------------------------------------------- */ + /* Fetch data from the FIFO or from the MASK */ + /* -------------------------------------------------------------------- */ + if (s->fifo_sel) { + d0 = s->fifo[(s->fifo_tail ) & 63]; + d1 = s->fifo[(s->fifo_tail + 1) & 63]; + + data = ((d1 << 10) | d0) >> s->fifo_bitp; + +#ifdef DEBUG_FIFO + dfprintf(("SP0256: RD_FIFO %.3X %d.%d %d\n", data & ((1 << len) - 1), + s->fifo_tail, s->fifo_bitp, s->fifo_head)); +#endif + + /* ---------------------------------------------------------------- */ + /* Note the PC doesn't advance when we execute from FIFO. */ + /* Just the FIFO's bit-pointer advances. (That's not REALLY */ + /* what happens, but that's roughly how it behaves.) */ + /* ---------------------------------------------------------------- */ + s->fifo_bitp += len; + if (s->fifo_bitp >= 10) { + s->fifo_tail++; + s->fifo_bitp -= 10; + } + } else { + /* ---------------------------------------------------------------- */ + /* Figure out which ROMs are being fetched into, and grab two */ + /* adjacent bytes. The byte we're interested in is extracted */ + /* from the appropriate bit-boundary between them. */ + /* ---------------------------------------------------------------- */ + int idx0 = (s->pc ) >> 3, page0 = idx0 >> 12; + int idx1 = (s->pc + 8) >> 3, page1 = idx1 >> 12; + + idx0 &= 0xfff; + idx1 &= 0xfff; + + d0 = d1 = 0; + + if (s->rom[page0]) d0 = s->rom[page0][idx0]; + if (s->rom[page1]) d1 = s->rom[page1][idx1]; + + data = ((d1 << 8) | d0) >> (s->pc & 7); + + s->pc += len; + } + + /* -------------------------------------------------------------------- */ + /* Mask data to the requested length. */ + /* -------------------------------------------------------------------- */ + data &= ((1 << len) - 1); + + return data; +} + +/* ======================================================================== */ +/* sp0256_micro -- Emulate the microsequencer in the SP0256. Executes */ +/* instructions either until the repeat count != 0 or */ +/* the sequencer gets halted by a RTS to 0. */ +/* ======================================================================== */ +static void +sp0256_micro(sp0256_t *s) +{ + uint8_t immed4; + uint8_t opcode; + uint16_t cr; + int ctrl_xfer = 0; + int repeat = 0; + int i, idx0, idx1; + + /* -------------------------------------------------------------------- */ + /* Only execute instructions while the filter is not busy. */ + /* -------------------------------------------------------------------- */ + while (s->filt.rpt <= 0 && s->filt.cnt <= 0) { + /* ---------------------------------------------------------------- */ + /* If the CPU is halted, see if we have a new command pending */ + /* in the Address LoaD buffer. */ + /* ---------------------------------------------------------------- */ + if (s->halted && !s->lrq) { + s->pc = s->ald | (0x1000 << 3); + s->fifo_sel = 0; + s->halted = 0; + s->lrq = 1; + s->ald = 0; +/* for (i = 0; i < 16; i++)*/ +/* s->filt.r[i] = 0;*/ + } + + /* ---------------------------------------------------------------- */ + /* If we're still halted, do nothing. */ + /* ---------------------------------------------------------------- */ + if (s->halted) { + s->filt.rpt = 1; + s->filt.cnt = 0; + s->lrq = 1; + s->ald = 0; +/* for (i = 0; i < 16; i++)*/ +/* s->filt.r[i] = 0;*/ + return; + } + + /* ---------------------------------------------------------------- */ + /* Fetch the first 8 bits of the opcode, which are always in the */ + /* same approximate format -- immed4 followed by opcode. */ + /* ---------------------------------------------------------------- */ + immed4 = sp0256_getb(s, 4); + opcode = sp0256_getb(s, 4); + repeat = 0; + ctrl_xfer = 0; + + debug_printf(("$%.4X.%.1X: OPCODE %d%d%d%d.%d%d\n", + (s->pc >> 3) - 1, s->pc & 7, + !!(opcode & 1), !!(opcode & 2), + !!(opcode & 4), !!(opcode & 8), + !!(s->mode&4), !!(s->mode&2))); + + /* ---------------------------------------------------------------- */ + /* Handle the special cases for specific opcodes. */ + /* ---------------------------------------------------------------- */ + switch (opcode) { + /* ------------------------------------------------------------ */ + /* OPCODE 0000: RTS / SETPAGE */ + /* ------------------------------------------------------------ */ + case 0x0: + /* -------------------------------------------------------- */ + /* If immed4 != 0, then this is a SETPAGE instruction. */ + /* -------------------------------------------------------- */ + if (immed4) { /* SETPAGE */ + s->page = bitrev(immed4) >> 13; + } else { + /* -------------------------------------------------------- */ + /* Otherwise, this is an RTS / HLT. */ + /* -------------------------------------------------------- */ + uint32_t btrg; + + /* ---------------------------------------------------- */ + /* Figure out our branch target. */ + /* ---------------------------------------------------- */ + btrg = s->stack; + + s->stack = 0; + + /* ---------------------------------------------------- */ + /* If the branch target is zero, this is a HLT. */ + /* Otherwise, it's an RTS, so set the PC. */ + /* ---------------------------------------------------- */ + if (!btrg) { + s->halted = 1; + s->pc = 0; + ctrl_xfer = 1; + } else { + s->pc = btrg; + ctrl_xfer = 1; + } + } + + break; + + /* ------------------------------------------------------------ */ + /* OPCODE 0111: JMP Jump to 12-bit/16-bit Abs Addr */ + /* OPCODE 1011: JSR Jump to Subroutine */ + /* ------------------------------------------------------------ */ + case 0xe: + case 0xd: + { + int btrg; + + /* -------------------------------------------------------- */ + /* Figure out our branch target. */ + /* -------------------------------------------------------- */ + btrg = s->page | + (bitrev(immed4) >> 17) | + (bitrev(sp0256_getb(s, 8)) >> 21); + ctrl_xfer = 1; + + /* -------------------------------------------------------- */ + /* If this is a JSR, push our return address on the */ + /* stack. Make sure it's byte aligned. */ + /* -------------------------------------------------------- */ + if (opcode == 0xd) + s->stack = (s->pc + 7) & ~7; + + /* -------------------------------------------------------- */ + /* Jump to the new location! */ + /* -------------------------------------------------------- */ + s->pc = btrg; + break; + } + + /* ------------------------------------------------------------ */ + /* OPCODE 1000: SETMODE Set the Mode and Repeat MSBs */ + /* ------------------------------------------------------------ */ + case 0x1: + s->mode = ((immed4 & 8) >> 2) | (immed4 & 4) | + ((immed4 & 3) << 4); + break; + + /* ------------------------------------------------------------ */ + /* OPCODE 0001: LOADALL Load All Parameters */ + /* OPCODE 0010: LOAD_2 Load Per, Ampl, Coefs, Interp. */ + /* OPCODE 0011: SETMSB_3 Load Pitch, Ampl, MSBs, & Intrp */ + /* OPCODE 0100: LOAD_4 Load Pitch, Ampl, Coeffs */ + /* OPCODE 0101: SETMSB_5 Load Pitch, Ampl, and Coeff MSBs */ + /* OPCODE 0110: SETMSB_6 Load Ampl, and Coeff MSBs. */ + /* OPCODE 1001: DELTA_9 Delta update Ampl, Pitch, Coeffs */ + /* OPCODE 1010: SETMSB_A Load Ampl and MSBs of 3 Coeffs */ + /* OPCODE 1100: LOAD_C Load Pitch, Ampl, Coeffs */ + /* OPCODE 1101: DELTA_D Delta update Ampl, Pitch, Coeffs */ + /* OPCODE 1110: LOAD_E Load Pitch, Amplitude */ + /* OPCODE 1111: PAUSE Silent pause */ + /* ------------------------------------------------------------ */ + default: + repeat = immed4 | (s->mode & 0x30); + break; + } + if (opcode != 1) s->mode &= 0xf; + + /* ---------------------------------------------------------------- */ + /* If this was a control transfer, handle setting "fifo_sel" */ + /* and all that ugliness. */ + /* ---------------------------------------------------------------- */ + if (ctrl_xfer) { + debug_printf(("jumping to $%.4X.%.1X: ", s->pc >> 3, s->pc & 7)); + + /* ------------------------------------------------------------ */ + /* Set our "FIFO Selected" flag based on whether we're going */ + /* to the FIFO's address. */ + /* ------------------------------------------------------------ */ + s->fifo_sel = s->pc == FIFO_ADDR; + + debug_printf(("%s ", s->fifo_sel ? "FIFO" : "ROM")); + + /* ------------------------------------------------------------ */ + /* Control transfers to the FIFO cause it to discard the */ + /* partial decle that's at the front of the FIFO. */ + /* ------------------------------------------------------------ */ + if (s->fifo_sel && s->fifo_bitp) { + debug_printf(("bitp = %d -> Flush", s->fifo_bitp)); + + /* Discard partially-read decle. */ + if (s->fifo_tail < s->fifo_head) s->fifo_tail++; + s->fifo_bitp = 0; + } + + debug_printf(("\n")); + + continue; + } + + /* ---------------------------------------------------------------- */ + /* Otherwise, if we have a repeat count, then go grab the data */ + /* block and feed it to the filter. */ + /* ---------------------------------------------------------------- */ + if (!repeat) continue; + + s->filt.rpt = repeat; + debug_printf(("repeat = %d\n", repeat)); + + /* clear delay line on new opcode */ + for (i = 0; i < 6; i++) + s->filt.z_data[i][0] = s->filt.z_data[i][1] = 0; + + i = (opcode << 3) | (s->mode & 6); + idx0 = sp0256_df_idx[i++]; + idx1 = sp0256_df_idx[i ]; + + assert(idx0 >= 0 && idx1 >= 0 && idx1 >= idx0); + + /* ---------------------------------------------------------------- */ + /* If we're in one of the 10-pole modes (x0), clear F5/B5. */ + /* ---------------------------------------------------------------- */ + if ((s->mode & 2) == 0) + s->filt.r[F5] = s->filt.r[B5] = 0; + + + /* ---------------------------------------------------------------- */ + /* Step through control words in the description for data block. */ + /* ---------------------------------------------------------------- */ + for (i = idx0; i <= idx1; i++) { + int len, shf, delta, field, prm, clrL; + libspectrum_signed_byte value; + + /* ------------------------------------------------------------ */ + /* Get the control word and pull out some important fields. */ + /* ------------------------------------------------------------ */ + cr = sp0256_datafmt[i]; + + len = CR_LEN(cr); + shf = CR_SHF(cr); + prm = CR_PRM(cr); + clrL = cr & CR_CLRL; + delta = cr & CR_DELTA; + field = cr & CR_FIELD; + value = 0; + + debug_printf(("$%.4X.%.1X: len=%2d shf=%2d prm=%2d d=%d f=%d ", + s->pc >> 3, s->pc & 7, len, shf, prm, !!delta, !!field)); + /* ------------------------------------------------------------ */ + /* Clear any registers that were requested to be cleared. */ + /* ------------------------------------------------------------ */ + if (clrL) + { + s->filt.r[F0] = s->filt.r[B0] = 0; + s->filt.r[F1] = s->filt.r[B1] = 0; + s->filt.r[F2] = s->filt.r[B2] = 0; + } + + /* ------------------------------------------------------------ */ + /* If this entry has a bitfield with it, grab the bitfield. */ + /* ------------------------------------------------------------ */ + if (len) { + value = sp0256_getb(s, len); + } else { + debug_printf((" (no update)\n")); + continue; + } + + /* ------------------------------------------------------------ */ + /* Sign extend if this is a delta update. */ + /* ------------------------------------------------------------ */ + if (delta) { /* Sign extend */ + if (value & (1 << (len - 1))) value |= -1 << len; + } + + /* ------------------------------------------------------------ */ + /* Shift the value to the appropriate precision. */ + /* ------------------------------------------------------------ */ + if (shf) { + value <<= shf; + } + + debug_printf(("v=%.2X (%c%.2X) ", value & 0xff, + value & 0x80 ? '-' : '+', + 0xff & (value & 0x80 ? -value : value))); + + s->silent = 0; + + /* ------------------------------------------------------------ */ + /* If this is a field-replace, insert the field. */ + /* ------------------------------------------------------------ */ + if (field) { + debug_printf(("--field-> r[%2d] = %.2X -> ", prm, + s->filt.r[prm])); + + s->filt.r[prm] &= ~(~0 << shf); /* Clear the old bits. */ + s->filt.r[prm] |= value; /* Merge in the new bits. */ + + debug_printf(("%.2X\n", s->filt.r[prm])); + + continue; + } + + /* ------------------------------------------------------------ */ + /* If this is a delta update, add to the appropriate field. */ + /* ------------------------------------------------------------ */ + if (delta) { + debug_printf(("--delta-> r[%2d] = %.2X -> ", prm, + s->filt.r[prm])); + + s->filt.r[prm] += value; + + debug_printf(("%.2X\n", s->filt.r[prm])); + + continue; + } + + /* ------------------------------------------------------------ */ + /* Otherwise, just write the new value. */ + /* ------------------------------------------------------------ */ + s->filt.r[prm] = value; + debug_printf(("--value-> r[%2d] = %.2X\n", prm, s->filt.r[prm])); + } + + /* ---------------------------------------------------------------- */ + /* Most opcodes clear IA, IP. */ + /* ---------------------------------------------------------------- */ + if (opcode != 0x1 && opcode != 0x2 && opcode != 0x3) + { + s->filt.r[IA] = 0; + s->filt.r[IP] = 0; + } + + /* ---------------------------------------------------------------- */ + /* Special case: Set PAUSE's equivalent period. */ + /* ---------------------------------------------------------------- */ + if (opcode == 0xf) + { + s->silent = 1; + s->filt.r[AM] = 0; + s->filt.r[PR] = PER_PAUSE; + } + + /* ---------------------------------------------------------------- */ + /* Now that we've updated the registers, go decode them. */ + /* ---------------------------------------------------------------- */ + lpc12_regdec(&s->filt); + + /* ---------------------------------------------------------------- */ + /* Break out since we now have a repeat count. */ + /* ---------------------------------------------------------------- */ + break; + } +} + +/* ======================================================================== */ +/* sp0256_rdrom -- Tries to read a ROM file in the current directory. */ +/* ======================================================================== */ +static int +sp0256_rdrom(sp0256_t *s, int page) +{ + uint8_t *rom; + char buf[32]; + FILE *f; + + /* -------------------------------------------------------------------- */ + /* Generate a file name, and then see if it exists. ... [truncated message content] |