/*************************************************************************
 *                dsp.c - DSP<->PC Interface functions                   *
 *-----------------------------------------------------------------------*
 *               Copyright (c) 1995   Jake Janovetz                      *
 *                                                                       *
 * 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     *
 * 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., 675 Mass Ave, Cambridge, MA 02139, USA.             *
 *************************************************************************
 *                                                                       *
 * This is the C module which communicates with the DSP board via the    *
 * ISA bus.  It provides all basic communication needs such as reset,    *
 * code download, host vectors, and data input and output.               *
 ************************************************************************/

#include <stdio.h>
#include <string.h>
#include "dsp.h"

extern _Far16 _Pascal OUTP8(unsigned short, unsigned char);
extern unsigned char _Far16 _Pascal INP8(unsigned short);

/* Host Interface port IDs */
#define HI_ICR  0x0
#define HI_CVR  0x1
#define HI_ISR  0x2
#define HI_IVR  0x3
#define HI_RXH  0x5
#define HI_RXM  0x6
#define HI_RXL  0x7
#define HI_TXH  0x5
#define HI_TXM  0x6
#define HI_TXL  0x7

/* DSP Monitor Commands */
#define CMD_LEAVE      0
#define CMD_GO         1
#define CMD_TRACE      2
#define CMD_EXECUTE    3
#define CMD_READBP     10
#define CMD_WRITEBP    11
#define CMD_CLEARBP    12
#define CMD_READREG    13
#define CMD_WRITEREG   14
#define CMD_READREGS   15
#define CMD_WRITEREGS  16
#define CMD_READSTACK  17
#define CMD_WRITESTACK 18
#define CMD_READMEM    19
#define CMD_WRITEMEM   20

/*
 * The names of the registers in the register file (all lower case)
 * NOTE: These are in the same order as the REG_IDs defined in dsp.h
 *       so that they can be accessed by those IDs.
 */
char *DSP_RegisterName[] =
{
    "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
    "n0", "n1", "n2", "n3", "n4", "n5", "n6", "n7",
    "m0", "m1", "m2", "m3", "m4", "m5", "m6", "m7",
    "x1", "x0", "y1", "y0",
    "a2", "a1", "a0", "b2", "b1", "b0",
    "pc", "sr", "omr", "sp", "la", "lc"
};
 

/* DSP_Port contains the I/O Base Address for the DSP board.
 * This variable is local to this file only
 */
static unsigned short DSP_Port;

/* DSP_setPort
 *-------------
 * Sets the I/O port address to be used for any DSP board accesses.
 * This must be done before any read/writing is done to the board.
 */
void DSP_setPort(unsigned short port)
{
    DSP_Port = port;
}

/* DSP_out
 *---------
 * Writes a single byte to the DSP board.
 */
void DSP_out(unsigned short port, unsigned char data)
{
    OUTP8(DSP_Port + port, data);
}

/* DSP_in
 *--------
 * Reads a single byte from the DSP board.
 */
unsigned char DSP_in(unsigned short port)
{
    return(INP8(DSP_Port + port));
}

/* DSP_reset
 *-----------
 * Sends a code to the DSP board to reset the DSP and peripherals.
 * This is done by setting A14 and writing to the DSP Port.
 */
void DSP_reset()
{
    OUTP8(DSP_Port | 0x4000, 1);
}

/* DSP_write1
 *------------
 * Writes a single byte into the DSP Host Interface's TX register
 */
void DSP_write1(unsigned char datum)
{
    DSP_out(HI_TXL, datum);
}

/* DSP_write2
 *------------
 * Writes two bytes into the DSP Host Interface's TX register
 */
void DSP_write2(unsigned short datum)
{
    DSP_out(HI_TXM, (datum>>8) & 0xff);
    DSP_out(HI_TXL, (datum) & 0xff);
}

/* DSP_write
 *-----------
 * Writes a word (3 bytes) into the DSP Host Interface's TX register
 */
void DSP_write(unsigned long datum)
{
    DSP_out(HI_TXH, (datum>>16) & 0xff);
    DSP_out(HI_TXM, (datum>>8) & 0xff);
    DSP_out(HI_TXL, (datum) & 0xff);
}

/* DSP_writefp
 *-------------
 * Converts a given floating point number to DSP fixed point and
 * writes the result to the DSP Host Interface's TX register
 */
void DSP_writefp(double datum)
{
    unsigned long temp;

    if (datum >= 0.0)
    {
        if (datum >= 1.0)
        {
            DSP_write(0x7ffffUL);
        }
        else
        {
            DSP_write((unsigned long)(temp * (double)0x800000));
        }
    }
    else
    {
        if (datum <= -1.0)
        {
            DSP_write(0x800000UL);
        }
        else
        {
            DSP_write(0x1000000 - (unsigned long)(-datum * (double)0x800000));
        }
    }
}

/* DSP_read1
 *-----------
 * Reads a single byte from the DSP Host Interface's RX register.
 */
unsigned long DSP_read1()
{
    return(DSP_in(HI_RXL));
}

/* DSP_read2
 *-----------
 * Reads two bytes from the DSP Host Interface's RX register.
 */
unsigned long DSP_read2()
{
    unsigned long result;

    result = ((unsigned long)DSP_in(HI_RXM)) << 8;
    result |= ((unsigned long)DSP_in(HI_RXL));
    return(result);
}

/* DSP_read
 *----------
 * Reads a word (3 bytes) from the DSP Host Interface's RX register.
 */
unsigned long DSP_read()
{
    unsigned long result;

    result = ((unsigned long)DSP_in(HI_RXH)) << 16;
    result |= ((unsigned long)DSP_in(HI_RXM)) << 8;
    result |= ((unsigned long)DSP_in(HI_RXL));
    return(result);
}

/* DSP_readfp
 *------------
 * Reads a word from the DSP Host Interface's RX register, then 
 * converts it to floating point.
 */
double DSP_readfp()
{
    unsigned long temp;

    temp = DSP_read();
    if (temp < 0x800000UL)
    {
        return((double)temp / (double)0x800000UL);
    }
    else
    {
        return(-((double)(0x1000000UL - temp)/(double)0x800000));
    }
}

/* DSP_hostVector
 *----------------
 * Writes to the DSP Host Interface Interrupt Vector to cause the
 * DSP to jump to the address given.
 */
void DSP_hostVector(unsigned short address)
{
    if (address != 0)
    {
        DSP_out(HI_CVR, 0x80 | (address / 2));
    }
}

/* DSP_boot
 *----------
 * Resets the DSP and downloads the boot code.
 */
int DSP_boot(char *filename)
{
    FILE         *fp;
    unsigned long pmem[512];
    unsigned long datum;
    char          space, line[82], token[10];
    int           npos, address, maxaddr, i, j;

    fp = fopen(filename, "r");
    if (!fp)
    {
        return(DSP_BOOTERR_NOFILE);
    }

    address = 0;
    maxaddr = 0;
    while (!feof(fp))
    {
        fgets(line, 80, fp);
        npos = 0;
        if (line[npos] == '_')
        {
            sscanf(line, "%s", token);
            npos += strlen(token);
            if (!strcmp(token, "_DATA"))
            {
                sscanf(line + npos, " %c %X\n", &space, &address);
            }
        }
        else if (!strcmp(token, "_DATA"))
        {
            if (space != 'P')
            {
                return(DSP_BOOTERR_ILLEGALSPACE);
            }
            while (npos < strlen(line)-1)
            {
                sscanf(line + npos, "%06X", &datum);
                if (address > 511)
                {
                    return(DSP_BOOTERR_PMEMEXCEEDED);
                }
                if (address > maxaddr)
                {
                    maxaddr = address;
                }
                pmem[address] = datum;
                address++;
                npos += 7;
            }
        }
    }
    fclose(fp);


    /* Reset the DSP, then download the program memory. */
    DSP_reset();
    for (i=0; i<=maxaddr; i++)
    {
        DSP_write(pmem[i]);
    }

    /* Tell the DSP we're done with the download */
    DSP_out(HI_ICR, 0x8);

    return(DSP_NOERROR);
}

/* DSP_download
 *--------------
 * Downloads a file to the target DSP board
 */
int DSP_download(char *filename)
{
    FILE          *fp;
    char           line[82], token[20], space;
    unsigned long  address, length;
    unsigned long *mem;
    unsigned char  d1, d2, d3;
    int            npos, done, i;

    mem = (unsigned long *)malloc(65536 * sizeof(unsigned long));
    fp = fopen(filename, "r");
    if (!fp)
    {
        return(DSP_DOWNLOADERR_NOFILE);
    }
    fgets(line, 80, fp);

    while (!feof(fp))
    {
        npos = 0;
        if (line[npos] == '_')
        {
            sscanf(line, "%s", token);
            npos += strlen(token);
            if (!strcmp(token, "_DATA"))
            {
                sscanf(line + npos, " %c %X\n", &space, &address);
            }
            fgets(line, 80, fp);
        }
        else if (!strcmp(token, "_DATA"))
        {
            done = 0;
            length = 0;
            while (!done)
            {
                npos = 0;
                while (npos < strlen(line) - 1)
                {
                    sscanf(line + npos, "%06X", &mem[length++]);
                    npos += 7;
                }
                fgets(line, 80, fp);
                if (line[0] == '_')
                {
                    done = 1;
                }
            }
            printf("%c: 0x%04X -> 0x%04X\n", space, address, address+length-1);
            DSP_hostVector(VEC_DOWNLOAD);
            switch (space)
            {
                case 'P':
                    DSP_write(0);
                    break;
                case 'X':
                    DSP_write(1);
                    break;
                case 'Y':
                    DSP_write(2);
                    break;
                case 'L':
                    DSP_write(3);
                    break;
                default:
                    return(DSP_DOWNLOADERR_BADFILE);
            }
            DSP_write(address);
            DSP_write(length);
            for (i=0; i<length; i++)
            {
                if (!(DSP_in(HI_ISR) & 0x02))
                {
                    return(DSP_DOWNLOADERR_TARGETNOTREADY);
                }
                DSP_write(mem[i]);
            }
        }
        else
        {
            fgets(line, 80, fp);
        }
    }

    fclose(fp);
    free(mem);
    return(DSP_NOERROR);
}

int DSP_debugEnter()
{
    DSP_hostVector(VEC_MONITOR);
    return(1);
}

int DSP_debugLeave()
{
    DSP_write(CMD_LEAVE);
    return(1);
}

int DSP_debugGo()
{
    DSP_write(CMD_GO);
    return(1);
}

int DSP_debugTrace()
{
    DSP_write(CMD_TRACE);
    return(1);
}

int DSP_debugExecute(unsigned long word0, unsigned long word1)
{
    DSP_write(CMD_EXECUTE);
    DSP_write(word0);
    DSP_write(word1);
    return(1);
}

int DSP_debugQueryBP(unsigned long *num,
                     unsigned long addrs[DSP_MAX_BREAKPOINTS])
{
    int i, j;

    DSP_write(CMD_READBP);
    *num = DSP_read();
    for (i=0; i<DSP_MAX_BREAKPOINTS; i++)
    {
        if (i < *num)
        {
            addrs[i] = DSP_read();
        }
        else
        {
            addrs[i] = 0;
        }
    }
    return 1;
}

int DSP_debugSetBP(unsigned long addr)
{
    DSP_write(CMD_WRITEBP);
    DSP_write(addr);
    return(1);
}

int DSP_debugClearBP(unsigned long addr)
{
    DSP_write(CMD_CLEARBP);
    DSP_write(addr);
    return(1);
}

int DSP_debugWriteRegister(unsigned long reg, unsigned long value)
{
    if (reg <= DSP_REG_MAX)
    {
        DSP_write(CMD_WRITEREG);
        DSP_write(reg);
        DSP_write(value);
        return(1);
    }
    else
    {
        return(0);
    }
}

int DSP_debugReadRegister(unsigned long reg, unsigned long *value)
{
    if (reg <= DSP_REG_MAX)
    {
        DSP_write(CMD_READREG);
        DSP_write(reg);
        *value = DSP_read();
        return(1);
    }
    else
    {
        return(0);
    }
}

int DSP_debugWriteRegisters(unsigned long regs[DSP_REG_MAX])
{
    int i;

    DSP_write(CMD_WRITEREGS);
    for (i=0; i<=DSP_REG_MAX; i++)
    {
        DSP_write(regs[i]);
    }
    return(1);
}

int DSP_debugReadRegisters(unsigned long regs[DSP_REG_MAX])
{
    int i;

    DSP_write(CMD_READREGS);
    for (i=0; i<=DSP_REG_MAX; i++)
    {
        regs[i] = DSP_read();
    }
    return(1);
}

int DSP_debugWriteStack(unsigned long ssh[15], unsigned long ssl[15])
{
    int i;

    DSP_write(CMD_WRITESTACK);
    for (i=0; i<15; i++)
    {
        DSP_write(ssl[i]);
        DSP_write(ssh[i]);
    }
    return(1);
}

int DSP_debugReadStack(unsigned long ssh[15], unsigned long ssl[15])
{
    int i;

    DSP_write(CMD_READSTACK);
    for (i=0; i<15; i++)
    {
        ssl[i] = DSP_read();
        ssh[i] = DSP_read();
    }
    return(1);
}

int DSP_debugWriteMemory(unsigned long space, unsigned long addr,
                         unsigned long count, unsigned long mem[])
{
    unsigned long i;

    DSP_write(CMD_WRITEMEM);
    DSP_write(space);
    DSP_write(addr);
    DSP_write(count);
    for (i=0; i<count; i++)
    {
        DSP_write(mem[i]);
    }
    return(1);
}

int DSP_debugReadMemory(unsigned long space, unsigned long addr,
                        unsigned long count, unsigned long mem[])
{
    unsigned long i;

    DSP_write(CMD_READMEM);
    DSP_write(space);
    DSP_write(addr);
    DSP_write(count);
    for (i=0; i<count; i++)
    {
        mem[i] = DSP_read();
    }
    return(1);
}

