/* 
 * This is a quick conversion of my MSDOS EGA/VGA oscilloscope
 * for soundblaster, originally written back in 1989 for the a86
 * MSDOS assembler and adapted in 1991 for use with tasm and masm, 
 * to Linux using svgalib and dsp fragments.
 *
 * With a little tweaking, GCC creates comparable assembler code out
 * of pure C code.  Fast enough to keep up with 1024x768 graphics
 * displaying 44.1KHz 8-bit Stereo samples on my 486dx2-66.
 *
 * Svgalib drawing is slow, so video memory is access directly as my dos
 * asm code does, so the program is hardwired to 1024x768x256.  Sorry.
 * If your display hardware isn't so fast, lower the sample rate and/or
 * turn off the stereo display so you don't get sound overruns.
 *  Exits on keypress (including virtual console switch).
 *
 * I hereby place this code in the public domain.  I don't care to
 * take credit for it.   Send me your (ab)used satellite equipment.
 *
 * -- Nathan Laredo, laredo@gnu.ai.mit.edu
 * Plano, Texas, USA -- September 27, 1994
 */

#define SAMPLERATE 44100
#define STEREO 1

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/soundcard.h>
#include <sys/time.h>
#include <vga.h>
#include <vgakeyboard.h>

int main(argc, argv)
int argc;
char **argv;
{
    int f, index, size, page;
    int stereo = STEREO, rate = SAMPLERATE, frag = 0x0002000a;
    unsigned char data[1024], olddata[1024], *display;
    struct timeval starttime, endtime;
    unsigned int frames = 0;
    float elapsed;

    /* parse command line arguments */
    for (index = 1 ; index < argc ; index++)
	if (argv[index][0] == '-') {
	    stereo = (argv[index][1] == 's');
	    if (!stereo && argv[index][1] != 'm')
		stereo = -1;
	} else {
	   rate = atoi(argv[index]);
	   if (rate < 4000 || rate > 44100)
		stereo = -1;
	}

    if (stereo < 0) {
	fprintf(stderr, "usage: %s [-stereo] [-mono] [rate]\n", argv[0]);
	exit(-1);
    }

    if ((f = open("/dev/dsp", O_RDONLY, 0)) == -1) {
	perror("/dev/dsp");
	exit(-1);
    }

    ioctl(f, SNDCTL_DSP_SPEED, &rate);
    ioctl(f, SNDCTL_DSP_STEREO, &stereo);
    ioctl(f, SNDCTL_DSP_SETFRAGMENT, &frag);
    ioctl(f, SNDCTL_DSP_GETBLKSIZE, &size);
    if (size != 1024) {
	fprintf (stderr, "Could not get proper dsp fragment size!\n");
	exit(-1);
    }

    vga_init();
    vga_setmode(G1024x768x256);
    vga_lockvc();
    display = vga_getgraphmem();
    for (index = 1; index < 128; index++)
	vga_setpalette(index, 0, 0, index / 4);
    vga_setpalette(254, 0, 255, 0);
    vga_setpalette(255, 255, 255, 0);
    for (page = 0; page < 12; page++) {
	vga_setpage(page); 
	for (index = 0; index < 64; index++)
	    if (page & 2)
		memset(&display[index * 1024],
		    (page & 1 ? 64 - index : 127 - index), 1024);
	    else
		memset(&display[index * 1024],
		    (page & 1 ? 64 + index : index), 1024);
    }
    if (stereo)
	for (page = 0; page < 12; page++) {
	    vga_setpage(page); 
	    for (index = 0; index < 64; index++)
		if (page & 2)
		    display[index * 1024 + 512] =
			(page & 1 ? index + 64 : index);
		else
		    display[index * 1024 + 512] =
			(page & 1 ? 64 - index : 127 - index);
	}
    gettimeofday(&starttime,NULL);
    keyboard_init();
    while (!keyboard_update() && read(f, data, size) == size) {
	for (index = 0; index < size; index++) {
	    register dx, di, si;
	    di = index;
	    if (stereo)
		if ((di = 512 + (di >> 1) - ((di & 1) << 9)) == 512)
		    continue;
	    dx = olddata[index];
	    si = 4 + (dx >> 6);
	    if (page != si)
		vga_setpage(page = si);
	    si = (dx << 10) + di;
	    display[(unsigned short) si] = ((char) dx < 0 ? 255 - dx : dx);
	    dx = data[index];
	    si = 4 + (dx >> 6);
	    if (page != si)
		vga_setpage(page = si);
	    si = (dx << 10) + di;
	    display[(unsigned short) si] =  255 - (index & stereo);
	}
	memcpy(olddata, data, size);
	frames++;
    }
    keyboard_close();
    gettimeofday(&endtime, NULL);
    elapsed = ((float) endtime.tv_sec * 1000000.0 + (float) endtime.tv_usec)
	- ((float) starttime.tv_sec * 1000000.0 + (float) starttime.tv_usec);
    elapsed /= 1000000.0;
    elapsed = (float) frames / elapsed;
    close(f);
    vga_setmode(TEXT);
    if (stereo)
	rate <<= 1;
    printf("%d frames/sec maximum, %.1f frames/sec counted\n",
	   rate >> 10, elapsed);
    vga_unlockvc();
    exit(0);
}
