#include <stdlib.h>
#include <dos.h>
#include <go32.h>
#include <dpmi.h>

int _dma_malloc(int bytes, int *sel, unsigned long *phys)
{
 int seg = __dpmi_allocate_dos_memory((bytes*2 + 15) >> 4, sel);
 if (seg < 0) { *sel = 0; *phys = 0; return -1; }
 *phys = seg << 4;
 if ((*phys>>16) != ((*phys+bytes)>>16)) *phys += bytes;
 return 0;
}
_dma_start(int channel, unsigned long addr, int size)
{
 unsigned long offset = addr&0xFFFF;
 size--;
 outportb(0x0A,channel+4);
 outportb(0x0B,channel+0x48);
 outportb(0x0C,0);
 outportb(channel*2,offset&0xFF);
 outportb(channel*2,offset>>8);
 outportb(!channel ? 0x87 : channel==1 ? 0x83 : 0x82,addr>>16);
 outportb(0x0C,0);
 outportb(channel*2+1,size&0xFF);
 outportb(channel*2+1,size>>8);
 outportb(0x0A,channel);
}

typedef struct{int (*handler)();int number;__dpmi_paddr old_vect;}IRQS;
int irq_virgin=1; IRQS irqs[3]; unsigned char *_irq_stack[3];
extern irq_wrap_0(),irq_wrap_1(),irq_wrap_2();

void shutdown_irq()
{
 int c;
 for (c=0; c<3; c++) {
 if (_irq_stack[c]) { _irq_stack[c] -= 3040; free(_irq_stack[c]); }
 }
}
int install_irq(int num, int (*handler)())
{
 int c;
 __dpmi_paddr addr;
 if (irq_virgin) {
 for (c=0; c<3; c++) {
 _irq_stack[c] = malloc(3072);
 if (_irq_stack[c]) _irq_stack[c] += 3040;
 }
 atexit(shutdown_irq);
 irq_virgin=0;
 }
 for (c=0; c<3; c++) {
 if (irqs[c].handler==0) {
 addr.selector = _my_cs();
 switch (c) {
 case 0: addr.offset32 = (long)irq_wrap_0; break;
 case 1: addr.offset32 = (long)irq_wrap_1; break;
 case 2: addr.offset32 = (long)irq_wrap_2; break;
 default: return -1;
 }
irqs[c].handler = handler;
irqs[c].number = num;
__dpmi_get_protected_mode_interrupt_vector(num,&irqs[c].old_vect);
__dpmi_set_protected_mode_interrupt_vector(num, &addr);
 return 0;
 }
 }
 return -1;
}
remove_irq(int num)
{
 int c;
 for (c=0; c<3; c++) {
 if (irqs[c].number == num) {
__dpmi_set_protected_mode_interrupt_vector(num,&irqs[c].old_vect);
 break;
 }
 }
}

int timer_semaphore;
long bios_counter, timer_delay;
struct{void ((*proc)()); long speed,counter;} int_queue[4];

set_timer_rate(long time)
{
outportb(0x43, 0x34);
outportb(0x40, time & 0xff);
outportb(0x40, time >> 8);
}
int timerint()
{
 int callback[4], bios=0, x;
 if (timer_semaphore) { timer_delay += 5966; outportb(0x20, 0x20);
 return 0;
 }
 timer_semaphore = 1;
 for (x=0; x<4; x++) { callback[x] = 0;
 if ((int_queue[x].proc) && (int_queue[x].speed > 0)) {
 int_queue[x].counter -= timer_delay;
 if (int_queue[x].counter <= 0) {
 int_queue[x].counter += int_queue[x].speed;
 callback[x] = 1;
 }
 }
 }
 bios_counter -= timer_delay; 
 if (bios_counter <= 0) { bios_counter += 0x10000; bios = 1;
 }
 timer_delay = 5966;
 if (!bios) {asm volatile ("sti"); outportb(0x20, 0x20);}
 for (x=0; x<4; x++) {
 if (callback[x]) {
 int_queue[x].proc();
 while ((int_queue[x].proc) && (int_queue[x].counter <= 0))
 { int_queue[x].counter += int_queue[x].speed;
 int_queue[x].proc(); }
 }
 }
 if (!bios) asm volatile ("cli");
 timer_semaphore = 0;
 return bios;
}
int install_int_ex(void (*proc)(), long speed)
{
 int x;
 for (x=0; x<4; x++)
 if (int_queue[x].proc == 0) break;
 int_queue[x].counter = speed;
 int_queue[x].proc = proc;
 int_queue[x].speed = speed;
}
remove_timer()
{
 set_timer_rate(0);
 remove_irq(8);
 set_timer_rate(0);
}
int install_timer()
{
 int x;
 bios_counter = 0x10000;
 timer_delay = 5966;
 install_irq(8, timerint);
 for (x=0; x<4; x++)
 set_timer_rate(timer_delay);
 return 0;
}

#define ABS(x) x>=0 ? x : -x
int joy_x,joy_y,joy_b1,joy_b2,joy_b3,joy_b4,joy_on;
int joyctrx,joyctry,joyoldx=0,joyoldy=0;
int poll(int *x, int *y, int *x2, int *y2, int poll_mask)
{
 return _poll_joystick(x, y, x2, y2, poll_mask);
}
int averaged_poll(int *x, int *y)
{
 int x1,y1,x2,y2,xtot=0,ytot=0,c;
 for (c=0; c<4; c++) {
 if (poll(&x1,&y1,&x2,&y2,1|2)!=0)
 return -1; xtot+=x1;ytot+=y1;
 }
 *x=xtot/4;*y=ytot/4;
 return 0;
}
int initialise_joystick()
{
 if(averaged_poll(&joyctrx,&joyctry))
 return; joy_on=1;
}
poll_joystick()
{
 int x,y,x2,y2;
 unsigned char status;
 if(joy_on){
 poll(&x,&y,&x2,&y2,1|2);
 status=inportb(0x201);
 if ((ABS(x-joyoldx)<x/4)&&(ABS(y-joyoldy)<y/4))
 joy_x=x-joyctrx,joy_y=y-joyctry;
 joy_b1=(status&0x10)==0;joy_b2=(status&0x20)==0;
 joy_b3=(status&0x40)==0;joy_b4=(status&0x80)==0;
 joyoldx=x;joyoldy=y;
 }
}

void (*key_callback)(int key) = 0;
unsigned char key_table[128] =
{
0,27,'1','2','3','4','5','6','7','8','9','0','-','=',8,9,
'q','w','e','r','t','y','u','i','o','p','[',']',13,0,'a','s',
'd','f','g','h','j','k','l',';',39,'`',0,92,'z','x','c','v',
'b','n','m',',','.','/',1,'*',0,' ',0,3,3,3,3,8,
3,3,3,3,3,0,0,0,0,0,'-',0,0,0,'+',0,0,0,0,127,0,0,92,3,3,0,0,0,0,0,0,0,
13,0,'/',0,0,0,0,0,0,0,0,0,0,0,0,127,0,0,0,0,0,0,0,0,0,0,'/',0,0,0,0,0
};
volatile int key_ext,key_pause;

int keyint()
{
 int scan = inportb(0x60);
 if (key_pause) key_pause--;
 else if (scan == 0xE1) { key_pause = 5; key_callback(123); }
 else if (scan == 0xE0) key_ext=1; else { if (key_ext)
 { key_ext--; if (key_table[scan&0x7F]==1) goto exit_kb; }
 key_callback(scan); }
 exit_kb: outportb(0x20,0x20);
 return 0;
}
remove_keyboard()
{
 remove_irq(9);
}
int install_keyboard()
{
 install_irq(9, keyint);
}

int mouse_b,mx2=0,my2=0,mx3=0,my3=0;
_go32_dpmi_seginfo mouse_seginfo;
_go32_dpmi_registers mouse_regs;

mouseint(_go32_dpmi_registers *r)
{ int x=(short)r->x.si,y=(short)r->x.di;
  mouse_b=r->x.bx;
  mx2+=x-mx3;my2+=y-my3;mx3=x;my3=y;
}
get_mouse_mickeys(int *mx, int *my)
{ *mx=mx2;*my=my2;mx2-=*mx;my2-=*my;
}
remove_mouse()
{
 __dpmi_regs r;
 r.x.ax = 0x0C;
 r.x.cx = r.x.dx = r.x.es = 0;
 __dpmi_int(0x33, &r);
 r.x.ax = 15;
 r.x.cx = 8;
 r.x.dx = 16;
 __dpmi_int(0x33, &r);
 _go32_dpmi_free_real_mode_callback(&mouse_seginfo);
}
int install_mouse()
{
 __dpmi_regs r;
 r.x.ax = 0;
 __dpmi_int(0x33, &r);
 if (r.x.ax == 0)
 return -1;
 mouse_seginfo.pm_offset = (int)mouseint;
 mouse_seginfo.pm_selector = _my_cs();
 _go32_dpmi_allocate_real_mode_callback_retf(&mouse_seginfo, &mouse_regs);
 r.x.ax = 0x0C;
 r.x.cx = 0x7F;
 r.x.dx = mouse_seginfo.rm_offset;
 r.x.es = mouse_seginfo.rm_segment;
 __dpmi_int(0x33, &r);
 return 2;
}

typedef struct {int freq;unsigned long len;void*data;} SAMPLE;
typedef struct {SAMPLE*sample;int num;} VOICE;
typedef struct {int lvol,num,pos,diff,len;unsigned char*data8;} MVOICE;
MVOICE mvoice[16]; VOICE _voice[16];
unsigned short *mix_buf=0,*clip_table=0; 
typedef short VOL_TABLE[256]; VOL_TABLE *vol_table=0;
int _sb_freq=16129,_sb_port=0x220,_sb_dma=1,_sb_irq=5; 
int sb_int,dsp_ver,sb_sel[1]; volatile int sb_semaphore;
unsigned long sb_buf[1];

int _mixer_init()
{
 int i, j, clip_size=1<<10;
 for (i=0; i<16; i++)
 mvoice[i].data8 = 0;
 mix_buf = malloc(512*sizeof(short));
 if (!mix_buf) return -1;
 vol_table = malloc(sizeof(VOL_TABLE) * 32);
 if (!vol_table) return -1;
 for (j=0; j<32; j++) for (i=0; i<256; i++)
 vol_table[j][i] = (i-128) * j;
 clip_table = malloc(sizeof(short) * clip_size);
 if (!clip_table) return -1;
 for (i=0; i<clip_size*3/8; i++) {
 clip_table[i] = 0; clip_table[clip_size-1-i] = 0xFF;
 }
 for (i=0; i<clip_size/4; i++)
 clip_table[clip_size*3/8 + i] = i;
 return 0;
}
mix_mono(MVOICE *spl, unsigned short *buf, int len)
{
 short *vol = (short *)(vol_table + spl->lvol);
 while (len-->0) {*(buf++)+=vol[spl->data8[spl->pos>>8]];
 spl->pos += spl->diff;
 if ((unsigned long)spl->pos >= (unsigned long)spl->len)
 {spl->data8=0; return;} }
}
mix_sound(unsigned long buf)
{
 int i;
 unsigned short *p = mix_buf;
 unsigned long *l = (unsigned long *)p;
 for (i=0; i<256; i++)
 *(l++) = 0x80008000;
 for (i=0; i<16; i++)
 if (mvoice[i].data8) mix_mono(mvoice+i, p, 512);
 _farsetsel(_dos_ds);
 for (i=0; i<512; i++) { _farnspokeb(buf, clip_table[*p>>6]);
 buf++; p++; }
}
volatile int sb_read_dsp()
{
 int x;
 for (x=0; x<0xffff; x++) if (inportb(0x0E + _sb_port) & 0x80)
 return inportb(0x0A+_sb_port);
 return -1; 
}
volatile int sb_write_dsp(unsigned char byte)
{
 int x;
 for (x=0; x<0xffff; x++) if (!(inportb(0x0C+_sb_port) & 0x80)) {
 outportb(0x0C+_sb_port, byte); return 0; }
 return -1; 
}
int _sb_reset_dsp()
{
 int x;
 outportb(0x06+_sb_port,1);
 for (x=0; x<8; x++) inportb(0x06+_sb_port); outportb(0x06+_sb_port,0);
 if (sb_read_dsp() != 0xAA)
 return -1;
 return 0;
}
sb_play_buf(int size)
{
 sb_write_dsp(0x14);
 sb_write_dsp((size-1) & 0xFF);
 sb_write_dsp((size-1) >> 8);
}
int sb_interrupt()
{
 if (!sb_semaphore) {
 sb_semaphore = 1;
 _dma_start(_sb_dma, sb_buf[0], 512);
 sb_play_buf(512);
 asm volatile ("cli");
 mix_sound(sb_buf[0]);
 asm volatile ("sti");
 inportb(_sb_port+0x0E);
 outportb(0x20,0x20);
 if(_sb_irq>7) outportb(0xA0,0x20);
 sb_semaphore = 0;
 }
 sb_semaphore = 0;
 return 0;
}
remove_sound()
{
 sb_write_dsp(0xD3);
 outportb(0x0A,_sb_dma+4);
 sb_write_dsp(0xD0);
 _sb_reset_dsp();
 remove_irq(sb_int);
 __dpmi_free_dos_memory(sb_sel[0]);
 free(mix_buf); mix_buf=0;
 free(vol_table); vol_table=0;
 free(clip_table); clip_table=0;
}
int install_sound()
{
 int c,p[] = {0x210,0x220,0x230,0x240,0x250,0x260,0};
 for (c=15;--c>-1;) mvoice[c].num = -1;
 if (_sb_reset_dsp()!=0)
 for (c=0; p[c]; c++) { _sb_port = p[c];
 if (_sb_reset_dsp()==0) break;
 }
 sb_write_dsp(0xE1);
 dsp_ver=((sb_read_dsp()<<8)|sb_read_dsp());
 if(dsp_ver<0||p[c]==0) return -1;
 if (dsp_ver>=0x400) {
 outportb(_sb_port+4, 0x80); c=inportb(_sb_port+5);
 if(c&1) _sb_irq=9; if(c&4) _sb_irq=7; if(c&8) _sb_irq=10;
 outportb(_sb_port+4, 0x81); c=inportb(_sb_port+5);
 if(c&1) _sb_dma=0; if(c&8) _sb_dma=3;
 }
 sb_int = _sb_irq>7 ? _sb_irq+104 : _sb_irq+8;
 if (dsp_ver>0x200) _sb_freq = 22727;
 if (_dma_malloc(512, &sb_sel[0], &sb_buf[0])!=0) return -1;
 if (_mixer_init()!=0) return -1;
 mix_sound(sb_buf[0]);
 if (_sb_irq>7) {
 outportb(0x21, inportb(0x21) & 0xFB); 
 outportb(0xA1, inportb(0xA1) & (~(1<<(_sb_irq-8))));
 } else outportb(0x21, inportb(0x21) & (~(1<<_sb_irq)));
 install_irq(sb_int, sb_interrupt);
 sb_write_dsp(0xD1);
 sb_write_dsp(0x40);
 sb_write_dsp((unsigned char)(-999744/_sb_freq));
 _dma_start(_sb_dma, sb_buf[0], 512);
 sb_play_buf(512);
 return 0;
}
int play_sample(SAMPLE *spl, int vol)
{
 int phys,c;
 for (c=0; c<16; c++)
 if (mvoice[c].num < 0) {phys=c;break;}
 if(phys!=c)phys=0;
 for(c=15;--c>-1;) if(!_voice[c].sample)break;
 if (c >= 0) {
 _voice[c].sample = spl;
 _voice[c].num = phys;
 mvoice[phys].num = c;
 mvoice[phys].pos = 0;
 mvoice[phys].len = spl->len<<8;
 mvoice[phys].data8 = spl->data;
 (mvoice+phys)->lvol=vol>>3;
 (mvoice+phys)->diff=(spl->freq<<8)/_sb_freq;
 }
}
stop_sample(SAMPLE *spl)
{
 int c;
 for (c=15;--c>-1;) if (_voice[c].sample == spl) {
 mvoice[_voice[c].num].data8=0;
 mvoice[_voice[c].num].num = -1;
 _voice[c].sample = 0;
 }
}