/***************************************************************************
*       NAME:  SBOSDEB.C $Revision: 1.20 $
**      COPYRIGHT:
**      "Copyright (c) 1994,1995 by e-Tek Labs"
**
**       "This software is furnished under a license and may be used,
**       copied, or disclosed only in accordance with the terms of such
**       license and with the inclusion of the above copyright notice.
**       This software or any other copies thereof may not be provided or
**       otherwise made available to any other person. No title to and
**       ownership of the software is hereby transfered."
****************************************************************************
****************************************************************************
**  For use with the InterWave SBOS only!!
**  controls the driver in debug mode
**  Only works with the board at port 220h
****************************************************************************/

#include <dos.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <bios.h>
#include <conio.h>
#include "sbosdata.h"
#include "sbosdefs.h"
#include "shared.h"
#include "findsbos.h"
#include "version.h"
#include "fm.h"
#include "dumpbnk.h"

#include "debug.h"

// local prototypes
void upload_deb_buff(void);
void display_command_table(void);
void display_main_menu(void);
void display_nmi_count(void);
void reset_values(void);
void play_file(void);
int hex2dec(char * st, int digits);
int do_single_command(void);
void send_commands(void);
void toggle_line_in(void);
void do_map(void);
void send(unsigned char reg, unsigned char val);
void draw_map(unsigned int,unsigned int, unsigned char);
void do_snap(void);
void do_inst(void);
void dump_registers(int voice);
void get_input(void);
void do_mpu(void);
extern unsigned char read_dram( unsigned long );

// local defins
#define K_ESC 0x011b
#define K_CR  0x1c0d
#define K_1   0x0231
#define K_2   0x0332
#define K_3   0x0433
#define K_4   0x0534
#define K_5   0x0635
#define K_6   0x0736
#define K_7   0x0837
#define K_8   0x0938
#define K_9   0x0a39
#define K_l   0x266c

#define SUBF_00 0x0000
#define SUBF_01 0x0100
#define SUBF_02 0x0200
#define SUBF_03 0x0300
#define SUBF_04 0x0400

                /** DRIVER POINTERS **/
unsigned int far * debug_ptrs;       /** pointer to debug offsets     **/
SHARED far * shared;                 /** pointer to shared data space **/
unsigned int far * nmi_count;        /** pointer to nmi activity ctr  **/
unsigned int far * nmi_table_count;  /** pointer to curr nmi tab off  **/
unsigned char far * data_ptr;        /** pointer to nmi data table    **/
FM_CHANNEL far * fm_channel;         /** pointer to synth fm_channel  **/
FM_OPERATOR far * fm_operator;       /** pointer to synth fm_operator **/
unsigned int far * cur_instrument;   /** pointer to current inst array**/
unsigned int far * cur_bank;         /** pointer to current bank array**/
SNAPSHOT far * snap;                 /** pointer to snapshot buffer   **/
unsigned int far * force_instrument; /** pointer to force_instrument a**/
unsigned char far * fmregs;          /** pointer to fm register buffer**/
int far * chan_disable;              /** pointer to channel disable arr**/
unsigned char far * pick_list;       /** pointer to pick_list array   **/
SBOSDATA far * sbosdata;             /** pointer to sbosdata          **/
unsigned long * far notes_playing;   /** pointer to mpu401 note map   **/
unsigned char * far chan_instrument; /** pointer to mpu401 channel map**/

union REGS regs;
unsigned int comm_vect;
unsigned int driver_seg;
int mpu401_mode;
int key;
int done = FALSE, quit = FALSE;
int line_in = FALSE;

unsigned int active_chan = 0, modulator = TRUE;
unsigned char oper_index = 0;
char line[5];
int can_force;

// MLS Stuff
mls_path[128];


    /** NOTE: not all these tables are used!  **/

    /** optab is used to index the operator registers by operator number **/
unsigned char optab[18] =   {0,1,2,3,4,5,8,9,10,11,12,13,16,17,18,19,20,21};

    /** opmodtab is used to index operator (modulator) number by channel **/
unsigned char opmodtab[9] = {0,1,2,8,9,10,16,17,18};

    /** opcartab is used to index operator (carrier) number by channel **/
unsigned char opcartab[9] = {3,4,5,11,12,13,19,20,21};

    /** chantab is used to index the channel by operator number **/
unsigned char chantab[18] = {0,1,2,0,1,2,3,4,5,3,4,5,6,7,8,6,7,8};

    /** modtab is used to set 'modulator' T/F based on operator number **/
unsigned char modtab[18] = {1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0};

    /** pointab is used to set the pointer to the active column **/
unsigned char pointab[22] = {1,5,9,13,17,21,0,0,25,29,33,37,41,45,0,0,49,53,57,61,65,69};

int skip_adlib = FALSE;
int show_chan = -1;
int last_chan = -1;

unsigned char read_dram( unsigned long address )
{
    unsigned char retval;

    outp(0x323, 0x43); /** set DRAM low **/
    outpw (0x324, (unsigned int)(address));
    outp(0x323,0x44); /** set DRAM high **/
    outp(0x325,LOW_B(MS_WORD(address)));
    retval = inportb(0x327);
    return retval;
}
void main(int argc,char *argv[])
{
    int toff;

if (argc >= 2)
    {
    if (strcmp(argv[1],"-a") == 0)
        skip_adlib = TRUE;
    if (strcmp(argv[1],"-c") == 0)
        show_chan = atoi(argv[2]);
    }

    /** See if IWSBOS is out there **/
    comm_vect = find_sbos();
    if (comm_vect)
        {
        /** It is out there  - get shared address **/
        regs.x.ax = COM_GET_SHARED;  /* Get Shared Data Address */
        int86(comm_vect,&regs,&regs);
        shared = (SHARED far *)MK_FP(regs.x.dx,regs.x.ax);
        sbosdata = (SBOSDATA far *)MK_FP(FP_SEG(shared),shared->sbosdata);
    
        /** Check to see if the driver has been initialized or not **/
        if(!(shared->status & STAT_DRIVER_INIT)) /** driver un-init'd **/
            {
            printf("Driver is un-initialized - run the loader first\n");
            exit(-1);
            }

        if((VERSION_HIGH != shared->version_high) || 
           (VERSION_LOW1 != shared->version_low1) ||
           (VERSION_LOW2 != shared->version_low2))
            {
            printf("Version number mismatch\n");
            printf("This version may not work with the installed driver \n");
            printf("Press any key to continue...");
            (void)getch();
            }

        /** See if the driver is in debug mode **/
        regs.x.ax = COM_DEBUG | SUBF_00;
        int86(comm_vect,&regs,&regs);
        if(regs.x.ax == 0xffff)
            {
            printf("Driver is not in debug mode - recompile with option on\n");
            exit(-1);
            }
        else // otherwise -- get pointers into internal data structures
            {
            driver_seg = regs.x.cx;
            debug_ptrs = (unsigned int far *)MK_FP(driver_seg,regs.x.dx);
            toff = debug_ptrs[OFF_NMI_COUNT];
            nmi_count = (unsigned int far *)MK_FP(driver_seg, toff);
            toff = debug_ptrs[OFF_TABLE_COUNT];
            nmi_table_count = (unsigned int far *)MK_FP(driver_seg, toff);
            toff = debug_ptrs[OFF_NMI_TABLE];
            data_ptr = (unsigned char far *)MK_FP(driver_seg, toff);
            toff = debug_ptrs[OFF_FM_CHANNEL];
            fm_channel = (FM_CHANNEL far *)MK_FP(driver_seg, toff);
            toff = debug_ptrs[OFF_FM_OPERATOR];
            fm_operator = (FM_OPERATOR far *)MK_FP(driver_seg, toff);
            toff = debug_ptrs[OFF_CUR_INSTRUMENT];
            cur_instrument = (unsigned int far *)MK_FP(driver_seg, toff);
            toff = debug_ptrs[driver_seg,OFF_CUR_BANK];
            cur_bank = (unsigned int far *)MK_FP(driver_seg, toff);
            toff = debug_ptrs[OFF_SNAP];
            snap = (SNAPSHOT far *)MK_FP(driver_seg, toff);
            toff = debug_ptrs[OFF_FORCE_INSTRUMENT];
            force_instrument = (unsigned int far *)MK_FP(driver_seg, toff);
            toff = debug_ptrs[OFF_FMREGS];
            fmregs = (unsigned char far *)MK_FP(driver_seg, toff);
            toff = debug_ptrs[OFF_CHAN_DISABLE];
            chan_disable = (int far *)MK_FP(driver_seg, toff);
            toff = debug_ptrs[OFF_PICK_LIST];
            pick_list = (unsigned char far *)MK_FP(driver_seg, toff);
            toff = debug_ptrs[OFF_NOTES_PLAYING];
            notes_playing = (unsigned long far *)MK_FP(driver_seg, toff);
            toff = debug_ptrs[OFF_CHAN_INSTRUMENT];
            chan_instrument = (unsigned char far *)MK_FP(driver_seg, toff);
            }

        /** Check to see which emulation mode the driver is in **/
        if(shared->status & STAT_MPU401_ENABLED) /** in mpu-401 mode **/
            mpu401_mode = TRUE;
        else
            mpu401_mode = FALSE;

        /** See if the line-in is enabled or not **/
        if(sbosdata->mixer_mask & 0x01) line_in = FALSE;
        else line_in = TRUE;

        display_main_menu();

        while(!quit)
            {
            if(kbhit())
                {
                key = bioskey(0);
                printf("\n");
                switch (key)
                    {
                    case K_ESC:
                        printf("Sure you want to quit? ");
                        key = getch();
                        if(key == 'y' || key == 'Y')    
                            quit = TRUE;
                        break;
                    case K_1:
                        display_command_table();
                        break;
                    case K_2:
                        reset_values();
                        break;
                    case K_3:
                        play_file();
                        break;
                    case K_4:
                        send_commands();
                        break;
                    case K_5:
                        do_map();
                        break;
                    case K_6:
                        do_snap();
                        break;
                    case K_7:
                        do_inst();
                        break;
                    case K_8:
//                        dump_registers(0);
                        upload_deb_buff();
                        break;
                    case K_9:
                        if(mpu401_mode) do_mpu();
                        break;
                    case K_l:
                        toggle_line_in();
                        break;
                    default:
                        break;
                    }
                if(!quit)
                    display_main_menu();
                }
            }

        exit(1);
        }
    else
        {
        printf("Driver Not Loaded\n");
        exit(-1);
        }
}

void
PeekString()
{
// Needed for linking purposes only (stuff.c)
}
/************************************************************************
    wait()
************************************************************************/
void wait(void)
{
    int i;
    for(i = 0; i< 100; i++) inp(0x201);
}

/************************************************************************
    display_main_menu()
************************************************************************/
void display_main_menu(void)
{
    clrscr();
    printf("IWSBOS Debug Utility Version %d.%d%d",
                    VERSION_HIGH,VERSION_LOW1,VERSION_LOW2);
    if(mpu401_mode)
        printf("  MPU-401 Emulation Mode  ");
    else
        printf("    FM Emulation Mode     ");
    if(line_in) printf("  L  ");
    else        printf("     ");
    printf("   ESC to Exit\n");
    display_nmi_count();
    printf("\n");
    printf("   1) Dump Command Table to Screen or File\n");
    printf("   2) Reset Command Table and NMI Counter\n");
    printf("   3) Play Data From File to Ports\n");
    printf("   4) Send Single Commands to Adlib Ports\n");
    printf("   5) View and Adjust Adlib Register Map\n");
    printf("   6) Copy snapshot to file\n");
    printf("   7) Instrument picker\n");
//    printf("   8) Dump registers\n");
    printf("   8) Upload debug buffer from DRAM\n");
if(mpu401_mode)
    printf("   9) MPU-401 Helper\n");
    printf("\n>");
}

/************************************************************************
    display_nmi_count()
************************************************************************/
void display_nmi_count(void)
{
    printf("The NMI was activated %d times\n",*nmi_count);
}

void do_388( unsigned int reggie, unsigned int value,FILE *fp)
{
unsigned int stlen;
char outbuffer[80];
char str1[80] = "";
int treg;
int chan;
int voice;
int to_chan[22] = {0,1,2,0,1,2,0,0,3,4,5,3,4,5,0,0,6,7,8,6,7,8};
int to_voice[22] = {0,0,0,1,1,1,0,0,0,0,0,1,1,1,0,0,0,0,0,1,1,1};

    if ((reggie >= 1) && (reggie <= 8))
        {
        }
    else if ((reggie >=0x20) && (reggie <= 0x35))
        {
        treg = reggie-0x20;
        chan = to_chan[treg];
        voice = to_voice[treg];
        sprintf(str1,"CH-%d ",chan);
        if (voice)
            strcat(str1,"car ");
        else
            strcat(str1,"mod ");
        strcat(str1,"am-vb-eg-ksr-mult");
        }
    else if ((reggie >=0x40) && (reggie <= 0x55))
        {
        treg = reggie-0x40;
        chan = to_chan[treg];
        voice = to_voice[treg];
        sprintf(str1,"CH-%d ",chan);
        if (voice)
            strcat(str1,"car ");
        else
            strcat(str1,"mod ");
        strcat(str1,"ksl-volume");
        }
    else if ((reggie >=0x60) && (reggie <= 0x75))
        {
        treg = reggie-0x60;
        chan = to_chan[treg];
        voice = to_voice[treg];
        sprintf(str1,"CH-%d ",chan);
        if (voice)
            strcat(str1,"car ");
        else
            strcat(str1,"mod ");
        strcat(str1,"att-dec");
        }
    else if ((reggie >=0x80) && (reggie <= 0x95))
        {
        treg = reggie-0x80;
        chan = to_chan[treg];
        voice = to_voice[treg];
        sprintf(str1,"CH-%d ",chan);
        if (voice)
            strcat(str1,"car ");
        else
            strcat(str1,"mod ");
        strcat(str1,"sus-rel");
        }
    else if ((reggie >=0xA0) && (reggie <= 0xA8))
        {
        chan = reggie - 0xA0;
        sprintf(str1,"CH-%d ",chan);
        strcat(str1,"    f-num");
        }
    else if ((reggie >=0xB0) && (reggie <= 0xB8))
        {
        chan = reggie - 0xB0;
        sprintf(str1,"CH-%d ",chan);
        strcat(str1,"    key-blk-f-num");
        }
    else if ((reggie >=0xC0) && (reggie <= 0xC8))
        {
        chan = reggie - 0xC0;
        sprintf(str1,"CH-%d ",chan);
        strcat(str1,"    feedbk-con");
        }
    else if (reggie ==0xBD)
        {
        strcat(str1,"          percussion");
        }
    else if ((reggie >=0xE0) && (reggie <= 0xF5))
        {
        treg = reggie-0xE0;
        chan = to_chan[treg];
        voice = to_voice[treg];
        sprintf(str1,"CH-%d ",chan);
        if (voice)
            strcat(str1,"car ");
        else
            strcat(str1,"mod ");
        strcat(str1,"wave-sel");
        }
    else
        strcat(str1,"         undefined");

    if ((show_chan == -1) || (chan == show_chan))
        {
        if (chan != last_chan)
            fwrite("\n",strlen("\n"),1,fp);
        stlen = sprintf(outbuffer,"388 W %.2X  ; %s\n",
                    reggie,str1);
        fwrite(outbuffer,stlen,1,fp);
        stlen = sprintf(outbuffer,"389 W %.2X  ;\n",value);
        fwrite(outbuffer,stlen,1,fp);
        }
    last_chan = chan;
}

/************************************************************************
    display_command_table()
************************************************************************/
void display_command_table(void)
{
    unsigned int i, stlen;
    int count, to_screen, lines, abort;
    unsigned long dma_addr;
    unsigned int dma_count;
    char filename[15];
    FILE *handle;
    char outbuffer[80];
    char temp_buffer[80];
    char *comment;
    int dsp_args_left = 0;
    unsigned char val;
    unsigned int rate;
    int sys_ex_active = 0;

    if(*nmi_table_count)
        {
        done = FALSE;
        lines = 0;
        abort = FALSE;
        }
    else /** no data to display **/
        {
        printf("Command table is empty.\n");
        printf("Press any key to continue...\n");
        getch();
        return;
        }

    printf("Enter filename or <CR> to dump to screen: ");
    gets(filename);
    if(strlen(filename) == 0)
        {
        to_screen = TRUE;
        clrscr();
        }
    else
        {
        to_screen = FALSE;
        handle = fopen(filename,"wt");
        if(handle == NULL)
            {
            printf("Unsuccessfull open of file %s\n",filename);
            done = TRUE;
            abort = TRUE;
            }
        else
            printf("File %s open for write...\t\t\t\t\n",filename);
        }

    for(i = 0; (i < *nmi_table_count) && (!done); i++)
        {
        if(!(lines%24) && to_screen)
            {
            if(lines != 0)
                {
                printf("Press any key to continue...");
                key = bioskey(0);
                if(key == K_ESC)
                    done = TRUE;
                }
            clrscr();
            }
        lines++;
        switch(data_ptr[i])
            {
            case GPR1W_CODE: /** MPU-401 DATA IN **/
                if(to_screen) printf("Write 330 0x%.2X\n",data_ptr[i+1]);
                else
                    {
                    stlen = sprintf(outbuffer,"330 W %.2X   MPU-401<--DATA   ",
                        data_ptr[i+1]);
                    val = data_ptr[i+1];
                    if((val & 0x80) && (sys_ex_active == 1))
                        sys_ex_active = 0;
                    else if(sys_ex_active == 1)
                        {
                        comment = "SYS_EX Message...\n";
                        goto skip_parse;
                        }
/****/
                    if((val >= 0x80) && (val <= 0x8f))
                        comment = "Note Off\n";
                    else if((val >= 0x90) && (val <= 0x9f))
                        comment = "Note On\n";
                    else if((val >= 0xa0) && (val <= 0xaf))
                        comment = "Polyphonic Key Pressure/aftertouch\n";
                    else if((val >= 0xb0) && (val <= 0xbf))
                        comment = "Control Change\n";
                    else if((val >= 0xc0) && (val <= 0xcf))
                        comment = "Program Change\n";
                    else if((val >= 0xd0) && (val <= 0xdf))
                        comment = "Channel Pressure/aftertouch\n";
                    else if((val >= 0xe0) && (val <= 0xef))
                        comment = "Pitch Bend\n";
                    else if(val == 0xf0)
                        {
                        comment="Start System Exclusive Message\n";
                        sys_ex_active = 1;
                        }
                    else if(val == 0xf7)
                        comment = "End System Exclusive Message\n";
                    else
                        comment = "???\n";
skip_parse:
                    strcat(outbuffer,comment);
                    fwrite(outbuffer,strlen(outbuffer),1,handle);
                    }
                i++;
                break;
            case GPR1R_CODE: /** MPU-401 DATA OUT **/
                if(to_screen) printf("Read  330 0x%.2X\n",data_ptr[i+1]);
                else
                    {
                    stlen = sprintf(outbuffer,"330 R %.2X   MPU-401-->DATA   ",
                        data_ptr[i+1]);
                    val = data_ptr[i+1];
                    switch (val)
                        {
                        case 0xfe:
                            comment = "Acknowledge\n";
                            break;
                        default:
                            comment = "???\n";
                            break;
                        }
                    strcat(outbuffer,comment);
                    fwrite(outbuffer,strlen(outbuffer),1,handle);
                    }
                i++;
                break;
            case GPR2W_CODE: /** COMMAND **/
                if(to_screen) printf("Write 331 0x%.2X\n",data_ptr[i+1]);
                else
                    {
                    stlen = sprintf(outbuffer,"331 W %.2X   MPU-401<--COMM   ",
                        data_ptr[i+1]);
                    val = data_ptr[i+1];
                    switch (val)
                        {
                        case 0x3f:
                            comment = "Enter UART mode\n";
                            break;
                        case 0xAC:
                            comment = "Request Version\n";
                            break;
                        case 0xff:
                            comment = "Full Reset\n";
                            break;
                        default:
                            comment = "???\n";
                            break;
                        }
                    strcat(outbuffer,comment);
                    fwrite(outbuffer,strlen(outbuffer),1,handle);
                    }
                i++;
                break;
            case GPR2R_CODE: /** STATUS **/
                if(to_screen) printf("Read  331 0x%.2X\n",data_ptr[i+1]);
                else
                    {
                    stlen = sprintf(outbuffer,"331 R %.2X   MPU-401-->STAT   ",
                        data_ptr[i+1]);
                    val = data_ptr[i+1];
                    if ((val & 0xC0) == 0xC0)
                        comment = "NOT ready to send, NO data to receive\n";
                    else if ((val & 0xC0) == 0x80)
                        comment = "NO data to send - DATA RCV Ready\n";
                    else if ((val & 0xC0) == 0x40)
                        comment = "DATA SEND Ready - NOT ready to receive\n";
                    else if ((val & 0xC0) == 0x00)
                        comment = "DATA SEND Ready - DATA RCV Ready\n";
                    strcat(outbuffer,comment);
                    fwrite(outbuffer,strlen(outbuffer),1,handle);
                    }
                i++;
                break;
            case R2xER_CODE:
                if(to_screen) printf("Read  22E 0x%.2X\n",data_ptr[i+1]);
                else
                    {
                    stlen = sprintf(outbuffer,"22E R %.2X\n",data_ptr[i+1]);
                    fwrite(outbuffer,strlen(outbuffer),1,handle);
                    }
                i++;
                break;
            case R2x6W_CODE:
                if(to_screen) printf("Write 226 0x%.2X\n",data_ptr[i+1]);
                else
                    {
                    stlen = sprintf(outbuffer,"226 W %.2X\n",data_ptr[i+1]);
                    fwrite(outbuffer,stlen,1,handle);
                    }
                i++;
                break;
            case R388W_CODE:
                if(to_screen) printf("Write 388 0x%.2X\n",data_ptr[i+1]);
                else
                    {
                    if (!skip_adlib)
                        do_388((unsigned)data_ptr[i+1],
                            (unsigned)data_ptr[i+3],handle);
                    }
                i++;
                break;
            case R389W_CODE:
                if(to_screen) printf("Write 389 0x%.2X\n",data_ptr[i+1]);
                /** 389 writes printed to file in do_388() **/
                i++;
                break;
            case R2xCW_CODE:
                if(to_screen) printf("Write 22C 0x%.2X\n",data_ptr[i+1]);
                else
                    {
                    val = data_ptr[i+1];
                    if (dsp_args_left == 0)
                        {
                        sprintf(outbuffer,"22C W %.2X  ; ",val);
                        comment = "";
                        switch (val)
                            {
                            case 00:    // silence
                                comment = "silence data\n";
                                dsp_args_left = 2;
                                break;
                            case 0x10: // single byte xfer
                                comment = "Direct DAC\n";
                                dsp_args_left = 1;
                                break;
                            case 0x11: // high speed DMA
                                comment = "High speed DAC enabled\n";
                                dsp_args_left = 0;
                                break;
                            case 0x14: // 8 bit DMA
                                comment = "DMA block\n";
                                dsp_args_left = 2;
                                break;
                            case 0x16: // DMA 2 bit ADPCM
                                comment = "DMA 2 bit ADPCM\n";
                                dsp_args_left = 2;
                                break;
                            case 0x17: // DMA 2 bit ADPCM with reference
                                comment = "DMA 2 bit ADPCM w/ref\n";
                                dsp_args_left = 2;
                                break;
                            case 0x19: // high speed ADC
                                comment = "High speed ADC enabled\n";
                                dsp_args_left = 0;
                                break;
                            case 0x1C: // Special loop command
                                comment = "Autoinit playback\n";
                                dsp_args_left = 0;
                                break;
                            case 0x20: // Direct mode ADC
                                comment = "Direct ADC\n";
                                dsp_args_left = 1;
                                break;
                            case 0x24: // DMA mode ADC
                                comment = "DMA block ADC\n";
                                dsp_args_left = 2;
                                break;
                            case 0x2C: // Special loop command
                                comment = "AutoInit Record\n";
                                dsp_args_left = 0;
                                break;
                            case 0x30: // midi read (polled)
                            case 0x31: // midi read (interrupt)
                            case 0x34: // midi uart (polled)
                            case 0x35: // midi uart (interrupt)
                                comment = "MIDI cmd\n";
                                break;
                            case 0x38: // midi write (polled)
                                comment = "MIDI write\n";
                                dsp_args_left = 1;
                                break;
                            case 0x40: // Set time constant
                    rate = (unsigned int)(1000000L/(256L-data_ptr[i+3]));
                    sprintf(temp_buffer,"Set Time Constant -- %d hertz\n",rate);
                                comment = temp_buffer;
                                dsp_args_left = 1;
                                break;
                            case 0x41: // Set frequency
                                comment = "Set frequency\n";
                                dsp_args_left = 2;
                                break;
                            case 0x46: // Unknown
                                comment = "Unknown ???\n";
                                dsp_args_left = 0;
                                break;
                            case 0x48: // Set block size
                                comment = "Set Block size\n";
                                dsp_args_left = 2;
                                break;
                            case 0x50: // halt DMA
                                comment = "Halt DMA\n";
                                dsp_args_left = 0;
                                break;
                            case 0x51: // Speaker on
                                comment = "Speaker on\n";
                                dsp_args_left = 0;
                                break;
                            case 0x53: // Speaker off
                                comment = "Speaker off\n";
                                dsp_args_left = 0;
                                break;
                            case 0x54: // Continue DMA
                                comment = "Continue DMA\n";
                                dsp_args_left = 0;
                                break;
                            case 0x58: // Get speaker status
                                comment = "Get speaker status\n";
                                dsp_args_left = 0;
                                break;
                            case 0x5A: // Get speaker status
                                comment = "Pause speaker\n";
                                dsp_args_left = 0;
                                break;
                            case 0x60: // Negation
                                comment = "Negate next byte\n";
                                dsp_args_left = 1;
                                break;
                            case 0x61: // Get DSP version
                                comment = "Get DSP version\n";
                                dsp_args_left = 0;
                                break;
                            case 0x62: // encoded
                                comment = "Encoded\n";
                                dsp_args_left = 1;
                                break;
                            case 0x64: // Return data byte
                                comment = "Return data byte\n";
                                dsp_args_left = 1;
                                break;
                            case 0x68: // Echo back
                                comment = "Echo back\n";
                                dsp_args_left = 0;
                                break;
                            case 0x72: // Generate IRQ back to app
                                comment = "Generate IRQ to app\n";
                                dsp_args_left = 0;
                                break;
                            case 0x74: // DMA mode 4 bit ADPCM
                                comment = "DMA 4 bit ADPCM\n";
                                dsp_args_left = 2;
                                break;
                            case 0x75: // DMA mode 4 bit ADPCM w/ref byte
                                comment = "DMA 4 bit ADPCM w/ref\n";
                                dsp_args_left = 2;
                                break;
                            case 0x76: // DMA mode 2.6 bit ADPCM
                                comment = "DMA 2.6 bit ADPCM n";
                                dsp_args_left = 2;
                                break;
                            case 0x77: // DMA mode 2.6 bit ADPCM w/ref byte
                                comment = "DMA 2.6 bit ADPCM w/ref\n";
                                dsp_args_left = 2;
                                break;
                            case 0x78: // reset buffer
                                comment = "Reset buffer\n";
                                dsp_args_left = 0;
                                break;
                            default:
                                comment = "****** Unknown cmd\n\n";
                                break;
                            }
                        strcat(outbuffer,comment);
                        }
                    else
                        {
                        dsp_args_left--;
                        sprintf(outbuffer,"22C W %.2X\n",val);
                        }
                    fwrite(outbuffer,strlen(outbuffer),1,handle);
                    }
                i++;
                break;
            case DMA_DEBUG:
                // page,cmd,addrlo,cmd,addrhi,cmd,countlo,cmd,counthi
                if(to_screen)
                    {
                    printf("Write F%.2X 0x%.2X\n",data_ptr[i],data_ptr[i+1]);
                    }
                else
                    {
                    // +1 = page, +3 = lsb addr +5 = msb addr
                    dma_addr = ((long)(data_ptr[i+1]))<<16;
                    dma_addr += ((long)(data_ptr[i+5]))<<8;
                    dma_addr += ((long)(data_ptr[i+3]));
                    dma_count = ((unsigned int)(data_ptr[i+9]))<<8;
                    dma_count += ((unsigned int)(data_ptr[i+7]));
                    sprintf(outbuffer,"            DMA addr %p length %x\n",dma_addr,dma_count);
                    fwrite(outbuffer,strlen(outbuffer),1,handle);
                    i+= 8;    // past excess data ....
                    }
                i++;
                break;
            case DMA_MARKER:
                switch (data_ptr[i+1])
                    {
                    case DMA_HALT_MARK:
                        comment = "DMA HALT";
                        break;
                    case DMA_INIT_MARK:    
                        comment = "DMA INIT RESET";
                        break;
                    case DMA_PROG_BLOCK_MARK:
                        comment = "DMA PROG BLOCK SIZE";
                        break;
                    case DMA_SET_BLOCK_MARK:
                        comment = "DMA SET_BLOCK SIZE";
                        break;
                    case DMA_IRQ_MARK:
                        comment = "DMA IRQ HANDLER";
                        break;
                    case DMA_ADC_BLOCK_MARK:
                        comment = "DMA ADC BLOCK";
                        break;
                    case DMA_ADPCM_MARK:
                        comment = "DMA ADPCM";
                        break;
                    case DMA_ADPCM_REF_MARK:
                        comment = "DMA ADPCM with REF";
                        break;
                    case DMA_IRQ_ADPCM_MARK:
                        comment = "DMA IRQ (ADPCM)";
                        break;
                    }
                stlen = sprintf(outbuffer,"F%.2X 0x%.2X  ; %s\n",
                            data_ptr[i],data_ptr[i+1],comment);
                fwrite(outbuffer,stlen,1,handle);
                i++;
                break;
            case TIMER_MARKER:
                if(to_screen) printf("Write F%.2X 0x%.2X\n",data_ptr[i],data_ptr[i+1]);
                else
                    {
                    stlen = sprintf(outbuffer,"F%.2X 0x%.2X  ; Timer Fired\n",
                                data_ptr[i],data_ptr[i+1]);
                    fwrite(outbuffer,stlen,1,handle);
                    }
                i++;
                break;
            case MARKER_CODE:
                if(to_screen) printf("Write F%.2X 0x%.2X\n",data_ptr[i],data_ptr[i+1]);
                else
                    {
                    stlen = sprintf(outbuffer,"F%.2X 0x%.2X  ; 1111111111\n",
                                data_ptr[i],data_ptr[i+1]);
                    fwrite(outbuffer,stlen,1,handle);
                    }
                i++;
                break;
            case MARKER4_CODE:
                if(to_screen) printf("Write F%.2X 0x%.2X\n",data_ptr[i],data_ptr[i+1]);
                else
                    {
                    stlen = sprintf(outbuffer,"F%.2X 0x%.2X  ; 4444444444\n",
                                data_ptr[i],data_ptr[i+1]);
                    fwrite(outbuffer,stlen,1,handle);
                    }
                i++;
                break;
            case MARKER3_CODE:
                if(to_screen) printf("Write F%.2X 0x%.2X\n",data_ptr[i],data_ptr[i+1]);
                else
                    {
                    stlen = sprintf(outbuffer,"F%.2X 0x%.2X  ; 3333333333\n",
                                data_ptr[i],data_ptr[i+1]);
                    fwrite(outbuffer,stlen,1,handle);
                    }
                i++;
                break;
            case MARKER2_CODE:
                if(to_screen) printf("Write F%.2X 0x%.2X\n",data_ptr[i],data_ptr[i+1]);
                else
                    {
                    stlen = sprintf(outbuffer,"F%.2X 0x%.2X  ; 2222222222\n",
                                data_ptr[i],data_ptr[i+1]);
                    fwrite(outbuffer,stlen,1,handle);
                    }
                i++;
                break;
            default:
                if(to_screen) 
                    printf("Invalid Debug code found: 0x%.2X\n",data_ptr[i]);
                else
                    {
                    stlen = sprintf(outbuffer,"; INVALID %.2X\n",data_ptr[i]);
                    fwrite(outbuffer,stlen,1,handle);
                    }
                break;
            }
        }
    if(!abort)
        if(to_screen) printf("End of Data\t\t\t\t\t\n");
        else printf("Data successfully written to file\n");
    (void)getch();
    fclose(handle);
}

/************************************************************************
    reset_values()
************************************************************************/
void reset_values(void)
{
    *nmi_count = 0;
    *nmi_table_count = 0;
}

/************************************************************************
    play_file()
************************************************************************/
void play_file(void)
{
    char filename[15];
    char line[50];
    FILE *handle;
    int port, value, write, key;
    unsigned long i, seq_time = 0;
    unsigned int count = 0;

    done = FALSE;
    printf("Enter filename to play from: ");
    gets(filename);
    handle = fopen(filename,"rt");
    if(handle == NULL)
        {
        printf("Unsuccessfull open of file %s\n",filename);
        done = TRUE;
        }
    else
        {
        printf("File %s open for read...\n",filename);
        printf("Press any key to cycle through commands or <ESC> to quit...\n");
        if(bioskey(0) == K_ESC)
            done = TRUE;
        clrscr();
        }

    /** do this until esc is pressed or eof **/
    while(!done)
        {
        count++;
        fgets(line,100,handle);
        if(feof(handle))
            break;
        if (strcmp(line,"\n") == 0)
            continue;
        port = hex2dec(line,3); /** shouldn't be wrong **/
        if (port <= 0xFFF && port >= 0xff0)
            continue;
        write = line[4] - 'R';
        line[8] = 0;
        printf("%s  - ?",line);

        /** if not sequencing now, get keystroke every time **/
        if(seq_time == 0)
            {
            key = getch();
            if(key == 0x1b)
                {
                done = TRUE;
                printf("\bNot Executed\n");
                }
            else if(key >= '0' && key <= '9')
                seq_time = key-'0';
            }

        /** Must be sequencing - see if kbhit **/
        else if(kbhit())
                seq_time = 0;

        /** no key hit and am sequencing - wait time ... **/
        else
            for(i = 0; i < (16L<<(seq_time<<1))/(seq_time<<2); i++)
                inp(0x201);

        /** Done every time done is FALSE **/
        if(!done)
            {
            if(count%2)
                printf("\b%u",count/2);
            else
                printf("\b\b\b   \b\b\b");
            printf("\n");
            //printf("\bExecuted\n");
            if(write)
                {
                value = hex2dec(&line[6],2); /** shouldn't be wrong **/
                outp(port,value);
                }
            else
                inp(port);
            }
        }
}

/************************************************************************
    hex2dec()
    
    converts a hex string to decimal
************************************************************************/
int hex2dec(char * line, int digits)
{
    int retval = 0;
    int index = 0;
    
    if(digits > 2)
        {
        if(line[index] >= 'A' && line[index] <= 'F')       /** A-F **/
            retval = (line[index]-'A'+10)*256;
        else if(line[index] >= 'a' && line[index] <= 'f')  /** a-f **/
            retval = (line[index]-'a'+10)*256;
        else if(line[index] >= '0' && line[index] <= '9')  /** 0-9 **/
            retval = (line[index]-'0')*256;
        else return -1;
        index++;
        }
    else
        index = 0;

    if(digits > 1)
        {
        if(line[index] >= 'A' && line[index] <= 'F')       /** A-F **/
            retval += (line[index]-'A'+10)*16;
        else if(line[index] >= 'a' && line[index] <= 'f')  /** a-f **/
            retval += (line[index]-'a'+10)*16;
        else if(line[index] >= '0' && line[index] <= '9')  /** 0-9 **/
            retval += (line[index]-'0')*16;
        else return -1;
        index++;
        }
    else 
        index = 0;

    if(line[index] >= 'A' && line[index] <= 'F')       /** A-F **/
        retval += (line[index]-'A'+10);
    else if(line[index] >= 'a' && line[index] <= 'f')  /** a-f **/
        retval += (line[index]-'a'+10);
    else if(line[index] >= '0' && line[index] <= '9')  /** 0-9 **/
        retval += (line[index]-'0');
    else return -1;

    return retval;
}

/************************************************************************
    send_commands()
    
************************************************************************/
void send_commands(void)
{
    printf("You will be asked to specify the register number and value\n");

    printf("for downloading to the ADLIB ports.\nPress <ESC> to exit\n");
    printf("Press any key to continue...");
    (void)getch();
    clrscr();
    while(do_single_command()) printf("\n");
}

#define GETTING_REG 1
#define GETTING_VAL 2
/************************************************************************
    do_single_command()
    
************************************************************************/
int do_single_command(void)
{
    int key, state = GETTING_REG, index = 0, got_two = FALSE;
    char hexnum[5];
    int reg = 0, value = 0;
    int i, retval = 1;

    printf("Register: ");
    while(1)
        {
        if(got_two)
            {
            got_two = FALSE;
            key = 0x0d;
            }
        else
            key = getch();
        if(key == 0x1b) /** esc **/
            {
            retval = 0;
            break;
            }
        else if(key == 0x08) /** BS **/
            {
            if(index)
                {
                printf("\b \b");
                hexnum[index] = 0x00;
                index--;
                }
            }
        else if(key == 0x0d) /** CR **/
            {
            if(state == GETTING_REG)
                {
                if(index)
                    reg = hex2dec(hexnum,strlen(hexnum));/** better be right **/
                while(index != 2) /** those stupid little things I do **/
                    {
                    printf(" ");
                    index++;
                    }
                index = 0;
                printf("      Value: ");
                state = GETTING_VAL;
                }
            else /** state better be GETTING_VAL **/
                {
                if(index)
                    value = hex2dec(hexnum,strlen(hexnum));
                send(reg,value);
                while(index != 2)
                    {
                    printf(" ");
                    index++;
                    }
                index = 0;
                printf("      Sent %.02X to register %.02X",value,reg);
                state = GETTING_REG;
                break;
                }
            }
        else /** good key **/
            {
            if((key >= '0' && key <= '9') ||
               (key >= 'A' && key <= 'F') ||
               (key >= 'a' && key <= 'f'))
                {
                putch(key);
                hexnum[index] = (char)(key & 0x00ff);
                index++;
                hexnum[index] = 0x00;
                if(index == 2)
                    got_two = TRUE;
                }
            }
        }
    return retval;
}

/************************************************************************
    toggle_line()
    
************************************************************************/
void toggle_line_in(void)
{
    if(line_in)
        {
        line_in = FALSE;
        sbosdata->mixer_mask |= 0x01; /** shut it off **/
        }
    else
        {
        line_in = TRUE;
        sbosdata->mixer_mask &= 0xFE; /** turn it on **/
        }
    outp(0x220,sbosdata->mixer_mask);
}

/************************************************************************
    do_map()
    
************************************************************************/
void do_map(void)
{
    /** Active channel is 0-8 **/
    /** modulator is TRUE or FALSE depending on operator number - carr/mod **/

    done = FALSE;
    can_force = FALSE;
    while(!done)
        {
        draw_map(active_chan,modulator,oper_index);
        get_input();
        }
}

/************************************************************************
    get_input()
    
************************************************************************/
void get_input(void)
{
    int key, value, val2, temp;
    unsigned int i,j;
    int oct, freqh, freql;

    printf("\n");
    if(modulator) printf("M> ");
    else printf("C> ");
    
    key = getch();
    if(key == 0x1b) /** esc **/
        done = TRUE;
    else if(key == 'l' || key == 'L')
        toggle_line_in();
    else if(key == 'p' || key == 'P') /** Play active channel **/
        {
        value = fmregs[0xb0+active_chan];
        value &= ~0x20;                  /** mask off 20 **/
        send(0xb0+active_chan,value);
        value |= 0x20;                   /** put 20 bit back on **/
        send(0xb0+active_chan,value);
        }
    else if(key == 'o' || key == 'O') /** turn all channels Off **/
        {
        for(j = 0; j < 9; j++)
            {
            value = fmregs[0xb0+j];
            value &= ~0x20;  /** mask off 20 **/
            send(0xb0+j,value);
            }
        }
    else if(key > '0' && key <= '9') /** set active Channel 0-9 **/
        {
        if((active_chan == (key - '1')) && can_force)
            if(chan_disable[active_chan] == 0)
                chan_disable[active_chan] = 1;
            else
                chan_disable[active_chan] = 0;
        active_chan = key - '1';
        oper_index = opmodtab[active_chan]; /** switch to modulator **/
        modulator = TRUE;
        }
    else if(key == '	') /** tab - switch between modulator / carrier **/
        {
        if(modulator) /** get carrier offset for current channel **/
            {
            modulator = FALSE;
            oper_index = opcartab[active_chan];
            }
        else  /** get modulator offset for current channel **/
            {
            modulator = TRUE;
            oper_index = opmodtab[active_chan];
            }
        }
    else if(key == 'e' || key == 'E') /** Feedback **/
        {
        value = fmregs[0xC0+active_chan];
        val2 = ((value & 0x0e) >> 1); /** get the old one **/
        printf("New Feedback Chan %d (0-7) %.1X: ",active_chan,val2);
        value &= 0xf1;  /** mask off the old one **/
        line[0] = (char)getch();
        line[1] = 0;
        temp = hex2dec(line,1);
        if(temp != -1) val2 = temp; /** <CR> leaves old value **/
        value |= (char)((val2 & 0x07)<<1);
        send(0xC0+active_chan,value);
        }
    else if(key == 'r' || key == 'R') /** Release **/
        {
        value = fmregs[0x80+oper_index];
        val2 = value & 0x0f; /** get the old one **/
        printf("New Release Chan %d (0-F) %.1X: ",active_chan,val2);
        value &= 0xf0;  /** mask off the old one **/
        line[0] = (char)getch();
        line[1] = 0;
        temp = hex2dec(line,1);
        if(temp != -1) val2 = temp; /** <CR> leaves old value **/
        value |= (char)(val2 & 0x0f);
        send(0x80+oper_index,value);
        }
    else if(key == 'a' || key == 'A') /** Attack **/
        {
        value = fmregs[0x60+oper_index];
        val2 = (value >> 4) & 0x0f; ; /** get the old one **/
        printf("New Attack Chan %d (0-F) %.1X: ",active_chan,val2);
        value &= 0x0f;  /** mask off the old one **/
        line[0] = (char)getch();
        line[1] = 0;
        temp = hex2dec(line,1);
        if(temp != -1) val2 = temp; /** <CR> leaves old value **/
        value |= (char)(val2 << 4);
        send(0x60+oper_index,value);
        }
    else if(key == 'd' || key == 'D') /** Decay **/
        {
        value = fmregs[0x60+oper_index];
        val2 = value & 0x0f;
        printf("New Decay Chan %d (0-F) %.1X: ",active_chan,val2);
        value &= 0xf0;  /** mask off the old one **/
        line[0] = (char)getch();
        line[1] = 0;
        temp = hex2dec(line,1);
        if(temp != -1) val2 = temp; /** <CR> leaves old value **/
        value |= (char)(val2 & 0x0f);
        send(0x60+oper_index,value);
        }
    else if(key == 's' || key == 'S') /** Sustain **/
        {
        value = fmregs[0x80+oper_index];
        val2 = (value >> 4) & 0x0f;
        printf("New Sustain Chan %d (0-F) %.1X: ",active_chan,val2);
        value &= 0x0f;  /** mask off the old one **/
        line[0] = (char)getch();
        line[1] = 0;
        temp = hex2dec(line,1);
        if(temp != -1) val2 = temp; /** <CR> leaves old value **/
        value |= (char)(val2 << 4);
        send(0x80+oper_index,value);
        }
    else if(key == 'm' || key == 'M') /** Modify register **/
        {
        do_single_command();
        }
    else if(key == 'f' || key == 'F') /** Modify frequency **/
        {
        value = fmregs[0xb0+active_chan]; /** bits 2,3,4 **/

        /** change octave **/
        oct = (value >> 2) & 0x07; /** save old **/
        printf("Enter new octave (0-7) %d: ",oct);
        line[0] = (char)getch();
        line[1] = 0;
        temp = hex2dec(line,1);
        if(temp != -1) oct = temp; /** <CR> leaves old value **/

        /** change frequency (H) **/
        freqh = value & 0x03;
        printf("Frequency(H) (0-3) %d: ",freqh);
        line[0] = (char)getch();
        line[1] = 0;
        temp = hex2dec(line,1);
        if(temp != -1) freqh = temp; /** <CR> leaves old value **/

        /** change frequency (L) **/
        freql = fmregs[0xa0+active_chan];
        printf("Frequency(L) (0-FF) %.2X: ",freql);
        line[0] = (char)getch();
        if(line[0] != 0x0d) /** first char is good **/
            {
            putch(line[0]);
            line[1] = (char)getch();
            if(line[1] == 0x0d)
                line[1] = 0;
            else
                line[2] = 0;
            temp = hex2dec(line,strlen(line));
            if(temp != -1) freql = temp;
            }

        /** Send freqh and oct **/
        value &= 0xE0;   /** clear everything else **/
        value |= (freqh &= 0x03); /** get in form I need **/
        value |= ((oct & 0x07) << 2); /** get in form I need **/
        send(0xb0+active_chan,value);

        /** send freql **/
        send(0xa0+active_chan,freql);
        }
    else if(key == 'w' || key == 'W') /** Modify Wave **/
        {
        value = fmregs[0xe0 + oper_index];
        value &= 0x03;
        printf("Wave (0-3) %.1X: ",value);
        line[0] = (char)getch();
        line[1] = 0;
        temp = hex2dec(line,1);
        if(temp != -1) value = temp; /** <CR> leaves old value **/
        send(0xe0+oper_index,value);
        }
    else if(key == 'v' || key == 'V') /** modify volume **/
        {
        value = fmregs[0x40 + oper_index];
        value &= 0x3f;
        printf("Volume (0-3F) %.2X: ",value);
        line[0] = (char)getch();
        if(line[0] != 0x0d) /** first char is good **/
            {
            putch(line[0]);
            line[1] = (char)getch();
            if(line[1] == 0x0d)
                line[1] = 0;
            else
                line[2] = 0;
            temp = hex2dec(line,strlen(line));
            if(temp != -1) value = (temp & 0x3f);
            }
        send(0x40+oper_index,value);
        }

    if(!can_force)
        return;

    if(key == 'c' || key == 'C') /** force instrument **/
        {
        if(force_instrument[active_chan] == 1) /** shut it off **/
            force_instrument[active_chan] = -1; /** force a reselect too **/
        else /** turn it on **/
            force_instrument[active_chan] = 1;
        }
    else if(key == 'i' || key == 'I') /** Set instrument Number **/
        {
        if(force_instrument[active_chan] == 1)
            {
            printf("\nNew instrument number: ");
            gets(line);
            temp = atoi(line);
            if((temp >= 0) && (temp <= 127))
                {
                cur_instrument[active_chan] = temp;
                }
            }
        }
    else if(key == 'b' || key == 'B') /** Set new bank number **/
        {
        if(force_instrument[active_chan] == 1)
            {
            printf("\nNew bank(1=Basic - 2=Mel - 3=Perc - 4=Effect: ");
            line[0] = (char)getch();
            line[1] = 0;
            temp = hex2dec(line,1);
            if((temp > 0) && (temp < 5))
                {
                cur_bank[active_chan] = temp;
                }
            }
        }
    else if(key == 'n') /** Next instrument **/
        {
        if(force_instrument[active_chan] == 1)
            {
            if(cur_instrument[active_chan]==127)cur_instrument[active_chan] = 0;
            else (cur_instrument[active_chan])++;
            }
        }
    else if(key == 'N') /** Prev Instrument **/
        {
        if(force_instrument[active_chan] == 1)
            {
            if(cur_instrument[active_chan]==0)cur_instrument[active_chan] = 127;
            else (cur_instrument[active_chan])--;
            }
        }
}

/************************************************************************
    send()
    
************************************************************************/
void send(unsigned char reg, unsigned char val)
{
    outp(0x388,reg);
    wait();
    outp(0x389,val);
    wait();
}

/************************************************************************
    do_snap()
    
************************************************************************/
void do_snap(void)
{
    char filename[15];
    FILE *handle;
    int cnum;
    
    printf("Enter filename to save snap to: ");
    gets(filename);
    handle = fopen(filename,"wt");
    if(handle == NULL)
        {
        printf("Unsuccessfull open of file %s\n",filename);
        done = TRUE;
        }
    else
        {
        for(cnum = 0; cnum < 9; cnum++)
            {
            fprintf(handle,"\t\t\t------ Channel %d ------\n",cnum);
            fprintf(handle,"Chosen Bank: %d\n",snap[cnum].bank);
            fprintf(handle,"Chosen Instrument: %d GM (%d FAT)\n",
                snap[cnum].instrument,snap[cnum].instrument+PERC_OFFSET);
            fprintf(handle,"- Channel information -\n");
            if(snap[cnum].chan.con & 0x01)
                fprintf(handle,"Connection: Sine Wave\n");
            else
                fprintf(handle,"Connection: FM\n");
            fprintf(handle,"Frequency (B,H,L): %X %X %X\n",
                (((snap[cnum].chan.freqh) & 0x1C) >> 2),
                  (snap[cnum].chan.freqh & 0x02),
                  (snap[cnum].chan.freql));
            fprintf(handle,"Feedback: %X\n",(snap[cnum].chan.con & 0x0e) >> 1);
            fprintf(handle,"\n- Modulator -\n");
            fprintf(handle,"Attack:  %X\n",snap[cnum].mod.attack);
            fprintf(handle,"Decay:   %X\n",snap[cnum].mod.decay);
            fprintf(handle,"Sustain: %X\n",snap[cnum].mod.sustain);
            fprintf(handle,"Release: %X\n",snap[cnum].mod.release);
            fprintf(handle,"Volume:  %X\n",snap[cnum].mod.level);
            fprintf(handle,"KSL:     %X\n",snap[cnum].mod.ksl);
            fprintf(handle,"Mult:    %X\n",snap[cnum].mod.param & 0x0f);
            if(snap[cnum].mod.param & 0x20)
                fprintf(handle,"Sustain: ON\n");
            else
                fprintf(handle,"Sustain: OFF\n");
            if(snap[cnum].mod.param & 0x10)
                fprintf(handle,"KSR:     ON\n");
            else
                fprintf(handle,"KSR:     OFF\n");
            fprintf(handle,"\n-  Carrier  -\n");
            fprintf(handle,"Attack:  %X\n",snap[cnum].car.attack);
            fprintf(handle,"Decay:   %X\n",snap[cnum].car.decay);
            fprintf(handle,"Sustain: %X\n",snap[cnum].car.sustain);
            fprintf(handle,"Release: %X\n",snap[cnum].car.release);
            fprintf(handle,"Volume:  %X\n",snap[cnum].car.level);
            fprintf(handle,"KSL:     %X\n",snap[cnum].car.ksl);
            fprintf(handle,"Mult:    %X\n",snap[cnum].car.param & 0x0f);
            if(snap[cnum].car.param & 0x20)
                fprintf(handle,"Sustain: ON\n");
            else
                fprintf(handle,"Sustain: OFF\n");
            if(snap[cnum].car.param & 0x10)
                fprintf(handle,"KSR:     ON\n");
            else
                fprintf(handle,"KSR:     OFF\n");
            fprintf(handle,"\n");
            }
        fclose(handle);
        }
}

/************************************************************************
    draw_map()
    
************************************************************************/
void draw_map(unsigned int active_chan, unsigned int modulator, 
              unsigned char index)
{
    int i;
    
    clrscr();
    if(modulator)
        printf("Active Channel %d - Modulator ",active_chan+1);
    else
        printf("Active Channel %d - Carrier   ",active_chan+1);
    if(line_in)
        printf("[Play-Off-Line-Modreg-A-D-S-R-Freq-Vol]   L\n");
    else
        printf("[Play-Off-Line-Modreg-A-D-S-R-Freq-Vol]    \n");

    for(i = 0; i< pointab[index]; i++) printf(" ");
    printf("x\n");
    printf("20  21  22  23  24  25  28  29  2A  2B  2C  2D  30  31  32  33  34  35\n");
    for(i = 0; i < 6; i++) printf("%.02X  ",fmregs[i+0x20]);
    for(i = 8; i < 14;i++) printf("%.02X  ",fmregs[i+0x20]);
    for(i = 16;i < 22;i++) printf("%.02X  ",fmregs[i+0x20]);

    printf("\n\n");
    printf("40  41  42  43  44  45  48  49  4A  4B  4C  4D  50  51  52  53  54  55\n");
    for(i = 0; i < 6; i++) printf("%.02X  ",fmregs[i+0x40]);
    for(i = 8; i < 14;i++) printf("%.02X  ",fmregs[i+0x40]);
    for(i = 16;i < 22;i++) printf("%.02X  ",fmregs[i+0x40]);

    printf("\n\n");
    printf("60  61  62  63  64  65  68  69  6A  6B  6C  6D  70  71  72  73  74  75\n");
    for(i = 0; i < 6; i++) printf("%.02X  ",fmregs[i+0x60]);
    for(i = 8; i < 14;i++) printf("%.02X  ",fmregs[i+0x60]);
    for(i = 16;i < 22;i++) printf("%.02X  ",fmregs[i+0x60]);

    printf("\n\n");
    printf("80  81  82  83  84  85  88  89  8A  8B  8C  8D  90  91  92  93  94  95\n");
    for(i = 0; i < 6; i++) printf("%.02X  ",fmregs[i+0x80]);
    for(i = 8; i < 14;i++) printf("%.02X  ",fmregs[i+0x80]);
    for(i = 16;i < 22;i++) printf("%.02X  ",fmregs[i+0x80]);

    printf("\n\n");
    printf("A0  A1  A2  A3  A4  A5  A6  A7  A8    ");
    printf("B0  B1  B2  B3  B4  B5  B6  B7  B8\n");
    for(i = 0; i < 9; i++) printf("%.02X  ",fmregs[i+0xA0]);
    printf("  ");
    for(i = 0; i < 9; i++) printf("%.02X  ",fmregs[i+0xB0]);

    printf("\n\n");
    printf("C0  C1  C2  C3  C4  C5  C6  C7  C8     BD\n");
    for(i = 0; i < 9; i++) printf("%.02X  ",fmregs[i+0xC0]);

    printf("   %.2X",fmregs[0xbd]);

    printf("\n\n");
    printf("E0  E1  E2  E3  E4  E5  E8  E9  EA  EB  EC  ED  F0  F1  F2  F3  F4  F5\n");
    for(i = 0; i < 6; i++) printf("%.02X  ",fmregs[i+0xE0]);
    for(i = 8; i < 14;i++) printf("%.02X  ",fmregs[i+0xE0]);
    for(i = 16;i < 22;i++) printf("%.02X  ",fmregs[i+0xE0]);
    printf("\n");
}

void upload_deb_buff()
{
unsigned int i;
for(i = 0; (i < 0xfff) && (!done); i++)
    {
    data_ptr[i] = read_dram(32002+i);
    }
*nmi_table_count=0xfff;
}

/************************************************************************
    dump_registers()
    
************************************************************************/
void dump_registers(int voice)
{
FILE *fp;
int i;
int bit8[32] = {1,0,0,0,0,0,1,1,1,0,0,0,0,1,1,1,
                1,0,0,0,1,1,0,1,1,1,0,0,0,0};
int bit8_g[33] = {1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,
                1,0,0,1,0,0,0,0,0,1,1,1,1,1,1,1,1};

unlink("sbosregs.txt");
fp = fopen("sbosregs.txt","wt");

for(i=0;i<32;i++)
    {
    outp(0x323,0x41+i);
    if (bit8_g[i])
        fprintf(fp,"0x%02X = 0x%02X\n",0x41+i,inp(0x325));
    else
        fprintf(fp,"0x%02X = 0x%04X\n",0x41+i,inpw(0x324));
    }

fprintf(fp,"\n");

outp(0x322,voice);
for(i=0;i<30;i++)
    {
    outp(0x323,0x80+i);
    if (bit8[i])
        fprintf(fp,"0x%02X = 0x%02X\n",0x80+i,inp(0x325));
    else
        fprintf(fp,"0x%02X = 0x%04X\n",0x80+i,inpw(0x324));
    }
printf("Hit any key to continue ....");
getch();
printf("\n");
fclose(fp);
}

/************************************************************************
    do_inst()
    
************************************************************************/
void do_inst(void)
{
    int far *ptr;
    int value,temp,val2;
    unsigned char oper;
    int i;

    int feedback;
    int car_attack;
    int car_decay;
    int car_sustain;
    int car_release;
    int car_volume;
    int mod_attack;
    int mod_decay;
    int mod_sustain;
    int mod_release;
    int mod_volume;

    done = FALSE;
    can_force = TRUE;
    while(!done)
        {
        clrscr();
        printf("Channel %d - Instrument Helper",active_chan+1);
        if(force_instrument[active_chan] == 1)
            printf("         forCe is ON");
        else /** should be either 0 or -1 **/
            printf("         forCe is OFF");
        if(chan_disable[active_chan] == 1)
            printf("    Channel DISABLED\n\n");
        else
            printf("    Channel ENABLED\n\n");
        printf("current Instrument: %d  ",cur_instrument[active_chan]);
        for(i = 0; i<PICK_LIST_ELEMENTS; i++)
            {
            if (*(pick_list+(active_chan*PICK_LIST_SIZE)+(i*2)) > 127)
                printf("(p%u %u) ",
                    *(pick_list+(active_chan*PICK_LIST_SIZE)+(i*2))&0x7f, 
                    *(pick_list+(active_chan*PICK_LIST_SIZE)+(i*2)+1));
            else
                printf("(%u %u) ",
                    *(pick_list+(active_chan*PICK_LIST_SIZE)+(i*2)), 
                    *(pick_list+(active_chan*PICK_LIST_SIZE)+(i*2)+1));
            }
        printf("\n");
        printf("current Bank:  ");
        if(cur_bank[active_chan] == TYPE_BASIC)
            printf("Basic\n");
        else if(cur_bank[active_chan] == TYPE_MELODIC)
            printf("Melodic\n");
        else if(cur_bank[active_chan] == TYPE_PERC)
            printf("Percussion\n");
        else if(cur_bank[active_chan] == TYPE_EFFECT)
            printf("Effect\n");
        else
            printf("Unknown Bank #%d\n",cur_bank[active_chan]);
        printf("\n");
        
        feedback   = (fmregs[0xC0+active_chan] >> 1) & 0x07;

        oper = opcartab[active_chan];
        car_attack = (fmregs[0x60+oper] >> 4) & 0x0f;
        car_decay  = (fmregs[0x60+oper] & 0x0f);
        car_sustain= (fmregs[0x80+oper] >> 4) & 0x0f;
        car_release= (fmregs[0x80+oper] & 0x0f);
        car_volume = (fmregs[0x40+oper] & 0x3f);

        oper = opmodtab[active_chan];
        mod_attack = ((fmregs[0x60+oper]) >> 4) & 0x0f;
        mod_decay  = ((fmregs[0x60+oper]) & 0x0f);
        mod_sustain= ((fmregs[0x80+oper]) >> 4) & 0x0f;
        mod_release= ((fmregs[0x80+oper]) & 0x0f);
        mod_volume = ((fmregs[0x40+oper]) & 0x3f);

        printf("Channel Parameters\n");
        printf("fEedback   %X\n",feedback);
        printf("\n");
        printf("Modulator Parameters\n");
        printf("Attack     %X\n",mod_attack);
        printf("Decay      %X\n",mod_decay);
        printf("Sustain    %X\n",mod_sustain);
        printf("Release    %X\n",mod_release);
        printf("Volume     %.2X\n",mod_volume);
        printf("\n");
        printf("Carrier Parameters\n");
        printf("Attack     %X\n",car_attack);
        printf("Decay      %X\n",car_decay);
        printf("Sustain    %X\n",car_sustain);
        printf("Release    %X\n",car_release);
        printf("Volume     %.2X\n",car_volume);

        get_input();
        }
}

/************************************************************************
    do_mpu()
    
************************************************************************/
void do_mpu(void)
{
    int done = FALSE;
    int key;
    unsigned long temp_notes_playing;
    int cur_channel = 0;
    int cur_note = 69;
    int cur_velocity = 0x70;
    int cur_pan = 0x40;
    int pedal = 0;
    int i;
    unsigned char line[10];
    int value, temp;

    outp(0x331,0xff);
    wait();
    while(!done)
        {
redraw:
        temp_notes_playing = *notes_playing;
        clrscr();
        printf("MPU-401 Helper               Currently Playing Channels\n");
        printf("                          0         1         2         3\n");
        printf("                          01234567890123456789012345678901\n");
        printf("                          ");
        for(i = 0; i<32; i++)
            {
            if(*notes_playing & (1L<<i)) printf("x");
            else printf(" ");
            }
        printf("\n\n");
        printf("Channel:    %d\n",cur_channel);
        printf("Instrument: %d\n",chan_instrument[cur_channel]);
        printf("Note:       %Xh\n",cur_note);
        printf("Velocity:   %Xh\n",cur_velocity);
        printf("(B)pan:     %Xh\n",cur_pan);
        if(pedal == 1) printf("peDal:      DOWN\n");
        else printf("peDal:      UP\n");
        printf("R - full reset\n");
        printf("\n>>");

        while(1)
            {
            if(kbhit())
                break;
            else if(temp_notes_playing != *notes_playing)
                goto redraw;
            }

        key = getch();
        if(key == 0x1b) /** esc **/
            done = TRUE;
        else if((key == 'b') || (key == 'B'))
            {
            printf("Enter new pan: ");
            line[0] = (char)getch();
            if(line[0] != 0x0d) /** first char is good **/
                {
                putch(line[0]);
                line[1] = (char)getch();
                if(line[1] == 0x0d)
                    line[1] = 0;
                else
                    line[2] = 0;
                temp = hex2dec(line,strlen(line));
                if(temp != -1) value = (temp & 0x7f);
                cur_pan = value;
                }
            outp(0x330,0xb0+cur_channel);
            wait();
            outp(0x330,10);
            wait();
            outp(0x330,cur_pan);
            wait();
            }
        else if((key == 'c') || (key == 'C'))
            {
            if(cur_channel == 0)
                cur_channel = 9;
            else
                cur_channel = 0;
            }
        else if((key == 'd') || (key == 'D'))
            {
            /** pedal **/
            outp(0x330,0xB0+cur_channel);
            wait();
            outp(0x330,0x40); /* damper pedal */
            wait();
            if(pedal)
                {
                pedal = 0;
                outp(0x330,0x00);
                }    
            else
                {
                pedal = 1;
                outp(0x330,0x7f);
                }
            wait();
            }
        else if(key == 'f')
            {
            cur_note++;
            if(cur_note >= 0x7f)
                cur_note = 0;
            }
        else if(key == 'F')
            {
            cur_note--;
            if(cur_note < 0)
                cur_note = 0x7f;
            }
        else if(key == 'N')
            {
            chan_instrument[cur_channel]--;
            if(chan_instrument[cur_channel] == 0xff)
                chan_instrument[cur_channel] = 0x7f;
            }
        else if(key == 'n')
            {
            chan_instrument[cur_channel]++;
            if(chan_instrument[cur_channel] == 128)
                chan_instrument[cur_channel] = 0;
            }
        else if((key == 'O') || (key == 'o'))
            {
            /** stop tone **/
            outp(0x330,0x80+cur_channel);
            wait();
            outp(0x330,cur_note);
            wait();
            outp(0x330,cur_velocity);
            wait();
            }
        else if((key == 'p') || (key == 'P'))
            {
            /** set pan **/
            outp(0x330,0xb0+cur_channel);
            wait();
            outp(0x330,10);
            wait();
            outp(0x330,cur_pan);
            wait();

            /** set instrument **/
            outp(0x330,0xC0+cur_channel);
            wait();
            outp(0x330,chan_instrument[cur_channel]);
            wait();

            /** start tone **/
            outp(0x330,0x90+cur_channel);
            wait();
            outp(0x330,cur_note);
            wait();
            outp(0x330,cur_velocity);
            wait();
            }
        else if((key == 'r') || (key == 'R'))
            {
            outp(0x331,0xff);
            wait();
            }
        else if((key == 'V') || (key == 'v'))
            {
            printf("Enter new volume: ");
            line[0] = (char)getch();
            if(line[0] != 0x0d) /** first char is good **/
                {
                putch(line[0]);
                line[1] = (char)getch();
                if(line[1] == 0x0d)
                    line[1] = 0;
                else
                    line[2] = 0;
                temp = hex2dec(line,strlen(line));
                if(temp != -1) value = (temp & 0x7f);
                cur_velocity = value;
                }
            outp(0x330,0xb0+cur_channel);
            wait();
            outp(0x330,7);
            wait();
            outp(0x330,cur_velocity);
            wait();
            }
        }
}
