From b3ef1139ab8026b6d7e1e96caf4dae4dd65e3c94 Mon Sep 17 00:00:00 2001 From: "Piotr Ulaszewski (aka BSpider)" Date: Sun, 11 Sep 2016 13:25:36 +0200 Subject: [PATCH] First commit, source code salvaged from http://rayer.g6.cz/download/judas210.zip (including improvements by RayeR). MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Volkert de Buisonjé --- !DEBUG.BAT | 1 + !DJGPP.BAT | 11 + !WATCOM.BAT | 9 + ANAL.C | 957 ++++++++++ ANAL.CFG | 1 + ANAL.DJ | 49 + ANAL.WAT | 16 + EXPLSION.WAV | Bin 0 -> 30640 bytes FART.WAV | Bin 0 -> 15714 bytes FILE_ID.DIZ | 17 + ICHINIT/!COMPILE.BAT | 7 + ICHINIT/!DEBUG.BAT | 2 + ICHINIT/!STUBIT.BAT | 1 + ICHINIT/CODEC.C | 222 +++ ICHINIT/DOS32/!BLIB.BAT | 1 + ICHINIT/DOS32/COLORS.TXT | 18 + ICHINIT/DOS32/DPMI.ASM | 337 ++++ ICHINIT/DOS32/DPMI.H | 65 + ICHINIT/DOS32/INPUT.ASM | 389 ++++ ICHINIT/DOS32/INPUT.H | 13 + ICHINIT/DOS32/SCANS.H | 162 ++ ICHINIT/DOS32/TIMER.ASM | 418 +++++ ICHINIT/DOS32/TIMER.H | 9 + ICHINIT/ICHINIT.C | 668 +++++++ ICHINIT/ICHINIT.TXT | 328 ++++ ICHINIT/JUDASAC.H | 493 +++++ ICHINIT/PCI.C | 239 +++ ICHINIT/SEGMENTS.INC | 11 + INTERPOL.TXT | 257 +++ JASMDJ.ASM | 3750 ++++++++++++++++++++++++++++++++++++++ JDAS210D.FRM | 17 + JDSWAV.ZIP | Bin 0 -> 66734 bytes JP.C | 733 ++++++++ JP.DJ | 43 + JP.WAT | 12 + JUDAS.C | 3004 ++++++++++++++++++++++++++++++ JUDAS.CMD | 1 + JUDAS.DOC | 804 ++++++++ JUDAS.H | 267 +++ JUDASAC.H | 1235 +++++++++++++ JUDASAC.INC | 523 ++++++ JUDASASM.ASM | 3626 ++++++++++++++++++++++++++++++++++++ JUDASCFG.H | 53 + JUDASCFG.INC | 6 + JUDASDMA.C | 73 + JUDASDMA.H | 16 + JUDASERR.H | 11 + JUDASGUS.H | 259 +++ JUDASGUS.INC | 218 +++ JUDASIO.C | 55 + JUDASLIB.A | Bin 0 -> 334152 bytes JUDASMEM.C | 239 +++ JUDASMEM.H | 11 + JUDASMOD.C | 1394 ++++++++++++++ JUDASRAW.C | 58 + JUDASS3M.C | 1735 ++++++++++++++++++ JUDASSMP.C | 378 ++++ JUDASTBL.C | 135 ++ JUDASTBL.H | 10 + JUDASW.GIF | Bin 0 -> 25165 bytes JUDASWAV.C | 462 +++++ JUDASXM.C | 2361 ++++++++++++++++++++++++ JUVSCPW.GIF | Bin 0 -> 21603 bytes KBD.C | 291 +++ KBD.H | 111 ++ KBDASM.ASM | 116 ++ MAKEEXDJ.BAT | 6 + MAKEEXWT.BAT | 5 + MAKEFILE.DJ | 73 + MAKEFILE.WAT | 38 + MENUMOVE.WAV | Bin 0 -> 5792 bytes MENUSEL.WAV | Bin 0 -> 10510 bytes PLRSHOOT.WAV | Bin 0 -> 8094 bytes README.TXT | 11 + SEGMENTS.INC | 22 + SPEED.DOC | 40 + TIMER.C | 129 ++ TIMER.H | 5 + TIMERASM.ASM | 63 + TIMERASMT.ASM | 56 + TUNE1.XM | Bin 0 -> 10520 bytes TUNE2.XM | Bin 0 -> 8934 bytes 82 files changed, 27156 insertions(+) create mode 100644 !DEBUG.BAT create mode 100644 !DJGPP.BAT create mode 100644 !WATCOM.BAT create mode 100644 ANAL.C create mode 100644 ANAL.CFG create mode 100644 ANAL.DJ create mode 100644 ANAL.WAT create mode 100644 EXPLSION.WAV create mode 100644 FART.WAV create mode 100644 FILE_ID.DIZ create mode 100644 ICHINIT/!COMPILE.BAT create mode 100644 ICHINIT/!DEBUG.BAT create mode 100644 ICHINIT/!STUBIT.BAT create mode 100644 ICHINIT/CODEC.C create mode 100644 ICHINIT/DOS32/!BLIB.BAT create mode 100644 ICHINIT/DOS32/COLORS.TXT create mode 100644 ICHINIT/DOS32/DPMI.ASM create mode 100644 ICHINIT/DOS32/DPMI.H create mode 100644 ICHINIT/DOS32/INPUT.ASM create mode 100644 ICHINIT/DOS32/INPUT.H create mode 100644 ICHINIT/DOS32/SCANS.H create mode 100644 ICHINIT/DOS32/TIMER.ASM create mode 100644 ICHINIT/DOS32/TIMER.H create mode 100644 ICHINIT/ICHINIT.C create mode 100644 ICHINIT/ICHINIT.TXT create mode 100644 ICHINIT/JUDASAC.H create mode 100644 ICHINIT/PCI.C create mode 100644 ICHINIT/SEGMENTS.INC create mode 100644 INTERPOL.TXT create mode 100644 JASMDJ.ASM create mode 100644 JDAS210D.FRM create mode 100644 JDSWAV.ZIP create mode 100644 JP.C create mode 100644 JP.DJ create mode 100644 JP.WAT create mode 100644 JUDAS.C create mode 100644 JUDAS.CMD create mode 100644 JUDAS.DOC create mode 100644 JUDAS.H create mode 100644 JUDASAC.H create mode 100644 JUDASAC.INC create mode 100644 JUDASASM.ASM create mode 100644 JUDASCFG.H create mode 100644 JUDASCFG.INC create mode 100644 JUDASDMA.C create mode 100644 JUDASDMA.H create mode 100644 JUDASERR.H create mode 100644 JUDASGUS.H create mode 100644 JUDASGUS.INC create mode 100644 JUDASIO.C create mode 100644 JUDASLIB.A create mode 100644 JUDASMEM.C create mode 100644 JUDASMEM.H create mode 100644 JUDASMOD.C create mode 100644 JUDASRAW.C create mode 100644 JUDASS3M.C create mode 100644 JUDASSMP.C create mode 100644 JUDASTBL.C create mode 100644 JUDASTBL.H create mode 100644 JUDASW.GIF create mode 100644 JUDASWAV.C create mode 100644 JUDASXM.C create mode 100644 JUVSCPW.GIF create mode 100644 KBD.C create mode 100644 KBD.H create mode 100644 KBDASM.ASM create mode 100644 MAKEEXDJ.BAT create mode 100644 MAKEEXWT.BAT create mode 100644 MAKEFILE.DJ create mode 100644 MAKEFILE.WAT create mode 100644 MENUMOVE.WAV create mode 100644 MENUSEL.WAV create mode 100644 PLRSHOOT.WAV create mode 100644 README.TXT create mode 100644 SEGMENTS.INC create mode 100644 SPEED.DOC create mode 100644 TIMER.C create mode 100644 TIMER.H create mode 100644 TIMERASM.ASM create mode 100644 TIMERASMT.ASM create mode 100644 TUNE1.XM create mode 100644 TUNE2.XM diff --git a/!DEBUG.BAT b/!DEBUG.BAT new file mode 100644 index 0000000..3b25e78 --- /dev/null +++ b/!DEBUG.BAT @@ -0,0 +1 @@ +rhgdb jp.exe tune1 \ No newline at end of file diff --git a/!DJGPP.BAT b/!DJGPP.BAT new file mode 100644 index 0000000..5850f14 --- /dev/null +++ b/!DJGPP.BAT @@ -0,0 +1,11 @@ +@echo off +set blaster= +rem set DJGPP=E:\DJGPP\DJGPP.ENV +rem set PATH=E:\DJGPP\BIN;%PATH% +make.exe -f makefile.dj +make.exe -f jp.dj +make.exe -f anal.dj +strip -s anal.exe +strip -s jp.exe +upx --best anal.exe +upx --best jp.exe diff --git a/!WATCOM.BAT b/!WATCOM.BAT new file mode 100644 index 0000000..85c44f2 --- /dev/null +++ b/!WATCOM.BAT @@ -0,0 +1,9 @@ +rem rebuild judas library +del *.lib +wmake /f makefile.wat +del *.obj + +rem rebuild example files +wmake /f jp.wat +wmake /f anal.wat +del *.obj diff --git a/ANAL.C b/ANAL.C new file mode 100644 index 0000000..e3355ef --- /dev/null +++ b/ANAL.C @@ -0,0 +1,957 @@ +/* + * ANAL INVADERS megagame! + * + * Shows how to use music & sound effects together, make a sound setup menu + * and how to pan the sfx according to the "location" the sound comes from. + * + * Like JP, this sets up the signal handlers & uses IRQ0 to update the sound. + * + * Channels used: + * 0-3 Music + * 4 Menumove & menuselect effect + * 5-6 Fart at the exit + * 7 Player shot effect + * 8 Enemy shot effect + * 9 Explosion effect + * + * This code is shitty! + * + * In V2.01 the player bounds checking was corrected! + * V2.03 a custom keyboard handler was added! + * V2.04y mixer changing is supported and ESC moves bacwards in menus + * V2.06y support for more volume levels was added + */ + +#include +#include +#include +#include +#include +#include +#include +#ifdef __DJGPP__ +#include +#include +#include +#include +#else +#define __djgpp_conventional_base 0 +#endif +#include "judas.h" +#include "timer.h" +#include "kbd.h" + +#define MAX_OBJECTS 250 +#define MAX_STARS 50 + +#define OT_NONE 0 +#define OT_PLAYER 1 +#define OT_ENEMY 2 +#define OT_PLAYERSHOT 3 +#define OT_ENEMYSHOT 4 +#define OT_EXPLOSION 5 + +typedef struct +{ + int x; + int y; + int sx; + int sy; + int time; + int type; +} OBJECT; + +typedef struct +{ + int x; + int y; + int sx; +} STAR; + +unsigned mixratetable[] = +{ + 5513, + 8269, + 11025, + 16538, + 22050, + 33075, + 44100 +}; + +char *onofftext[] = +{ + "OFF", + "ON", +}; + +char *mainmenutext[] = +{ + "Start The Massacre", + "Setup Sound", + "Quit To The Realms Of MS-DOS" +}; + +unsigned char colortable[] = +{ + 0, 8, 5, 9, 7, 14, 15 +}; + +unsigned char expshapetable[] = +{ + 176, 176, 176, 177, 177, 177, 178, 219 +}; + +unsigned char expcolortable[] = +{ + 4, 4, 6, 6, 12, 12, 14, 15 +}; + +char *scrbuffer; +char textbuffer[80]; +int mixrate; +int mixer; +int interpolation; +int musicvol; +int sfxvol; +int mainmenuchoice = 0; +int soundmenuchoice = 0; + +int score = 0; +int hiscore = 0; +int lives = 0; +int diff = 0; + +SAMPLE *fart; +SAMPLE *explosion; +SAMPLE *plrshoot; +SAMPLE *menumove; +SAMPLE *menuselect; +OBJECT object[MAX_OBJECTS]; +STAR star[MAX_STARS]; + +int main(void); +void mainmenu(void); +void playgame(void); +void initstars(void); +void movestars(void); +void drawstars(void); +void initobjects(void); +void drawobjects(void); +void initplayer(void); +void moveplayer(void); +void moveobjects(void); +void spawnenemy(void); +void checkplayershots(void); +void checkenemies(void); +void createexplosion(int x, int y, int pieces); +int scalepanning(int x); +void soundmenu(void); +void resetmode(void); +void loadconfig(void); +void saveconfig(void); +void fliptob8000(void); +void clearscreen(void); +void printtext(int x, int y, int color, const char *text); +void printtextc(int y, int color, const char *text); +static void handle_int(int a); + +int main(void) +{ + mixrate = 4; + mixer = FASTMIXER; + interpolation = 0; + musicvol = 50; + sfxvol = 50; + + + #ifdef __DJGPP__ + if (__djgpp_nearptr_enable() == 0) { + printf("ERROR: Couldn't enable near pointers for DJGPP!\n"); + return 1; + } + /* Trick: Avoid re-setting DS selector limit on each memory allocation call */ + __djgpp_selector_limit = 0xffffffff; + #endif + + atexit(judas_uninit); + atexit(timer_uninit); + atexit(kbd_uninit); + signal(SIGINT, handle_int); + #if defined(SIGBREAK) + signal(SIGBREAK, handle_int); + #endif + + srand(666 + *( (unsigned *) ((unsigned char *)0x46c + __djgpp_conventional_base) )); + resetmode(); + scrbuffer = malloc(80 * 25 * 2); + + if (!scrbuffer) + { + printf("ERROR: No Memory For Virtual Screen!"); + return 666; + } + + loadconfig(); + + judas_config(); + if (!judas_init(mixratetable[mixrate], mixer, SIXTEENBIT | STEREO, interpolation)) + { + printf("JUDAS ERROR: %s\n", judas_errortext[judas_error]); + return 666; + } + + kbd_init(); + timer_init(0x4280, judas_update); + + judas_setmusicmastervolume(4, musicvol); + judas_setsfxmastervolume(4, sfxvol); + + judas_loadxm("tune1.xm"); + judas_playxm(0); + + fart = judas_loadwav("fart.wav"); + explosion = judas_loadwav("explsion.wav"); + plrshoot = judas_loadwav("plrshoot.wav"); + menumove = judas_loadwav("menumove.wav"); + menuselect = judas_loadwav("menusel.wav"); + mainmenu(); + + judas_playsample(fart, 5, 22050, 64*256, LEFT); + judas_playsample(fart, 6, 22050, 64*256, RIGHT); + sleep(1); + + saveconfig(); + resetmode(); + return 0; +} + +void resetmode(void) +{ + union REGS glenregs; + + glenregs.w.ax = 0x0003; + int386(0x10, &glenregs, &glenregs); +} + +void mainmenu(void) +{ + for(;;) + { + int count; + int key; + clearscreen(); + printtextc(6, 15, "-+* ANAL INVADERS And The DEFENDERS OF THE RECTUM *+-"); + printtextc(8, 14, "Megagame by Cadaver!"); + sprintf(textbuffer, "LAST SCORE:%06d HISCORE:%06d", score, hiscore); + printtextc(11, 10, textbuffer); + for (count = 0; count < 3; count++) + { + int color = 7; + if (mainmenuchoice == count) color = 15; + + printtextc(15 + count, color, mainmenutext[count]); + } + fliptob8000(); + do + { + key = kbd_getkey(); + } while (!key); + switch(key) + { + case KEY_ESC: + judas_playsample(menumove, 4, 22050, 64*256, MIDDLE); + mainmenuchoice = 3; + + case KEY_UP: + judas_playsample(menumove, 4, 22050, 64*256, MIDDLE); + mainmenuchoice--; + if (mainmenuchoice < 0) mainmenuchoice = 2; + break; + + case KEY_DOWN: + judas_playsample(menumove, 4, 22050, 64*256, MIDDLE); + mainmenuchoice++; + if (mainmenuchoice > 2) mainmenuchoice = 0; + break; + + case KEY_SPACE: + case KEY_ENTER: + judas_playsample(menuselect, 4, 22050, 64*256, MIDDLE); + switch(mainmenuchoice) + { + case 0: + judas_loadxm("tune2.xm"); + judas_playxm(0); + playgame(); + judas_loadxm("tune1.xm"); + judas_playxm(0); + break; + + case 1: + soundmenu(); + break; + + case 2: + goto EXITTODOS; + } + break; + } + } + EXITTODOS: + return; +} + +void soundmenu(void) +{ + int soundmenuchoice = 0; + + for(;;) + { + int count; + int key; + clearscreen(); + printtextc(6, 15, "-+* ANAL INVADERS And The DEFENDERS OF THE RECTUM *+-"); + printtextc(8, 14, "Megagame by Cadaver!"); + sprintf(textbuffer, "LAST SCORE:%06d HISCORE:%06d", score, hiscore); + printtextc(11, 10, textbuffer); + for (count = 0; count < 6; count++) + { + int color = 7; + if (soundmenuchoice == count) color = 15; + + switch(count) + { + case 0: + sprintf(textbuffer, "Mixrate: %d", mixratetable[mixrate]); + break; + + case 1: + sprintf(textbuffer, "Interpolation: %s", judas_ipmodename[interpolation]); + break; + + case 2: + sprintf(textbuffer, "Music Volume: %d", musicvol); + break; + + case 3: + sprintf(textbuffer, "Sfx Volume: %d", sfxvol); + break; + + case 4: + sprintf(textbuffer, "Mixer: %s", judas_mixername[mixer]); + break; + + case 5: + sprintf(textbuffer, "Exit To Previous Menu"); + break; + } + printtextc(14 + count, color, textbuffer); + } + fliptob8000(); + do + { + key = kbd_getkey(); + } while (!key); + switch(key) + { + case KEY_ESC: + judas_playsample(menuselect, 4, 22050, 64*256, MIDDLE); + goto EXITTOMAIN; + + case KEY_UP: + judas_playsample(menumove, 4, 22050, 64*256, MIDDLE); + soundmenuchoice--; + if (soundmenuchoice < 0) soundmenuchoice = 5; + break; + + case KEY_DOWN: + judas_playsample(menumove, 4, 22050, 64*256, MIDDLE); + soundmenuchoice++; + if (soundmenuchoice > 5) soundmenuchoice = 0; + break; + + case KEY_LEFT: + judas_playsample(menumove, 4, 22050, 64*256, MIDDLE); + switch(soundmenuchoice) + { + case 0: + mixrate--; + if (mixrate < 0) mixrate = 0; + judas_init(mixratetable[mixrate], mixer, SIXTEENBIT | STEREO, interpolation); + break; + + case 1: + interpolation = 0; + judas_init(mixratetable[mixrate], mixer, SIXTEENBIT | STEREO, interpolation); + break; + + case 2: + if (musicvol > 10) { + musicvol -= 10; + } else musicvol -= 1; + if (musicvol < 0) musicvol = 0; + judas_setmusicmastervolume(4, musicvol); + break; + + case 3: + if (sfxvol > 10) { + sfxvol -= 10; + } else sfxvol -= 1; + if (sfxvol < 0) sfxvol = 0; + judas_setsfxmastervolume(4, sfxvol); + break; + + case 4: + mixer = FASTMIXER; + judas_init(mixratetable[mixrate], mixer, SIXTEENBIT | STEREO, interpolation); + break; + } + break; + + case KEY_RIGHT: + judas_playsample(menumove, 4, 22050, 64*256, MIDDLE); + switch(soundmenuchoice) + { + case 0: + mixrate++; + if (mixrate > 6) mixrate = 6; + judas_init(mixratetable[mixrate], mixer, SIXTEENBIT | STEREO, interpolation); + break; + + case 1: + interpolation = 1; + judas_init(mixratetable[mixrate], mixer, SIXTEENBIT | STEREO, interpolation); + break; + + case 2: + if (musicvol >= 10) { + musicvol += 10; + } else musicvol += 1; + if (musicvol > 100) musicvol = 100; + judas_setmusicmastervolume(4, musicvol); + break; + + case 3: + if (sfxvol >= 10) { + sfxvol += 10; + } else sfxvol += 1; + if (sfxvol > 100) sfxvol = 100; + judas_setsfxmastervolume(4, sfxvol); + break; + + case 4: + mixer = QUALITYMIXER; + judas_init(mixratetable[mixrate], mixer, SIXTEENBIT | STEREO, interpolation); + break; + } + break; + + case KEY_SPACE: + case KEY_ENTER: + switch(soundmenuchoice) + { + case 5: + judas_playsample(menuselect, 4, 22050, 64*256, MIDDLE); + goto EXITTOMAIN; + } + break; + } + } + EXITTOMAIN: + return; +} + +void playgame(void) +{ + int delay = 0; + int deathcounter = 0; + int nextlife = 10000; + + diff = 0; + lives = 3; + score = 0; + initstars(); + initobjects(); + initplayer(); + timer_count = 0; + + for (;;) + { + int speed; + while (!timer_count) + { + } + speed = timer_count; + if (speed > 8) speed = 8; + timer_count = 0; + while (speed) + { + movestars(); + moveplayer(); + moveobjects(); + spawnenemy(); + checkplayershots(); + checkenemies(); + if (score > hiscore) hiscore = score; + if (score >= nextlife) + { + judas_playsample(menuselect, 4, 22050, 64*256, MIDDLE); + lives++; + nextlife += 10000; + } + delay++; + if (delay == 1500) + { + delay = 0; + diff++; + if (diff > 50) diff = 50; + } + speed--; + if (!object[0].type) + { + deathcounter++; + if ((!lives) && (!(rand() % 7))) + { + int x = rand() % 640; + judas_playsample(explosion, 9, 22050, 64*256, scalepanning(x)); + createexplosion(x, rand() % 200, 10 + rand() % 20); + } + if (deathcounter >= 280) + { + deathcounter = 0; + if (lives) + { + initobjects(); + initplayer(); + } + else + { + return; + } + } + } + } + clearscreen(); + drawstars(); + drawobjects(); + sprintf(textbuffer, "SCORE:%06d", score); + printtext(1, 23, 10, textbuffer); + sprintf(textbuffer, "LIVES:%02d", lives); + printtext(36, 23, 10, textbuffer); + sprintf(textbuffer, "HISCORE:%06d", hiscore); + printtext(65, 23, 10, textbuffer); + if (!lives) + { + printtextc(10, 15, "G A M E O V E R"); + printtextc(12, 12, "The Anal Invaders infiltrate Earth!"); + printtextc(13, 12, "Everyone will suffer from hemorrhoids!"); + } + fliptob8000(); + if (checkkey(KEY_ESC)) break; + } +} + +void initobjects(void) +{ + int count; + OBJECT *optr = &object[1]; + + for (count = MAX_OBJECTS - 1; count; count--) + { + optr->type = OT_NONE; + optr++; + } +} + +void initplayer(void) +{ + OBJECT *optr = &object[0]; + optr->type = OT_PLAYER; + optr->x = 32; + optr->y = 100; + optr->time = 0; +} + +void moveplayer(void) +{ + OBJECT *optr = &object[0]; + if (optr->type != OT_PLAYER) return; + if (optr->time) optr->time--; + + if (kt[KEY_UP]) + { + optr->y -= 2; + if (optr->y < 0) optr->y = 0; + } + + if (kt[KEY_DOWN]) + { + optr->y += 2; + if (optr->y > 199) optr->y = 199; + } + + if (kt[KEY_LEFT]) + { + optr->x -= 4; + if (optr->x < 0) optr->x = 0; + } + + if (kt[KEY_RIGHT]) + { + optr->x += 4; + if (optr->x > 639) optr->x = 639; + } + + if (kt[KEY_SPACE]) + { + if (!optr->time) + { + OBJECT *fptr = &object[1]; + int f; + for (f = MAX_OBJECTS - 1; f; f--) + { + if (!fptr->type) + { + judas_playsample(plrshoot, 7, 22050, 64*256, scalepanning(optr->x)); + optr->time = 12; + fptr->type = OT_PLAYERSHOT; + fptr->x = optr->x; + fptr->y = optr->y; + fptr->sx = 8; + break; + } + fptr++; + } + } + } +} + +void moveobjects(void) +{ + int count; + OBJECT *optr = &object[1]; + + for (count = MAX_OBJECTS - 1; count; count--) + { + switch(optr->type) + { + case OT_NONE: + break; + + case OT_PLAYERSHOT: + optr->x += optr->sx; + if (optr->x >= 640) optr->type = OT_NONE; + break; + + case OT_ENEMYSHOT: + optr->x += optr->sx; + optr->y += optr->sy; + optr->time--; + if (optr->time <= 0) optr->type = OT_NONE; + break; + + case OT_EXPLOSION: + optr->x += optr->sx; + optr->y += optr->sy; + optr->time--; + if (optr->time & 1) optr->sy++; + if (optr->time <= 0) optr->type = OT_NONE; + break; + + case OT_ENEMY: + optr->x += optr->sx; + optr->y -= optr->sy; + optr->time--; + if (optr->time <= 0) + { + optr->sx = (rand() % 7) - 5; + optr->sy = (rand() % 5) - 2; + optr->time = (rand() % 20) + 5; + if ((rand() % 60) < diff) + { + OBJECT *fptr = &object[1]; + int f; + for (f = MAX_OBJECTS - 1; f; f--) + { + if (!fptr->type) + { + judas_playsample(fart, 8, 44100, 40*256, scalepanning(optr->x)); + fptr->type = OT_ENEMYSHOT; + fptr->x = optr->x; + fptr->y = optr->y; + fptr->sx = (object[0].x - optr->x) / 70; + fptr->sy = (object[0].y - optr->y) / 70; + fptr->time = 80; + break; + } + fptr++; + } + } + } + if ((optr->x >= 680) || (optr->x < -40) || (optr->y >= 240) || (optr->y < -40)) + optr->type = OT_NONE; + break; + } + optr++; + } +} + +void checkplayershots(void) +{ + int count; + OBJECT *optr = &object[1]; + + for (count = MAX_OBJECTS - 1; count; count--) + { + if (optr->type == OT_PLAYERSHOT) + { + OBJECT *tptr = &object[1]; + int tcount; + for (tcount = MAX_OBJECTS - 1; tcount; tcount--) + { + if (tptr->type == OT_ENEMY) + { + if ((abs(optr->x - tptr->x) < 8) && (abs(optr->y - tptr->y) < 8)) + { + judas_playsample(explosion, 9, 22050, 64*256, scalepanning(optr->x)); + optr->type = OT_NONE; + tptr->type = OT_NONE; + score += 100; + createexplosion(optr->x, optr->y, 10); + break; + } + } + tptr++; + } + } + optr++; + } +} + +void checkenemies(void) +{ + int count; + OBJECT *optr = &object[1]; + + if (!object[0].type) return; + for (count = MAX_OBJECTS - 1; count; count--) + { + if ((optr->type == OT_ENEMYSHOT) || (optr->type == OT_ENEMY)) + { + if ((abs(optr->x - object[0].x) < 8) && (abs(optr->y - object[0].y) < 8)) + { + judas_playsample(explosion, 9, 22050, 64*256, scalepanning(optr->x)); + lives--; + optr->type = OT_NONE; + object[0].type = OT_NONE; + createexplosion(optr->x, optr->y, 30); + break; + } + } + optr++; + } +} + +void createexplosion(int x, int y, int pieces) +{ + int count; + OBJECT *optr = &object[1]; + + for (count = MAX_OBJECTS - 1; count; count--) + { + if (!optr->type) + { + optr->type = OT_EXPLOSION; + optr->x = x; + optr->y = y; + optr->sx = (rand() % 9) - 4; + optr->sy = -(rand() % 9); + optr->time = (rand() % 64) + 64; + pieces--; + if (!pieces) break; + } + optr++; + } +} + +void drawobjects(void) +{ + int count; + OBJECT *optr = &object[0]; + + for (count = MAX_OBJECTS; count; count--) + { + if ((optr->type) && (optr->x >= 0) && (optr->y >= 0) && (optr->x < 640) && (optr->y < 200)) + { + unsigned char *dptr = scrbuffer + (((optr->x >> 3) + (optr->y >> 3) * 80) << 1); + switch(optr->type) + { + case OT_PLAYER: + dptr[0] = 16; + dptr[1] = 10; + break; + + case OT_PLAYERSHOT: + dptr[0] = 7; + dptr[1] = 15; + break; + + case OT_ENEMYSHOT: + dptr[0] = 15; + dptr[1] = (rand() % 15) + 1; + break; + + case OT_ENEMY: + dptr[0] = 2; + dptr[1] = 2; + break; + + case OT_EXPLOSION: + dptr[0] = expshapetable[optr->time / 16]; + dptr[1] = expcolortable[optr->time / 16]; + break; + } + } + optr++; + } +} + +void spawnenemy(void) +{ + int count; + OBJECT *optr = &object[1]; + + if ((rand() % 666) < (650 - diff * 3)) return; + for (count = MAX_OBJECTS - 1; count; count--) + { + if (!optr->type) + { + optr->type = OT_ENEMY; + optr->x = 640; + optr->y = rand() % 200; + optr->sx = (rand() % 3) - 4; + optr->sy = (rand() % 5) - 2; + optr->time = (rand() % 20) + 5; + break; + } + optr++; + } +} + + +void initstars(void) +{ + int count; + STAR *sptr = &star[0]; + + for (count = MAX_STARS; count; count--) + { + sptr->x = rand() % 640; + sptr->y = rand() % 200; + sptr->sx = (rand() % 6) + 1; + sptr++; + } +} + +void movestars(void) +{ + int count; + STAR *sptr = &star[0]; + + for (count = MAX_STARS; count; count--) + { + sptr->x -= sptr->sx; + if (sptr->x < 0) + { + sptr->x = 640; + sptr->y = rand() % 200; + sptr->sx = (rand() % 6) + 1; + } + sptr++; + } +} + +void drawstars(void) +{ + int count; + STAR *sptr = &star[0]; + + for (count = MAX_STARS; count; count--) + { + if ((sptr->x >= 0) && (sptr->y >= 0) && (sptr->x < 640) && (sptr->y < 200)) + { + unsigned char *dptr = scrbuffer + (((sptr->x >> 3) + (sptr->y >> 3) * 80) << 1); + dptr[0] = 250; + dptr[1] = colortable[sptr->sx]; + } + sptr++; + } +} + +/* + * Scales the x-coordinates used in the game (0-639) on the 0-255 panning + * scale. + */ +int scalepanning(int x) +{ + int panning = (x * 4) / 10; + if (panning < 0) panning = 0; + if (panning > 255) panning = 255; + return panning; +} + +void loadconfig(void) +{ + FILE *file = fopen("anal.cfg", "rt"); + if (!file) return; + + fscanf(file, "%d %d %d %d %d", &mixrate, &mixer, &interpolation, &musicvol, &sfxvol); +} + +void saveconfig(void) +{ + FILE *file = fopen("anal.cfg", "wt"); + if (!file) return; + + fprintf(file, "%d %d %d %d %d", mixrate, mixer, interpolation, musicvol, sfxvol); +} + +void clearscreen(void) +{ + memset(scrbuffer, 0, 80 * 25 * 2); +} + +void printtext(int x, int y, int color, const char *text) +{ + unsigned char *dptr = scrbuffer + ((x + y * 80) << 1); + + while (*text) + { + *dptr++ = *text++; + *dptr++ = color; + } +} + +void printtextc(int y, int color, const char *text) +{ + int x = (80 - strlen(text)) / 2; + unsigned char *dptr = scrbuffer + ((x + y * 80) << 1); + + while (*text) + { + *dptr++ = *text++; + *dptr++ = color; + } +} + +void fliptob8000(void) +{ + memcpy((char *)0xb8000 + __djgpp_conventional_base, scrbuffer, 80 * 25 * 2); +} + +static void handle_int(int a) +{ + resetmode(); /* To prevent user panic when no cursor is seen! */ + exit(0); /* Atexit functions will be called! */ +} diff --git a/ANAL.CFG b/ANAL.CFG new file mode 100644 index 0000000..831d4cd --- /dev/null +++ b/ANAL.CFG @@ -0,0 +1 @@ +6 2 1 10 8 \ No newline at end of file diff --git a/ANAL.DJ b/ANAL.DJ new file mode 100644 index 0000000..2e0c5f3 --- /dev/null +++ b/ANAL.DJ @@ -0,0 +1,49 @@ +# +# JUDAS library examples makefile for DJGPP v2.0 +# +# note : nasm compiles for coff output : +# nasm jasmdj.asm -ddjgpp -fcoff -E!nasm.err +# rem nasm jasmdj.asm -dwatcom -fobj -E!nasm.err +# + +CC = gcc +AS = nasm +AR = ar +RANLIB = ranlib +STRIP = strip +COFF2EXE = stubify +AFLAGS = -ddjgpp -fcoff +CFLAGS = -g -c -Wall -Wno-pointer-sign -march=pentium -O2 +LFLAGS = -g + +OBJS = anal.o timer.o timerasm.o kbd.o kbdasm.o + +LIB = judaslib.a + +all : anal.exe clean + @echo done. + +# make the ANAL INVADERS game anal.exe +anal.exe : $(OBJS) $(LIB) + $(CC) -o anal $(OBJS) $(LIB) $(LFLAGS) +# $(STRIP) anal + $(COFF2EXE) anal + @del anal > nul + +anal.o : anal.c + $(CC) $(CFLAGS) $< + +timer.o : timer.c + $(CC) $(CFLAGS) $< + +timerasm.o : timerasm.asm + $(AS) $(AFLAGS) $< + +kbd.o : kbd.c + $(CC) $(CFLAGS) $< + +kbdasm.o : kbdasm.asm + $(AS) $(AFLAGS) $< + +clean : + @if exist *.o del *.o > nul diff --git a/ANAL.WAT b/ANAL.WAT new file mode 100644 index 0000000..e87a49b --- /dev/null +++ b/ANAL.WAT @@ -0,0 +1,16 @@ +# ANAL INVADERS makefile + +anal.exe: anal.obj timer.obj timerasm.obj kbd.obj kbdasm.obj +# wlink F anal,timer,timerasm,kbd,kbdasm N anal.exe L judas.lib SYS dos4g + wcl386 -d2 -ldos4g anal.c timer.c timerasm.obj kbd.c kbdasm.obj judas.lib + +anal.obj: anal.c + wcc386 -d2 -w3 -zp4 anal.c +timer.obj: timer.c + wcc386 -d2 -w3 -zp4 timer.c +kbd.obj: kbd.c + wcc386 -d2 -w3 -zp4 kbd.c +timerasm.obj: timerasm.asm + nasm -dwatcom -fobj timerasm.asm +kbdasm.obj: kbdasm.asm + nasm -dwatcom -fobj kbdasm.asm diff --git a/EXPLSION.WAV b/EXPLSION.WAV new file mode 100644 index 0000000000000000000000000000000000000000..1b9f8f13a769afad1545e037f9d8841b801ce7ac GIT binary patch literal 30640 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 literal 0 HcmV?d00001 diff --git a/FART.WAV b/FART.WAV new file mode 100644 index 0000000000000000000000000000000000000000..52f18f67ea73a8f56fb2f17bb6539ba8724f40ad GIT binary patch literal 15714 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 JUDAS V2.10b <<<***+++-- +Apocalyptic Softwaremixing Soundsystem +by Cadaver & Yehar, for WATCOM 32bit +flat mode. AC97/HDA by Black Spider. +NEW VERSION WITH NEW FIXES, ENHANCED +QUALITY MIXER, TRUE VUMETERS, LOOPING +CONTROL, WAV WRITER... + MORE + +Works with multitaskers. PMODEW/DOS4GW +SB/SBPRO/SB16/GUS/AC97/HDA XM/S3M/MOD/WAV +Total softwaremixing - No GUS limit!!! +Clipping, Hermite interpolation, 32bit +mixing, Click removal, An alternative +ultra-fast mixer. Examples,Docs,Player +and Full source included. Part of the +library code related to HDA support +falls under the GNU/GPL license. \ No newline at end of file diff --git a/ICHINIT/!COMPILE.BAT b/ICHINIT/!COMPILE.BAT new file mode 100644 index 0000000..d4a85d9 --- /dev/null +++ b/ICHINIT/!COMPILE.BAT @@ -0,0 +1,7 @@ +nasm dos32\input.asm -g -fobj -Einput.err +nasm dos32\timer.asm -g -fobj -Etimer.err +nasm dos32\dpmi.asm -g -fobj -Edpmi.err +wcl386 /d2 /l=dos4g /bt=dos ichinit.c pci.c codec.c dos32\dpmi.obj dos32\timer.obj dos32\input.obj +del ichinit.obj +rem stubit ichinit.exe +del *.err diff --git a/ICHINIT/!DEBUG.BAT b/ICHINIT/!DEBUG.BAT new file mode 100644 index 0000000..311aa45 --- /dev/null +++ b/ICHINIT/!DEBUG.BAT @@ -0,0 +1,2 @@ +C:\WCC386\BINW\WD /tr=rsi ichinit.exe + diff --git a/ICHINIT/!STUBIT.BAT b/ICHINIT/!STUBIT.BAT new file mode 100644 index 0000000..0c34080 --- /dev/null +++ b/ICHINIT/!STUBIT.BAT @@ -0,0 +1 @@ +stubit ichinit.exe diff --git a/ICHINIT/CODEC.C b/ICHINIT/CODEC.C new file mode 100644 index 0000000..c731d59 --- /dev/null +++ b/ICHINIT/CODEC.C @@ -0,0 +1,222 @@ + +// codec module for ICHINIT v2.0+ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "judasac.h" +#include "dos32\dpmi.h" +#include "dos32\input.h" +#include "dos32\scans.h" +#include "dos32\timer.h" + + +extern BOOL codec_config (AC97_PCI_DEV *ac97_pci, int sample_rate); +extern BOOL semaphore (AC97_PCI_DEV *ac97_pci); + +extern unsigned int ich_INL (AC97_PCI_DEV *ac97_pci, int base, unsigned int a); +extern unsigned short ich_INW (AC97_PCI_DEV *ac97_pci, int base, unsigned short a); +extern unsigned char ich_INB (AC97_PCI_DEV *ac97_pci, int base, unsigned char a); +extern void ich_OUTL (AC97_PCI_DEV *ac97_pci, unsigned int d, int base, unsigned int a); +extern void ich_OUTW (AC97_PCI_DEV *ac97_pci, unsigned short d, int base, unsigned short a); +extern void ich_OUTB (AC97_PCI_DEV *ac97_pci, unsigned char d, int base, unsigned char a); + +void codec_stop (AC97_PCI_DEV *ac97_pci); +void set_volume (AC97_PCI_DEV *ac97_pci, int volume); + + +unsigned int ich_INL (AC97_PCI_DEV *ac97_pci, int base, unsigned int a) +{ + if (ac97_pci->mem_mode == 1) { + if (base == CTL_BASE) return *(unsigned int *) (ac97_pci->base3 + (a)); // bus master base memory + else return *(unsigned int *) (ac97_pci->base2 + (a)); // audio mixer base memory + } else { + if (base == CTL_BASE) return inpd (ac97_pci->base1 + a); // bus master base IO + else return inpd (ac97_pci->base0 + a); // audio mixer base IO + } +} + +unsigned short ich_INW (AC97_PCI_DEV *ac97_pci, int base, unsigned short a) +{ + if (ac97_pci->mem_mode == 1) { + if (base == CTL_BASE) return *(unsigned short *) (ac97_pci->base3 + (a)); // bus master base memory + else return *(unsigned short *) (ac97_pci->base2 + (a)); // audio mixer base memory + } else { + if (base == CTL_BASE) return inpw (ac97_pci->base1 + a); // bus master base IO + else return inpw (ac97_pci->base0 + a); // audio mixer base IO + } +} + +unsigned char ich_INB (AC97_PCI_DEV *ac97_pci, int base, unsigned char a) +{ + if (ac97_pci->mem_mode == 1) { + if (base == CTL_BASE) return *(unsigned char *) (ac97_pci->base3 + (a)); // bus master base memory + else return *(unsigned char *) (ac97_pci->base2 + (a)); // audio mixer base memory + } else { + if (base == CTL_BASE) return inp (ac97_pci->base1 + a); // bus master base IO + else return inp (ac97_pci->base0 + a); // audio mixer base IO + } +} + +void ich_OUTL (AC97_PCI_DEV *ac97_pci, unsigned int d, int base, unsigned int a) +{ + if (ac97_pci->mem_mode == 1) { + if (base == CTL_BASE) *(unsigned int *) (ac97_pci->base3 + (a)) = d; // bus master base memory + else *(unsigned int *) (ac97_pci->base2 + (a)) = d; // audio mixer base memory + } else { + if (base == CTL_BASE) outpd (ac97_pci->base1 + a, d); // bus master base IO + else outpd (ac97_pci->base0 + a, d); // audio mixer base IO + } +} + +void ich_OUTW (AC97_PCI_DEV *ac97_pci, unsigned short d, int base, unsigned short a) +{ + if (ac97_pci->mem_mode == 1) { + if (base == CTL_BASE) *(unsigned short *) (ac97_pci->base3 + (a)) = d; // bus master base memory + else *(unsigned short *) (ac97_pci->base2 + (a)) = d; // audio mixer base memory + } else { + if (base == CTL_BASE) outpw (ac97_pci->base1 + a, d); // bus master base IO + else outpw (ac97_pci->base0 + a, d); // audio mixer base IO + } +} + +void ich_OUTB (AC97_PCI_DEV *ac97_pci, unsigned char d, int base, unsigned char a) +{ + if (ac97_pci->mem_mode == 1) { + if (base == CTL_BASE) *(unsigned char *) (ac97_pci->base3 + (a)) = d; // bus master base memory + else *(unsigned char *) (ac97_pci->base2 + (a)) = d; // audio mixer base memory + } else { + if (base == CTL_BASE) outp (ac97_pci->base1 + a, d); // bus master base IO + else outp (ac97_pci->base0 + a, d); // audio mixer base IO + } +} + +// stop the codec +void codec_stop (AC97_PCI_DEV *ac97_pci) +{ + // stop all PCM out data + ich_OUTB (ac97_pci, 0, CTL_BASE, ICH_REG_PO_CR); // 1Bh control register at NABMBAR + delay (50); // 50ms delay + + // reset PCM out registers + ich_OUTB (ac97_pci, ICH_RESETREGS, CTL_BASE, ICH_REG_PO_CR); // 1Bh control register at NABMBAR + delay (50); // 50ms delay +} + +// wait until codec is reay - returns status (TRUE or FALSE) +BOOL semaphore (AC97_PCI_DEV *ac97_pci) +{ + DWORD flags = 0; + int i = 0; + + flags = ich_INL (ac97_pci, CTL_BASE, ICH_REG_GLOB_STA); // 30h global status register at NABMBAR + if ((flags & ICH_PCR) == 0) return TRUE; // exit with success if primary codec not ready ! + + for (i = 0; i < 0xffff; i++) { + flags = ich_INB (ac97_pci, CTL_BASE, ICH_REG_ACC_SEMA); // 34h codec write semaphore register at NABMBAR + if ((flags & ICH_CAS) == 0) return TRUE; // exit if codec not busy! + } + + return FALSE; // exit with failure +} + +// set voulme to volume level +void set_volume (AC97_PCI_DEV *ac97_pci, int volume) +{ + semaphore (ac97_pci); + ich_OUTW (ac97_pci, 0, MIXER_BASE, AC97_RESET); // register reset the codec (0) + semaphore (ac97_pci); + ich_OUTW (ac97_pci, volume * 0x101, MIXER_BASE, AC97_MASTER); // set volume for both channels (2) + semaphore (ac97_pci); + ich_OUTW (ac97_pci, volume * 0x101, MIXER_BASE, AC97_HEADPHONE); // set hp volume for both channels (4) + semaphore (ac97_pci); + ich_OUTW (ac97_pci, volume * 0x101, MIXER_BASE, AC97_CD_VOL); // set CD out volume for both channels (12h) + semaphore (ac97_pci); + ich_OUTW (ac97_pci, volume * 0x101, MIXER_BASE, AC97_PCM); // set CD out volume for both channels (12h) + +} + +// enable codec, unmute stuff, set output to desired rate +// in = desired sample rate +// out = true or false +BOOL codec_config (AC97_PCI_DEV *ac97_pci, int sample_rate) +{ + DWORD flags = 0; + int i = 0; + + // stop the codec if currently playing + codec_stop (ac97_pci); + + // do a cold reset + // enable AC Link off clear + ich_OUTL (ac97_pci, 0, CTL_BASE, ICH_REG_GLOB_CNT); // 2ch global control reg at NABMBAR + delay (50); + + // cold reset + primary resume -> 2 channels and 16 bit samples + ich_OUTL (ac97_pci, ICH_AC97COLD + ICH_PRIE, CTL_BASE, ICH_REG_GLOB_CNT); // 2ch global control reg at NABMBAR + + // test cold reset + flags = ich_INL (ac97_pci, CTL_BASE, ICH_REG_GLOB_CNT); // 2ch global control reg at NABMBAR + if ((flags & ICH_AC97COLD) == 0) return FALSE; + + // wait for primary codec ready status - ignore for ICH4+ + if (ac97_pci->mem_mode == 0) { // for normal IO access only + i = 128; + while (i) { + flags = ich_INL (ac97_pci, CTL_BASE, ICH_REG_GLOB_STA); // 30h global status register at NABMBAR + if (flags & ICH_PCR) break; // primary codec ready bit set + delay (1); // 1ms delay + i--; + } + + // wait until codec init ready (replaces warm reset wait) + delay (800); // delay 800ms + + // test if codec ready bit is finally set + flags = ich_INL (ac97_pci, CTL_BASE, ICH_REG_GLOB_STA); // 30h global status register at NABMBAR + if ((flags & ICH_PCR) == 0) return FALSE; // primary codec ready bit not set - error + } + + // clear semaphore flag + flags = ich_INW (ac97_pci, MIXER_BASE, AC97_RESET); // register reset the codec (0) + + // check if codec sections ready + if (semaphore (ac97_pci) == 0) return FALSE; // exit with error + flags = ich_INW (ac97_pci, MIXER_BASE, AC97_POWER_CTRL); // 26h powerdown control + flags &= BIT0 + BIT1 + BIT2 +BIT3; + if (flags != (BIT0 + BIT1 + BIT2 +BIT3)) return FALSE; // codec sections not ready + + // disable interrupts + ich_OUTB (ac97_pci, 0, CTL_BASE, ICH_REG_PI_CR); // 0Bh control register at NABMBAR + ich_OUTB (ac97_pci, 0, CTL_BASE, ICH_REG_PO_CR); // 1Bh control register at NABMBAR + ich_OUTB (ac97_pci, 0, CTL_BASE, ICH_REG_MC_CR); // 2Bh control register at NABMBAR + + // reset channels + ich_OUTB (ac97_pci, ICH_RESETREGS, CTL_BASE, ICH_REG_PI_CR); // 0Bh control register at NABMBAR + ich_OUTB (ac97_pci, ICH_RESETREGS, CTL_BASE, ICH_REG_PO_CR); // 1Bh control register at NABMBAR + ich_OUTB (ac97_pci, ICH_RESETREGS, CTL_BASE, ICH_REG_MC_CR); // 2Bh control register at NABMBAR + + // set default volume + // set_volume (ac97_pci, 15); + + // set VRA and clear DRA (if not supported will be skipped) + if (semaphore (ac97_pci) == 0) return FALSE; // exit with error + flags = ich_INW (ac97_pci, MIXER_BASE, AC97_EXTENDED_STATUS); // 2ah get extended audio ctl NAMBAR + if (semaphore (ac97_pci) == 0) return FALSE; // exit with error + flags &= 0xffff - BIT1; + flags |= BIT0; + ich_OUTW (ac97_pci, flags, MIXER_BASE, AC97_EXTENDED_STATUS); // 2ah set extended audio ctl NAMBAR + + // set desired sample rate + if (semaphore (ac97_pci) == 0) return FALSE; // exit with error + ich_OUTW (ac97_pci, sample_rate, MIXER_BASE, AC97_PCM_FRONT_DAC_RATE); // 2ch set sample rate NAMBAR + + return TRUE; +} diff --git a/ICHINIT/DOS32/!BLIB.BAT b/ICHINIT/DOS32/!BLIB.BAT new file mode 100644 index 0000000..f60b10b --- /dev/null +++ b/ICHINIT/DOS32/!BLIB.BAT @@ -0,0 +1 @@ +wlib /n /c dosio.lib +dpmi.obj +input.obj +timer.obj diff --git a/ICHINIT/DOS32/COLORS.TXT b/ICHINIT/DOS32/COLORS.TXT new file mode 100644 index 0000000..edcd61f --- /dev/null +++ b/ICHINIT/DOS32/COLORS.TXT @@ -0,0 +1,18 @@ +; foreground colors (bits 0-3) +; 0 = black +; 1 = blue +; 2 = green +; 3 = cyan +; 4 = red +; 5 = magneta +; 6 = brown +; 7 = light gray +; 8 = dark grey +; 9 = light blue +; 10 = light green +; 11 = light cyan +; 12 = light red +; 13 = light magneta +; 14 = yellow +; 15 = white +; background colors (bits 4-7) diff --git a/ICHINIT/DOS32/DPMI.ASM b/ICHINIT/DOS32/DPMI.ASM new file mode 100644 index 0000000..611b0df --- /dev/null +++ b/ICHINIT/DOS32/DPMI.ASM @@ -0,0 +1,337 @@ + +; +; basic DPMI functions for C/C++ by Piotr Ulaszewski (2000 - 2002) +; modified and improved for === THE NEXT STEP RELEASE 0.10 === +; + +global _DPMI_DOSmalloc ; function 100h +global _DPMI_DOSfree ; function 101h +global _DPMI_GetRMVector ; function 200h +global _DPMI_SetRMVector ; function 201h +global _DPMI_GetPMVector ; function 204h +global _DPMI_SetPMVector ; function 205h +global _DPMI_SimulateRMI ; function 300h +global _DPMI_Malloc ; function 501h +global _DPMI_Free ; function 502h +global _DPMI_Lock ; function 600h +global _DPMI_Unlock ; function 601h +global _DPMI_MapMemory ; function 800h +global _DPMI_UnmapMemory ; function 801h + + +segment _TEXT public align=256 class=CODE use32 + +; bool DPMI_DOSmalloc (unsigned long size, unsigned short *segment, unsigned short *selector) +_DPMI_DOSmalloc: + push ebx + push edx + mov eax,0100h + mov ebx,[esp+4+8] ; size in bytes + add ebx,0fh + shr ebx,4 ; convert to para + int 31h + jnc .malloc_ok + xor eax,eax + jmp short .exit +.malloc_ok: + mov ebx,[esp+8+8] ; ebx = pointer to segment of allocated block + mov [ebx],ax + mov ebx,[esp+12+8] ; ebx = pointer to selector of allocated block + mov [ebx],dx + mov eax,1 +.exit: + pop edx + pop ebx + ret + + +; void DPMI_DOSfree (unsigned short *selector); +_DPMI_DOSfree: + push eax + push edx + mov eax,[esp+4+8] ; eax = pointer to selector of freed block + mov dx,[eax] + mov eax,0101h + int 31h + pop edx + pop eax + ret + + +; void DPMI_GetRMVector (unsigned char IntNum, unsigned short *segment, unsigned short *offset); +_DPMI_GetRMVector: + push ebp + mov ebp,esp + push eax + push ebx + push ecx + push edx + mov eax,0200h + movzx ebx,byte [ebp+8] ; IntNum + int 31h + mov ebx,[ebp+12] ; pointer to segment of RM interrupt vector + mov [ebx],cx + mov ebx,[ebp+16] ; pointer to offset of RM interrupt vector + mov [ebx],dx + pop edx + pop ecx + pop ebx + pop eax + ret + + +; void DPMI_SetRMVector (unsigned char IntNum, unsigned short *segment, unsigned short *offset); +_DPMI_SetRMVector: + push ebp + mov ebp,esp + push eax + push ebx + push ecx + push edx + mov ebx,[ebp+12] ; pointer to segment of RM interrupt vector + mov cx,[ebx] ; segment + mov ebx,[ebp+16] ; pointer to offset of RM interrupt vector + mov dx,[ebx] ; offset + movzx ebx,byte [ebp+8] ; IntNum + mov eax,0201h + int 31h + pop edx + pop ecx + pop ebx + pop eax + ret + + +; void DPMI_GetPMVector (unsigned char IntNum, unsigned short *selector, unsigned long *offset); +_DPMI_GetPMVector: + push ebp + mov ebp,esp + push eax + push ebx + push ecx + push edx + mov eax,0204h + movzx ebx,byte [ebp+8] ; IntNum + int 31h + mov ebx,[ebp+12] ; pointer to selector of interrupt vector + mov [ebx],cx + mov ebx,[ebp+16] ; pointer to offset of interrupt vector + mov [ebx],edx + pop edx + pop ecx + pop ebx + pop eax + ret + + +; void DPMI_SetPMVector (unsigned char IntNum, unsigned short *selector, unsigned long *offset); +_DPMI_SetPMVector: + push ebp + mov ebp,esp + push eax + push ebx + push ecx + push edx + mov ebx,[ebp+12] ; pointer to selector of interrupt vector + mov cx,[ebx] ; selector + mov ebx,[ebp+16] ; pointer to offset of interrupt vector + mov edx,[ebx] ; offset + movzx ebx,byte [ebp+8] ; IntNum + mov eax,0205h + int 31h + pop edx + pop ecx + pop ebx + pop eax + ret + + +; bool DPMI_SimulateRMI (unsigned char IntNum, DPMIREGS *regs); +_DPMI_SimulateRMI: + push ebp + mov ebp,esp + pushad + push es + push ds + pop es + mov edi,[ebp+12] ; ES:EDI = DPMIREGS pointer + sub ecx,ecx + sub ebx,ebx + mov bl,[ebp+8] ; IntNum + mov eax,300h + int 31h + jc .error + pop es + popad + pop ebp + mov eax,1 ; success + ret +.error: + pop es + popad + pop ebp + sub eax,eax ; failure + ret + + +; void *DPMI_Malloc (unsigned long size, unsigned long *handle) +_DPMI_Malloc: + push ebp + mov ebp,esp + push ebx + push ecx + push edx + push esi + push edi + + mov eax,501h + mov ebx,[ebp+8] ; size + shr ebx,16 + mov ecx,[ebp+8] ; size + and ecx,0ffffh ; BX:CX = size + int 31h + jnc .malloc_ok + xor eax,eax + jmp short .exit +.malloc_ok: + shl ebx,16 + mov bx,cx + mov eax,ebx + mov ebx,[ebp+12] ; pointer to handle + shl esi,16 + mov si,di + mov [ebx],esi ; handle +.exit: + pop edi + pop esi + pop edx + pop ecx + pop ebx + pop ebp + ret + + +; void DPMI_Free (unsigned long *handle) +_DPMI_Free: + push eax + push esi + push edi + mov eax,[esp+4+12] ; pointer to handle + mov esi,[eax] + mov di,si + shr esi,16 + mov eax,502h + int 31h + pop edi + pop esi + pop eax + ret + + +; bool DPMI_Lock (unsigned long *address, unsigned long size); +_DPMI_Lock: + push ebp + mov ebp,esp + pushad + mov eax,[ebp+8] ; pointer to address + mov ebx,[eax] ; starting address + mov cx,bx + shr ebx,16 ; BX:CX = starting address + mov esi,[ebp+12] ; size in bytes + mov di,si + shr esi,16 ; SI:DI = size in bytes + mov eax,600h ; lock linear region + int 31h + jc .error + popad + pop ebp + mov eax,1 ; success + ret +.error: + popad + pop ebp + sub eax,eax ; failure + ret + + +; bool DPMI_Unlock (unsigned long *address, unsigned long size); +_DPMI_Unlock: + push ebp + mov ebp,esp + pushad + mov eax,[ebp+8] ; pointer to address + mov ebx,[eax] ; starting address + mov cx,bx + shr ebx,16 ; BX:CX = starting address + mov esi,[ebp+12] ; size in bytes + mov di,si + shr esi,16 ; SI:DI = size in bytes + mov eax,601h ; unlock linear region + int 31h + jc .error + popad + pop ebp + mov eax,1 ; success + ret +.error: + popad + pop ebp + sub eax,eax ; failure + ret + + +; bool DPMI_MapMemory (unsigned long *physaddress, unsigned long *linaddress, unsigned long size); +_DPMI_MapMemory: + push ebp + mov ebp,esp + pushad + mov eax,[ebp+8] ; pointer to physical address + mov ebx,[eax] ; physical address + mov cx,bx + shr ebx,16 ; BX:CX = physical address + mov esi,[ebp+16] ; size in bytes + mov di,si + shr esi,16 ; SI:DI = size in bytes + mov eax,800h ; physical address mapping + int 31h + jc .error + shl ebx,16 + mov bx,cx + mov eax,[ebp+12] ; pointer to linear address + mov [eax],ebx ; linaddress = BX:CX + popad + pop ebp + mov eax,1 ; success + ret +.error: + mov eax,[ebp+12] ; pointer to linear address + mov dword [eax],0 ; linaddress = NULL on error + popad + pop ebp + sub eax,eax ; failure + ret + + +; bool DPMI_UnmapMemory (unsigned long *linaddress); +_DPMI_UnmapMemory: + push ebp + mov ebp,esp + pushad + mov eax,[ebp+8] ; pointer to linear address + mov ebx,[eax] ; linear address + mov cx,bx + shr ebx,16 ; BX:CX = linear address + mov eax,801h ; free physical address mapping + int 31h + jc .error + popad + pop ebp + mov eax,1 ; success + ret +.error: + popad + pop ebp + sub eax,eax ; failure + ret + + diff --git a/ICHINIT/DOS32/DPMI.H b/ICHINIT/DOS32/DPMI.H new file mode 100644 index 0000000..a063daa --- /dev/null +++ b/ICHINIT/DOS32/DPMI.H @@ -0,0 +1,65 @@ + +/* + + Header file for basic DPMI functions + (2000-2002) Piotr Ulaszewski + +*/ + +#ifndef __DPMI_H__ +#define __DPMI_H__ + +#ifndef FLIP2API +#define FLIP2API _cdecl +#endif + + +/* DPMI regs structure for simulate real mode interrupt */ +#pragma pack (push, 1); +typedef struct _DPMIREGS{ + unsigned int edi; + unsigned int esi; + unsigned int ebp; + unsigned int reserved; + unsigned int ebx; + unsigned int edx; + unsigned int ecx; + unsigned int eax; + unsigned short flags; + unsigned short es; + unsigned short ds; + unsigned short fs; + unsigned short gs; + unsigned short ip; + unsigned short cs; + unsigned short sp; + unsigned short ss; +} DPMIREGS; +#pragma pack (pop); + + +#ifdef __cplusplus +extern "C" { +#endif + + +extern bool FLIP2API DPMI_DOSmalloc (unsigned long size, unsigned short *segment, unsigned short *selector); +extern void FLIP2API DPMI_DOSfree (unsigned short *selector); +extern void FLIP2API DPMI_GetRMVector (unsigned char IntNum, unsigned short *segment, unsigned short *offset); +extern void FLIP2API DPMI_SetRMVector (unsigned char IntNum, unsigned short *segment, unsigned short *offset); +extern void FLIP2API DPMI_GetPMVector (unsigned char IntNum, unsigned short *selector, unsigned long *offset); +extern void FLIP2API DPMI_SetPMVector (unsigned char IntNum, unsigned short *selector, unsigned long *offset); +extern bool FLIP2API DPMI_SimulateRMI (unsigned char IntNum, DPMIREGS *regs); +extern void * FLIP2API DPMI_Malloc (unsigned long size, unsigned long *handle); +extern void FLIP2API DPMI_Free (unsigned long *handle); +extern bool FLIP2API DPMI_Lock (unsigned long *address, unsigned long size); +extern bool FLIP2API DPMI_Unlock (unsigned long *address, unsigned long size); +extern bool FLIP2API DPMI_MapMemory (unsigned long *physaddress, unsigned long *linaddress, unsigned long size); +extern bool FLIP2API DPMI_UnmapMemory (unsigned long *linaddress); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/ICHINIT/DOS32/INPUT.ASM b/ICHINIT/DOS32/INPUT.ASM new file mode 100644 index 0000000..42a366d --- /dev/null +++ b/ICHINIT/DOS32/INPUT.ASM @@ -0,0 +1,389 @@ + +;****************************************************************************** +;* +;* INPUT.ASM module for ULTIMATE_FLIP library +;* by Piotr Ulaszewski . +;* piotrkn22@poczta.onet.pl +;* http://www.republika.pl/piotrkn22/ +;* Keyboard interrupt handler for protected mode. +;* Some info in Judas 2.06y source code about extended keys processing +;* was very usefull to develop this handler. +;* Includes DPMI lock and extended keys. +;* This keyboard handler may sometimes behave strangely under DOS4GW, +;* please use WDOSX or PMODEW instead (especially on slow systems). +;* +;* This is an extended version for C/C++ support. +;* developemnent version 0.44 (special without mouse handler) +;* +;****************************************************************************** + +%include "segments.inc" + +; for keyboard input +global _flip2_KeyTable ; pointer to key table (locked) +global _flip2_KeyInit ; proc +global _flip2_KeyDeinit ; proc +global _flip2_GetKey ; proc +global _flip2_GetLastKey ; proc +global _flip2_GetKeyAscii ; proc +global _flip2_ScanToAscii ; proc +global _flip2_KeyErrorcode ; dword + + +segment _DATA +; data values for keyboard handler +_flip2_KeyTable dd 0 ; address of key_tab +old_key_handler_offset dd 0 +old_key_handler_selector dd 0 +_flip2_KeyErrorcode dd 0 ; error code for key functions + + +;********* A S C I I C O N V E R S I O N T A B L E 1 **************** +; Ascii conversion table (SHIFT key depressed) +ScanToAsciiTable1 db 0,27,49,50,51,52,53,54,55,56,57,48,45,61,8,9 +; nt esc 1 2 3 4 5 6 7 8 9 0 - = bs ht + +db 113,119,101,114,116,121,117,105,111,112,91,93,13,0,97,115 +; q w e r t y u i o p [ ] cr (ctrll) a s + +db 100,102,103,104,106,107,108,59,39,96,0,92,122,120,99,118 +; d f g h j k l ; ' ` (shl) \ z x c v + +db 98,110,109,44,46,47,0,42,0,32,0,0,0,0,0,0 +; b n m , . / (shr) * (altl) spc (caps) (f1) (f2) (f3) (f4) (f5) + +db 0,0,0,0,0,0,0,55,56,57,45,52,53,54,43,49 +; (f6) (f7) (f8) (f9) (f10) (nl) (sl) kp7 kp8 kp9 g- kp4 kp5 kp6 g+ kp1 + +db 50,51,48,46,0,0,0,0,0,0,0,0,0,0,0,0 +; kp2 kp3 kp0 kppt nt nt nt (f11) (f12) nt nt nt nt nt nt nt + +times 160 db 0 +; all extended keys zeroed + + +;********* A S C I I C O N V E R S I O N T A B L E 2 **************** +; Ascii conversion table (SHIFT key pressed) +ScanToAsciiTable2 db 0,27,33,64,35,36,37,94,38,42,40,41,95,43,8,9 +; nt esc ! @ # $ % ^ & * ( ) _ + bs ht + +db 81,87,69,82,84,89,85,73,79,80,123,125,13,0,65,83 +; Q W E R T Y U I O P { } cr (ctrll) A S + +db 68,70,71,72,74,75,76,58,34,126,0,124,90,88,67,86 +; D F G H J K L : " ~ (shl) | Z X C V + +db 66,78,77,60,62,63,0,42,0,32,0,0,0,0,0,0 +; B N M < > ? (shr) * (altl) spc (caps) (f1) (f2) (f3) (f4) (f5) + +db 0,0,0,0,0,0,0,55,56,57,45,52,53,54,43,49 +; (f6) (f7) (f8) (f9) (f10) (nl) (sl) kp7 kp8 kp9 g- kp4 kp5 kp6 g+ kp1 + +db 50,51,48,46,0,0,0,0,0,0,0,0,0,0,0,0 +; kp2 kp3 kp0 kppt nt nt nt (f11) (f12) nt nt nt nt nt nt nt + +times 160 db 0 +; all extended keys zeroed + +;****************************************************************************** + + +;****************************************************************************** +;* +;* Keyboard handler functions. +;* Multiple calls to these functions will not hang the machine. +;* Warning - this will only work with 4GB flat mode zero based segments +;* where the base and limit of CS is equal to those of DS. +;* +;* enable : call _flip2_KeyInit - returns TRUE or FALSE +;* +;* disable : call _flip2_KeyDeinit - returns nothing +;* +;* out : eax = TRUE(1) on success or FALSE(0) on error +;* _flip2_KeyErrorcode holds the function exec status +;* = 0 - success +;* = 1 - DPMI lock error +;* = 2 - set interrupt vector error +;* +;****************************************************************************** +segment _TEXT +_flip2_KeyInit: + cmp dword [old_key_handler_offset],0 + jne near .exit_2 ; key handler already running -> exit + push ebx + push ecx + push edx + push esi + push edi + push es + push ds + pop es + cld + cli + + mov eax,600h ; lock linear region + mov ebx,irq9 ; offset to key handler + mov cx,bx + shr ebx,16 ; BX:CX = address + sub esi,esi + mov edi,end_of_key_lock - irq9 ; length of handler + table + int 31h + jnc .dpmi_lock_ok + sub eax,eax + mov dword [_flip2_KeyErrorcode],1 ; DPMI lock error + jmp short .exit + +.dpmi_lock_ok: + mov eax,key_tab ; clear key table + mov [_flip2_KeyTable],eax ; address of key_tab + mov edi,eax + mov ecx,64 + sub eax,eax + rep stosd + + mov bl,9 ; get old key vector + mov eax,204h + int 31h + mov [old_key_handler_offset],edx + mov [old_key_handler_selector],cx + + mov ax,ds + mov [key_flat_mode_data_selector],ax + + mov edx,irq9 ; set new key vector + mov cx,cs + mov bl,9 + mov eax,205h + int 31h + jnc .set_vector_ok + sub eax,eax + mov dword [_flip2_KeyErrorcode],2 ; set interrupt vector error + jmp short .exit + +.set_vector_ok: + mov eax,1 + mov dword [_flip2_KeyErrorcode],0 ; status = success + +.exit: + sti + pop es + pop edi + pop esi + pop edx + pop ecx + pop ebx +.exit_2: + ret + + + +;****************************************************************************** +_flip2_KeyDeinit: + cmp dword [old_key_handler_offset],0 + je .exit ; no key handler running -> exit + pushad + cli + + mov edx,[old_key_handler_offset] ; restore old key handler + mov cx,[old_key_handler_selector] + mov bl,9 + mov eax,205h + int 31h + + mov eax,601h ; unlock linear region + mov ebx,irq9 ; offset to key handler + mov cx,bx + shr ebx,16 + sub esi,esi + mov edi,end_of_key_lock - irq9 ; length of handler + table + int 31h + + mov dword [old_key_handler_offset],0 + mov word [old_key_handler_selector],0 + mov dword [_flip2_KeyErrorcode],0 ; status = success + sti + popad +.exit: + ret + + + +;****************************************************************************** + align 32 + +irq9: ; KEYBOARD HANDLER +; pushfd + pushad + push ds + mov ds,[cs:key_flat_mode_data_selector] ; needed for IRQ + + sub eax,eax + in al,60h ; get key code + cmp byte [pause_key],0 + je .@@no_pause + dec byte [pause_key] + jmp .@@end +.@@no_pause: + cmp al,0e0h ; will the next be extended + jne .@@no_extended + mov byte [extended_key],1 ; set flag + jmp .@@end +.@@no_extended: + cmp al,0e1h ; is it pause + jne .@@no_pause2 + xor byte [key_tab + 255],1 ; set/clear pause state + mov byte [pause_key],5 ; 5 codes coming to discard + mov byte [was_key_pressed],1 ; for GetKey function + mov byte [last_key_scancode],255 ; pause key scancode + jmp short .@@end +.@@no_pause2: + sub ebx,ebx ; reset to normal key offset + cmp byte [extended_key],1 + jne .@@no_extended2 + mov byte [extended_key],0 ; reset flag + cmp al,2ah ; skip system request + je .@@end + mov bl,80h ; extended key offset +.@@no_extended2: + cmp al,080h ; press or release + jae .@@clr + mov edi,key_tab ; offset of key table + add edi,eax + add edi,ebx ; normal or extended + mov byte [edi],1 ; press + mov byte [was_key_pressed],1 ; for GetKey function + mov byte [last_key_scancode],bl ; extended key ? + add byte [last_key_scancode],al ; key scancode + jmp short .@@end +.@@clr: + mov edi,key_tab ; offset of key table + sub eax,080h + add edi,eax + add edi,ebx ; normal or extended + mov byte [edi],0 ; release +.@@end: + mov al,020h ; send EOI + out 20h,al + pop ds + popad +; popfd + iretd + + align 32 + +key_tab resb 256 ; a 256 byte buffer to hold key info +key_flat_mode_data_selector dd 0 ; needed for IRQ +extended_key db 0 +pause_key db 0 +was_key_pressed db 0 ; for GetKey function +last_key_scancode db 0 +end_of_key_lock: + + + +;****************************************************************************** +;* +;* flip2_GetKey - Waits until a key is pressed and retrieves it's scan code. +;* Look at scans.h or scans.inc for info on key naming. +;* +;* in : call _flip2_GetKey +;* +;* out : eax = pressed key scancode +;* +;****************************************************************************** +_flip2_GetKey: +.wait: + cmp byte [was_key_pressed],1 + jne .wait + sub eax,eax + mov al,[last_key_scancode] + mov byte [was_key_pressed],0 + ret + + + +;****************************************************************************** +;* +;* flip2_GetLastKey - returns immediately with last pressed key scan code +;* or NULL if no key pressed. +;* Look at scans.h or scans.inc for info on key naming. +;* +;* in : call _flip2_GetLastKey +;* +;* out : eax = pressed key scancode +;* +;****************************************************************************** +_flip2_GetLastKey: + sub eax,eax + mov al,[last_key_scancode] + mov byte [last_key_scancode],0 + mov byte [was_key_pressed],0 + ret + + + +;****************************************************************************** +;* +;* flip2_GetKeyAscii +;* +;* Waits until a key is pressed and retrieves it's ascii code. +;* NumLock is considered as active (even if it is not) +;* CapsLock is considered as inactive (even if it is active) +;* ScrollLock is considered as inactive (even if it is active) +;* Special keys (Alt, Ctrl, Shift, ...) will not be returned. +;* Backspace-8, Tab-9, Enter-13, Escape-27 will be returned. +;* +;* in : call _flip2_GetKeyAscii +;* +;* out : eax = pressed key ascii code +;* +;****************************************************************************** +_flip2_GetKeyAscii: + push ebx +.wait: + cmp byte [was_key_pressed],1 + jne .wait + sub eax,eax + cmp byte [key_tab + 42],1 ; is left shift pressed ? + je .second_ascii_table + cmp byte [key_tab + 54],1 ; is right shift pressed ? + je .second_ascii_table + mov ebx,ScanToAsciiTable1 ; translation table 1 + jmp short .retrieve_ascii_code +.second_ascii_table: + mov ebx,ScanToAsciiTable2 ; translation table 2 +.retrieve_ascii_code: + mov al,[last_key_scancode] + xlatb + mov byte [was_key_pressed],0 + test al,al ; do not return special keys + jz .wait + pop ebx + ret + + + +;****************************************************************************** +;* +;* flip2_ScanToAscii +;* Converts a key scan code to it's ascii code. +;* Special keys (Alt, Ctrl, Shift, ...) will return NULL. +;* Backspace-8, Tab-9, Enter-13, Escape-27 will be returned. +;* This function will use the ASCII conversion table 1 to +;* perform the translation therefore only chars from that +;* table will be returend (eg. only small letters). +;* +;* push dword scan_key +;* in : call _flip2_ScanToAscii +;* +;* out : eax = pressed key ascii code +;* +;****************************************************************************** +_flip2_ScanToAscii: + push ebx + sub eax,eax + mov ebx,ScanToAsciiTable1 ; translation table 1 + mov al,[esp + 8] ; scan_key + xlatb + pop ebx + ret diff --git a/ICHINIT/DOS32/INPUT.H b/ICHINIT/DOS32/INPUT.H new file mode 100644 index 0000000..50e2f5f --- /dev/null +++ b/ICHINIT/DOS32/INPUT.H @@ -0,0 +1,13 @@ + +/*****************************************************************************/ +/* import functions for keyboard handler (INPUT.ASM) */ +/*****************************************************************************/ + +extern DWORD flip2_KeyErrorcode; /* dword - keyboard error code */ +extern BOOL FLIP2API flip2_KeyInit (void); +extern void FLIP2API flip2_KeyDeinit (void); +extern BYTE FLIP2API flip2_GetKey (void); +extern BYTE FLIP2API flip2_GetLastKey (void); +extern BYTE FLIP2API flip2_GetKeyAscii (void); +extern BYTE FLIP2API flip2_ScanToAscii (BYTE scan_key); +extern BYTE * flip2_KeyTable; diff --git a/ICHINIT/DOS32/SCANS.H b/ICHINIT/DOS32/SCANS.H new file mode 100644 index 0000000..9f4d5db --- /dev/null +++ b/ICHINIT/DOS32/SCANS.H @@ -0,0 +1,162 @@ +/***************************************************************************** + + keyboard scan codes for the ULTIMATE_FLIP library (extended version) + by Piotr Ulaszewski 14 March 2002 + +*****************************************************************************/ + +#define scan_Esc 1 +#define scan_F1 59 +#define scan_F2 60 +#define scan_F3 61 +#define scan_F4 62 +#define scan_F5 63 +#define scan_F6 64 +#define scan_F7 65 +#define scan_F8 66 +#define scan_F9 67 +#define scan_F10 68 +#define scan_F11 87 +#define scan_F12 88 + +#define scan_Apostroph 41 +#define scan_Tilde 41 +#define scan_1 2 +#define scan_2 3 +#define scan_3 4 +#define scan_4 5 +#define scan_5 6 +#define scan_6 7 +#define scan_7 8 +#define scan_8 9 +#define scan_9 10 +#define scan_0 11 +#define scan_Minus 12 +#define scan_Equal 13 +#define scan_Plus 13 +#define scan_BkSpace 14 + +#define scan_Tab 15 +#define scan_q 16 +#define scan_Q 16 +#define scan_w 17 +#define scan_W 17 +#define scan_e 18 +#define scan_E 18 +#define scan_r 19 +#define scan_R 19 +#define scan_t 20 +#define scan_T 20 +#define scan_y 21 +#define scan_Y 21 +#define scan_u 22 +#define scan_U 22 +#define scan_i 23 +#define scan_I 23 +#define scan_o 24 +#define scan_O 24 +#define scan_p 25 +#define scan_P 25 +#define scan_LeftBracket 26 +#define scan_RightBracket 27 +#define scan_Enter 28 + +#define scan_CapsLock 58 +#define scan_a 30 +#define scan_A 30 +#define scan_s 31 +#define scan_S 31 +#define scan_d 32 +#define scan_D 32 +#define scan_f 33 +#define scan_F 33 +#define scan_g 34 +#define scan_G 34 +#define scan_h 35 +#define scan_H 35 +#define scan_j 36 +#define scan_J 36 +#define scan_k 37 +#define scan_K 37 +#define scan_l 38 +#define scan_L 38 +#define scan_Semicolon 39 +#define scan_Colon 39 +#define scan_Tick 40 +#define scan_Quote 40 + +#define scan_LeftShift 42 +#define scan_z 44 +#define scan_Z 44 +#define scan_x 45 +#define scan_X 45 +#define scan_c 46 +#define scan_C 46 +#define scan_v 47 +#define scan_V 47 +#define scan_b 48 +#define scan_B 48 +#define scan_n 49 +#define scan_N 49 +#define scan_m 50 +#define scan_M 50 +#define scan_Comma 51 +#define scan_Less 51 +#define scan_Period 52 +#define scan_Greater 52 +#define scan_Slash 53 +#define scan_Question 53 +#define scan_RightShift 54 +#define scan_Backslash 43 +#define scan_Or 43 + +#define scan_LeftCtrl 29 +#define scan_LeftWindows 219 /* disabled under MS Windows */ +#define scan_LeftAlt 56 +#define scan_Space 57 +#define scan_RightAlt 184 +#define scan_RightWindows 220 /* disabled under MS Windows */ +#define scan_Menu 221 +#define scan_RightCtrl 157 + +/****************************************************************************/ + +#define scan_PrintScreen 183 /* disabled under MS Windows */ +#define scan_SysRq 183 /* disabled under MS Windows */ +#define scan_ScrollLock 70 +#define scan_Pause 255 /* special behaviour (active = 1) */ +#define scan_Break 255 /* special behaviour (active = 1) */ + +#define scan_Insert 210 +#define scan_Home 199 +#define scan_PageUp 201 +#define scan_Delete 211 +#define scan_End 207 +#define scan_PageDown 209 + +#define scan_UpArrow 200 +#define scan_LeftArrow 203 +#define scan_DownArrow 208 +#define scan_RightArrow 205 + +/****************************************************************************/ + +#define scan_NumLock 69 +#define scan_KeypadSlash 181 +#define scan_KeypadAsterix 55 +#define scan_KeypadMinus 74 +#define scan_KeypadPlus 78 +#define scan_KeypadEnter 156 +#define scan_KeypadPeriod 83 +#define scan_Keypad0 82 +#define scan_Keypad1 79 +#define scan_Keypad2 80 +#define scan_Keypad3 81 +#define scan_Keypad4 75 +#define scan_Keypad5 76 +#define scan_Keypad6 77 +#define scan_Keypad7 71 +#define scan_Keypad8 72 +#define scan_Keypad9 73 + + diff --git a/ICHINIT/DOS32/TIMER.ASM b/ICHINIT/DOS32/TIMER.ASM new file mode 100644 index 0000000..f9f5ed5 --- /dev/null +++ b/ICHINIT/DOS32/TIMER.ASM @@ -0,0 +1,418 @@ + +;****************************************************************************** +;* +;* TIMER.ASM module for ULTIMATE_FLIP library +;* by Piotr Ulaszewski - basing on timer examples from USMP +;* piotrkn22@poczta.onet.pl +;* http://www.republika.pl/piotrkn22/ +;* Multiple timers interface (defullt 16). +;* The structure and behaviour of the multiple timers was based on the timer +;* interface of the USMP 1.04 music player, although DPMI lock was added for +;* both, the main INT 8 handler and for all the user's procedures, direct +;* INT 31h functions were used, the timer structure was simplified and +;* some code parts have been rearanged (mainly StartTimer and the interrupt +;* routine). However most credits should go to Freddy Vételé (FreddyV/Useless) +;* for releasing his source code, so that others can learn from it. +;* +;* This is an extended version for C/C++ support. +;* developemnent version 0.35 +;* +;****************************************************************************** + +MaxTimers equ 16 ; this value can be increased +TimerSpeed equ 1193181 ; PIC Frequency +TimerStrucSize equ 16 + +struc Timer ; structure size = 16 bytes + tspeed resd 1 ; speed of this timer + tcount resd 1 ; ticks of this timer + tproc resd 1 ; procedure to call for this timer + tactive resb 1 ; 1=timer on 0=timer off + treserved resb 3 ; reserved to stay aligned +endstruc + + +%include "segments.inc" + +global _flip2_StartTimer ; proc +global _flip2_StopTimer ; proc +global _flip2_TimerErrorcode ; dword + +segment _DATA +_flip2_TimerErrorcode dd 0 ; error code for timer functions + + +;****************************************************************************** +;* +;* StartTimer: Starts a new timer procedure. +;* User sub timers can be slower than 18.2Hz and must terminate +;* with a RET instruction instead of an IRETD !!! +;* Other interrupts will be allowed while executing +;* a user timer procedure (so the CLI instruction may be used) !!! +;* User timer procedures should be smaller than 4KB in virtual +;* environments such as Windows because only the first 4KB +;* of a timer procedure will be locked !!! +;* Warning - this will only work with 4GB flat mode zero based +;* segments where the base and limit of CS is equal to those of DS +;* +;* +;* In: push TimerSpeed (1193181/freq) +;* push TimerProcedure +;* call StartTimer +;* +;* Out: eax = TRUE(1) on success and FALSE(0) on error +;* _flip2_TimerErrorcode holds execution status : +;* 00 = success (CF clear) +;* 01 = could not lock linear region +;* 02 = set protected mode interrupt error +;* 03 = no more timers available +;* 04 = not used +;* +;****************************************************************************** +segment _TEXT +_flip2_StartTimer: + push ebp + mov ebp,esp + push ebx + push ecx + push edx + push esi + push edi + cli + + mov ecx,MaxTimers + mov esi,timersdata ; offset to timers data +.timer_add_loop: + cmp byte [esi+tactive],0 + je .timer_found + add esi,TimerStrucSize + dec ecx + jnz .timer_add_loop + sub eax,eax + mov dword [_flip2_TimerErrorcode],3 ; 3 - no free timer available + stc + jmp .exit + +.timer_found: + push esi + mov eax,600h ; lock linear region (for user timer) + mov ebx,[ebp+8] ; offset to new timer handler + mov cx,bx + shr ebx,16 ; BX:CX = address + sub esi,esi + mov edi,4096 ; assume 4096 bytes to lock + int 31h + jnc .user_timer_lock_ok + pop esi + sub eax,eax + mov dword [_flip2_TimerErrorcode],1 ; 1 - colud not lock linear region + stc + jmp .exit + +.user_timer_lock_ok: + pop esi + mov eax,[ebp+12] ; timer speed + mov [esi+tspeed],eax ; set new timer speed + mov dword [esi+tcount],0 + mov eax,[ebp+8] ; timer procedure + mov [esi+tproc],eax ; set new timer procedure + mov byte [esi+tactive],1 ; activate the new timer + cmp dword [timerspeed],0 ; global timer speed = 18.2Hz ??? + jne near .do_not_hook_timer + mov word [flat_ds_selector],ds + +; get old and set the new IRQ 0 (timer) main procedure + mov eax,0204h ; get PM interrupt handler + mov bl,8 ; timer interrupt + int 31h + mov [oldtimeroffset],edx + mov [oldtimerselector],cx ; CX:EDX = old PM handler + + mov eax,600h ; lock linear region (global handler) + mov ebx,new_timer_procedure ; offset to new timer handler + mov cx,bx + shr ebx,16 ; BX:CX = address + sub esi,esi + mov edi,new_timer_procedure_end - new_timer_procedure + int 31h + jnc .global_timer_lock_ok + push es ; clear all timers data + push ds + pop es + cld + mov edi,timersdata ; offset to timers data + mov ecx,MaxTimers*TimerStrucSize + sub al,al + rep stosb + pop es + sub eax,eax + mov dword [_flip2_TimerErrorcode],1 ; 1 - colud not lock linear region + stc + jmp short .exit + +.global_timer_lock_ok: + mov eax,0205h ; set PM interrupt handler + mov bl,8 + mov cx,cs + mov edx,new_timer_procedure ; CX:EDX = new PM handler + int 31h + jnc .do_not_hook_timer ; jump if set PM IRQ ok + + mov eax,601h ; must unlock linear region on error + mov ebx,new_timer_procedure ; offset to PM handler + mov cx,bx + shr ebx,16 ; BX:CX = address + sub esi,esi + mov edi,new_timer_procedure_end - new_timer_procedure + int 31h + + sub eax,eax + mov dword [_flip2_TimerErrorcode],2 ; 2 - error in set PM interrupt call + stc + jmp short .exit + +.do_not_hook_timer: + call GetMaxSpeed ; out eax=speed of the fastest timer + call SetTimerSpeed ; in eax=new timer speed value + mov eax,1 + mov dword [_flip2_TimerErrorcode],0 ; 0 - success + clc +.exit: + sti + pop edi + pop esi + pop edx + pop ecx + pop ebx + pop ebp + ret + + + +;****************************************************************************** +;* +;* StopTimer: Stops a timer procedure. +;* +;* In: push TimerProcedure +;* call StartTimer +;* +;* Out: eax = TRUE(1) on success and FALSE(0) on error +;* _flip2_TimerErrorcode holds execution status : +;* 00 = success (CF clear) +;* 01 = not used +;* 02 = not used +;* 03 = not used +;* 04 = timer not found +;* +;****************************************************************************** +_flip2_StopTimer: + push ebp + mov ebp,esp + push ebx + push ecx + push edx + push esi + push edi + cli + + mov ecx,MaxTimers + mov esi,timersdata ; offset to timers data +.timer_stop_loop: + cmp byte [esi+tactive],0 + je .timer_not_active + mov eax,[ebp+8] ; timer procedure to stop offset + cmp [esi+tproc],eax + je .timer_found +.timer_not_active: + add esi,TimerStrucSize + dec ecx + jnz .timer_stop_loop + sub eax,eax + mov dword [_flip2_TimerErrorcode],4 ; 4 - timer not found error + stc + jmp .exit + +.timer_found: + mov byte [esi+tactive],0 ; stop timer + mov eax,601h ; unlock his linear region + mov ebx,[esi+tproc] ; offset to this timer handler + mov cx,bx + shr ebx,16 ; BX:CX = address + sub esi,esi + mov edi,4096 ; assume 4096 bytes to unlock + int 31h + call GetMaxSpeed ; out eax = speed of the fastest timer + call SetTimerSpeed ; in eax = new global timer value + ; set speed to the fastest timer speed + mov ecx,MaxTimers + mov esi,timersdata ; offset to timers data +.timer_stop_loop_2: + cmp byte [esi+tactive],1 + je .timer_stop_end ; is timer is active? + add esi,TimerStrucSize + dec ecx + jnz .timer_stop_loop_2 + + mov eax,0205h ; no user timer is active so + mov bl,8 ; restore previous BIOS timer + mov cx,[oldtimerselector] + mov edx,[oldtimeroffset] + int 31h + + mov eax,601h ; unlock linear region + mov ebx,new_timer_procedure ; offset to PM handler + mov cx,bx + shr ebx,16 ; BX:CX = address + sub esi,esi + mov edi,new_timer_procedure_end - new_timer_procedure + int 31h + + mov dword [timerspeed],0 ; all user timers and main timer off + +.timer_stop_end: + mov eax,1 + mov dword [_flip2_TimerErrorcode],0 ; 0 - success + clc +.exit: + sti + pop edi + pop esi + pop edx + pop ecx + pop ebx + pop ebp + ret + + + +;****************************************************************************** +;* +;* new_timer_procedure: Main timer interrupt handler. +;* Called at the speed of the fastest user timer. +;* Warning : Stack must be 4KB or more (suggested 8KB). +;* +;****************************************************************************** +align 32 +new_timer_procedure: + pushfd + pushad + push ds + push es + mov ax,[cs:flat_ds_selector] + mov ds,ax + mov es,ax + + mov ecx,MaxTimers + mov esi,timersdata ; offset to timers data +.timer_call_loop: + cmp byte [esi+tactive],0 + je .timer_not_active + mov eax,[esi+tcount] ; timer count of this user timer + add eax,[timerspeed] ; add main (global) timer speed + mov [esi+tcount],eax + cmp eax,[esi+tspeed] ; is it time to call this user timer? + jb .timer_not_active + sub eax,[esi+tspeed] ; ticks that exceeded tspeed + mov [esi+tcount],eax ; tcount = ticks that exceeded tspeed + push ecx + push esi + call dword [esi+tproc] ; call this user timer procedure + pop esi + pop ecx +.timer_not_active: + add esi,TimerStrucSize + dec ecx + jnz .timer_call_loop + + mov eax,[timerspeed] ; is it time to call the old BIOS timer + add [oldtimercount],eax + cmp dword [oldtimercount],10000h + jae .call_old_timer + mov al,20h ; send EOI (end of interrupt) + out 20h,al + pop es + pop ds + popad + popfd + iretd ; irq end + +.call_old_timer: + sub dword [oldtimercount],10000h + pop es + pop ds + popad + popfd + jmp dword far [cs:oldtimeroffset] ; chain to old BIOS timer + + + align 32 +timersdata times (MaxTimers*TimerStrucSize) db 0 +timerspeed dd 0 ; main (global) timer speed +oldtimercount dd 0 ; old BIOS timer count +flat_ds_selector dd 0 ; for the interrupt handler +oldtimeroffset dd 0 ; old BIOS timer address +oldtimerselector dd 0 + +new_timer_procedure_end: + + + +;****************************************************************************** +;* +;* GetMaxSpeed: Returns the speed of the fastest timer. +;* (internal to timer.asm). +;* +;* Out: eax = speed of the fastest timer +;* +;****************************************************************************** +GetMaxSpeed: + push ecx + push esi + mov eax,10000h ; old BIOS timer speed + mov ecx,MaxTimers + mov esi,timersdata ; offset to timers data +.timer_get_speed_loop: + cmp byte [esi+tactive],0 + je .timer_not_active + cmp [esi+tspeed],eax ; slowest value for the fastest timer + jae .timer_not_active ; is the old BIOS timer speed. + mov eax,[esi+tspeed] +.timer_not_active: + add esi,TimerStrucSize + dec ecx + jnz .timer_get_speed_loop + pop esi + pop ecx + ret + + + +;****************************************************************************** +;* +;* SetTimerSpeed: Sets the new main (global) timer speed. +;* (internal to timer.asm). +;* +;* In: eax = timer value (frequency = 1193181/eax) +;* +;****************************************************************************** +SetTimerSpeed: + cmp [timerspeed],eax + je .same_speed + push eax + push ebx + mov ebx,eax + cli ; set the PIT channel 0 frequency + mov al,00110110b ; 34h - 54 - load 16bit value (mode 2) + out 43h,al ; control word + mov eax,ebx ; eax = speed + out 40h,al ; LSB (least significant byte) + mov al,ah + out 40h,al ; MSB (most significant byte) + sti + pop ebx + pop eax + mov [timerspeed],eax ; set new main (global) timer speed +.same_speed: + ret + + diff --git a/ICHINIT/DOS32/TIMER.H b/ICHINIT/DOS32/TIMER.H new file mode 100644 index 0000000..19815d7 --- /dev/null +++ b/ICHINIT/DOS32/TIMER.H @@ -0,0 +1,9 @@ + +/*****************************************************************************/ +/* globals for the timer handler functions (TIMER.ASM) */ +/*****************************************************************************/ + +#define PITFrequency 1193181 +extern DWORD flip2_TimerErrorcode; /* dword - timer error code */ +extern BOOL FLIP2API flip2_StartTimer (void *tproc,int freq); +extern BOOL FLIP2API flip2_StopTimer (void *tproc); diff --git a/ICHINIT/ICHINIT.C b/ICHINIT/ICHINIT.C new file mode 100644 index 0000000..7ff72bd --- /dev/null +++ b/ICHINIT/ICHINIT.C @@ -0,0 +1,668 @@ + +/***************************************************************************** + * Intel ICH AC97?HDA Audio codec initialization for real mode DOS + * By Piotr Ulaszewski (February 2003 - August 2007) + * piotrkn22@poczta.onet.pl + * http://wwww.republika.pl/piotrkn22/ + * some new controllers and codec IDs added by Matthias Goeke + * developement ver 2.0 (August 2007) + *****************************************************************************/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "judasac.h" +#include "dos32\dpmi.h" +#include "dos32\input.h" +#include "dos32\scans.h" +#include "dos32\timer.h" + + +// pci.c functions for ich ac97 +BYTE pci_config_read_byte (AC97_PCI_DEV *ac97_pci, int index); +WORD pci_config_read_word (AC97_PCI_DEV *ac97_pci, int index); +DWORD pci_config_read_dword (AC97_PCI_DEV *ac97_pci, int index); +void pci_config_write_byte (AC97_PCI_DEV *ac97_pci, int index, BYTE data); +void pci_config_write_word (AC97_PCI_DEV *ac97_pci, int index, WORD data); +void pci_config_write_dword (AC97_PCI_DEV *ac97_pci, int index, DWORD data); +BOOL pci_check_bios (void); +BOOL pci_find_device (AC97_PCI_DEV *ac97_pci); +void pci_enable_io_access (AC97_PCI_DEV *ac97_pci); +void pci_enable_memory_access (AC97_PCI_DEV *ac97_pci); +void pci_enable_busmaster (AC97_PCI_DEV *ac97_pci); +BOOL detect_windows(void); +BOOL detect_os2(void); +BOOL detect_linux(void); + +// codec.c functions +BOOL codec_config (AC97_PCI_DEV *ac97_pci, int sample_rate); +BOOL semaphore (AC97_PCI_DEV *ac97_pci); + +unsigned int ich_INL (AC97_PCI_DEV *ac97_pci, int base, unsigned int a); +unsigned short ich_INW (AC97_PCI_DEV *ac97_pci, int base, unsigned short a); +unsigned char ich_INB (AC97_PCI_DEV *ac97_pci, int base, unsigned char a); +void ich_OUTL (AC97_PCI_DEV *ac97_pci, unsigned int d, int base, unsigned int a); +void ich_OUTW (AC97_PCI_DEV *ac97_pci, unsigned short d, int base, unsigned short a); +void ich_OUTB (AC97_PCI_DEV *ac97_pci, unsigned char d, int base, unsigned char a); + +// prototype for cclor display color_printf - no formating / prototype for map DPMI memory region +void color_printf (char *string, unsigned char color); +BOOL DPMI_MapMemory (unsigned long *physaddress, unsigned long *linaddress, unsigned long size); +BOOL DPMI_UnmapMemory (unsigned long *linaddress); + + +int master_vol = -1; +int hp_vol = -1; +int mono_vol = -1; +int wave_vol = -1; +int beep_vol = -1; +int phone_vol = -1; +int mic_vol = -1; +int line_in_vol = -1; +int cd_vol = -1; +int video_vol = -1; +int aux_vol = -1; +int a3c_vol = -1; +int a3d_vol = -1; +int a3p_vol = -1; +int bass_vol = -1; +int treble_vol = -1; +int sstereo_vol = -1; +int bassbst_vol = -1; + +AC97_PCI_DEV ac97_pci = {0}; // Pci device structure + + +void main (int argc, char **argv) +{ + int counter = 0; + char *strptr = NULL; + DWORD flags = 0; + + + printf ("\n"); + printf ("-----------------------------------------------------------------------\n"); + printf ("Real mode DOS initializer for Intel ICH based AC97/HDA codecs.\n"); + printf ("Developement ver 2.0 (August 2007).\n"); + printf ("Written by Piotr Ulaszewski (PETERS).\n"); + printf ("-----------------------------------------------------------------------\n"); + + if (argc > 1) { + for (counter = 1; counter < argc; counter++) { + strptr = argv[counter]; + strptr += 4; + + // master out + if (!strnicmp (argv[counter], "/MO:", 4)) master_vol = atoi (strptr); + else if (!strnicmp (argv[counter], "-MO:", 4)) master_vol = atoi (strptr); + + // headphones out + else if (!strnicmp (argv[counter], "/HO:", 4)) hp_vol = atoi (strptr); + else if (!strnicmp (argv[counter], "-HO:", 4)) hp_vol = atoi (strptr); + + // master mono + else if (!strnicmp (argv[counter], "/MM:", 4)) mono_vol = atoi (strptr); + else if (!strnicmp (argv[counter], "-MM:", 4)) mono_vol = atoi (strptr); + + // wave out + else if (!strnicmp (argv[counter], "/WO:", 4)) wave_vol = atoi (strptr); + else if (!strnicmp (argv[counter], "-WO:", 4)) wave_vol = atoi (strptr); + + // beep out + else if (!strnicmp (argv[counter], "/BO:", 4)) beep_vol = atoi (strptr); + else if (!strnicmp (argv[counter], "-BO:", 4)) beep_vol = atoi (strptr); + + // phone out + else if (!strnicmp (argv[counter], "/PO:", 4)) phone_vol = atoi (strptr); + else if (!strnicmp (argv[counter], "-PO:", 4)) phone_vol = atoi (strptr); + + // microphone in + else if (!strnicmp (argv[counter], "/MI:", 4)) mic_vol = atoi (strptr); + else if (!strnicmp (argv[counter], "-MI:", 4)) mic_vol = atoi (strptr); + + // line in + else if (!strnicmp (argv[counter], "/LI:", 4)) line_in_vol = atoi (strptr); + else if (!strnicmp (argv[counter], "-LI:", 4)) line_in_vol = atoi (strptr); + + // cd out + else if (!strnicmp (argv[counter], "/CD:", 4)) cd_vol = atoi (strptr); + else if (!strnicmp (argv[counter], "-CD:", 4)) cd_vol = atoi (strptr); + + // video out + else if (!strnicmp (argv[counter], "/VD:", 4)) video_vol = atoi (strptr); + else if (!strnicmp (argv[counter], "-VD:", 4)) video_vol = atoi (strptr); + + // aux in + else if (!strnicmp (argv[counter], "/AX:", 4)) aux_vol = atoi (strptr); + else if (!strnicmp (argv[counter], "-AX:", 4)) aux_vol = atoi (strptr); + + // 3d center adjustment + else if (!strnicmp (argv[counter], "/3C:", 4)) a3c_vol = atoi (strptr); + else if (!strnicmp (argv[counter], "-3C:", 4)) a3c_vol = atoi (strptr); + + // 3d depth adjustment + else if (!strnicmp (argv[counter], "/3D:", 4)) a3d_vol = atoi (strptr); + else if (!strnicmp (argv[counter], "-3D:", 4)) a3d_vol = atoi (strptr); + + // 3d path specifier + else if (!strnicmp (argv[counter], "/3P:", 4)) a3p_vol = atoi (strptr); + else if (!strnicmp (argv[counter], "-3P:", 4)) a3p_vol = atoi (strptr); + + // master bass gain + else if (!strnicmp (argv[counter], "/MB:", 4)) bass_vol = atoi (strptr); + else if (!strnicmp (argv[counter], "-MB:", 4)) bass_vol = atoi (strptr); + + // master treble gain + else if (!strnicmp (argv[counter], "/MT:", 4)) treble_vol = atoi (strptr); + else if (!strnicmp (argv[counter], "-MT:", 4)) treble_vol = atoi (strptr); + + // simulated stereo + else if (!strnicmp (argv[counter], "/SS:", 4)) sstereo_vol = atoi (strptr); + else if (!strnicmp (argv[counter], "-SS:", 4)) sstereo_vol = atoi (strptr); + + // bass bost + else if (!strnicmp (argv[counter], "/BB:", 4)) bassbst_vol = atoi (strptr); + else if (!strnicmp (argv[counter], "-BB:", 4)) bassbst_vol = atoi (strptr); + + else { + printf ("Error : Invalid parameters!\n"); + printf ("\n"); + printf ("/MO: or /mo: master out volume\n"); + printf ("/HO: or /ho: headphones out volume\n"); + printf ("/MM: or /mm: master mono out volume\n"); + printf ("/WO: or /wo: wave out volume\n"); + + printf ("/BO: or /bo: beep out volume\n"); + printf ("/PO: or /po: phone out volume\n"); + + printf ("/MI: or /mi: microphone in volume\n"); + printf ("/LI: or /li: line in volume\n"); + + printf ("/CD: or /cd: CD out volume\n"); + printf ("/VD: or /vd: VIDEO out volume\n"); + printf ("/AX: or /ax: AUX in volume\n"); + printf ("/3C: or /3c: 3D center adjustment\n"); + printf ("/3D: or /3d: 3D depth adjustment\n"); + printf ("/3P: or /3p: 3D path specifier\n"); + + printf ("/MB: or /mb: master bass gain\n"); + printf ("/MT: or /mt: master treble gain\n"); + printf ("/SS: or /ss: simulated stereo\n"); + printf ("/BB: or /bb: bass boost\n"); + printf ("\n"); + printf ("Volume values available range : 0 - 32\n"); + + exit(0); + } // else + } // counter + } // argc + + // detect if we run under windows + if (detect_windows() == TRUE) { + printf ("Error : This program can not be run under Windows!"); + printf ("\n"); + exit(0); + } + + // detect if we run under linux + if (detect_linux() == TRUE) { + printf ("Error : This program can not be run under Linux!"); + printf ("\n"); + exit(0); + } + + // detect if we run under os2 + if (detect_os2() == TRUE) { + printf ("Error : This program can not be run under OS2!"); + printf ("\n"); + exit(0); + } + + // detect if PCI BIOS is available + if (pci_check_bios() == FALSE) { + printf ("Error : PCI local bus not available on this machine!"); + printf ("\n"); + exit(0); + } + + // detect controller + counter = 0; + while (ac97_dev_list[counter].vender_id != 0x0000){ + ac97_pci.vender_id = ac97_dev_list[counter].vender_id; + ac97_pci.device_id = ac97_dev_list[counter].device_id; + ac97_pci.sub_vender_id = ac97_dev_list[counter].sub_vender_id; + ac97_pci.sub_device_id = ac97_dev_list[counter].sub_device_id; + if (pci_find_device(&ac97_pci) == TRUE){ + printf ("%s detected.\n", ac97_dev_list[counter].string); + break; + } + counter++; + } + + if (ac97_dev_list[counter].vender_id == NULL){ + printf ("Error : Could not find Intel ICH (AC97/HDA) compatible controller!\n"); + exit(0); + } + + // save device info + ac97_pci.device_type = ac97_dev_list[counter].type; + strcpy(ac97_pci.device_name, ac97_dev_list[counter].string); + + // read pci configuration + ac97_pci.command = pci_config_read_word (&ac97_pci, PCI_COMMAND); + ac97_pci.irq = pci_config_read_byte (&ac97_pci, PCI_INTERRUPT_LINE); + ac97_pci.pin = pci_config_read_byte (&ac97_pci, PCI_INT_LINE); + ac97_pci.base0 = pci_config_read_dword (&ac97_pci, PCI_BASE_ADDRESS_0); // NAMBAR + ac97_pci.base1 = pci_config_read_dword (&ac97_pci, PCI_BASE_ADDRESS_1); // NABMBAR + + // if memory type IO is enabled then use memory type IO (other manufacturers) + if (ac97_pci.command & PCI_COMMAND_MEMORY) ac97_pci.mem_mode = 1; + + // start configuring devices + switch (ac97_pci.device_type) { + case DEVICE_INTEL_ICH4: + // try to go for memory type IO as default for ICH4+ Intel controllers even if disabled in PCI command config + ac97_pci.mem_mode = 1; + break; + case DEVICE_NFORCE: + pci_config_write_dword (&ac97_pci, 0x4c, pci_config_read_dword (&ac97_pci, 0x4c) | 0x1000000); + break; + case DEVICE_HDA: + ac97_pci.hda_mode = 1; + break; + default: + break; + } + + // HDA not supported yet + if (ac97_pci.hda_mode) { + printf ("Error : High Definition Audio controllers not supported yet!\n"); + exit(0); + } + + // get memory mapped IO for ICH4+ and other new chips + if ((ac97_pci.base0 == 0) || (ac97_pci.mem_mode)) { + ac97_pci.base2 = pci_config_read_dword (&ac97_pci, PCI_MEM_BASE_ADDRESS_2); // NAMBAR + ac97_pci.base3 = pci_config_read_dword (&ac97_pci, PCI_MEM_BASE_ADDRESS_3); // NABMBAR + + // map linear memory - convery physical address to linear address + if (!DPMI_MapMemory ((unsigned long *)&ac97_pci.base2, (unsigned long *)&ac97_pci.base2, 0x1000)) ac97_pci.mem_mode = 0; + if (!DPMI_MapMemory ((unsigned long *)&ac97_pci.base3, (unsigned long *)&ac97_pci.base3, 0x1000)) ac97_pci.mem_mode = 0; + } + + // test if both I/O range and memory mapped range are NULL + // note : memory range address will be NULL if DPMI_MapMemory function fails for some reason + if ((ac97_pci.base0 == 0) && (ac97_pci.mem_mode == 0)) { + printf ("Error : AC97/HDA normal I/O and memory mapped I/O access failed!\n"); + exit(0); + } + + // legacy I/O access + if (ac97_pci.mem_mode == FALSE) { + if (ac97_pci.device_type == DEVICE_INTEL_ICH4) { + // enable the IOSE bit in 0x41 for legacy mode for ICH4/ICH5 + pci_config_write_byte (&ac97_pci, PCI_ICH4_CFG_REG, 1); + // Set the secondary codec ID + pci_config_write_byte (&ac97_pci, PCI_COMMAND_PARITY, 0x39); + } + + // Remove I/O space marker in bit 0 + ac97_pci.base0 &= ~0xf; + ac97_pci.base1 &= ~0xf; + + // enable IO and bus master + ac97_pci.command |= PCI_COMMAND_MASTER | PCI_COMMAND_IO; + pci_config_write_word (&ac97_pci, PCI_COMMAND, ac97_pci.command); + + // diaplay codec information + printf ("Mixer Base I/O port : %#06x\n", ac97_pci.base0); + printf ("Mixer Bus Master I/O port : %#06x\n", ac97_pci.base1); + printf ("AC97 Interrupt Line : IRQ %d", ac97_pci.irq); + printf ("/PIN %c\n", ac97_pci.pin + 64); + printf ("I/O range and Bus Master access enabled.\n"); + printf ("AC97 configured for normal IO.\n"); + printf ("-----------------------------------------------------------------------\n"); + color_printf ("Primary codec :", 15); + printf ("\n"); + + } else { + + // enable port IO access compatibility even though we're going for memory mapped IO !!! + if (ac97_pci.device_type == DEVICE_INTEL_ICH4) { + // enable the IOSE bit in 0x41 for legacy mode for ICH4/ICH5 + pci_config_write_byte (&ac97_pci, PCI_ICH4_CFG_REG, 1); + // Set the secondary codec ID + pci_config_write_byte (&ac97_pci, PCI_COMMAND_PARITY, 0x39); + } + + // Remove I/O space marker in bit 0 + ac97_pci.base0 &= ~0xf; + ac97_pci.base1 &= ~0xf; + + // enable memory, IO and bus master + ac97_pci.command |= PCI_COMMAND_MASTER | PCI_COMMAND_IO | PCI_COMMAND_MEMORY; + pci_config_write_word (&ac97_pci, PCI_COMMAND, ac97_pci.command); + + // diaplay codec information + printf ("Mixer Base I/O port : %#06x\n", ac97_pci.base0); + printf ("Mixer Bus Master I/O port : %#06x\n", ac97_pci.base1); + printf ("Mixer Base memory address : %#06x\n", pci_config_read_dword (&ac97_pci, PCI_MEM_BASE_ADDRESS_2)); + printf ("Mixer Bus Master memory address : %#06x\n", pci_config_read_dword (&ac97_pci, PCI_MEM_BASE_ADDRESS_3)); + printf ("Mixer Base virtual address : %#06x\n", ac97_pci.base2); + printf ("Mixer Bus Master virtual address : %#06x\n", ac97_pci.base3); + printf ("AC97 Interrupt Line : IRQ %d", ac97_pci.irq); + printf ("/PIN %c\n", ac97_pci.pin + 64); + printf ("Memory range, I/O range and Bus Master access enabled.\n"); + printf ("AC97 configured for memory mapped IO.\n"); + printf ("-----------------------------------------------------------------------\n"); + color_printf ("Primary codec :", 15); + printf ("\n"); + + } + + + // initialize codec to 44100Hz sample rate + if (codec_config (&ac97_pci, 44100) == FALSE) { + DPMI_UnmapMemory ((unsigned long *)&ac97_pci.base2); + DPMI_UnmapMemory ((unsigned long *)&ac97_pci.base3); + printf("Error : Could not initialize Intel ICH AC97/HDA audio codec!\n"); + exit(0); + } + + // set the volume entered on the command line + semaphore (&ac97_pci); + ich_OUTW (&ac97_pci, 0, MIXER_BASE, AC97_RESET); // register reset the codec (0) + + semaphore (&ac97_pci); + ich_OUTW (&ac97_pci, 44100, MIXER_BASE, AC97_PCM_FRONT_DAC_RATE); // set sample rate - VRA already set (2ch) + + // set master volume (range is 0 - 64, our range is 0 - 32) - leave lower level range to adjust x2 + if (master_vol == -1) master_vol = 16; // attenuation by default + if (master_vol < 0) master_vol = 0; + if (master_vol > 32) master_vol = 32; + semaphore (&ac97_pci); + if (master_vol == 0) ich_OUTW (&ac97_pci, BIT15, MIXER_BASE, AC97_MASTER); // mute master volume (2) + else ich_OUTW (&ac97_pci, abs (master_vol - 32) * 0x101, MIXER_BASE, AC97_MASTER); // set master volume (2) + + // set hp volume (range is 0 - 64, our range is 0 - 32) - leave lower level range to adjust x2 + if (hp_vol == -1) hp_vol = 16; // attenuation by default + if (hp_vol < 0) hp_vol = 0; + if (hp_vol > 32) hp_vol = 32; + semaphore (&ac97_pci); + if (hp_vol == 0) ich_OUTW (&ac97_pci, BIT15, MIXER_BASE, AC97_HEADPHONE); // mute hp volume (4) + else ich_OUTW (&ac97_pci, abs (hp_vol - 32) * 0x101, MIXER_BASE, AC97_HEADPHONE); // set hp volume (4) + + // set mono volume (range is 0 - 64, our range is 0 - 32) - leave lower level range to adjust x2 + if (mono_vol == -1) mono_vol = 16; // attenuation by default + if (mono_vol < 0) mono_vol = 0; + if (mono_vol > 32) mono_vol = 32; + semaphore (&ac97_pci); + if (mono_vol == 0) ich_OUTW (&ac97_pci, BIT15, MIXER_BASE, AC97_MASTER_MONO); // mute mono volume (6) + else ich_OUTW (&ac97_pci, abs (mono_vol - 32), MIXER_BASE, AC97_MASTER_MONO); // set mono volume (6) + + // set beep volume (range is 0 - 16, our range is 0 - 32) + if (beep_vol == -1) beep_vol = 32; // 0b attenuation by default + if (beep_vol < 0) beep_vol = 0; + if (beep_vol > 32) beep_vol = 32; + semaphore (&ac97_pci); + if (beep_vol == 0) ich_OUTW (&ac97_pci, BIT15, MIXER_BASE, AC97_PCBEEP_VOL); // mute beep volume (0ah) + else ich_OUTW (&ac97_pci, abs ((beep_vol >> 1) - 16) << 1, MIXER_BASE, AC97_PCBEEP_VOL); // set beep volume (0ah) + + // set wave volume PCM out (range is 0 - 32, our range is 0 - 32) + if (wave_vol == -1) wave_vol = 24; // 0db gain by default + if (wave_vol < 0) wave_vol = 0; + if (wave_vol > 32) wave_vol = 32; + semaphore (&ac97_pci); + if (wave_vol == 0) ich_OUTW (&ac97_pci, BIT15, MIXER_BASE, AC97_PCM); // mute wave volume (18h) + else ich_OUTW (&ac97_pci, abs (wave_vol - 32) * 0x101, MIXER_BASE, AC97_PCM); // set wave volume (18h) + + // set phone volume (range is 0 - 32, our range is 0 - 32) + if (phone_vol == -1) phone_vol = 28; // gain by default + if (phone_vol < 0) phone_vol = 0; + if (phone_vol > 32) phone_vol = 32; + semaphore (&ac97_pci); + if (phone_vol == 0) ich_OUTW (&ac97_pci, BIT15, MIXER_BASE, AC97_PHONE_VOL); // mute phonee volume (0ch) + else ich_OUTW (&ac97_pci, abs (phone_vol - 32), MIXER_BASE, AC97_PHONE_VOL); // set phone volume (0ch) + + // set mic volume (range is 0 - 32, our range is 0 - 32) + if (mic_vol == -1) mic_vol = 0; // gain by default + if (mic_vol < 0) mic_vol = 0; + if (mic_vol > 32) mic_vol = 32; + semaphore (&ac97_pci); + if (mic_vol == 0) ich_OUTW (&ac97_pci, BIT15, MIXER_BASE, AC97_MIC_VOL); // mute mic volume (0eh) + else ich_OUTW (&ac97_pci, abs (mic_vol - 32), MIXER_BASE, AC97_MIC_VOL); // set mic volume (0eh) + + // set line in volume (range is 0 - 32, our range is 0 - 32) + if (line_in_vol == -1) line_in_vol = 28; // gain by default + if (line_in_vol < 0) line_in_vol = 0; + if (line_in_vol > 32) line_in_vol = 32; + semaphore (&ac97_pci); + if (line_in_vol == 0) ich_OUTW (&ac97_pci, BIT15, MIXER_BASE, AC97_LINE_IN_VOL); // mute line in volume (10h) + else ich_OUTW (&ac97_pci, abs (line_in_vol - 32) * 0x101, MIXER_BASE, AC97_LINE_IN_VOL); // set line in volume (10h) + + // set CD volume (range is 0 - 32, our range is 0 - 32) + if (cd_vol == -1) cd_vol = 28; // gain by default + if (cd_vol < 0) cd_vol = 0; + if (cd_vol > 32) cd_vol = 32; + semaphore (&ac97_pci); + if (cd_vol == 0) ich_OUTW (&ac97_pci, BIT15, MIXER_BASE, AC97_CD_VOL); // mute CD volume (12h) + else ich_OUTW (&ac97_pci, abs (cd_vol - 32) * 0x101, MIXER_BASE, AC97_CD_VOL); // set CD volume (12h) + + // set video volume (range is 0 - 32, our range is 0 - 32) + if (video_vol == -1) video_vol = 28; // gain by default + if (video_vol < 0) video_vol = 0; + if (video_vol > 32) video_vol = 32; + semaphore (&ac97_pci); + if (video_vol == 0) ich_OUTW (&ac97_pci, BIT15, MIXER_BASE, AC97_VID_VOL); // mute video volume (14h) + else ich_OUTW (&ac97_pci, abs (video_vol - 32) * 0x101, MIXER_BASE, AC97_VID_VOL); // set video volume (14h) + + // set aux volume (range is 0 - 32, our range is 0 - 32) + if (aux_vol == -1) aux_vol = 28; // gain by default + if (aux_vol < 0) aux_vol = 0; + if (aux_vol > 32) aux_vol = 32; + semaphore (&ac97_pci); + if (aux_vol == 0) ich_OUTW (&ac97_pci, BIT15, MIXER_BASE, AC97_AUX_VOL); // mute aux volume (16h) + else ich_OUTW (&ac97_pci, abs (aux_vol - 32) * 0x101, MIXER_BASE, AC97_AUX_VOL); // set aux volume (16h) + + // check if 3D effect available - adjust to SE4..SE0 bits + semaphore (&ac97_pci); + flags = (ich_INW (&ac97_pci, MIXER_BASE, AC97_RESET) >> 10) & (BIT0 + BIT1 + BIT2 + BIT3 + BIT4); + if (flags) { // get Ac97 reset status (0) + printf ("%s", ac97_stereo_technology[flags]); + printf (" technology detected.\n"); + + // enable 3D depth and center if requested + semaphore (&ac97_pci); + flags = ich_INW (&ac97_pci, MIXER_BASE, AC97_GP_REG); // general purpose 20h register + semaphore (&ac97_pci); + if ( ((a3d_vol != 0) && (a3d_vol != -1)) || ((a3c_vol != 0) && (a3c_vol != -1)) ) flags |= BIT13; // set BIT13 (enable 3D) + else flags &= (0xffff - BIT13); + ich_OUTW (&ac97_pci, flags, MIXER_BASE, AC97_GP_REG); // general purpose 20h register + + // set 3D depth and center (range is 0 - 15, our range is 0 - 31) + if (a3d_vol < 0) a3d_vol = 0; // no 3D depth as default + if (a3d_vol > 31) a3d_vol = 31; + if (a3c_vol < 0) a3c_vol = 0; // no 3D center as default + if (a3c_vol > 31) a3c_vol = 31; + semaphore (&ac97_pci); + ich_OUTW (&ac97_pci, (a3d_vol >> 1) + (0x100 * (a3c_vol >> 1)), MIXER_BASE, AC97_3D_CONTROL_REG); // 3D control register 22h + semaphore (&ac97_pci); + flags = ich_INW (&ac97_pci, MIXER_BASE, AC97_3D_CONTROL_REG); // 3D control register 22h + if ((flags & 0x0f00) != (0x100 * (a3c_vol >> 1))) { + color_printf ("3D center adjustment not supported by hardware!", 12); + printf ("\n"); + } + if ((flags & 0x0f) != (a3d_vol >> 1)) { + color_printf ("3D depth adjustment not supported by hardware!", 12); + printf ("\n"); + } + + // set 3D path + semaphore (&ac97_pci); + flags = ich_INW (&ac97_pci, MIXER_BASE, AC97_GP_REG); // general purpose 20h register + semaphore (&ac97_pci); + if (a3p_vol <= 0) ich_OUTW (&ac97_pci, flags & (0xffff - BIT15), MIXER_BASE, AC97_GP_REG); // PRE 3D path as default + if (a3p_vol >= 1) ich_OUTW (&ac97_pci, flags | BIT15, MIXER_BASE, AC97_GP_REG); // POST 3D path if set + semaphore (&ac97_pci); + flags = ich_INW (&ac97_pci, MIXER_BASE, AC97_GP_REG); // 3D control register 22h + if ((a3p_vol >= 1) && !(flags & BIT15)) { + color_printf ("3D path control not supported by hardware!", 12); + printf ("\n"); + } + + } else { + printf ("3D Stereo Enhancement technology not detected.\n"); + if ((a3c_vol != -1) || (a3d_vol != -1)) { + color_printf ("3D center/depth adjustment not supported by hardware!", 12); + printf ("\n"); + } + if (a3p_vol != -1) { + color_printf ("3D path control not supported by hardware!", 12); + printf ("\n"); + } + } + + // check for bass/treble support + semaphore (&ac97_pci); + flags = ich_INW (&ac97_pci, MIXER_BASE, AC97_RESET) & BIT2; // ID2 bass and treble control + if (flags) { + // set bass/treble range 0 - 16, our range is 0 - 31 + if (bass_vol < 2) bass_vol = 2; // default bass = bypass + if (bass_vol > 32) bass_vol = 32; + if (treble_vol < 2) treble_vol = 2; // default treble = bypass + if (treble_vol > 32) treble_vol = 32; + semaphore (&ac97_pci); + ich_OUTW (&ac97_pci, abs ((treble_vol >> 1) - 16) + (0x100 * abs ((bass_vol >> 1) - 16)), MIXER_BASE, AC97_MASTER_TONE); // master tone register 8 + } else { + if ((bass_vol != -1) || (treble_vol != -1)) { + color_printf ("Bass/Treble gain control not supported by hardware!", 12); + printf ("\n"); + } + } + + // check for bass boost support + semaphore (&ac97_pci); + flags = ich_INW (&ac97_pci, MIXER_BASE, AC97_RESET) & BIT5; // ID5 bass boost control + if (flags) { + // set/clear bass boost + semaphore (&ac97_pci); + flags = ich_INW (&ac97_pci, MIXER_BASE, AC97_GP_REG); + semaphore (&ac97_pci); + if (bassbst_vol <= 0) ich_OUTW (&ac97_pci, flags & (0xffff - BIT12), MIXER_BASE, AC97_GP_REG); // default bass = 0 + if (bassbst_vol >= 1) ich_OUTW (&ac97_pci, flags | BIT12, MIXER_BASE, AC97_GP_REG); + } else { + if (bassbst_vol != -1) { + color_printf ("Bass boost control not supported by hardware!", 12); + printf ("\n"); + } + } + + // check for simulated stereo support + semaphore (&ac97_pci); + flags = ich_INW (&ac97_pci, MIXER_BASE, AC97_RESET) & BIT3; // ID3 simulated stereo control + if (flags) { + // set/clear bass boost + semaphore (&ac97_pci); + flags = ich_INW (&ac97_pci, MIXER_BASE, AC97_GP_REG); + semaphore (&ac97_pci); + if (sstereo_vol <= 0) ich_OUTW (&ac97_pci, flags & (0xffff - BIT14), MIXER_BASE, AC97_GP_REG); // default ss = 0 + if (sstereo_vol >= 1) ich_OUTW (&ac97_pci, flags | BIT14, MIXER_BASE, AC97_GP_REG); + } else { + if (sstereo_vol != -1) { + color_printf ("Simulated stereo control not supported by hardware!", 12); + printf ("\n"); + } + } + + // identify codec manufacturer + semaphore (&ac97_pci); + ac97_pci.codec_id1 = ich_INW (&ac97_pci, MIXER_BASE, AC97_VENDERID1_REG); // codec vender ID1 + semaphore (&ac97_pci); + ac97_pci.codec_id2 = ich_INW (&ac97_pci, MIXER_BASE, AC97_VENDERID2_REG); // codec vender ID2 + counter = 0; + while (ac97_codec_list[counter].vender_id1 != 0x0000){ + if ( (ac97_pci.codec_id1 == ac97_codec_list[counter].vender_id1) && (ac97_pci.codec_id2 == ac97_codec_list[counter].vender_id2) ) { + printf ("%s initialization success.\n", ac97_codec_list[counter].string); + printf ("\n"); + strcpy (ac97_pci.codec_name, ac97_codec_list[counter].string); + break; + } + counter++; + } + + // could not identify manufacturer + if (ac97_codec_list[counter].vender_id1 == 0x0000) { + printf ("%s initialization success.\n", ac97_codec_list[counter].string); + printf ("\n"); + strcpy (ac97_pci.codec_name, ac97_codec_list[counter].string); + } + + // unmap linear memory + DPMI_UnmapMemory ((unsigned long *)&ac97_pci.base2); + DPMI_UnmapMemory ((unsigned long *)&ac97_pci.base3); + + // end of initialization +} + +void color_printf (char *string, unsigned char color) +{ + // foreground colors (bits 0-3) + // 0 = black + // 1 = blue + // 2 = green + // 3 = cyan + // 4 = red + // 5 = magneta + // 6 = brown + // 7 = light gray + // 8 = dark grey + // 9 = light blue + // 10 = light green + // 11 = light cyan + // 12 = light red + // 13 = light magneta + // 14 = yellow + // 15 = white + // background colors (bits 4-7) + + int counter = 0; + union REGS r; + + memset(&r, 0, sizeof(r)); + r.w.ax = 0x1A00; // test VGA mode + r.w.bx = 0; + int386 (0x10, &r, &r); + if (r.w.bx == 0) { + printf (string); + return; + } + + memset(&r, 0, sizeof(r)); + r.h.ah = 0x0F; // get current page + int386 (0x10, &r, &r); // bh = current page + r.h.bl = color; // bl = attribute (text mode) + + while (string[counter]) { // NULL char ends + r.w.cx = 1; // nr of times to write char + r.h.al = string[counter]; + r.h.ah = 9; // write char at cursor position + int386 (0x10, &r, &r); + + r.h.ah = 3; // get cursor position + int386 (0x10, &r, &r); + + r.h.dl++; // advance cursor + r.h.ah = 2; // set cursor position + int386 (0x10, &r, &r); + + counter++; + } +} diff --git a/ICHINIT/ICHINIT.TXT b/ICHINIT/ICHINIT.TXT new file mode 100644 index 0000000..cd5e39b --- /dev/null +++ b/ICHINIT/ICHINIT.TXT @@ -0,0 +1,328 @@ + +**************************************************************************** +* Intel ICH AC97 Audio codec initialization for real mode DOS +* By Piotr Ulaszewski (August 2007) +* piotrkn22@poczta.onet.pl +* http://wwww.republika.pl/piotrkn22/ +* developement ver 2.0 (August 2007) +***************************************************************************** + +Use and modify at your own risk! +================================ + + Legal (freeware license agreement) : + ------------------------------------ + This program (ICHINIT.EXE) is provided "AS IS". The author + (Piotr Ulaszewski) does not assume any responsibility for special, + incidental or any other kind of damages arising out of or resulting + from the use, misuse or modification of this software. + + This software comes without any kind of warranty, either expressed + or implied, including, but not limited to the implied warranties of + merchantability or fitness for a particulair purpose. + + This software is "FREEWARE". A non-exclusive permission to use this + software in it's unmodified form is granted. + + You may redistribute copies of this software provided that you + include all of the files that came with the original package. + In case ICHINIT is redistributed as part of another freeware or + commercial package only ICHINIT.EXE and ICHINIT.TXT files need + to be included in the redistributed package. + + You may further sell applications, products or packages with this + utility included. However you are not allowed to sell this software + as a stand alone product, except for a small, usual fee covering + the redistribution itself. + + The included source code may be modified for your own use. However + modified versions of the source code or binaries compiled from modified + versions of the source code must not be redistributed without prior + permission from the author. + + If you don't agree with those terms or if your jurisdiction does not + allow the exclusion of warranty and liability as stated above you are + not allowed to use this software at all. + + + +NOTE : THIS UTILITY IS NOT A SOUND DRIVER (SB COMPATIBLE) FOR REAL MODE DOS ! + +--------------------------------------------------------------------------- +This is a real mode DOS initializer (V86 mode supported too) for Intel 8xx +chipsets (or anything else with the ICH compatible southbridge access) +with integrated AC'97 Audio codec on board. The program will not run under +any Windows, OS/2 or Linux DosEmu version. When a known operating system, +such a Windows or OS/2 is detected the utility will simply terminate. +The user should not force (by code modification) this program to run +in advanced protected mode operating systems (such as Win 9x or OS/2). +Doing so could seriously mess up the integrity of the operating system, +even with a possibilty to damage the ICH controller. +--------------------------------------------------------------------------- + +This simple utility allows you to enable the AC'97 Audio codec under clean +DOS (if it was disabled after a RESET and the BIOS did not activate it on +startup) and set the desired volumes for any in/out channel. +The BIOS setting of the machine that boots directly to DOS should be a +non plug-n-play O/S with the onboard audio device enabled. + +After launching the initialization utility you will be able to listen to a CD, +change the volume of the beep signal transmited on to the AC97 (which you can +hear in your speakers), modify the volume of the wave out signal, and all +other AC'97 compatible channels. In fact this initialization utility will +unlock the audio device set the AC97 sample rate to 44100Hz and allow +the user access to that device at register level (Mixer and Bus Master I/O +base must be retrieved from the PCI bus) without writeing complicated +startup code. The AC97 device will be initialized to 2 channels (stereo), +16 bit PCM OUT samples, DRA (Double Rate Audio) disabled by cold reset +and VRA (Variable Rate Audio) enabled. All unsupported volume levels +will reset to their default values by executing a cold reset. + +NOTE : This utility should work (although there is absolutely +no guarantee it will) with AC97 audio codecs (primary AC97 codec only) +integrated on most Intel/SIS chipsets and some AMD/nVidia chipsets. + + + +The following switches with a volume range from 0 to 32 are available : + +/MO: or /mo: master out volume (16) +/HO: or /ho: headphones out volume (16) +/MM: or /mm: master mono out volume (16) +/WO: or /wo: wave out volume (16) +/BO: or /bo: beep out volume (32) +/PO: or /po: phone out volume (28) +/MI: or /mi: microphone in volume (0) +/LI: or /li: line in volume (28) +/CD: or /cd: CD out volume (28) +/VD: or /vd: VIDEO out volume (28) +/AX: or /ax: AUX in volume (28) +/3C: or /3c: 3D center adjustment (0 = off) +/3D: or /3d: 3D depth adjustment (0 = off) +/3P: or /3p: 3D path specifier (0 = pre) +/MB: or /mb: master bass gain (0 = off) +/MT: or /mt: master treble gain (0 = off) +/SS: or /ss: simulated stereo (0 = off) +/BB: or /bb: bass boost (0 = off) + +The values in parenthesis are the default settings which will be applied +when no command line options are entered. Specifying a 0 (zero) level +volume will mute/disable the appropriate channel. Please note, that +the hardware must support bass and treble gain, simulated stereo, bass boost +and 3D center/depth/path adjustment in order to use those extensions. + +NOTE: 3D path (pre/post), simulated stereo (off/on) and bass boost (off/on) +only accept values 0 and 1. Any other value will be treated as 1 (enabled). + + + +master out/headphones out/master mono out +========================================= +limited intervals available : 3.0dB +original intervals : 1.5dB +limited range available : (-91.5dB > 0dB attenuation) +original range : (-94.5dB > 0dB attenuation) + +beep out +======== +original range available : (-45.0dB > 0dB attenuation) + +wave vol +======== +limited intervals available : 1.5dB every two steps +original intervals : 1.5dB +limited range available : (-12.0dB > +12dB boost) +original range : (-34.5dB > +12dB boost) +default settings : 0dB boost + +bass/treble gain +================ +limited intervals available : 1.5dB every two steps +original intervals : 1.5dB +original range available : (-10.5dB > 10.5dB attenuation) + + + +Supported controllers : +===================================== +// supported controllers AC97 INTEL +Intel 82801AA (ICH) integrated AC97 audio codec +Intel 82801AB (ICH0) integrated AC97 audio codec +Intel 82801BA (ICH2) integrated AC97 audio codec +Intel 82801CA (ICH3) integrated AC97 audio codec +Intel 82801DB (ICH4) integrated AC97 audio codec +Intel 82801EB/ER (ICH5/ICH5R) integrated AC97 audio codec +Intel 6300ESB integrated AC97 audio codec +Intel 82801FB (ICH6) integrated AC97 audio codec +Intel 82801GB (ICH7) integrated AC97 audio codec +Intel 82443MX integrated AC97 audio codec + +// supported controllers AC97 other (AMD/NVIDIA/SIS) +AMD 768 integrated AC97 audio codec +AMD 8111 integrated AC97 audio codec +Nvidia nForce integrated AC97 audio codec +Nvidia nForce2 integrated AC97 audio codec +Nvidia CK8 (nForce compatible) integrated AC97 audio codec +Nvidia nForce3 integrated AC97 audio codec +Nvidia CK8S (nForce compatible) integrated AC97 audio codec +Nvidia nForce4 integrated AC97 audio codec +Nvidia nForce MCP51 integrated AC97 audio codec +Nvidia nForce MCP04 integrated AC97 audio codec +Nvidia nForce MCP integrated AC97 audio codec +SiS SI7012 integrated AC97 audio codec + +// supported controllers HDA INTEL (development stage) +Intel 82801FB (ICH6) integrated High Definition Audio controller +Intel 82801G (ICH7) integrated High Definition Audio controller +Intel ESB2 integrated High Definition Audio controller +Intel 82801H (ICH8) integrated High Definition Audio controller +Intel 82801I (ICH9) integrated High Definition Audio controller + +// supported controllers HDA other (ATI/NVIDIA/SIS/ULI/VIA) (development stage) +ATI Technologies SB450 integrated High Definition Audio controller +ATI Technologies SB600 integrated High Definition Audio controller +Nvidia nForce MCP51 integrated High Definition Audio controller +Nvidia nForce MCP55 integrated High Definition Audio controller +Nvidia nForce MCP61 integrated High Definition Audio controller +Nvidia nForce MCP61b integrated High Definition Audio controller +Nvidia nForce MCP65 integrated High Definition Audio controller +Nvidia nForce MCP67 integrated High Definition Audio controller +SIS Technologies integrated High Definition Audio controller +ULI integrated High Definition Audio controller +VIA Technologies integrated High Definition Audio controller + +Note : ICH4+ controllers support is slightly different. The init procedure +continues with success even if the primary codec ready bit doesn't show up. +Legacy (ICH/ICH2/ICH3) I/O access compatibility will be enabled after init. + + + +The program will be able to identify over 80 different codecs : +=============================================================== +Analog Devices AD1819 +Analog Devices AD1881 +Analog Devices AD1881A +Analog Devices AD1885 +Analog Devices AD1886 +Analog Devices AD1887 +Analog Devices AD1886A +Analog Devices AD1980 +Asahi Kasei AK4540 +Asahi Kasei AK4542 +Asahi Kasei AK4543 +ALC100 +ALC100P +ALC200/200P +ALC650 +ALC650D +ALC650E +ALC650F +ALC655 +ALC658 +ALC658D +ALC850 +CMedia CM9738 +CMedia +CMedia CM9739 +CMedia CM9761 rev A +CMedia CM9761 rev B +CMedia CM9761 rev C +Cirrus Logic CS4297 +Cirrus Logic CS4297 +Cirrus Logic CS4297A +Cirrus Logic CS4297A rev A +Cirrus Logic CS4297A rev B +Cirrus Logic CS4298 +Cirrus Logic CS4294 +Cirrus Logic CS4294 +Cirrus Logic CS4299 +Cirrus Logic CS4299 rev A +Cirrus Logic CS4299 rev C +Cirrus Logic CS4299 rev D +Cirrus Logic CS4201 +Cirrus Logic CS4205 +CXT66 +Diamond Technology DT0893 +ESS Allegro ES1988 +ICEnsemble ICE1232 +VIA Vinyl series +National Semiconductor LM4549 +Silicon Laboratory Si3036 +Silicon Laboratory Si3038 +TriTech TR????? +TriTech TR28022 +TriTech TR28023 +TriTech TR28026 +TriTech TR28028 +TriTech TR A5 +Winbond 83971D +Wolfson WM9704 +WM9703/07/08/17 +WM9704M/WM9704Q +Wolfson WM9705/WM9710 +Yamaha YMF753 +SigmaTel STAC9700 +SigmaTel STAC9704 +SigmaTel STAC9705 +SigmaTel STAC9708 +SigmaTel STAC9721/23 +SigmaTel STAC9744/45 +SigmaTel STAC9756/57 +SigmaTel STAC9750T +SigmaTel STAC9783/84 + +note : full list still to come +Any unknown codec on an Intel ICH 8xx/9xx motherboard should still be initialized +properly. + + + +The program will detect 28 different stereo 3D Enhancement technologys : +======================================================================== +Analog Devices Phat Stereo +Creative Stereo Enhancement +National Semi 3D Stereo Enhancement +YAMAHA Ymersion +BBE 3D Stereo Enhancement +Crystal Semi 3D Stereo Enhancement +Qsound QXpander +Spatializer 3D Stereo Enhancement +SRS 3D Stereo Enhancement +Platform Tech 3D Stereo Enhancement +AKM 3D Audio +Aureal Stereo Enhancement +Aztech 3D Enhancement +Binaura 3D Audio Enhancement +ESS Technology Stereo Enhancement +Harman International VMAx +Nvidea 3D Stereo Enhancement +Philips Incredible Sound +Texas Instruments 3D Stereo Enhancement +VLSI Technology 3D Stereo Enhancement +TriTech 3D Stereo Enhancement +Realtek 3D Stereo Enhancement +Samsung 3D Stereo Enhancement +Wolfson Microelectronics 3D Enhancement +Delta Integration 3D Enhancement +SigmaTel 3D Enhancement +Winbond 3D Stereo Enhancement +Rockwell 3D Stereo Enhancement + + + +Many thanks to : +================ +Jeff Leyda - for helping me to understand how the AC'97 works and +especially his Intel ICH AC'97 wav player sources available at +http://www.programmersheaven.com +In fact this program uses his pci.asm (pci access procedures). +Moreover the ac97ich.inc include file is based on his ac97 defines. + +Alex Rodopoulos - for his feedback about the ICH4 controller AC'97 +integrated codec behaviour, and for the link to the ICH4 specs which +proved to be very helpful. + +Matthias Goeke - for his feedback and for including some new controllers +and codec IDs. + +Michael Tippach for the WDOSX DOS extender. diff --git a/ICHINIT/JUDASAC.H b/ICHINIT/JUDASAC.H new file mode 100644 index 0000000..b67c0ba --- /dev/null +++ b/ICHINIT/JUDASAC.H @@ -0,0 +1,493 @@ + +/* + * Internal header file: simplified definitions (used by AC97 code) + * by Piotr Ulaszewski (PETERS) + * August 2007 rev + */ + +#define FALSE 0 +#define TRUE 1 +#define NULL 0 +typedef int bool; +typedef int BOOL; +typedef unsigned char BYTE; +typedef unsigned short WORD; +typedef unsigned int DWORD; +typedef unsigned char UCHAR; +typedef unsigned short USHORT; +typedef unsigned int UINT; +typedef unsigned long ULONG; +typedef DWORD dword; +typedef WORD word; +typedef BYTE byte; +#define LOWORD(l) ((WORD)((DWORD)(l))) +#define HIWORD(l) ((WORD)(((DWORD)(l)>>16)&0xFFFF)) +#define LOBYTE(w) ((BYTE)(w)) +#define HIBYTE(w) ((BYTE)(((WORD)(w)>>8)&0xFF)) + + +#define BIT0 1 +#define BIT1 2 +#define BIT2 4 +#define BIT3 8 +#define BIT4 0x10 +#define BIT5 0x20 +#define BIT6 0x40 +#define BIT7 0x80 +#define BIT8 0x100 +#define BIT9 0x200 +#define BIT10 0x400 +#define BIT11 0x800 +#define BIT12 0x1000 +#define BIT13 0x2000 +#define BIT14 0x4000 +#define BIT15 0x8000 +#define BIT16 0x10000 +#define BIT17 0x20000 +#define BIT18 0x40000 +#define BIT19 0x80000 +#define BIT20 0x100000 +#define BIT21 0x200000 +#define BIT22 0x400000 +#define BIT23 0x800000 +#define BIT24 0x1000000 +#define BIT25 0x2000000 +#define BIT26 0x4000000 +#define BIT27 0x8000000 +#define BIT28 0x10000000 +#define BIT29 0x20000000 +#define BIT30 0x40000000 +#define BIT31 0x80000000 + + +// pci static defines +#define PCI_CLASS_MULTIMEDIA_AUDIO 0x0401 +#define PCI_CLASS_MULTIMEDIA_OTHER 0x0480 +#define PCI_VENDER_ID 0x00 +#define PCI_REVISION_ID 0x08 +#define PCI_COMMAND 0x04 +#define PCI_DEVICE_ID 0x02 +#define PCI_INTERRUPT_LINE 0x3c +#define PCI_INT_LINE 0x3d + +#define PCI_MEM_BASE_ADDRESS_0 0x10 +#define PCI_MEM_BASE_ADDRESS_1 0x14 +#define PCI_MEM_BASE_ADDRESS_2 0x18 +#define PCI_MEM_BASE_ADDRESS_3 0x1c + +#define PCI_BASE_ADDRESS_0 0x10 +#define PCI_BASE_ADDRESS_1 0x14 +#define PCI_BASE_ADDRESS_2 0x18 +#define PCI_BASE_ADDRESS_3 0x1c +#define PCI_BASE_ADDRESS_4 0x20 +#define PCI_BASE_ADDRESS_5 0x24 + +#define PCI_COMMAND_IO 0x01 +#define PCI_COMMAND_MEMORY 0x02 +#define PCI_COMMAND_MASTER 0x04 +#define PCI_COMMAND_PARITY 0x40 +#define PCI_ICH4_CFG_REG 0x41 +#define PCI_COMMAND_SERR 0x100 + +#define PCI_STATUS 0x06 +#define PCI_SUBSYSTEM_VENDER_ID 0x2c +#define PCI_SUBSYSTEM_ID 0x2e + +#define CTL_BASE 0 /* addressing controller regs */ +#define MIXER_BASE 1 /* addressing mixer regs */ + + +/* + * Internal header file: Intel ICH AC97 and compatibles. + * (AC97 pci device structure and defines partially based on MAME AC97 support header) + * + */ + +typedef struct { + WORD vender_id; + WORD device_id; + WORD sub_vender_id; + WORD sub_device_id; + int type; + char *string; +} AC97_DEVICE_LIST; + +typedef struct { + char *string; +} AC97_STEREO_TECHNOLOGY; + +typedef struct { + WORD vender_id1; + WORD vender_id2; + char *string; +} AC97_CODEC_LIST; + + +/* + * AC97 PCI device structure for device access + */ +#pragma pack(push,1) + typedef struct { + WORD vender_id; + WORD device_id; + WORD sub_vender_id; + WORD sub_device_id; + WORD device_bus_number; + BYTE irq; //PCI IRQ + BYTE pin; // PCI IRQ PIN + WORD command; // PCI command reg + DWORD base0; // PCI BAR for mixer registers NAMBAR_REG + DWORD base1; // PCI BAR for bus master registers NABMBAR_REG + DWORD base2; + DWORD base3; + DWORD base4; + DWORD base5; + DWORD device_type; // any + int mem_mode; // 0 for IO access, 1 for memory access + int hda_mode; // 0 for AC97 mode, 1 for HDA mode + WORD codec_id1; + WORD codec_id2; + char device_name[128]; // controller name + char codec_name[128]; // codec name +} AC97_PCI_DEV; +#pragma pack(pop) + + +/* registers accessed via NAMBAR - base0 - Audio Mixer registers */ + +#define AC97_RESET 0x00 // Reset register +#define AC97_MASTER 0x02 // Master Volume +#define AC97_HEADPHONE 0x04 // Headphone Volume (optional) +#define AC97_MASTER_MONO 0x06 +#define AC97_MASTER_TONE 0x08 +#define AC97_PCBEEP_VOL 0x0a +#define AC97_PHONE_VOL 0x0c +#define AC97_MIC_VOL 0x0e +#define AC97_LINE_IN_VOL 0x10 +#define AC97_CD_VOL 0x12 +#define AC97_VID_VOL 0x14 +#define AC97_AUX_VOL 0x16 +#define AC97_PCM 0x18 // PCM OUT Volume +#define AC97_RECORD_SELECT 0x1a +#define AC97_RECORD_VOL 0x1c +#define AC97_RECORD_MIC_VOL 0x1e +#define AC97_GP_REG 0x20 // general purpose +#define AC97_3D_CONTROL_REG 0x22 // 3D control + // 24h is reserved +#define AC97_POWER_CTRL 0x26 // powerdown control +#define AC97_EXTENDED_ID 0x28 // Extended Audio ID +#define AC97_EXTENDED_STATUS 0x2a // Extended Audio Status (control) +#define AC97_PCM_FRONT_DAC_RATE 0x2c // PCM OUT Front DAC Rate +#define AC97_PCM_SURND_DAC_RATE 0x2e // surround sound sample rate +#define AC97_PCM_LFE_DAC_RATE 0x30 // LFE samplerate +#define AC97_LR_ADC_DAC_RATE 0x32 // pcm in sample rate +#define AC97_MIC_ADC_RATE 0x34 // mic in sample rate + //registers 36-7a are reserved on the ICH +#define AC97_VENDERID1_REG 0x7c // codec vender ID 1 +#define AC97_VENDERID2_REG 0x7e // codec vender ID 2 + +// When 2 codecs are present in the system, use BIT7 to access the 2nd set of registers, ie 80h-feh + +#define PRIMARY_CODEC 0 // 0-7F for primary codec +#define SECONDARY_CODEC BIT7 // 80-8f registers for 2ndary + +#define AC97_EA_VRA BIT0 /* Variable Rate Audio enable bit */ +#define AC97_EA_DRA BIT1 /* Double Rate Audio enable bit */ + + +/* registers accessed via NABMBAR - base1 - Bus Master registers */ + +/* capture block - PCM IN */ +#define ICH_REG_PI_BDBAR 0x00 /* dword - Buffer Descriptor list base address */ +#define ICH_REG_PI_CIV 0x04 /* byte - Current Index Value */ +#define ICH_REG_PI_LVI 0x05 /* byte - Last Valid Index */ +#define ICH_REG_PI_SR 0x06 /* byte - Status Register */ +#define ICH_REG_PI_PICB 0x08 /* word - Position In Current Buffer */ +#define ICH_REG_PI_PIV 0x0a /* byte - Prefetched Index Value */ +#define ICH_REG_PI_CR 0x0b /* byte - Control Register */ + + +/* playback block - PCM OUT */ +#define ICH_REG_PO_BDBAR 0x10 /* dword - Buffer Descriptor list base address */ +#define ICH_REG_PO_CIV 0x14 /* byte - Current Index Value */ +#define ICH_REG_PO_LVI 0x15 /* byte - Last Valid Index */ +#define ICH_REG_PO_SR 0x16 /* byte - Status Register */ +#define ICH_REG_PO_PICB 0x18 /* word - Position In Current Buffer */ +#define ICH_REG_PO_PIV 0x1a /* byte - Prefetched Index Value */ +#define ICH_REG_PO_CR 0x1b /* byte - Control Register */ + + +/* mic capture block - MIC IN */ +#define ICH_REG_MC_BDBAR 0x20 /* dword - Buffer Descriptor list base address */ +#define ICH_REG_MC_CIV 0x24 /* byte - Current Index Value */ +#define ICH_REG_MC_LVI 0x25 /* byte - Last Valid Index */ +#define ICH_REG_MC_SR 0x26 /* byte - Status Register */ +#define ICH_REG_MC_PICB 0x28 /* word - Position In Current Buffer */ +#define ICH_REG_MC_PIV 0x2a /* byte - Prefetched Index Value */ +#define ICH_REG_MC_CR 0x2b /* byte - Control Register */ + + +/* bitfields for (PCM IN/PCM OUT/MIC IN) */ +#define ICH_REG_LVI_MASK 0x1f /* LVI range mask -> 0 - 31 */ +#define ICH_REG_PIV_MASK 0x1f /* PIV range mask -> 0 - 31 */ + +/* status register bitfields */ +#define ICH_FIFOE 0x10 /* FIFO error (overrun/underrun) */ +#define ICH_BCIS 0x08 /* Buffer Completion Interrupt Status */ +#define ICH_LVBCI 0x04 /* Last Valid Buffer Completion Interrupt */ +#define ICH_CELV 0x02 /* Current Equals Last Valid */ +#define ICH_DCH 0x01 /* DMA Controller Halted */ + +/* control register bitfields */ +#define ICH_IOCE 0x10 /* Interrupt On Completion Enable */ +#define ICH_FEIE 0x08 /* Fifo Error Interrupt Enable */ +#define ICH_LVBIE 0x04 /* Last Valid Buffer Interrupt Enable */ +#define ICH_RESETREGS 0x02 /* Reset busmaster Registers */ +#define ICH_STARTBM 0x01 /* Start Busmaster operation */ + + +/* global block - control/status */ +#define ICH_REG_GLOB_CNT 0x2c /* dword - global control register */ +#define ICH_24_BIT BIT23 /* 24 bit samples */ +#define ICH_20_BIT BIT22 /* 20 bit samples */ +#define ICH_PCM_246_MASK BIT20 | BIT21 /* 6 channels (not all chips) */ +#define ICH_PCM_6 BIT21 /* 6 channels (not all chips) */ +#define ICH_PCM_4 BIT20 /* 4 channels (not all chips) */ +#define ICH_PCM_2 0 /* 2 channels (stereo) same as ICH_SIS */ +#define ICH_SIS_PCM_246_MASK BIT6 | BIT7 /* 6 channels (not all chips) */ +#define ICH_SIS_PCM_6 BIT7 /* 6 channels (not all chips) */ +#define ICH_SIS_PCM_4 BIT6 /* 4 channels (not all chips) */ +#define ICH_SIS_PCM_2 0 /* 2 channels (stereo) same as ICH */ +#define ICH_SRIE BIT5 /* Secondary Resume Interrupt Enable */ +#define ICH_PRIE BIT4 /* Primary Resume Interrupt Enable */ +#define ICH_ACLINK BIT3 /* AClink shut off */ +#define ICH_AC97WARM BIT2 /* AC97 Warm reset */ +#define ICH_AC97COLD BIT1 /* AC97 Cold reset */ +#define ICH_GIE BIT0 /* GPI Interrupt Enable */ + +#define ICH_REG_GLOB_STA 0x30 /* dword - global status register */ +#define ICH_24_BIT_CAP BIT23 /* 24 bit sample support */ +#define ICH_20_BIT_CAP BIT22 /* 20 bit sample support */ +#define ICH_PCM_6_CAP BIT21 /* 6 channels capability */ +#define ICH_PCM_4_CAP BIT20 /* 4 channels capability */ +#define ICH_MD3 BIT17 /* Modem power Down semaphore (status) */ +#define ICH_AD3 BIT16 /* Audio power Down semaphore (status) */ +#define ICH_RCS BIT15 /* Read Completion Status (0=normal) */ +#define ICH_BIT3 BIT14 /* shadowed status of bit 3 in slot 12 */ +#define ICH_BIT2 BIT13 /* shadowed status of bit 2 in slot 12 */ +#define ICH_BIT1 BIT12 /* shadowed status of bit 1 in slot 12 */ +#define ICH_SRI BIT11 /* Secondary codec Resume Interrupt */ +#define ICH_PRI BIT10 /* Primary codec Resume Interrupt */ +#define ICH_SCR BIT9 /* Secondary Codec Ready */ +#define ICH_PCR BIT8 /* Primary Codec Ready */ +#define ICH_MCINT BIT7 /* MIC In Interrupt - mic capture */ +#define ICH_POINT BIT6 /* PCM Out Interrupt - pcm playback */ +#define ICH_PIINT BIT5 /* PCM In Interrupt - pcm capture */ +#define ICH_MOINT BIT2 /* Modem Out Interrupt - modem playback */ +#define ICH_MIINT BIT1 /* Modem In Interrupt - modem capture */ +#define ICH_GSCI BIT0 /* GPI Status Change Interrupt */ + +#define ICH_REG_ACC_SEMA 0x34 /* byte - codec write semaphore register */ +#define ICH_CAS BIT0 /* Codec Access Semaphore */ + + +/* AC97 supported controller types */ +#define DEVICE_INTEL 0 /* AC97 device Intel ICH compatible */ +#define DEVICE_SIS 1 /* AC97 device SIS compatible */ +#define DEVICE_INTEL_ICH4 2 /* AC97 device Intel ICH4 compatible */ +#define DEVICE_NFORCE 3 /* AC97 device nForce compatible */ +#define DEVICE_HDA 4 /* HDA audio device */ + +#define PCI_ANY_ID ((WORD)(~0)) + +static AC97_DEVICE_LIST ac97_dev_list[] = { + // supported controllers AC97 INTEL + { 0x8086, 0x2415, PCI_ANY_ID, PCI_ANY_ID, DEVICE_INTEL, "Intel 82801AA (ICH) integrated AC97 audio codec" }, + { 0x8086, 0x2425, PCI_ANY_ID, PCI_ANY_ID, DEVICE_INTEL, "Intel 82801AB (ICH0) integrated AC97 audio codec" }, + { 0x8086, 0x2445, PCI_ANY_ID, PCI_ANY_ID, DEVICE_INTEL, "Intel 82801BA (ICH2) integrated AC97 audio codec" }, + { 0x8086, 0x2485, PCI_ANY_ID, PCI_ANY_ID, DEVICE_INTEL, "Intel 82801CA (ICH3) integrated AC97 audio codec" }, + { 0x8086, 0x24c5, PCI_ANY_ID, PCI_ANY_ID, DEVICE_INTEL_ICH4, "Intel 82801DB (ICH4) integrated AC97 audio codec" }, + { 0x8086, 0x24d5, PCI_ANY_ID, PCI_ANY_ID, DEVICE_INTEL_ICH4, "Intel 82801EB/ER (ICH5/ICH5R) integrated AC97 audio codec" }, + { 0x8086, 0x25a6, PCI_ANY_ID, PCI_ANY_ID, DEVICE_INTEL_ICH4, "Intel 6300ESB integrated AC97 audio codec" }, + { 0x8086, 0x266e, PCI_ANY_ID, PCI_ANY_ID, DEVICE_INTEL_ICH4, "Intel 82801FB (ICH6) integrated AC97 audio codec" }, + { 0x8086, 0x27de, PCI_ANY_ID, PCI_ANY_ID, DEVICE_INTEL_ICH4, "Intel 82801GB (ICH7) integrated AC97 audio codec" }, + { 0x8086, 0x7195, PCI_ANY_ID, PCI_ANY_ID, DEVICE_INTEL, "Intel 82443MX integrated AC97 audio codec" }, + + // supported controllers AC97 other (AMD/NVIDIA/SIS) + { 0x1022, 0x7445, PCI_ANY_ID, PCI_ANY_ID, DEVICE_INTEL, "AMD 768 integrated AC97 audio codec" }, + { 0x1022, 0x746d, PCI_ANY_ID, PCI_ANY_ID, DEVICE_INTEL, "AMD 8111 integrated AC97 audio codec" }, + + { 0x10de, 0x01b1, PCI_ANY_ID, PCI_ANY_ID, DEVICE_NFORCE, "Nvidia nForce integrated AC97 audio codec" }, + { 0x10de, 0x006a, PCI_ANY_ID, PCI_ANY_ID, DEVICE_NFORCE, "Nvidia nForce2 integrated AC97 audio codec" }, + { 0x10de, 0x008a, PCI_ANY_ID, PCI_ANY_ID, DEVICE_NFORCE, "Nvidia CK8 (nForce compatible) integrated AC97 audio codec" }, + { 0x10de, 0x00da, PCI_ANY_ID, PCI_ANY_ID, DEVICE_NFORCE, "Nvidia nForce3 integrated AC97 audio codec" }, + { 0x10de, 0x00ea, PCI_ANY_ID, PCI_ANY_ID, DEVICE_NFORCE, "Nvidia CK8S (nForce compatible) integrated AC97 audio codec" }, + { 0x10de, 0x0059, PCI_ANY_ID, PCI_ANY_ID, DEVICE_NFORCE, "Nvidia nForce4 integrated AC97 audio codec" }, + { 0x10de, 0x026b, PCI_ANY_ID, PCI_ANY_ID, DEVICE_NFORCE, "Nvidia nForce MCP51 integrated AC97 audio codec" }, + { 0x10de, 0x003a, PCI_ANY_ID, PCI_ANY_ID, DEVICE_NFORCE, "Nvidia nForce MCP04 integrated AC97 audio codec" }, + { 0x10de, 0x006b, PCI_ANY_ID, PCI_ANY_ID, DEVICE_NFORCE, "Nvidia nForce MCP integrated AC97 audio codec" }, + + { 0x1039, 0x7012, PCI_ANY_ID, PCI_ANY_ID, DEVICE_SIS, "SiS SI7012 integrated AC97 audio codec" }, + + // supported controllers HDA INTEL + { 0x8086, 0x2668, PCI_ANY_ID, PCI_ANY_ID, DEVICE_HDA, "Intel 82801FB (ICH6) integrated High Definition Audio controller" }, + { 0x8086, 0x27d8, PCI_ANY_ID, PCI_ANY_ID, DEVICE_HDA, "Intel 82801G (ICH7) integrated High Definition Audio controller" }, + { 0x8086, 0x269a, PCI_ANY_ID, PCI_ANY_ID, DEVICE_HDA, "Intel ESB2 integrated High Definition Audio controller" }, + { 0x8086, 0x284b, PCI_ANY_ID, PCI_ANY_ID, DEVICE_HDA, "Intel 82801H (ICH8) integrated High Definition Audio controller" }, + { 0x8086, 0x293f, PCI_ANY_ID, PCI_ANY_ID, DEVICE_HDA, "Intel 82801I (ICH9) integrated High Definition Audio controller" }, + { 0x8086, 0x3a6e, PCI_ANY_ID, PCI_ANY_ID, DEVICE_HDA, "Intel 82801J (ICH10) integrated High Definition Audio controller" }, + { 0x8086, 0x3b56, PCI_ANY_ID, PCI_ANY_ID, DEVICE_HDA, "Intel 5-series integrated High Definition Audio controller" }, + { 0x8086, 0x1c20, PCI_ANY_ID, PCI_ANY_ID, DEVICE_HDA, "Intel 6-series integrated High Definition Audio controller" }, + { 0x8086, 0x1d20, PCI_ANY_ID, PCI_ANY_ID, DEVICE_HDA, "Intel 7-series integrated High Definition Audio controller" }, + + // supported controllers HDA other (ATI/NVIDIA/SIS/ULI/VIA) + { 0x1002, 0x437b, PCI_ANY_ID, PCI_ANY_ID, DEVICE_HDA, "ATI Technologies SB450 integrated High Definition Audio controller" }, + { 0x1002, 0x4383, PCI_ANY_ID, PCI_ANY_ID, DEVICE_HDA, "ATI Technologies SB600 integrated High Definition Audio controller" }, + + { 0x10de, 0x026c, PCI_ANY_ID, PCI_ANY_ID, DEVICE_HDA, "Nvidia nForce MCP51 integrated High Definition Audio controller" }, + { 0x10de, 0x0371, PCI_ANY_ID, PCI_ANY_ID, DEVICE_HDA, "Nvidia nForce MCP55 integrated High Definition Audio controller" }, + { 0x10de, 0x03e4, PCI_ANY_ID, PCI_ANY_ID, DEVICE_HDA, "Nvidia nForce MCP61 integrated High Definition Audio controller" }, + { 0x10de, 0x03f0, PCI_ANY_ID, PCI_ANY_ID, DEVICE_HDA, "Nvidia nForce MCP61b integrated High Definition Audio controller" }, + { 0x10de, 0x044a, PCI_ANY_ID, PCI_ANY_ID, DEVICE_HDA, "Nvidia nForce MCP65 integrated High Definition Audio controller" }, + { 0x10de, 0x055c, PCI_ANY_ID, PCI_ANY_ID, DEVICE_HDA, "Nvidia nForce MCP67 integrated High Definition Audio controller" }, + + { 0x1039, 0x7502, PCI_ANY_ID, PCI_ANY_ID, DEVICE_HDA, "SIS Technologies integrated High Definition Audio controller" }, + + { 0x10b9, 0x5461, PCI_ANY_ID, PCI_ANY_ID, DEVICE_HDA, "ULI integrated High Definition Audio controller" }, + + { 0x1106, 0x3288, PCI_ANY_ID, PCI_ANY_ID, DEVICE_HDA, "VIA Technologies integrated High Definition Audio controller" }, + + // null entry + { 0x0000, 0x0000, PCI_ANY_ID, PCI_ANY_ID, 0, "" } +}; + +static AC97_STEREO_TECHNOLOGY ac97_stereo_technology[] = { + {"No 3D Stereo Enhancement"}, + {"Analog Devices Phat Stereo"}, // 1 + {"Creative Stereo Enhancement"}, // 2 + {"National Semi 3D Stereo Enhancement"}, // 3 + {"YAMAHA Ymersion"}, // 4 + {"BBE 3D Stereo Enhancement"}, // 5 + {"Crystal Semi 3D Stereo Enhancement"}, // 6 + {"Qsound QXpander"}, // 7 + {"Spatializer 3D Stereo Enhancement"}, // 8 + {"SRS 3D Stereo Enhancement"}, // 9 + {"Platform Tech 3D Stereo Enhancement"}, // 10 + {"AKM 3D Audio"}, // 11 + {"Aureal Stereo Enhancement"}, // 12 + {"Aztech 3D Enhancement"}, // 13 + {"Binaura 3D Audio Enhancement"}, // 14 + {"ESS Technology Stereo Enhancement"}, // 15 + {"Harman International VMAx"}, // 16 + {"Nvidea 3D Stereo Enhancement"}, // 17 + {"Philips Incredible Sound"}, // 18 + {"Texas Instruments 3D Stereo Enhancement"}, // 19 + {"VLSI Technology 3D Stereo Enhancement"}, // 20 + {"TriTech 3D Stereo Enhancement"}, // 21 + {"Realtek 3D Stereo Enhancement"}, // 22 + {"Samsung 3D Stereo Enhancement"}, // 23 + {"Wolfson Microelectronics 3D Enhancement"}, // 24 + {"Delta Integration 3D Enhancement"}, // 25 + {"SigmaTel 3D Enhancement"}, // 26 + {"Winbond 3D Stereo Enhancement"}, // 27 + {"Rockwell 3D Stereo Enhancement"}, // 28 + {"Unknown 3D Stereo Enhancement"}, // 29 + {"Unknown 3D Stereo Enhancement"}, // 30 + {"Unknown 3D Stereo Enhancement"}, // 31 +}; + +static AC97_CODEC_LIST ac97_codec_list[] = { + {0x4144, 0x5303, "Analog Devices AD1819"}, + {0x4144, 0x5340, "Analog Devices AD1881"}, + {0x4144, 0x5348, "Analog Devices AD1881A"}, + {0x4144, 0x5360, "Analog Devices AD1885"}, + {0x4144, 0x5361, "Analog Devices AD1886"}, + {0x4144, 0x5362, "Analog Devices AD1887"}, + {0x4144, 0x5363, "Analog Devices AD1886A"}, + {0x4144, 0x5370, "Analog Devices AD1980"}, + + {0x414B, 0x4D00, "Asahi Kasei AK4540"}, + {0x414B, 0x4D01, "Asahi Kasei AK4542"}, + {0x414B, 0x4D02, "Asahi Kasei AK4543"}, + + {0x414C, 0x4300, "ALC100"}, + {0x414C, 0x4326, "ALC100P"}, + {0x414C, 0x4710, "ALC200/200P"}, + {0x414C, 0x4720, "ALC650"}, + {0x414C, 0x4721, "ALC650D"}, + {0x414C, 0x4722, "ALC650E"}, + {0x414C, 0x4723, "ALC650F"}, + {0x414C, 0x4760, "ALC655"}, + {0x414C, 0x4780, "ALC658"}, + {0x414C, 0x4781, "ALC658D"}, + {0x414C, 0x4790, "ALC850"}, + + {0x434D, 0x4941, "CMedia CM9738"}, + {0x434D, 0x4942, "CMedia"}, + {0x434D, 0x4961, "CMedia CM9739"}, + {0x434D, 0x4978, "CMedia CM9761 rev A"}, + {0x434D, 0x4982, "CMedia CM9761 rev B"}, + {0x434D, 0x4983, "CMedia CM9761 rev C"}, + + {0x4352, 0x5900, "Cirrus Logic CS4297"}, + {0x4352, 0x5903, "Cirrus Logic CS4297"}, + {0x4352, 0x5910, "Cirrus Logic CS4297A"}, + {0x4352, 0x5913, "Cirrus Logic CS4297A rev A"}, + {0x4352, 0x5914, "Cirrus Logic CS4297A rev B"}, + {0x4352, 0x5923, "Cirrus Logic CS4298"}, + {0x4352, 0x592B, "Cirrus Logic CS4294"}, + {0x4352, 0x592D, "Cirrus Logic CS4294"}, + {0x4352, 0x5930, "Cirrus Logic CS4299"}, + {0x4352, 0x5931, "Cirrus Logic CS4299 rev A"}, + {0x4352, 0x5933, "Cirrus Logic CS4299 rev C"}, + {0x4352, 0x5934, "Cirrus Logic CS4299 rev D"}, + {0x4352, 0x5948, "Cirrus Logic CS4201"}, + {0x4352, 0x5958, "Cirrus Logic CS4205"}, + + {0x4358, 0x5442, "CXT66"}, + + {0x4454, 0x3031, "Diamond Technology DT0893"}, + + {0x4583, 0x8308, "ESS Allegro ES1988"}, + + {0x4943, 0x4511, "ICEnsemble ICE1232"}, + {0x4943, 0x4541, "VIA Vinyl"}, + {0x4943, 0x4551, "VIA Vinyl"}, + {0x4943, 0x4552, "VIA Vinyl"}, + {0x4943, 0x6010, "VIA Vinyl"}, + {0x4943, 0x6015, "VIA Vinyl"}, + {0x4943, 0x6017, "VIA Vinyl"}, + + {0x4e53, 0x4331, "National Semiconductor LM4549"}, + + {0x5349, 0x4c22, "Silicon Laboratory Si3036"}, + {0x5349, 0x4c23, "Silicon Laboratory Si3038"}, + + {0x5452, 0x00FF, "TriTech TR?????"}, + {0x5452, 0x4102, "TriTech TR28022"}, + {0x5452, 0x4103, "TriTech TR28023"}, + {0x5452, 0x4106, "TriTech TR28026"}, + {0x5452, 0x4108, "TriTech TR28028"}, + {0x5452, 0x4123, "TriTech TR A5"}, + + {0x5745, 0x4301, "Winbond 83971D"}, + + {0x574D, 0x4C00, "Wolfson WM9704"}, + {0x574D, 0x4C03, "WM9703/07/08/17"}, + {0x574D, 0x4C04, "WM9704M/WM9704Q"}, + {0x574D, 0x4C05, "Wolfson WM9705/WM9710"}, + + {0x594D, 0x4803, "Yamaha YMF753"}, + + {0x8384, 0x7600, "SigmaTel STAC9700"}, + {0x8384, 0x7604, "SigmaTel STAC9704"}, + {0x8384, 0x7605, "SigmaTel STAC9705"}, + {0x8384, 0x7608, "SigmaTel STAC9708"}, + {0x8384, 0x7609, "SigmaTel STAC9721/23"}, + {0x8384, 0x7644, "SigmaTel STAC9744/45"}, + {0x8384, 0x7656, "SigmaTel STAC9756/57"}, + {0x8384, 0x7666, "SigmaTel STAC9750T"}, + {0x8384, 0x7684, "SigmaTel STAC9783/84"}, + + {0x0000, 0x0000, "Unknown Device"}, +}; diff --git a/ICHINIT/PCI.C b/ICHINIT/PCI.C new file mode 100644 index 0000000..d684294 --- /dev/null +++ b/ICHINIT/PCI.C @@ -0,0 +1,239 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "judasac.h" +#include "dos32\dpmi.h" +#include "dos32\input.h" +#include "dos32\scans.h" +#include "dos32\timer.h" + + +extern BYTE pci_config_read_byte(AC97_PCI_DEV *ac97_pci, int index); +extern WORD pci_config_read_word(AC97_PCI_DEV *ac97_pci, int index); +extern DWORD pci_config_read_dword(AC97_PCI_DEV *ac97_pci, int index); +extern void pci_config_write_byte(AC97_PCI_DEV *ac97_pci, int index, BYTE data); +extern void pci_config_write_word(AC97_PCI_DEV *ac97_pci, int index, WORD data); +extern void pci_config_write_dword(AC97_PCI_DEV *ac97_pci, int index, DWORD data); +extern BOOL pci_check_bios(void); +extern BOOL pci_find_device(AC97_PCI_DEV *ac97_pci); +extern void pci_enable_io_access(AC97_PCI_DEV *ac97_pci); +extern void pci_enable_memory_access(AC97_PCI_DEV *ac97_pci); +extern void pci_enable_busmaster(AC97_PCI_DEV *ac97_pci); +extern BOOL detect_windows(void); +extern BOOL detect_os2(void); +extern BOOL detect_linux(void); + + +/******************************************************************** + * Intel PCI BIOS helper funtions + ********************************************************************/ + +#ifndef PCI_ANY_ID +#define PCI_ANY_ID ((WORD)(~0)) +#endif + +BYTE pci_config_read_byte(AC97_PCI_DEV *ac97_pci, int index) +{ + union REGS r; + + memset(&r, 0, sizeof(r)); + r.x.eax = 0x0000B108; // config read byte + r.x.ebx = (DWORD)ac97_pci->device_bus_number; + r.x.edi = (DWORD)index; + int386(0x1a, &r, &r); + if (r.h.ah != 0) { + #ifdef AC97_DEBUG + logmessage("Error : PCI read config byte failed\n"); + #endif + r.x.ecx = 0; + } + return (BYTE)r.x.ecx; +} + +WORD pci_config_read_word(AC97_PCI_DEV *ac97_pci, int index) +{ + union REGS r; + + memset(&r, 0, sizeof(r)); + r.x.eax = 0x0000B109; // config read word + r.x.ebx = (DWORD)ac97_pci->device_bus_number; + r.x.edi = (DWORD)index; + int386(0x1a, &r, &r); + if (r.h.ah != 0 ){ + #ifdef AC97_DEBUG + logmessage("Error : PCI read config word failed\n"); + #endif + r.x.ecx = 0; + } + return (WORD)r.x.ecx; +} + +DWORD pci_config_read_dword(AC97_PCI_DEV *ac97_pci, int index) +{ + union REGS r; + + memset(&r, 0, sizeof(r)); + r.x.eax = 0x0000B10A; // config read dword + r.x.ebx = (DWORD)ac97_pci->device_bus_number; + r.x.edi = (DWORD)index; + int386(0x1a, &r, &r); + if (r.h.ah != 0 ){ + #ifdef AC97_DEBUG + logmessage("Error : PCI read config dword failed\n"); + #endif + r.x.ecx = 0; + } + return (DWORD)r.x.ecx; +} + +void pci_config_write_byte(AC97_PCI_DEV *ac97_pci, int index, BYTE data) +{ + union REGS r; + + memset(&r, 0, sizeof(r)); + r.x.eax = 0x0000B10B; // config write byte + r.x.ebx = (DWORD)ac97_pci->device_bus_number; + r.x.ecx = (DWORD)data; + r.x.edi = (DWORD)index; + int386(0x1a, &r, &r); + if (r.h.ah != 0 ){ + #ifdef AC97_DEBUG + logmessage("Error : PCI write config byte failed\n"); + #endif + } +} + +void pci_config_write_word(AC97_PCI_DEV *ac97_pci, int index, WORD data) +{ + union REGS r; + + memset(&r, 0, sizeof(r)); + r.x.eax = 0x0000B10C; // config write word + r.x.ebx = (DWORD)ac97_pci->device_bus_number; + r.x.ecx = (DWORD)data; + r.x.edi = (DWORD)index; + int386(0x1a, &r, &r); + if (r.h.ah != 0 ){ + #ifdef AC97_DEBUG + logmessage("Error : PCI write config word failed\n"); + #endif + } +} + +void pci_config_write_dword(AC97_PCI_DEV *ac97_pci, int index, DWORD data) +{ + union REGS r; + + memset(&r, 0, sizeof(r)); + r.x.eax = 0x0000B10D; // config write dword + r.x.ebx = (DWORD)ac97_pci->device_bus_number; + r.x.ecx = (DWORD)data; + r.x.edi = (DWORD)index; + int386(0x1a, &r, &r); + if (r.h.ah != 0 ){ + #ifdef AC97_DEBUG + logmessage("Error : PCI write config dword failed\n"); + #endif + } +} + +BOOL pci_check_bios(void) +{ + union REGS r; + + memset(&r, 0, sizeof(r)); + r.x.eax = 0x0000B101; // PCI BIOS - installation check + r.x.edi = 0x00000000; + int386(0x1a, &r, &r); + if (r.x.edx != 0x20494350) return FALSE; // ' ICP' identifier found ? + return TRUE; +} + +BOOL pci_find_device(AC97_PCI_DEV *ac97_pci) +{ + union REGS r; + + memset(&r, 0, sizeof(r)); + r.x.eax = 0x0000B102; // PCI BIOS - find PCI device + r.x.ecx = ac97_pci->device_id; // device ID + r.x.edx = ac97_pci->vender_id; // vender ID + r.x.esi = 0x00000000; // device index + int386(0x1a, &r, &r); + if (r.h.ah != 0 ) return FALSE; // device not found + ac97_pci->device_bus_number = r.w.bx; // save device & bus/funct number + if(ac97_pci->sub_vender_id != PCI_ANY_ID){ + // check subsystem vender id + if(pci_config_read_word(ac97_pci, 0x2C) != ac97_pci->sub_vender_id) return FALSE; + } + if(ac97_pci->sub_device_id != PCI_ANY_ID){ + // check subsystem device id + if(pci_config_read_word(ac97_pci, 0x2E) != ac97_pci->sub_device_id) return FALSE; + } + return TRUE; // device found +} + +void pci_enable_io_access(AC97_PCI_DEV *ac97_pci) +{ + pci_config_write_word(ac97_pci, 0x04, pci_config_read_word(ac97_pci, 0x04) | BIT0); +} + +void pci_enable_memory_access(AC97_PCI_DEV *ac97_pci) +{ + pci_config_write_word(ac97_pci, 0x04, pci_config_read_word(ac97_pci, 0x04) | BIT1); +} + +void pci_enable_busmaster(AC97_PCI_DEV *ac97_pci) +{ + pci_config_write_word(ac97_pci, 0x04, pci_config_read_word(ac97_pci, 0x04) | BIT2); +} + + +/******************************************************************** + * Windows/OS2/Linux detection helper functions + ********************************************************************/ + +BOOL detect_windows(void) // Win 3.1+ detection +{ + union REGS r; + + memset(&r, 0, sizeof(r)); + r.x.eax = 0x1600; + int386(0x2F, &r, &r); + if ((r.h.al & 0x7F) != 0) return TRUE; + return FALSE; +} + +BOOL detect_os2(void) // OS2 2.0+ detection +{ + union REGS r; + + memset(&r, 0, sizeof(r)); + r.x.eax = 0x4010; + int386(0x2F, &r, &r); + if (r.w.ax == 0) return TRUE; + return FALSE; +} + +BOOL detect_linux(void) // Linux DOSEMU detection ??? +{ + union REGS r; + + memset(&r, 0, sizeof(r)); + r.x.eax = 0x0200; + r.x.ebx = 0x00E6; + int386(0x31, &r, &r); // segment of this vector should be 0xF000 + if (r.w.cx != 0xF000) return FALSE; + + memset(&r, 0, sizeof(r)); + r.x.eax = 0; + int386(0xE6, &r, &r); + if (r.w.ax == 0xAA55) return TRUE; + return FALSE; +} diff --git a/ICHINIT/SEGMENTS.INC b/ICHINIT/SEGMENTS.INC new file mode 100644 index 0000000..9f66081 --- /dev/null +++ b/ICHINIT/SEGMENTS.INC @@ -0,0 +1,11 @@ +; predefined segments for NASM - 32 bit obj format + +segment _TEXT public align=256 class=CODE use32 + +segment _DATA public align=256 class=DATA use32 + +segment _BSS public align=256 class=BSS use32 + +segment STACK stack align=256 class=STACK use32 + +segment _BSS diff --git a/INTERPOL.TXT b/INTERPOL.TXT new file mode 100644 index 0000000..3a0d02a --- /dev/null +++ b/INTERPOL.TXT @@ -0,0 +1,257 @@ + SOUND INTERPOLATION ALGORITHMS v19980602 + by Yehar + + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + "Fake sinc". Cubic through 2 known points. Continuous differential. + Differential of the curve at a known sample point = precalculated + derivat of sinc interpolation curve at the same point. Fast in use, + very high quality, 4x memory consumption, for systems where sampledata + doesn't change in action. + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Known sample points: + + All of them! + + Interpolation between points x=0 and x=1 + + Interpolated sample: + + f(x) = ax^3 + bx^2 + cx + y(0) (x=0..1) + + where + + a = 2*y(0) - 2*y(1) + k(0) + k(1) + b = 3*y(1) - 3*y(0) - 2*k(0) - k(1) + c = k(0) + d = y(0) + + where + + max + ____ + \ | + k(0) = > pi * d_sinc(pi * i) * y(i) + /___| + + i = min + + max + ____ + \ | + k(1) = > pi * d_sinc(pi * (i-1)) * y(i) + /___| + + i = min + + The math behind this: + + f(x) = ax^3 + bx^2 + cx + d + + / f(0) = y(0) + | + | f(1) = y(1) + < + | f'(0) = k(0) + | + \ f'(1) = k(1) + + k(0) and k(1) calculated with sinc interpolation. + + How to use: + + Precalculate a, b and c values for all sample points in memory. + After that, when you interpolate a sample, all you need to do is: + + a * x + a + b + a * x + a + c + a * x + a + y(0) + + out = a + + (total 3 muls, 3 adds) + + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + "Hermite curve". Cubic through 2 known points. Continuous differential. + Differential of the curve at a known sample point = slope of a straight + line drawn through the two neigbouring known points. + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Known sample points: + + y(-1), y(0), y(1), y(2) + + Interpolation takes place between points x=0 and x=1 + + Interpolated sample: + + f(x) = ax^3 + bx^2 + cx + y(0) (x=0..1) + + where + + 3 ( y(0) - y(1) ) - y(-1) + y(2) + a = -------------------------------- + 2 + + 5 y(0) + y(2) + b = 2 y(1) + y(-1) - ------------- + 2 + + y(1) - y(-1) + c = ------------ + 2 + + The math behind this: + + f(x) = ax^3 + bx^2 + cx + d + + / f(0) = y(0) + | + | f(1) = y(1) + < + | f'(0) = (y(1) - y(-1)) / 2 + | + \ f'(1) = (y(2) - y(0)) / 2 + + Optimized pseudocode: (3 muls, 19 adds/subs/shifts) + + a = y(0) + a - y(1) + a sal 2 + a - y(0) + a + y(1) + a - y(-1) + a + y(2) + a sar 1 + + temp = y(0) + temp sal 2 + temp + y(0) + temp + y(2) + temp sar 1 + b = y(1) + b + y(1) + b + y(-1) + b - temp + + c = y(1) + c - y(-1) + c sar 1 + + a * x + a + b + a * x + a + c + a * x + a + y(0) + + out = a + + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + + Hermite curve: + +* + + | + + + + | | + + | + | + + | | + + | + | | + + + + | | + + | + * | + + + + + | + ++ | + * * *+++ | * * + +++++++ | + + + | + | + + | + + | | + + + + | | + | + + | | + + + + | + + * * + | + + + + + + ++ + ++ *+++ + +++ + Linear interpolation: + + * + + | + + + + | | + + | + + | + | + + + | + + | + | | + + + + | | + +* | + ++ | + ++ + + ++ | + *+++++++*+++++++* | *+++++++* + | + + + + + | | + | + + | + + + | + | + + | + + | | + + + + | | + * +* + ++ ++ + ++ ++ + ++ ++ + +* + No interpolation: + + +++*++++ + | | + | | + | | + | | + | | + | | + | | + | | + | | + | | + | | + +++*++++ | + | | + | | + | | + *+++++++*+++++++*++++ | +++*+++++++* + | | + | | + | | + | | + | | + | | + | | + | | + | | + | | + | | + +++*++++ +++*++++ + | | + | | + | | + +++*++++ diff --git a/JASMDJ.ASM b/JASMDJ.ASM new file mode 100644 index 0000000..7a29c31 --- /dev/null +++ b/JASMDJ.ASM @@ -0,0 +1,3750 @@ +; This module contains soundcard IRQ handlers, DMA routines, mixing routines +; as well as some time-critical GUS routines and AC97 access code. +; +; N„m„ ei mit„„n (kovin) optimisoituja rutiineja ole, PERKELE! +; +; Modified by BSpider for NASM on 5th Sep 2006 + + %idefine offset + %include "segments.inc" + %include "judascfg.inc" + %include "judasgus.inc" + %include "judasac.inc" + +%define MONO 0 +%define EIGHTBIT 0 +%define STEREO 1 +%define SIXTEENBIT 2 + +%define VM_OFF 0 +%define VM_ON 1 +%define VM_LOOP 2 +%define VM_16BIT 4 + +%define DEV_NOSOUND 0 +%define DEV_SB 1 +%define DEV_SBPRO 2 +%define DEV_SB16 3 +%define DEV_GUS 4 +%define DEV_AC97 5 +%define DEV_HDA 6 +%define DEV_FILE 7 + +%define CACHESLOTS 16 + +%define IPMINUS1 -1 +%define IP0 0 +%define IP1 1 +%define IP2 2 + +; predefined to 0 +struc CACHESLOT + GDC_Pos resd 1 + GDC_Length resd 1 +endstruc + +; predefined to 0 +struc DMACHANNEL + DMA_PagePort resw 1 + DMA_AdrPort resw 1 + DMA_LenPort resw 1 + DMA_MaskPort resw 1 + DMA_ModePort resw 1 + DMA_ClearPort resw 1 + DMA_Mask resb 1 + DMA_UnMask resb 1 + DMA_Unused resw 1 +endstruc + +; predefined to 0 +struc CHANNEL + Chn_Pos resd 1 + Chn_Repeat resd 1 + Chn_End resd 1 + Chn_Sample resd 1 + Chn_Freq resd 1 + Chn_FractPos resw 1 + Chn_MasterVol resb 1 + Chn_Panning resb 1 + Chn_Vol resw 1 + Chn_VoiceMode resb 1 + Chn_PrevVM resb 1 + Chn_PrevPos resd 1 + Chn_LastValL resd 1 + Chn_LastValR resd 1 + Chn_SmoothVolL resd 1 + Chn_SmoothVolR resd 1 +endstruc + + + +; not predefined +struc AUDIO_PCI_DEV + .vender_id resw 1 + .device_id resw 1 + .sub_vender_id resw 1 + .sub_device_id resw 1 + .device_bus_number resw 1 + .irq resb 1 + .pin resb 1 + .command resw 1 + .base0 resd 1 + .base1 resd 1 + .base2 resd 1 + .base3 resd 1 + .base4 resd 1 + .base5 resd 1 + .device_type resd 1 + + .mem_mode resd 1 + .hda_mode resd 1 + +; memory allocated for BDL and PCM buffers + .bdl_buffer resd 1 + .pcmout_buffer0 resd 1 + .pcmout_buffer1 resd 1 + .hda_buffer resd 1 + .pcmout_bufsize resd 1 + .pcmout_bdl_entries resd 1 + .pcmout_bdl_size resd 1 + .pcmout_dmasize resd 1 + .pcmout_dma_lastgoodpos resd 1 + .pcmout_dma_pos_ptr resd 1 + +; AC97 only properties + .ac97_vra_supported resd 1 + +; HDA modified structure will be placed here. + .codec_mask resd 1 + .codec_index resd 1 + + .afg_root_nodenum resw 1 + .afg_num_nodes resd 1 + .afg_nodes resd 1 + .def_amp_out_caps resd 1 + .def_amp_in_caps resd 1 + + .dac_node resd 1 + .out_pin_node resd 1 + .adc_node resd 1 + .in_pin_node resd 1 + .input_items resd 1 + .pcm_num_vols resd 1 + .pcm_vols resd 1 + + .format_val resd 1 + .dacout_num_bits resd 1 + .dacout_num_channels resd 1 + .stream_tag resd 1 + .supported_formats resd 1 + .supported_max_freq resd 1 + .supported_max_bits resd 1 + + .freq_card resd 1 + .chan_card resd 1 + .bits_card resd 1 + + .codec_id1 resw 1 + .codec_id2 resw 1 + .device_name resb 128 + .codec_name resb 128 +endstruc + +%define DEVICE_INTEL 0 ; AC97 device Intel ICH compatible +%define DEVICE_SIS 1 ; AC97 device SIS compatible +%define DEVICE_INTEL_ICH4 2 ; AC97 device Intel ICH4 compatible +%define DEVICE_NFORCE 3 ; AC97 device nForce compatible +%define DEVICE_HDA_INTEL 4 ; HDA audio device for Intel and others +%define DEVICE_HDA_ATI 5 +%define DEVICE_HDA_ATIHDMI 6 +%define DEVICE_HDA_NVIDIA 7 +%define DEVICE_HDA_SIS 8 +%define DEVICE_HDA_ULI 9 +%define DEVICE_HDA_VIA 10 + + ; register calling convention for WATCOM C++ + global judas_code_lock_start_ + global judas_code_lock_end_ + global judas_update_ + global judas_get_ds_ + global sb_handler_ + global sb_aihandler_ + global sb16_handler_ + global gus_handler_ + global gus_peek_ + global gus_poke_ + global gus_dmawait_ + global gus_dmainit_ + global gus_dmaprogram_ + global gus_startchannels_ + global fmixer_ + global qmixer_ + global safemixer_ + global normalmix_ + global ipmix_ + global qmix_linear_ + global qmix_cubic_ + global dma_program_ + ; stack calling convention for anything else + global _judas_code_lock_start + global _judas_code_lock_end + global _judas_update + global _judas_get_ds + global _sb_handler + global _sb_aihandler + global _sb16_handler + global _gus_handler + global _gus_peek + global _gus_poke + global _gus_dmawait + global _gus_dmainit + global _gus_dmaprogram + global _gus_startchannels + global _fmixer + global _qmixer + global _safemixer + global _normalmix + global _ipmix + global _qmix_linear + global _qmix_cubic + global _dma_program + + extern _judas_ds;word + extern _judas_initialized;byte + extern _judas_mixmode;byte + extern _judas_samplesize;byte + extern _judas_clipbuffer;dword + extern _judas_zladdbuffer;dword + extern _judas_zerolevell;dword + extern _judas_zerolevelr;dword + extern _judas_cliptable;dword + extern _judas_volumetable;dword + extern _judas_mixrate;dword + extern _judas_channel;dword + extern _judas_mixroutine;dword + extern _judas_mixersys;dword + extern _judas_device;dword + extern _judas_port;dword + extern _judas_irq;dword + extern _judas_dma;dword + extern _judas_irqcount;dword + extern _judas_bufferlength;dword + extern _judas_buffermask;dword + extern _judas_bpmcount;dword + extern _judas_bpmtempo;byte + extern _judas_player;dword + extern _judas_mixpos;dword + extern _dma_address;dword + extern ___djgpp_base_address;dword + extern _judas_clipped;byte + extern _audio_pci;AUDIO_PCI_DEV + extern _hda_civ ; dword + extern _hda_lpib ; dword + +%ifdef djgpp +section .text +%else +segment _TEXT +%endif + +judas_get_ds_: +_judas_get_ds: + mov AX, DS + mov [_judas_ds], AX + ret + +judas_code_lock_start_: +_judas_code_lock_start: +; this code is constant - TASM declaration of .const + + align 4 + +DMAChannels: + istruc DMACHANNEL + at DMA_PagePort, dw 87h + at DMA_AdrPort, dw 0h + at DMA_LenPort, dw 1h + at DMA_MaskPort, dw 0ah + at DMA_ModePort, dw 0bh + at DMA_ClearPort, dw 0ch + at DMA_Mask, db 4h + at DMA_UnMask, db 0h + at DMA_Unused, dw 0h + iend + + istruc DMACHANNEL + at DMA_PagePort, dw 83h + at DMA_AdrPort, dw 2h + at DMA_LenPort, dw 3h + at DMA_MaskPort, dw 0ah + at DMA_ModePort, dw 0bh + at DMA_ClearPort, dw 0ch + at DMA_Mask, db 5h + at DMA_UnMask, db 1h + at DMA_Unused, dw 0h + iend + + istruc DMACHANNEL + at DMA_PagePort, dw 81h + at DMA_AdrPort, dw 4h + at DMA_LenPort, dw 5h + at DMA_MaskPort, dw 0ah + at DMA_ModePort, dw 0bh + at DMA_ClearPort, dw 0ch + at DMA_Mask, db 6h + at DMA_UnMask, db 2h + at DMA_Unused, dw 0h + iend + + istruc DMACHANNEL + at DMA_PagePort, dw 82h + at DMA_AdrPort, dw 6h + at DMA_LenPort, dw 7h + at DMA_MaskPort, dw 0ah + at DMA_ModePort, dw 0bh + at DMA_ClearPort, dw 0ch + at DMA_Mask, db 7h + at DMA_UnMask, db 3h + at DMA_Unused, dw 0h + iend + + istruc DMACHANNEL + at DMA_PagePort, dw 8fh + at DMA_AdrPort, dw 0c0h + at DMA_LenPort, dw 0c2h + at DMA_MaskPort, dw 0d4h + at DMA_ModePort, dw 0d6h + at DMA_ClearPort, dw 0d8h + at DMA_Mask, db 4h + at DMA_UnMask, db 0h + at DMA_Unused, dw 0h + iend + + istruc DMACHANNEL + at DMA_PagePort, dw 8bh + at DMA_AdrPort, dw 0c4h + at DMA_LenPort, dw 0c6h + at DMA_MaskPort, dw 0d4h + at DMA_ModePort, dw 0d6h + at DMA_ClearPort, dw 0d8h + at DMA_Mask, db 5h + at DMA_UnMask, db 1h + at DMA_Unused, dw 0h + iend + + istruc DMACHANNEL + at DMA_PagePort, dw 89h + at DMA_AdrPort, dw 0c8h + at DMA_LenPort, dw 0cah + at DMA_MaskPort, dw 0d4h + at DMA_ModePort, dw 0d6h + at DMA_ClearPort, dw 0d8h + at DMA_Mask, db 6h + at DMA_UnMask, db 2h + at DMA_Unused, dw 0h + iend + + istruc DMACHANNEL + at DMA_PagePort, dw 8ah + at DMA_AdrPort, dw 0cch + at DMA_LenPort, dw 0ceh + at DMA_MaskPort, dw 0d4h + at DMA_ModePort, dw 0d6h + at DMA_ClearPort, dw 0d8h + at DMA_Mask, db 7h + at DMA_UnMask, db 3h + at DMA_Unused, dw 0h + iend + + align 4 + +shittable dd 0, 60, 56, 52, 48, 44, 40, 36 + dd 32, 28, 24, 20, 16, 12, 8, 4 + +%ifdef djgpp +section .data +%else +segment _DATA +%endif + + align 4 + +gdc: + %rep CACHESLOTS + istruc CACHESLOT + at GDC_Pos, dd 0 + at GDC_Length, dd 0 + iend + %endrep + + align 4 + +loopcount dd 0 +fractadd dd 0 +integeradd dd 0 +smpend dd 0 +smpsubtract dd 0 +samples dd 0 +totalwork dd 0 +postproc dd 0 +cptr dd 0 +dptr dd 0 +fptr dd 0 +ipminus1 dd 0 +ip0 dd 0 +ip1 dd 0 +ip2 dd 0 +leftvol dd 0 +rightvol dd 0 +SmoothVolL dd 0 +SmoothVolR dd 0 +saved_reg dd 0 + +mix_exec db 0 +gus_dmainprogress db 0 +ac97_buffer0_set db 0 +ac97_buffer1_set db 0 + +%ifdef djgpp +section .text +%else +segment _TEXT +%endif + + align 4 + + ;DMA functions. DMA polling is really fucked up: if reading the + ;position too often (> 100 Hz) one may get bogus values. This is + ;compensated by reading two values, and if their offset is too big or + ;they're outside the buffer, the position is read again. + ; + ;Actually GUS fucks up just in the same way when reading the channel + ;position. Shit, what is wrong with the hardware?! + ; + ;Previously I though that EMM386 causes these fuckups, but no, it + ;wasn't so. However, under DPMI there's no fuckups! + ; + ;It would be really nice & simple to just update one bufferhalf at a + ;time in the soundcard interrupt, but I think it's important to give + ;the user full control of the sound updating, even at the expense of + ;PAIN!!! + + +dma_program_asm: + push EBX + push EDX + push EAX + call dma_program_ + pop EAX + pop EDX + pop EBX + ret + + ; void dma_program(unsigned char mode, unsigned offset, unsigned length) +dma_program_: +_dma_program: + push EAX + push EDX + push EBX + mov EAX, [esp + 4 + 12] + mov EDX, [esp + 8 + 12] + mov EBX, [esp + 12 + 12] + + push ESI + push EDI + push ECX + mov ECX, EAX ;ECX = mode + mov EDI, EDX ;EDI = offset + mov ESI, [_judas_dma] ;Get channel num + cmp ESI, 4 + jae dma16_program + shl ESI, 4 ;16 = dma struc len + add ESI, offset DMAChannels ;Ptr now ready + mov DX, [ESI + DMA_MaskPort] + mov AL, [ESI + DMA_Mask] + out DX, AL ;Mask the DMA channel + xor AL, AL + mov DX, [ESI + DMA_ClearPort] + out DX, AL ;Clear byte ptr. + mov DX, [ESI + DMA_ModePort] + mov AL, CL ;Get mode + or AL, [ESI + DMA_UnMask] ;Or with channel num + out DX, AL ;Set DMA mode + mov DX, [ESI + DMA_LenPort] + dec EBX ;EBX = length + mov AL, BL + out DX, AL ;Set length low and + mov AL, BH ;high bytes + out DX, AL + mov DX, [ESI + DMA_AdrPort] + mov EBX, [_dma_address] ;Get DMA buffer address + add EBX, EDI ;Add offset + mov AL, BL + out DX, AL ;Set offset + mov AL, BH + out DX, AL + mov DX, [ESI + DMA_PagePort] + shr EBX, 16 + mov AL, BL + out DX, AL ;Set page + mov DX, [ESI + DMA_MaskPort] + mov AL, [ESI + DMA_UnMask] + out DX, AL ;Unmask the DMA channel + pop ECX + pop EDI + pop ESI + + pop EBX + pop EDX + pop EAX + ret + +dma16_program: shl ESI, 4 ;16 = dma struc len + add ESI, offset DMAChannels ;Ptr now ready + mov DX, [ESI + DMA_MaskPort] + mov AL, [ESI + DMA_Mask] + out DX, AL ;Mask the DMA channel + xor AL, AL + mov DX, [ESI + DMA_ClearPort] + out DX, AL ;Clear byte ptr. + mov DX, [ESI + DMA_ModePort] + mov AL, CL ;Get mode + or AL, [ESI + DMA_UnMask] ;Or with channel num + out DX, AL ;Set DMA mode + mov DX, [ESI + DMA_LenPort] + shr EBX, 1 + dec EBX + mov AL, BL + out DX, AL ;Set length low and + mov AL, BH ;high bytes + out DX, AL + mov DX, [ESI + DMA_AdrPort] + mov EBX, [_dma_address] ;Get DMA buffer address + add EBX, EDI ;Add offset + shr EBX, 1 ;Because of 16-bitness + mov AL, BL + out DX, AL ;Set offset + mov AL, BH + out DX, AL + mov DX, [ESI + DMA_PagePort] + shr EBX, 15 + mov AL, BL + out DX, AL ;Set page + mov DX, [ESI + DMA_MaskPort] + mov AL, [ESI + DMA_UnMask] + out DX, AL ;Unmask the DMA channel + pop ECX + pop EDI + pop ESI + + pop EBX + pop EDX + pop EAX + ret + +dma_query_: cli + push EBX + push ECX + push EDX + push ESI + mov ESI, [_judas_dma] + cmp ESI, 4 + jae dma16_query + shl ESI, 4 ;16 = dma struc len + add ESI, offset DMAChannels ;Ptr now ready + xor EAX, EAX + mov DX, [ESI + DMA_ClearPort] ;Clear flip-flop + out DX, AL + mov DX, [ESI + DMA_AdrPort] +dqloop1: xor EAX, EAX + in AL, DX + xchg AL, AH + in AL, DX + xchg AL, AH + sub AX, word [_dma_address] ;Subtract page offset + mov EBX, EAX ;EBX = position 1 + in AL, DX + xchg AL, AH + in AL, DX + xchg AL, AH + sub AX, word [_dma_address] ;Subtract page offset + mov ECX, EAX ;ECX = position 2 + cmp EBX, [_judas_bufferlength] ;Outside buffer? + jae dqloop1 + mov EAX, EBX + sub EAX, ECX + cmp EAX, 64 + jg dqloop1 + cmp EAX, -64 + jl dqloop1 + mov EAX, EBX + pop ESI + pop EDX + pop ECX + pop EBX + sti + ret +dma16_query: shl ESI, 4 ;16 = dma struc len + add ESI, offset DMAChannels ;Ptr now ready + mov DX, [ESI + DMA_ClearPort] ;Clear flip-flop + xor EAX, EAX + out DX, AL + mov DX, [ESI + DMA_AdrPort] + mov ESI, [_dma_address] + and ESI, 1ffffh +dqloop2: xor EAX, EAX + in AL, DX + xchg AL, AH + in AL, DX + xchg AL, AH + shl EAX, 1 + sub EAX, ESI ;Subtract page offset + mov EBX, EAX ;EBX = position 1 + xor EAX, EAX + in AL, DX + xchg AL, AH + in AL, DX + xchg AL, AH + shl EAX, 1 + sub EAX, ESI ;Subtract page offset + mov ECX, EAX ;ECX = position 2 + cmp EBX, [_judas_bufferlength] ;Outside buffer? + jae dqloop2 + mov EAX, EBX + sub EAX, ECX + cmp EAX, 64 + jg dqloop2 + cmp EAX, -64 + jl dqloop2 + mov EAX, EBX + pop ESI + pop EDX + pop ECX + pop EBX + sti + ret + + ;Generic send-EOI routine. + +send_eoi: inc dword [_judas_irqcount] + cmp dword [_judas_irq], 8 + jae highirq + mov AL, 20h + out 20h, AL + ret +highirq: mov AL, 20h + out 0a0h, AL + mov AL, 00001011b + out 0a0h, AL + in AL, 0a0h + or AL, AL + jnz sb_noeoi + mov AL, 20h + out 20h, AL +sb_noeoi: ret + + ;Soundblaster IRQ handlers, one for singlecycle, one for 8bit autoinit + ;and one for 16bit autoinit. + +sb_handler_: +_sb_handler: + pushad + push DS + mov AX, [CS:_judas_ds] + mov DS, AX + mov EDX, [_judas_port] + add EDX, 0eh + in AL, DX + sub EDX, 2h +sb_wait1: in AL, DX + or AL, AL + js sb_wait1 + mov AL, 14h + out DX, AL +sb_wait2: in AL, DX + or AL, AL + js sb_wait2 + mov AX, 0fff0h + out DX, AL +sb_wait3: in AL, DX + or AL, AL + js sb_wait3 + mov AL, AH + out DX, AL + sti + call send_eoi + pop DS + popad + iretd + +sb_aihandler_: +_sb_aihandler: + pushad + push DS + mov AX, [CS:_judas_ds] + mov DS, AX + mov EDX, [_judas_port] + add EDX, 0eh + in AL, DX + sti + call send_eoi + pop DS + popad + iretd + +sb16_handler_: +_sb16_handler: + pushad + push DS + mov AX, [CS:_judas_ds] + mov DS, AX + mov EDX, [_judas_port] + add EDX, 0fh + in AL, DX + sti + call send_eoi + pop DS + popad + iretd + + ;GUS IRQ handler + +gus_handler_: +_gus_handler: + pushad + push DS + mov AX, [CS:_judas_ds] + mov DS, AX +gus_irqloop: mov EDX, [_judas_port] + add EDX, GF1_IRQ_STAT + in AL, DX + test AL, DMA_TC_IRQ + jz near gus_irqdone + mov EDX, [_judas_port] ;Acknowledge the DMA + add EDX, GF1_REG_SELECT ;interrupt + mov AL, DMA_CONTROL + out DX, AL + mov EDX, [_judas_port] + add EDX, GF1_DATA_HI + in AL, DX + dec byte [gus_dmainprogress] + mov ESI, offset gdc + mov ECX, CACHESLOTS +gusirq_seekslot:cmp dword [ESI + GDC_Length], 0 + jnz gusirq_slotfound + add ESI, CACHESLOT_size ;type CACHESLOT in TASM + dec ECX + jnz gusirq_seekslot + jmp gus_irqloop +gusirq_slotfound: + mov EBX, [ESI + GDC_Pos] ;DMA offset + shr EBX, 4 + mov CL, DMA_ENABLE | DMA_R0 | DMA_TWOS_COMP | DMA_IRQ_ENABLE + test byte [_judas_mixmode], SIXTEENBIT + jz gus_dma_eight2 + mov CL, DMA_ENABLE | DMA_R0 | DMA_DATA_16 | DMA_IRQ_ENABLE +gus_dma_eight2: cmp dword [_judas_dma], 4 + jb gus_nohighdma2 + or CL, DMA_WIDTH_16 + shr EBX, 1 +gus_nohighdma2: mov EDX, [_judas_port] + add EDX, GF1_REG_SELECT + mov AL, SET_DMA_ADDRESS + out DX, AL + mov EDX, [_judas_port] + add EDX, GF1_DATA_LOW + mov AX, BX + out DX, AX + mov EDX, [_judas_port] + add EDX, GF1_REG_SELECT + mov AL, DMA_CONTROL + out DX, AL + mov EDX, [_judas_port] + add EDX, GF1_DATA_HI + mov AL, CL + out DX, AL + mov EBX, [ESI + GDC_Length] + mov dword [ESI + GDC_Length], 0 + mov EDX, [ESI + GDC_Pos] ;DMA offset + mov EAX, 48h ;DMA mode + call dma_program_asm ;Program it! + jmp gus_irqloop +gus_irqdone: sti + call send_eoi + pop DS + popad + iretd + + ;Various GUS functions + +; to be called from C with stack calling convention +gus_peek_: +_gus_peek: + mov EAX, [esp + 4] + push EBX + mov EBX, EAX + mov AL, SET_DRAM_LOW + mov EDX, [_judas_port] + add EDX, GF1_REG_SELECT + out DX, AL + mov AX, BX + mov EDX, [_judas_port] + add EDX, GF1_DATA_LOW + out DX, AX + mov AL, SET_DRAM_HIGH + mov EDX, [_judas_port] + add EDX, GF1_REG_SELECT + out DX, AL + shr EBX, 16 + mov AL, BL + mov EDX, [_judas_port] + add EDX, GF1_DATA_HI + out DX, AL + mov EDX, [_judas_port] + add EDX, GF1_DRAM + in AL, DX + pop EBX + ret + +gus_peek_asm: + push EBX + mov EBX, EAX + mov AL, SET_DRAM_LOW + mov EDX, [_judas_port] + add EDX, GF1_REG_SELECT + out DX, AL + mov AX, BX + mov EDX, [_judas_port] + add EDX, GF1_DATA_LOW + out DX, AX + mov AL, SET_DRAM_HIGH + mov EDX, [_judas_port] + add EDX, GF1_REG_SELECT + out DX, AL + shr EBX, 16 + mov AL, BL + mov EDX, [_judas_port] + add EDX, GF1_DATA_HI + out DX, AL + mov EDX, [_judas_port] + add EDX, GF1_DRAM + in AL, DX + pop EBX + ret + +; to be called from C with stack calling convention +gus_poke_: +_gus_poke: + push EAX + push EDX + mov EAX, [esp + 4 + 8] + mov EDX, [esp + 8 + 8] + push EBX + push EDX + mov EBX, EAX + mov AL, SET_DRAM_LOW + mov EDX, [_judas_port] + add EDX, GF1_REG_SELECT + out DX, AL + mov AX, BX + mov EDX, [_judas_port] + add EDX, GF1_DATA_LOW + out DX, AX + mov AL, SET_DRAM_HIGH + mov EDX, [_judas_port] + add EDX, GF1_REG_SELECT + out DX, AL + shr EBX, 16 + mov AL, BL + mov EDX, [_judas_port] + add EDX, GF1_DATA_HI + out DX, AL + mov EDX, [_judas_port] + add EDX, GF1_DRAM + pop EAX + out DX, AL + pop EBX + pop EDX + pop EAX + ret + +gus_poke_asm: + push EBX + push EDX + mov EBX, EAX + mov AL, SET_DRAM_LOW + mov EDX, [_judas_port] + add EDX, GF1_REG_SELECT + out DX, AL + mov AX, BX + mov EDX, [_judas_port] + add EDX, GF1_DATA_LOW + out DX, AX + mov AL, SET_DRAM_HIGH + mov EDX, [_judas_port] + add EDX, GF1_REG_SELECT + out DX, AL + shr EBX, 16 + mov AL, BL + mov EDX, [_judas_port] + add EDX, GF1_DATA_HI + out DX, AL + mov EDX, [_judas_port] + add EDX, GF1_DRAM + pop EAX + out DX, AL + pop EBX + ret + +gus_startchannels_: +_gus_startchannels: + push EBX ;This routine starts + push ECX ;the two channels + push EDX ;as quickly as possible. + mov EBX, [_judas_port] + add EBX, GF1_PAGE + mov ECX, [_judas_port] + add ECX, GF1_DATA_HI + mov EDX, [_judas_port] + add EDX, GF1_REG_SELECT + mov AL, SET_CONTROL + out DX, AL + test byte [_judas_mixmode], SIXTEENBIT + jz gus_start8 + mov EDX, EBX + mov AL, 0 + out DX, AL + mov EDX, ECX + mov AL, VC_LOOP_ENABLE | VC_DATA_TYPE + out DX, AL + mov EDX, EBX + mov AL, 1 + out DX, AL + mov EDX, ECX + mov AL, VC_LOOP_ENABLE | VC_DATA_TYPE + out DX, AL + pop EDX + pop ECX + pop EBX + ret +gus_start8: mov EDX, EBX + xor AL, AL + out DX, AL + mov EDX, ECX + mov AL, VC_LOOP_ENABLE + out DX, AL + mov EDX, EBX + mov AL, 1 + out DX, AL + mov EDX, ECX + mov AL, VC_LOOP_ENABLE + out DX, AL + pop EDX + pop ECX + pop EBX + ret + + +; to be called from assembly for DJGPP (note - didn't have time to make it faster and more clear :) +gus_dmaprogram_asm: + push EDX + push EAX + call gus_dmaprogram_ + pop EAX + pop EDX + ret + +; to be called from C for DJGPP stack based convention +gus_dmaprogram_: +_gus_dmaprogram: + push EAX + push EDX + mov EAX, [esp + 4 + 8] + mov EDX, [esp + 8 + 8] + or EDX, EDX ;Zero length fucks up! + jz gus_skipdma + pushad + cli + cmp byte [gus_dmainprogress], 0 ;Do we have to cache the + je gus_dontcache ;block? + mov EBX, offset gdc + mov ECX, CACHESLOTS +gus_seekslot: cmp dword [EBX + GDC_Length], 0 + je gus_slotfound + add EBX, CACHESLOT_size ;type CACHESLOT in TASM + dec ECX + jnz gus_seekslot + sti + popad +gus_skipdma: pop EDX + pop EAX + ret +gus_slotfound: mov [EBX + GDC_Pos], EAX + mov [EBX + GDC_Length], EDX + inc byte [gus_dmainprogress] + sti + popad + pop EDX + pop EAX + ret +gus_dontcache: sti + inc byte [gus_dmainprogress] + mov ESI, EAX + mov EDI, EDX + mov EBX, ESI ;DMA offset + shr EBX, 4 + mov CL, DMA_ENABLE | DMA_R0 | DMA_TWOS_COMP | DMA_IRQ_ENABLE + test byte [_judas_mixmode], SIXTEENBIT + jz gus_dma_eight + mov CL, DMA_ENABLE | DMA_R0 | DMA_DATA_16 | DMA_IRQ_ENABLE +gus_dma_eight: cmp dword [_judas_dma], 4 + jb gus_nohighdma + or CL, DMA_WIDTH_16 + shr EBX, 1 +gus_nohighdma: mov EDX, [_judas_port] + add EDX, GF1_REG_SELECT + mov AL, SET_DMA_ADDRESS + out DX, AL + mov EDX, [_judas_port] + add EDX, GF1_DATA_LOW + mov EAX, EBX + out DX, AX + mov EDX, [_judas_port] + add EDX, GF1_REG_SELECT + mov AL, DMA_CONTROL + out DX, AL + mov EDX, [_judas_port] + add EDX, GF1_DATA_HI + mov AL, CL + out DX, AL + mov EBX, EDI ;DMA length + mov EDX, ESI ;DMA offset + mov EAX, 48h ;DMA mode + call dma_program_asm ;Program it! + popad + pop EDX + pop EAX + ret + +gus_dmainit_: +_gus_dmainit: + cli + mov byte [gus_dmainprogress], 0 + push EAX + push EDX + mov EDX, [_judas_port] ;Acknowledge the DMA + add EDX, GF1_REG_SELECT ;interrupt + mov AL, DMA_CONTROL + out DX, AL + mov EDX, [_judas_port] + add EDX, GF1_DATA_HI + in AL, DX + mov EAX, offset gdc +diloop: mov dword [EAX + GDC_Pos], 0 + mov dword [EAX + GDC_Length], 0 + add EAX, CACHESLOT_size ;type CACHESLOT + cmp EAX, offset gdc + CACHESLOTS * CACHESLOT_size ;type CACHESLOT in TASM + jne diloop + pop EDX + pop EAX + sti + ret + +gus_dmawait_: +_gus_dmawait: + mov EAX, 200000h ;Timeout counter +gus_dmawaitloop:cmp byte [gus_dmainprogress], 0 ;(might time out if + je gus_dmadone ;there is a DMA + dec EAX ;conflict.) This routine + jnz gus_dmawaitloop ;is used just for click +gus_dmadone: ret ;removal! + +gus_getpos: push EBX + push EDX + mov EDX, [_judas_port] ;Get the channel + add EDX, GF1_PAGE ;playing position to + xor AL, AL ;know where we'll mix + out DX, AL + mov EDX, [_judas_port] + add EDX, GF1_REG_SELECT + mov AL, GET_ACC_HIGH + out DX, AL + mov EDX, [_judas_port] + add EDX, GF1_DATA_LOW + in AX, DX + and EAX, 8191 + shl EAX, 7 + mov EBX, EAX + mov EDX, [_judas_port] + add EDX, GF1_REG_SELECT + mov AL, GET_ACC_LOW + out DX, AL + mov EDX, [_judas_port] + add EDX, GF1_DATA_LOW + in AX, DX + shr AX, 9 + or EAX, EBX + test byte [_judas_mixmode], SIXTEENBIT + jz ggp_not16 + shl EAX, 1 +ggp_not16: pop EDX + pop EBX + ret + + +;***************************************************************************** +; Intel ICH AC97 stuff +;***************************************************************************** +; When CIV == LVI, set LVI <> CIV to never run out of buffers to play. +ac97_updateLVI: + push eax + push edx + cmp dword [_audio_pci + AUDIO_PCI_DEV.mem_mode], 0 ; memory mapped IO? + jne ac97_updateLVI_mem + + mov edx, [_judas_port] + add edx, PO_CIV_REG ; PCM OUT Current Index Value + in ax, dx ; and Last Valid Index + and al, 01fh ; bits 0-5 only (important for SIS) + and ah, 01fh ; bits 0-5 only (important for SIS) + cmp al, ah ; CIV == LVI? + jnz ac97_updateLVI_ok ; no, don't change LVI + call ac97_setNewIndex ; set LVI to something else + jmp short ac97_updateLVI_ok +ac97_updateLVI_mem: + mov edx, [_audio_pci + AUDIO_PCI_DEV.base3] ; NABMBAR for memory mapped IO + add edx, PO_CIV_REG ; PCM OUT Current Index Value + mov ax, [edx] ; and Last Valid Index + and al, 01fh ; bits 0-5 only (important for SIS) + and ah, 01fh ; bits 0-5 only (important for SIS) + cmp al, ah ; CIV == LVI? + jnz ac97_updateLVI_ok ; no, don't change LVI + call ac97_setNewIndex ; set LVI to something else +ac97_updateLVI_ok: + pop edx + pop eax + ret + +; Set the Last Valid Index to 1 less than the Current Index Value, +; so that we never run out of buffers. +ac97_setNewIndex: + push eax + call ac97_getCurrentIndex ; get CIV + dec al ; make LVI != CIV + and al, INDEX_MASK ; make sure new value is 0-31 + call ac97_setLastValidIndex ; write new LVI + pop eax + ret + +; return AL = PCM OUT Current Index Value +ac97_getCurrentIndex: + push edx + cmp dword [_audio_pci + AUDIO_PCI_DEV.mem_mode], 0 ; memory mapped IO? + jne ac97_getCurrentIndex_mem + + mov edx, [_judas_port] + add edx, PO_CIV_REG + in al, dx + jmp short ac97_getCurrentIndex_ok +ac97_getCurrentIndex_mem: + mov edx, [_audio_pci + AUDIO_PCI_DEV.base3] ; NABMBAR for memory mapped IO + add edx, PO_CIV_REG ; PCM OUT Current Index Value + mov ax, [edx] +ac97_getCurrentIndex_ok: + pop edx + ret + +; input AL = PCM OUT Last Valid Index (index to stop on) +ac97_setLastValidIndex: + push edx + cmp dword [_audio_pci + AUDIO_PCI_DEV.mem_mode], 0 ; memory mapped IO? + jne ac97_setLastValidIndex_mem + + mov edx, [_judas_port] + add edx, PO_LVI_REG + out dx, al + jmp short ac97_setLastValidIndex_ok +ac97_setLastValidIndex_mem: + mov edx, [_audio_pci + AUDIO_PCI_DEV.base3] ; NABMBAR for memory mapped IO + add edx, PO_LVI_REG + mov [edx], al ; and Last Valid Index +ac97_setLastValidIndex_ok: + pop edx + ret + + +;***************************************************************************** +; Intel HDA stuff +;***************************************************************************** +hda_get_lpib: + + push edx + + mov edx, dword [_audio_pci + AUDIO_PCI_DEV.base0] + add edx, HDA_SDO0LPIB + mov eax, [edx] + + pop edx + ret + +hda_dma_start: + push edx + + mov edx, dword [_audio_pci + AUDIO_PCI_DEV.base0] + add edx, HDA_SDO0CTL + mov eax, [edx] + or eax, SD_CTL_DMA_START + mov [edx], eax + + pop edx + ret + + + ;General DMAbuffer update routine (call either from main program or + ;within your timer interrupt) + +judas_update_: +_judas_update: + cmp dword [_judas_device], DEV_NOSOUND + je near judas_gotohell + cmp dword [_judas_device], DEV_FILE + je near judas_gotohell + cmp byte [mix_exec], 0 + jne near judas_gotohell + cmp byte [_judas_initialized], 0 + je near judas_gotohell + inc byte [mix_exec] + pushad + cmp dword [_judas_device], DEV_GUS + je near updategus ;This is a different story + cmp dword [_judas_device], DEV_AC97 + je near updateac97 ; audio_pci update for AC97 + cmp dword [_judas_device], DEV_HDA + je near updatehda ; audio_pci update for HDA + call dma_query_ + ;Must be aligned on 8 + and EAX, [_judas_buffermask] ;samples (unrolling!) + mov EBX, [_judas_mixpos] ;This is the old pos + cmp EAX, EBX + je judas_donothing + jb judas_wrap +judas_normal: mov [_judas_mixpos], EAX + mov EDX, EAX + sub EDX, EBX ;EDX = length to mix + mov EAX, EBX ;EAX = pos. to mix + add EAX, [_dma_address] + sub EAX, [___djgpp_base_address] + call dword [_judas_mixersys] +judas_donothing:popad + dec byte [mix_exec] +judas_gotohell: ret +judas_wrap: mov [_judas_mixpos], EAX + mov EAX, EBX ;Mix to buffer end + mov EDX, [_judas_bufferlength] + sub EDX, EBX + add EAX, [_dma_address] + sub EAX, [___djgpp_base_address] + call dword [_judas_mixersys] + mov EAX, [_dma_address] ;Then to start + mov EDX, [_judas_mixpos] + or EDX, EDX + jz judas_donothing + sub EAX, [___djgpp_base_address] + call dword [_judas_mixersys] + jmp judas_donothing + + +updateac97: call ac97_updateLVI ; set CIV != LVI + call ac97_getCurrentIndex +update_hda_buffers: + test al, 1 ; check parity + jz ac97_playing_buffer0 + + ; playing buffer 1 -> refresh buffer 0 (Bus Master DMA) +ac97_playing_buffer1: + cmp byte [ac97_buffer0_set], 1 ; is buffer 0 + je judas_donothing ; already refreshed + mov eax, [_audio_pci + AUDIO_PCI_DEV.pcmout_buffer0] ; buffer 0 address + mov edx, [_judas_bufferlength] ; buffer 0 size + sub eax, [___djgpp_base_address] + call dword [_judas_mixersys] + mov byte [ac97_buffer0_set], 1 ; set buffer 0 + mov byte [ac97_buffer1_set], 0 ; as refreshed + jmp judas_donothing + + ; playing buffer 0 -> refresh buffer 1 (Bus Master DMA) +ac97_playing_buffer0: + cmp byte [ac97_buffer1_set], 1 ; is buffer 1 + je near judas_donothing ; already refreshed + mov eax, [_audio_pci + AUDIO_PCI_DEV.pcmout_buffer1] ; buffer 1 address + mov edx, [_judas_bufferlength] ; buffer 1 size + sub eax, [___djgpp_base_address] + call dword [_judas_mixersys] + mov byte [ac97_buffer1_set], 1 ; set buffer 1 + mov byte [ac97_buffer0_set], 0 ; as refreshed + jmp judas_donothing + + +updatehda: mov eax, [_hda_lpib] + or eax, eax + jnz hda_update_civ + mov eax, [_hda_civ] + or eax, eax + jnz hda_update_civ + call hda_dma_start ; 1st time run, start the DMA engine + +hda_update_civ: + call hda_get_lpib ; get LPIB + cmp eax, dword [_hda_lpib] ; compare wih last LPIB position + jae hda_skip_civ ; if no wrap around don't update CIV + + inc dword [_hda_civ] + cmp dword [_hda_civ], 32 + jne hda_skip_civ + mov dword [_hda_civ], 0 + +hda_skip_civ: + mov [_hda_lpib], eax + mov eax, [_hda_civ] + jmp update_hda_buffers ; same as AC97 on next step + + +updategus: cmp byte [gus_dmainprogress], CACHESLOTS - 4 ;Is there too many + jb ug_notstuck ;DMA's waiting? I mean, + call gus_dmainit_ ;maybe WIN95 has stuck + ;the card somehow. In + ;that case, release all + ;waiting DMAs manually! +ug_notstuck: cli + test byte [_judas_mixmode], STEREO + jz near updategus_mono +ipc_s: xor EAX, EAX + call gus_peek_asm + mov DL, AL + mov EAX, [_judas_bufferlength] + shr EAX, 1 + call gus_poke_asm + mov EAX, 1 + call gus_peek_asm + mov DL, AL + mov EAX, [_judas_bufferlength] + shr EAX, 1 + inc EAX + call gus_poke_asm + mov EAX, [_judas_bufferlength] + shr EAX, 1 + add EAX, 32 + call gus_peek_asm + mov DL, AL + mov EAX, [_judas_bufferlength] + add EAX, 32 + call gus_poke_asm + mov EAX, [_judas_bufferlength] + shr EAX, 1 + add EAX, 33 + call gus_peek_asm + mov DL, AL + mov EAX, [_judas_bufferlength] + add EAX, 33 + call gus_poke_asm + mov EDX, [_judas_bufferlength] + shr EDX, 1 +ugs_shitloop: call gus_getpos + mov EBX, EAX + call gus_getpos + mov ECX, EAX + cmp EBX, EDX + jae ugs_shitloop + mov EAX, EBX + sub EAX, ECX + cmp EAX, 64 + jg ugs_shitloop + cmp EAX, -64 + jl ugs_shitloop + mov EAX, EBX + sti + and EAX, [_judas_buffermask] + mov EBX, [_judas_mixpos] ;EBX = old mixpos + cmp EAX, EBX + je near judas_donothing + jb updategus_wrap + mov [_judas_mixpos], EAX ;New "oldpos" + mov EDX, EAX + sub EDX, EBX ;How much to mix + mov EAX, EBX ;Where to mix + push EAX + push EDX + shl EDX, 1 + add EAX, [_dma_address] + sub EAX, [___djgpp_base_address] + call dword [_judas_mixersys] + pop EDX + pop EAX + call gus_dmaprogram_asm + add EAX, 32 + mov EBX, [_judas_bufferlength] + shr EBX, 1 + add EAX, EBX + call gus_dmaprogram_asm + jmp judas_donothing +updategus_wrap: mov [_judas_mixpos], EAX ;Mix first to buffer + mov EAX, EBX ;end, then to start + mov EDX, [_judas_bufferlength] + shr EDX, 1 + sub EDX, EBX + push EAX + push EDX + shl EDX, 1 + add EAX, [_dma_address] + sub EAX, [___djgpp_base_address] + call dword [_judas_mixersys] + mov EAX, [_dma_address] + mov EDX, [_judas_mixpos] + shl EDX, 1 + sub EAX, [___djgpp_base_address] + call dword [_judas_mixersys] + pop EDX + pop EAX + call gus_dmaprogram_asm + add EAX, 32 + mov EBX, [_judas_bufferlength] + shr EBX, 1 + add EAX, EBX + call gus_dmaprogram_asm + xor EAX, EAX + mov EDX, [_judas_mixpos] + call gus_dmaprogram_asm + add EAX, 32 + mov EBX, [_judas_bufferlength] + shr EBX, 1 + add EAX, EBX + call gus_dmaprogram_asm + jmp judas_donothing + +updategus_mono: xor EAX, EAX + call gus_peek_asm + mov DL, AL + mov EAX, [_judas_bufferlength] + call gus_poke_asm + mov EAX, 1 + call gus_peek_asm + mov DL, AL + mov EAX, [_judas_bufferlength] + inc EAX + call gus_poke_asm + mov EDX, [_judas_bufferlength] +ugm_shitloop: call gus_getpos + mov EBX, EAX + call gus_getpos + mov ECX, EAX + cmp EBX, EDX + jae ugm_shitloop + mov EAX, EBX + sub EAX, ECX + cmp EAX, 64 + jg ugm_shitloop + cmp EAX, -64 + jl ugm_shitloop + mov EAX, EBX + sti + and EAX, [_judas_buffermask] + mov EBX, [_judas_mixpos] ;EBX = old mixpos + cmp EAX, EBX + je near judas_donothing + jb updategusm_wrap + mov [_judas_mixpos], EAX ;New "oldpos" + mov EDX, EAX + sub EDX, EBX ;How much to mix + mov EAX, EBX ;Where to mix + push EAX + push EDX + add EAX, [_dma_address] + sub EAX, [___djgpp_base_address] + call dword [_judas_mixersys] + pop EDX + pop EAX + call gus_dmaprogram_asm + jmp judas_donothing +updategusm_wrap:mov [_judas_mixpos], EAX ;Mix first to buffer + mov EAX, EBX ;end + mov EDX, [_judas_bufferlength] + sub EDX, EBX + push EAX + push EDX + add EAX, [_dma_address] + sub EAX, [___djgpp_base_address] + call dword [_judas_mixersys] + mov EAX, [_dma_address] + mov EDX, [_judas_mixpos] + sub EAX, [___djgpp_base_address] + call dword [_judas_mixersys] + pop EDX + pop EAX + call gus_dmaprogram_asm + xor EAX, EAX + mov EDX, [_judas_mixpos] + call gus_dmaprogram_asm + jmp judas_donothing + + + + + + + + + +;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ +;³ Fast Mixer ³ +;³ ³ +;³ by Cadaver ³ +;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ + +;First macros... + +%macro getvolume 0 + xor EAX, EAX + mov AX, [ESI + Chn_Vol] + cmp EAX, 64*256 + jbe %%limit + mov EAX, 64*256 +%%limit: xor EBX, EBX + mov BL, [ESI + Chn_MasterVol] + imul EAX, EBX + shr EAX, 6+8 + or AL, AL + jz near zerovolume + mov EBX, EAX +%endmacro + +%macro stereoadjust 0 + shl EBX, 1 ;Stereo can have 2x vol + cmp EBX, 255 ;Check that volume is + jbe %%limit ;within volumetable + mov EBX, 255 ;limits though +%%limit: +%endmacro + +%macro smoothpanadjust 0 + xor EAX, EAX + mov AL, [ESI + Chn_Panning] + mov ECX, EBX + imul ECX, EAX + shr ECX, 7 + neg EAX + add EAX, 256 + imul EBX, EAX + shr EBX, 7 + cmp EBX, 255 + jbe %%limit1 + mov EBX, 255 +%%limit1: cmp ECX, 255 + jbe %%limit2 + mov ECX, 255 +%%limit2: +%endmacro + +%macro mix8_mono_n 1 + mov BL, [ESI] + add EDX, ECX + mov EAX, [EBX * 4] + adc ESI, EBP + add [EDI + 4 * %1], EAX +%endmacro + +%macro mix8_left_n 1 + mov BL, [ESI] + add EDX, ECX + mov EAX, [EBX * 4] + adc ESI, EBP + add [EDI + 8 * %1], EAX +%endmacro + +%macro mix8_middle_n 1 + mov BL, [ESI] + add EDX, ECX + mov EAX, [EBX * 4] + adc ESI, EBP + add [EDI + 8 * %1], EAX + add [EDI + 8 * %1 + 4], EAX +%endmacro + +%macro mix8_right_n 1 + mov BL, [ESI] + add EDX, ECX + mov EAX, [EBX * 4] + adc ESI, EBP + add [EDI + 8 * %1 + 4], EAX +%endmacro + +%macro mix8_smooth_n 1 + mov BL, [ESI] + add EDX, [fractadd] + mov EAX, [EBX * 4] + adc ESI, EBP + mov CL, BL + add [EDI + 8 * %1], EAX + mov EAX, [ECX * 4] + add [EDI + 8 * %1 + 4], EAX +%endmacro + +%macro mix8_mono_i 1 + movsx EAX, byte [ESI+1] + movsx ECX, byte [ESI] + sub EAX, ECX + movzx ECX, DH + imul AX, CX + mov BL, AH + add BL, [ESI] + add DX, word [fractadd + 2] + mov EAX, [EBX * 4] + adc ESI, EBP + add [EDI + 4 * %1], EAX +%endmacro + +%macro mix8_left_i 1 + movsx EAX, byte [ESI+1] + movsx ECX, byte [ESI] + sub EAX, ECX + movzx ECX, DH + imul AX, CX + mov BL, AH + add BL, [ESI] + add DX, word [fractadd + 2] + mov EAX, [EBX * 4] + adc ESI, EBP + add [EDI + 8 * %1], EAX +%endmacro + +%macro mix8_middle_i 1 + movsx EAX, byte [ESI+1] + movsx ECX, byte [ESI] + sub EAX, ECX + movzx ECX, DH + imul AX, CX + mov BL, AH + add BL, [ESI] + add DX, word [fractadd + 2] + mov EAX, [EBX * 4] + adc ESI, EBP + add [EDI + 8 * %1], EAX + add [EDI + 8 * %1 + 4], EAX +%endmacro + +%macro mix8_right_i 1 + movsx EAX, byte [ESI+1] + movsx ECX, byte [ESI] + sub EAX, ECX + movzx ECX, DH + imul AX, CX + mov BL, AH + add BL, [ESI] + add DX, word [fractadd + 2] + mov EAX, [EBX * 4] + adc ESI, EBP + add [EDI + 8 * %1 + 4], EAX +%endmacro + +%macro mix8_smooth_i 1 + movsx EAX, byte [ESI+1] + movsx EBP, byte [ESI] + sub EAX, EBP + movzx EBP, DH + imul AX, BP + mov BL, AH + add BL, [ESI] + add DX, word [fractadd + 2] + mov EAX, [EBX * 4] + adc ESI, [integeradd] + mov CL, BL + add [EDI + 8 * %1], EAX + mov EAX, [ECX * 4] + add [EDI + 8 * %1 + 4], EAX +%endmacro + +%macro mix16_mono_n 1 + movsx EAX, word [ESI * 2] + imul EAX, EBX + sar EAX, 8 + (16 - SIGNIFICANT_BITS_16) + add EDX, ECX + adc ESI, EBP + add [EDI + 4 * %1], EAX +%endmacro + +%macro mix16_left_n 1 + movsx EAX, word [ESI * 2] + imul EAX, EBX + sar EAX, 8 + (16 - SIGNIFICANT_BITS_16) + add EDX, ECX + adc ESI, EBP + add [EDI + 8 * %1], EAX +%endmacro + +%macro mix16_middle_n 1 + movsx EAX, word [ESI * 2] + imul EAX, EBX + sar EAX, 8 + (16 - SIGNIFICANT_BITS_16) + add EDX, ECX + adc ESI, EBP + add [EDI + 8 * %1], EAX + add [EDI + 8 * %1 + 4], EAX +%endmacro + +%macro mix16_right_n 1 + movsx EAX, word [ESI * 2] + imul EAX, EBX + sar EAX, 8 + (16 - SIGNIFICANT_BITS_16) + add EDX, ECX + adc ESI, EBP + add [EDI + 8 * %1 + 4], EAX +%endmacro + +%macro mix16_smooth_n 1 + movsx EAX, word [ESI * 2] + imul EAX, EBX + sar EAX, 8 + (16 - SIGNIFICANT_BITS_16) + add [EDI + 8 * %1], EAX + movsx EAX, word [ESI * 2] + imul EAX, ECX + sar EAX, 8 + (16 - SIGNIFICANT_BITS_16) + add EDX, [fractadd] + adc ESI, EBP + add [EDI + 8 * %1 + 4], EAX +%endmacro + +%macro mix16_mono_i 1 + movsx EAX, word [ESI * 2 + 2] + movsx ECX, word [ESI * 2] + sub EAX, ECX + movzx EBX, DH + imul EAX, EBX + sar EAX, 8 + add EAX, ECX + imul EAX, [leftvol] + sar EAX, 8 + (16 - SIGNIFICANT_BITS_16) + add DX, word [fractadd + 2] + adc ESI, EBP + add [EDI + 4 * %1], EAX +%endmacro + +%macro mix16_left_i 1 + movsx EAX, word [ESI * 2 + 2] + movsx ECX, word [ESI * 2] + sub EAX, ECX + movzx EBX, DH + imul EAX, EBX + sar EAX, 8 + add EAX, ECX + imul EAX, [leftvol] + sar EAX, 8 + (16 - SIGNIFICANT_BITS_16) + add DX, word [fractadd + 2] + adc ESI, EBP + add [EDI + 8 * %1], EAX +%endmacro + +%macro mix16_middle_i 1 + movsx EAX, word [ESI * 2 + 2] + movsx ECX, word [ESI * 2] + sub EAX, ECX + movzx EBX, DH + imul EAX, EBX + sar EAX, 8 + add EAX, ECX + imul EAX, [leftvol] + sar EAX, 8 + (16 - SIGNIFICANT_BITS_16) + add DX, word [fractadd + 2] + adc ESI, EBP + add [EDI + 8 * %1], EAX + add [EDI + 8 * %1 + 4], EAX +%endmacro + +%macro mix16_right_i 1 + movsx EAX, word [ESI * 2 + 2] + movsx ECX, word [ESI * 2] + sub EAX, ECX + movzx EBX, DH + imul EAX, EBX + sar EAX, 8 + add EAX, ECX + imul EAX, [leftvol] + sar EAX, 8 + (16 - SIGNIFICANT_BITS_16) + add DX, word [fractadd + 2] + adc ESI, EBP + add [EDI + 8 * %1 + 4], EAX +%endmacro + +%macro mix16_smooth_i 1 + movsx EAX, word [ESI * 2 + 2] + movsx ECX, word [ESI * 2] + sub EAX, ECX + movzx EBX, DH + imul EAX, EBX + sar EAX, 8 + add EAX, ECX + mov EBX, EAX + imul EAX, [leftvol] + sar EAX, 8 + (16 - SIGNIFICANT_BITS_16) + add [EDI + 8 * %1], EAX + mov EAX, EBX + imul EAX, [rightvol] + sar EAX, 8 + (16 - SIGNIFICANT_BITS_16) + add DX, word [fractadd + 2] + adc ESI, EBP + add [EDI + 8 * %1 + 4], EAX +%endmacro + +%macro mixloop8 3 ; routine, samplesize, ip + mov DX, [ESI + Chn_FractPos] ;Get fractional pos + %if (%3 == 0) ; if (ip == 0) + shl EDX, 16 + %endif + mov EAX, [ESI + Chn_End] ;Get end & endsubtract- + mov [smpend], EAX ;value + sub EAX, [ESI + Chn_Repeat] + mov [smpsubtract], EAX + mov ESI, [ESI + Chn_Pos] ;Get sample position + mov EDI, [dptr] ;Get bufferptr + mov EAX, [samples] ;Fix loopcount & + dec EAX + shr EAX, 4 ;jumpoffset & subtract + inc EAX ;EDI accordingly + mov [loopcount], EAX + mov EAX, [samples] + and EAX, 15 + mov EAX, [EAX * 4 + shittable] + %if (%2 == 4) ; if (samplesize == 4) + sub EDI, EAX + %else + sub EDI, EAX + sub EDI, EAX + %endif + add EAX, offset %%offsettable + jmp [EAX] + + align 16 + +%%offset0: + %1 0 ;routine 0 +%%offset1: + %1 1 ;routine 1 +%%offset2: + %1 2 ;routine 2 +%%offset3: + %1 3 ;routine 3 +%%offset4: + %1 4 ;routine 4 +%%offset5: + %1 5 ;routine 5 +%%offset6: + %1 6 ;routine 6 +%%offset7: + %1 7 ;routine 7 +%%offset8: + %1 8 ;routine 8 +%%offset9: + %1 9 ;routine 9 +%%offseta: + %1 10 ;routine 10 +%%offsetb: + %1 11 ;routine 11 +%%offsetc: + %1 12 ;routine 12 +%%offsetd: + %1 13 ;routine 13 +%%offsete: + %1 14 ;routine 14 +%%offsetf: + %1 15 ;routine 15 + add EDI, 16 * %2 ; samplesize + cmp ESI, [smpend] + jae %%hitend + dec dword [loopcount] + jnz near %%offset0 + mov EAX, [cptr] + mov [EAX + Chn_Pos], ESI + %if (%3 == 0) ; if (ip == 0) + shr EDX, 16 + %endif + mov [EAX + Chn_FractPos], DX + ret +%%hitend: mov EAX, [cptr] + test byte [EAX + Chn_VoiceMode], VM_LOOP + jz %%oneshot +%%subloop: sub ESI, [smpsubtract] + cmp ESI, [smpend] + jae %%subloop + dec dword [loopcount] + jnz near %%offset0 + mov [EAX + Chn_Pos], ESI + %if (%3 == 0) ;if (ip == 0) + shr EDX, 16 + %endif + mov [EAX + Chn_FractPos], DX + ret +%%oneshot: mov byte [EAX + Chn_VoiceMode], VM_OFF + ret + align 4 +%%offsettable: dd offset %%offset0 + dd offset %%offset1 + dd offset %%offset2 + dd offset %%offset3 + dd offset %%offset4 + dd offset %%offset5 + dd offset %%offset6 + dd offset %%offset7 + dd offset %%offset8 + dd offset %%offset9 + dd offset %%offseta + dd offset %%offsetb + dd offset %%offsetc + dd offset %%offsetd + dd offset %%offsete + dd offset %%offsetf +%endmacro + +%macro mixloop16 3 ; routine, samplesize, ip + mov DX, [ESI + Chn_FractPos] ;Get fractional pos + %if (%3 == 0) ; if (ip == 0) + shl EDX, 16 + %endif + mov EAX, [ESI + Chn_End] ;Get end & endsubtract- + shr EAX, 1 + mov [smpend], EAX ;value + mov EAX, [ESI + Chn_End] + sub EAX, [ESI + Chn_Repeat] + shr EAX, 1 + mov [smpsubtract], EAX + mov ESI, [ESI + Chn_Pos] ;Get sample position + shr ESI, 1 + mov EDI, [dptr] ;Get bufferptr + mov EAX, [samples] ;Fix loopcount & + dec EAX + shr EAX, 4 ;jumpoffset & subtract + inc EAX ;EDI accordingly + mov [loopcount], EAX + mov EAX, [samples] + and EAX, 15 + mov EAX, [EAX * 4 + shittable] + %if (%2 == 4) ; if (samplesize == 4) + sub EDI, EAX + %else + sub EDI, EAX + sub EDI, EAX + %endif + add EAX, offset %%offsettable + jmp [EAX] + + align 16 + +%%offset0: + %1 0 ;routine 0 +%%offset1: + %1 1 ;routine 1 +%%offset2: + %1 2 ;routine 2 +%%offset3: + %1 3 ;routine 3 +%%offset4: + %1 4 ;routine 4 +%%offset5: + %1 5 ;routine 5 +%%offset6: + %1 6 ;routine 6 +%%offset7: + %1 7 ;routine 7 +%%offset8: + %1 8 ;routine 8 +%%offset9: + %1 9 ;routine 9 +%%offseta: + %1 10 ;routine 10 +%%offsetb: + %1 11 ;routine 11 +%%offsetc: + %1 12 ;routine 12 +%%offsetd: + %1 13 ;routine 13 +%%offsete: + %1 14 ;routine 14 +%%offsetf: + %1 15 ;routine 15 + add EDI, 16 * %2 ; samplesize + cmp ESI, [smpend] + jae %%hitend + dec dword [loopcount] + jnz near %%offset0 + mov EAX, [cptr] + shl ESI, 1 + mov [EAX + Chn_Pos], ESI + %if (%3 == 0) ; if (ip == 0) + shr EDX, 16 + %endif + mov [EAX + Chn_FractPos], DX + ret +%%hitend: mov EAX, [cptr] + test byte [EAX + Chn_VoiceMode], VM_LOOP + jz %%oneshot +%%subloop: sub ESI, [smpsubtract] + cmp ESI, [smpend] + jae %%subloop + dec dword [loopcount] + jnz near %%offset0 + shl ESI, 1 + mov [EAX + Chn_Pos], ESI + %if (%3 == 0) ; if (ip == 0) + shr EDX, 16 + %endif + mov [EAX + Chn_FractPos], DX + ret +%%oneshot: mov byte [EAX + Chn_VoiceMode], VM_OFF + ret + align 4 +%%offsettable: dd offset %%offset0 + dd offset %%offset1 + dd offset %%offset2 + dd offset %%offset3 + dd offset %%offset4 + dd offset %%offset5 + dd offset %%offset6 + dd offset %%offset7 + dd offset %%offset8 + dd offset %%offset9 + dd offset %%offseta + dd offset %%offsetb + dd offset %%offsetc + dd offset %%offsetd + dd offset %%offsete + dd offset %%offsetf +%endmacro + + ;16bit fast mixer routines start here! + ;This is the main mixing routine, which mixes EDX bytes of sound into + ;address EAX, calling the music player at correct intervals. EDX must + ;be a multiply of (samplesize * 8) because there is an unrolled + ;postprocessing loop. + ;WARNING: This routine destroys every register! + + align 4 + +fmixer_: +_fmixer: + or EDX, EDX ;Check zero length + jz near mix_quit + mov ECX, EDX + test byte [_judas_mixmode], STEREO ;Stereo or mono? + jz mix_noshift1 + shr EDX, 1 +mix_noshift1: test byte [_judas_mixmode], SIXTEENBIT ;8- or 16bit? + jz mix_noshift2 + shr EDX, 1 + shr ECX, 1 +mix_noshift2: mov [samples], EDX ;Save number of samples + mov [totalwork], EDX ;"Total work" counter + mov [fptr], EAX ;Save final destination + shr ECX, 3 + mov [postproc], ECX ;Save clipbuffer size + mov EDI, [_judas_clipbuffer] ;Clear the clipbuffer + mov [dptr], EDI + xor EAX, EAX +mix_clearloop: mov [EDI], EAX + mov [EDI + 4], EAX + mov [EDI + 8], EAX + mov [EDI + 12], EAX + mov [EDI + 16], EAX + mov [EDI + 20], EAX + mov [EDI + 24], EAX + mov [EDI + 28], EAX + add EDI, 32 + dec ECX + jnz mix_clearloop + cmp dword [_judas_player], 0 + jne mix_hardwayloop + call dword [_judas_mixroutine] + jmp mix_firstphasedone +mix_hardwayloop:cmp dword [_judas_bpmcount], 0 ;Time to play? + jne mix_skipplaying + cmp dword [_judas_player], 0 ;Might change in the + je mix_fuckshitup ;middle of a loop + call dword [_judas_player] +mix_fuckshitup: mov EAX, [_judas_mixrate] + mov EBX, 5 + mul EBX + shr EAX, 1 + xor EDX, EDX + movzx EBX, byte [_judas_bpmtempo] + div EBX + mov [_judas_bpmcount], EAX +mix_skipplaying:mov EAX, [totalwork] + cmp EAX, [_judas_bpmcount] + jbe mix_nolimit + mov EAX, [_judas_bpmcount] +mix_nolimit: mov [samples], EAX + call dword [_judas_mixroutine] + mov EAX, [samples] + sub [_judas_bpmcount], EAX + mov EBX, EAX + shl EBX, 2 + test byte [_judas_mixmode], STEREO + jz mix_noshift3 + shl EBX, 1 +mix_noshift3: add [dptr], EBX + sub [totalwork], EAX + jnz near mix_hardwayloop +mix_firstphasedone: + test byte [_judas_mixmode], SIXTEENBIT + jz near mix_8bit_endphase +mix_16bit_endphase: + test byte [_judas_mixmode], STEREO + jz mix_nogusshit1 + cmp dword [_judas_device], DEV_GUS + je near mix_gus16_endphase +mix_nogusshit1: mov EDI, [fptr] + mov EBX, [postproc] + mov ESI, [_judas_clipbuffer] + mov ECX, [_judas_cliptable] + xor EAX, EAX +mix_16bit_endphase_loop: + mov AX, [ESI] + mov AX, [ECX + EAX * 2] + mov [EDI], AX + mov AX, [ESI + 4] + mov AX, [ECX + EAX * 2] + mov [EDI + 2], AX + mov AX, [ESI + 8] + mov AX, [ECX + EAX * 2] + mov [EDI + 4], AX + mov AX, [ESI + 12] + mov AX, [ECX + EAX * 2] + mov [EDI + 6], AX + mov AX, [ESI + 16] + mov AX, [ECX + EAX * 2] + mov [EDI + 8], AX + mov AX, [ESI + 20] + mov AX, [ECX + EAX * 2] + mov [EDI + 10], AX + mov AX, [ESI + 24] + mov AX, [ECX + EAX * 2] + mov [EDI + 12], AX + mov AX, [ESI + 28] + mov AX, [ECX + EAX * 2] + mov [EDI + 14], AX + add ESI, 32 + add EDI, 16 + dec EBX + jnz mix_16bit_endphase_loop +mix_quit: ret +mix_8bit_endphase: + test byte [_judas_mixmode], STEREO + jz mix_nogusshit2 + cmp dword [_judas_device], DEV_GUS + je near mix_gus8_endphase +mix_nogusshit2: mov EDI, [fptr] + mov EBX, [postproc] + mov ESI, [_judas_clipbuffer] + mov ECX, [_judas_cliptable] + xor EAX, EAX +mix_8bit_endphase_loop: + mov AX, [ESI] + mov AL, [ECX + EAX] + mov [EDI], AL + mov AX, [ESI + 4] + mov AL, [ECX + EAX] + mov [EDI + 1], AL + mov AX, [ESI + 8] + mov AL, [ECX + EAX] + mov [EDI + 2], AL + mov AX, [ESI + 12] + mov AL, [ECX + EAX] + mov [EDI + 3], AL + mov AX, [ESI + 16] + mov AL, [ECX + EAX] + mov [EDI + 4], AL + mov AX, [ESI + 20] + mov AL, [ECX + EAX] + mov [EDI + 5], AL + mov AX, [ESI + 24] + mov AL, [ECX + EAX] + mov [EDI + 6], AL + mov AX, [ESI + 28] + mov AL, [ECX + EAX] + mov [EDI + 7], AL + add ESI, 32 + add EDI, 8 + dec EBX + jnz mix_8bit_endphase_loop + jmp mix_quit +mix_gus16_endphase: + mov EDI, [fptr] + mov EBX, [postproc] + mov EDX, [_judas_bufferlength] + shr EDX, 1 + add EDX, EDI + add EDX, 32 + mov ESI, [_judas_clipbuffer] + mov ECX, [_judas_cliptable] + xor EAX, EAX +mix_gus16_endphase_loop: + mov AX, [ESI] + mov AX, [ECX + EAX * 2] + mov [EDI], AX + mov AX, [ESI + 4] + mov AX, [ECX + EAX * 2] + mov [EDX], AX + mov AX, [ESI + 8] + mov AX, [ECX + EAX * 2] + mov [EDI + 2], AX + mov AX, [ESI + 12] + mov AX, [ECX + EAX * 2] + mov [EDX + 2], AX + mov AX, [ESI + 16] + mov AX, [ECX + EAX * 2] + mov [EDI + 4], AX + mov AX, [ESI + 20] + mov AX, [ECX + EAX * 2] + mov [EDX + 4], AX + mov AX, [ESI + 24] + mov AX, [ECX + EAX * 2] + mov [EDI + 6], AX + mov AX, [ESI + 28] + mov AX, [ECX + EAX * 2] + mov [EDX + 6], AX + add ESI, 32 + add EDI, 8 + add EDX, 8 + dec EBX + jnz mix_gus16_endphase_loop + jmp mix_quit +mix_gus8_endphase: + mov EDI, [fptr] + mov EBX, [postproc] + mov EDX, [_judas_bufferlength] + shr EDX, 1 + add EDX, EDI + add EDX, 32 + mov ESI, [_judas_clipbuffer] + mov ECX, [_judas_cliptable] + xor EAX, EAX +mix_gus8_endphase_loop: + mov AX, [ESI] + mov AL, [ECX + EAX] + mov [EDI], AX + mov AX, [ESI + 4] + mov AL, [ECX + EAX] + mov [EDX], AX + mov AX, [ESI + 8] + mov AL, [ECX + EAX] + mov [EDI + 1], AX + mov AX, [ESI + 12] + mov AL, [ECX + EAX] + mov [EDX + 1], AX + mov AX, [ESI + 16] + mov AX, [ECX + EAX] + mov [EDI + 2], AX + mov AX, [ESI + 20] + mov AL, [ECX + EAX] + mov [EDX + 2], AX + mov AX, [ESI + 24] + mov AL, [ECX + EAX] + mov [EDI + 3], AX + mov AX, [ESI + 28] + mov AL, [ECX + EAX] + mov [EDX + 3], AX + add ESI, 32 + add EDI, 4 + add EDX, 4 + dec EBX + jnz mix_gus8_endphase_loop + jmp mix_quit + +normalmix_: +_normalmix: + mov dword [cptr], offset _judas_channel +normalmixloop: call mixchannel + add dword [cptr], CHANNEL_size ;type CHANNEL in TASM + cmp dword [cptr], offset _judas_channel + CHANNELS * CHANNEL_size ;type CHANNEL in TASM + jne normalmixloop + ret + +ipmix_: +_ipmix: + mov dword [cptr], offset _judas_channel +ipmixloop: call ipmixchannel + add dword [cptr], CHANNEL_size ;type CHANNEL in TASM + cmp dword [cptr], offset _judas_channel + CHANNELS * CHANNEL_size ;type CHANNEL + jne ipmixloop + ret + + ;Mixes [samples] of channel [cptr] to buffer at [dptr]. Destroys + ;every register. + +mixchannel: mov ESI, [cptr] + test byte [ESI + Chn_VoiceMode], VM_ON + jz near mixchannel_quit + mov EAX, [ESI + Chn_Freq] ;Get playing speed here + cmp EAX, 535232 ;Highest linear freq + jbe mixchannel_freqok + mov EAX, 535232 +mixchannel_freqok: + mov EDX, EAX ;Don't worry: overflow + shr EDX, 16 ;prevented by check + shl EAX, 16 ;above + div dword [_judas_mixrate] ;DIV is always + mov word [fractadd + 2], AX ;frightening!!! + shr EAX, 16 + mov [integeradd], EAX + test byte [ESI + Chn_VoiceMode], VM_16BIT ;16bit takes the branch + jnz near mixchannel16 ;because it's unusual + test byte [_judas_mixmode], STEREO ;Mono takes the branch + jz near mixchannel_mono ;because it's faster +mixchannel_stereo: + getvolume + cmp byte [ESI + Chn_Panning], 0 ;Left panning? + jne near mc8_notleft + stereoadjust + shl EBX, 8 ;Convert to volumetable + add EBX, [_judas_volumetable] ;ofs. + mov EBP, [integeradd] ;EBP = integeradd + mov ECX, [fractadd] ;ECX = fraction add + mixloop8 mix8_left_n, 8, 0 ;DO IT! +mc8_notleft: cmp byte [ESI + Chn_Panning], 128 ;Middle panning? + jne near mc8_notmiddle + shl EBX, 8 ;Convert to volumetable + add EBX, [_judas_volumetable] ;ofs. + mov EBP, [integeradd] ;EBP = integeradd + mov ECX, [fractadd] ;ECX = fraction add + mixloop8 mix8_middle_n, 8, 0 ;DO IT! +mc8_notmiddle: cmp byte [ESI + Chn_Panning], 255 ;Right panning? + jne near mc8_notright + stereoadjust + shl EBX, 8 ;Convert to volumetable + add EBX, [_judas_volumetable] ;ofs. + mov EBP, [integeradd] ;EBP = integeradd + mov ECX, [fractadd] ;ECX = fraction add + mixloop8 mix8_right_n, 8, 0 ;DO IT! +mc8_notright: smoothpanadjust ;Oh no, smooth panning! + shl EBX, 8 ;Convert to volumetable + add EBX, [_judas_volumetable] ;ofs. + shl ECX, 8 + add ECX, [_judas_volumetable] + mov EBP, [integeradd] ;ECX not available! + mixloop8 mix8_smooth_n, 8, 0 ;But yet we must do it.. +mixchannel_mono:getvolume + shl EBX, 8 ;Convert to volumetable + add EBX, [_judas_volumetable] ;ofs. + mov EBP, [integeradd] ;EBP = integeradd + mov ECX, [fractadd] ;ECX = fraction add + mixloop8 mix8_mono_n, 4, 0 ;DO IT! +mixchannel_quit:ret +mixchannel16: test byte [_judas_mixmode], STEREO ;Mono takes the branch + jz near mixchannel16_mono ;because it's faster +mixchannel16_stereo: + getvolume + cmp byte [ESI + Chn_Panning], 0 ;Left panning? + jne near mc16_notleft + stereoadjust + mov ECX, [fractadd] ;ECX = fraction add + mov EBP, [integeradd] ;EBP = integeradd + mixloop16 mix16_left_n, 8, 0 ;DO IT! +mc16_notleft: cmp byte [ESI + Chn_Panning], 128 ;Middle panning? + jne near mc16_notmiddle + mov ECX, [fractadd] ;ECX = fraction add + mov EBP, [integeradd] ;EBP = integeradd + mixloop16 mix16_middle_n, 8, 0 ;DO IT! +mc16_notmiddle: cmp byte [ESI + Chn_Panning], 255 ;Right panning? + jne near mc16_notright + stereoadjust + mov ECX, [fractadd] ;ECX = fraction add + mov EBP, [integeradd] ;EBP = integeradd + mixloop16 mix16_right_n, 8, 0 ;DO IT! +mc16_notright: smoothpanadjust ;Oh no, smooth panning! + mov EBP, [integeradd] + mixloop16 mix16_smooth_n, 8, 0 ;But yet we must do it.. +mixchannel16_mono: + getvolume + mov ECX, [fractadd] ;ECX = fraction add + mov EBP, [integeradd] ;EBP = integeradd + mixloop16 mix16_mono_n, 4, 0 ;DO IT! +mixchannel16_quit: + ret + + ;Mixes [samples] of channel [cptr] to buffer at [dptr] with + ;interpolation. Destroys every register. + +ipmixchannel: mov ESI, [cptr] + test byte [ESI + Chn_VoiceMode], VM_ON + jz near ipmixchannel_quit + mov EAX, [ESI + Chn_Freq] ;Get playing speed here + cmp EAX, 535232 ;Highest linear freq + jbe ipmixchannel_freqok + mov EAX, 535232 +ipmixchannel_freqok: + mov EDX, EAX + shr EDX, 16 + shl EAX, 16 + div dword [_judas_mixrate] + mov word [fractadd + 2], AX + shr EAX, 16 + mov [integeradd], EAX + test byte [ESI + Chn_VoiceMode], VM_16BIT ;16bit takes the branch + jnz near ipmixchannel16 ;because it's unusual + test byte [_judas_mixmode], STEREO ;Mono takes the branch + jz near ipmixchannel_mono ;because it's faster +ipmixchannel_stereo: + getvolume + cmp byte [ESI + Chn_Panning], 0 ;Left panning? + jne near imc8_notleft + stereoadjust + shl EBX, 8 ;Convert to volumetable + add EBX, [_judas_volumetable] ;ofs. + mov EBP, [integeradd] ;EBP = integeradd + mixloop8 mix8_left_i, 8, 1 ;DO IT! +imc8_notleft: cmp byte [ESI + Chn_Panning], 128 ;Middle panning? + jne near imc8_notmiddle + shl EBX, 8 ;Convert to volumetable + add EBX, [_judas_volumetable] ;ofs. + mov EBP, [integeradd] ;EBP = integeradd + mixloop8 mix8_middle_i, 8, 1 ;DO IT! +imc8_notmiddle: cmp byte [ESI + Chn_Panning], 255 ;Right panning? + jne near imc8_notright + stereoadjust + shl EBX, 8 ;Convert to volumetable + add EBX, [_judas_volumetable] ;ofs. + mov EBP, [integeradd] ;EBP = integeradd + mixloop8 mix8_right_i, 8, 1 ;DO IT! +imc8_notright: smoothpanadjust ;Oh no, smooth panning! + shl EBX, 8 ;Convert to volumetable + add EBX, [_judas_volumetable] ;ofs. + shl ECX, 8 + add ECX, [_judas_volumetable] + mixloop8 mix8_smooth_i, 8, 1 +ipmixchannel_mono:getvolume + shl EBX, 8 ;Convert to volumetable + add EBX, [_judas_volumetable] ;ofs. + mov EBP, [integeradd] ;EBP = integeradd + mixloop8 mix8_mono_i, 4, 1 ;DO IT! +ipmixchannel_quit:ret +ipmixchannel16: test byte [_judas_mixmode], STEREO ;Mono takes the branch + jz near ipmixchannel16_mono ;because it's faster +ipmixchannel16_stereo: + getvolume + cmp byte [ESI + Chn_Panning], 0 ;Left panning? + jne near imc16_notleft + stereoadjust + mov [leftvol], EBX + mov EBP, [integeradd] ;EBP = integeradd + mixloop16 mix16_left_i, 8, 1 ;DO IT! +imc16_notleft: cmp byte [ESI + Chn_Panning], 128 ;Middle panning? + jne near imc16_notmiddle + mov [leftvol], EBX + mov EBP, [integeradd] ;EBP = integeradd + mixloop16 mix16_middle_i, 8, 1 ;DO IT! +imc16_notmiddle:cmp byte [ESI + Chn_Panning], 255 ;Right panning? + jne near imc16_notright + stereoadjust + mov [leftvol], EBX + mov EBP, [integeradd] ;EBP = integeradd + mixloop16 mix16_right_i, 8, 1 ;DO IT! +imc16_notright: smoothpanadjust ;Oh no, smooth panning! + mov [leftvol], EBX + mov [rightvol], ECX + mov EBP, [integeradd] + mixloop16 mix16_smooth_i, 8, 1 ;But yet we must do it.. +ipmixchannel16_mono: + getvolume + mov [leftvol], EBX + mov EBP, [integeradd] ;EBP = integeradd + mixloop16 mix16_mono_i, 4, 1 ;DO IT! +ipmixchannel16_quit: + ret + +zerovolume: mov EBP, [samples] + mov EBX, [ESI + Chn_Pos] + mov ECX, EBX + shr EBX, 16 + shl ECX, 16 + mov CX, [ESI + Chn_FractPos] + mov EAX, [ESI + Chn_Freq] + mov EDX, EAX + shr EDX, 16 + shl EAX, 16 + div dword [_judas_mixrate] ;EAX = mixrate + test byte [ESI + Chn_VoiceMode], VM_16BIT ;If 16bit then double + jz zerovolume_not16bit ;the count + shl EBP, 1 +zerovolume_not16bit: + mul EBP ;EDX:EAX = pos. add + add ECX, EAX ;Add low part + adc EBX, EDX ;Add high part + mov [ESI + Chn_FractPos], CX ;Save fractpos + shl EBX, 16 ;(won't change now) + shr ECX, 16 ;Now shift back: ECX + or ECX, EBX ;is integer pos + test byte [ESI + Chn_VoiceMode], VM_16BIT ;Final adjust for 16bit + jz zerovolume_no16bitadjust + and ECX, 0fffffffeh +zerovolume_no16bitadjust: + test byte [ESI + Chn_VoiceMode], VM_LOOP ;Is it looped? + jnz zerovolume_looped + cmp ECX, [ESI + Chn_End] + jae zerovolume_oneshot_end +zerovolume_ready: + mov [ESI + Chn_Pos], ECX ;Store pos + ret +zerovolume_oneshot_end: + mov byte [ESI + Chn_VoiceMode], VM_OFF + ret +zerovolume_looped: + mov EAX, [ESI + Chn_End] + sub EAX, [ESI + Chn_Repeat] ;EAX = subtract value +zerovolume_looped_loop: + cmp ECX, [ESI + Chn_End] + jb zerovolume_ready + sub ECX, EAX + jmp zerovolume_looped_loop + + + + + + + + + +;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ +;³ Quality Mixer ³ +;³ ³ +;³ by Yehar ³ +;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ +; +;32 bit mixing with 30 significant bits. 16 bit interpolation for both 8 and 16 +;bit samples. Cubic (8192x) and linear (32768x) interpolation aka oversampling. +;14 bit volume. Zero-level reposition & volume change smoothing click removal. +; +;Don't try to read for educational purposes =)))) It's just too shitty & lazy! + + ;32bit quality mixer main routine starts here. This is separated from + ;the usual mixer because of different postprocessing etc. This is an + ;alternative main mixing routine, which mixes EDX bytes of sound into + ;address EAX, calling the music player at correct intervals. + ;WARNING: This routine destroys every register! + +qmixer_: +_qmixer: + test EDX, EDX ;Buffer size is zero -> nothing to do + jz near qmixer_quit + test byte [_judas_mixmode], SIXTEENBIT ;16/8bit? 16 means half + jz qmixer_noshift2 ;the number of samples. + shr EDX, 1 ;It is 16 bit! +qmixer_noshift2: + test byte [_judas_mixmode], STEREO ;Stereo/mono? Stereo means half + jz qmixer_noshift1 ;the number of samples. + shr EDX, 1 ;It is stereo! +qmixer_noshift1: + mov [fptr], EAX ;Save pointer to final buffer. + mov [samples], EDX ;Save number of samples. + mov [totalwork], EDX ;Save "total samples left" counter. + mov [postproc], EDX ;Number of samples to postprocess + mov EDI, [_judas_clipbuffer] + mov [dptr], EDI ;Dptr says to mixroutine: "Mix to clipbuffer!" + mov EBX, [_judas_zladdbuffer] + xor EAX, EAX + mov ECX, EDX + shl ECX, 1 +qmixer_clearloop: ;Clear clipbuffer & zeroleveladdbuffer + mov [EDI], EAX + mov [EBX], EAX + add EDI, 4 + add EBX, 4 + dec ECX + jnz qmixer_clearloop + + ;Player calling shit... + + cmp dword [_judas_player], 0 ;Is there a player? + jne qmixer_hardwayloop + call dword [_judas_mixroutine] ;No, just mix the sfx and + jmp qmixer_firstphasedone ;postprocess +qmixer_hardwayloop: + cmp dword [_judas_bpmcount], 0 ;Yes, check if it has to be + jne qmixer_skipplaying ;called. + cmp dword [_judas_player], 0 ;It has to, but player might + je qmixer_fuckshitup ;have disappeared. + call dword [_judas_player] ;Call if it hasn't. +qmixer_fuckshitup: + mov EAX, [_judas_mixrate] + mov EBP, 5 + mul EBP + shr EAX, 1 + xor EDX, EDX + movzx EBP, byte [_judas_bpmtempo] + div EBP + mov [_judas_bpmcount], EAX +qmixer_skipplaying: + mov EAX, [totalwork] + cmp EAX, [_judas_bpmcount] + jbe qmixer_nolimit + mov EAX, [_judas_bpmcount] +qmixer_nolimit: + mov [samples], EAX + call dword [_judas_mixroutine] + mov EAX, [samples] + sub [_judas_bpmcount], EAX + mov EBP, EAX + shl EBP, 3 + add [dptr], EBP + sub [totalwork], EAX + jnz qmixer_hardwayloop + + ;Postprocessing... + +qmixer_firstphasedone: + test byte [_judas_mixmode], SIXTEENBIT + jz near qmixer_8bit_endphase +qmixer_16bit_endphase: + test byte [_judas_mixmode], STEREO + jz near qmixer_nogusshit1 + cmp dword [_judas_device], DEV_GUS + je near qmixer_gus16s_endphase + ;stereo + mov ESI, [_judas_clipbuffer] + mov EBX, [_judas_zladdbuffer] + mov EDI, [fptr] +qmixer_s16bit_endphase_loop: + ;left + mov EAX, [ESI] ;Get value from clipbuffer + mov ECX, [_judas_zerolevell] + add ECX, [EBX] ;Add from zladdbuffer to zerolevel + mov EBP, ECX ;zerolevel gets 1/2048 closer to real zero + sar EBP, QMIXER_ZEROLEVELDECAY + sub ECX, EBP + mov [_judas_zerolevell], ECX + add EAX, ECX + + sar EAX, SIGNIFICANT_BITS_32 - 16 ;Shrink to 16 bit + cmp EAX, 32767 + jg qmixer_s16bit_overflowl + cmp EAX, -32768 + jl qmixer_s16bit_underflowl + mov [EDI], AX +qmixer_s16bit_cutdonel: + + ;right + mov EAX, [ESI+4] ;Get value from clipbuffer + mov ECX, [_judas_zerolevelr] + add ECX, [EBX+4] ;Add from zladdbuffer to zerolevel + mov EBP, ECX ;zerolevel gets 1/2048 closer to real zero + sar EBP, QMIXER_ZEROLEVELDECAY + sub ECX, EBP + mov [_judas_zerolevelr], ECX + add EAX, ECX + + sar EAX, SIGNIFICANT_BITS_32 - 16 ;Shrink to 16 bit + cmp EAX, 32767 + jg qmixer_s16bit_overflowr + cmp EAX, -32768 + jl qmixer_s16bit_underflowr + mov [EDI+2], AX +qmixer_s16bit_cutdoner: + + add ESI, 8 + add EBX, 8 + add EDI, 4 + dec dword [postproc] + jnz qmixer_s16bit_endphase_loop + ret +qmixer_s16bit_overflowl: + mov word [EDI], 32767 + shr EAX, 9 + cmp [_judas_clipped], AL + jae qmixer_s16bit_cutdonel + mov [_judas_clipped], AL + jmp qmixer_s16bit_cutdonel +qmixer_s16bit_underflowl: + mov word [EDI], -32768 + neg EAX + shr EAX, 9 + cmp [_judas_clipped], AL + jae qmixer_s16bit_cutdonel + mov [_judas_clipped], AL + jmp qmixer_s16bit_cutdonel +qmixer_s16bit_overflowr: + mov word [EDI+2], 32767 + shr EAX, 9 + cmp [_judas_clipped], AL + jae qmixer_s16bit_cutdoner + mov [_judas_clipped], AL + jmp qmixer_s16bit_cutdoner +qmixer_s16bit_underflowr: + mov word [EDI+2], -32768 + neg EAX + shr EAX, 9 + cmp [_judas_clipped], AL + jae qmixer_s16bit_cutdoner + mov [_judas_clipped], AL + jmp qmixer_s16bit_cutdoner +qmixer_nogusshit1: ;mono 16bit + mov ESI, [_judas_clipbuffer] + mov EBX, [_judas_zladdbuffer] + mov EDI, [fptr] +qmixer_m16bit_endphase_loop: + + mov EAX, [ESI] ;Get value from clipbuffer + sar EAX, 1 + mov ECX, [ESI+4] ; + right + sar ECX, 1 + add EAX, ECX + + mov ECX, [_judas_zerolevell] ;left zerolevel + add ECX, [EBX] ;Add from zladdbuffer to zerolevel + mov EBP, ECX ;zerolevel gets 1/2048 closer to real zero + sar EBP, QMIXER_ZEROLEVELDECAY + sub ECX, EBP + mov [_judas_zerolevell], ECX + sar ECX, 1 + add EAX, ECX + mov ECX, [_judas_zerolevelr] ;right zerolevel + add ECX, [EBX+4] ;Add from zladdbuffer to zerolevel + mov EBP, ECX ;zerolevel gets 1/2048 closer to real zero + sar EBP, QMIXER_ZEROLEVELDECAY + sub ECX, EBP + mov [_judas_zerolevelr], ECX + sar ECX, 1 + add EAX, ECX + + sar EAX, SIGNIFICANT_BITS_32 - 16 ;Shrink to 16 bit + cmp EAX, 32767 + jg qmixer_m16bit_overflow + cmp EAX, -32768 + jl qmixer_m16bit_underflow + mov [EDI], AX +qmixer_m16bit_cutdone: + + add ESI, 8 + add EBX, 8 + add EDI, 2 + dec dword [postproc] + jnz qmixer_m16bit_endphase_loop + ret +qmixer_m16bit_overflow: + mov word [EDI], 32767 + shr EAX, 9 + cmp [_judas_clipped], AL + jae qmixer_m16bit_cutdone + mov [_judas_clipped], AL + jmp qmixer_m16bit_cutdone +qmixer_m16bit_underflow: + mov word [EDI], -32768 + neg EAX + shr EAX, 9 + cmp [_judas_clipped], AL + jae qmixer_m16bit_cutdone + mov [_judas_clipped], AL + jmp qmixer_m16bit_cutdone + +qmixer_8bit_endphase: + test byte [_judas_mixmode], STEREO + jz near qmixer_nogusshit2 + cmp dword [_judas_device], DEV_GUS + je near qmixer_gus8s_endphase + ;stereo + mov ESI, [_judas_clipbuffer] + mov EBX, [_judas_zladdbuffer] + mov EDI, [fptr] +qmixer_s8bit_endphase_loop: + ;left + mov EAX, [ESI] ;Get value from clipbuffer + mov ECX, [_judas_zerolevell] + add ECX, [EBX] ;Add from zladdbuffer to zerolevel + mov EBP, ECX ;zerolevel gets 1/2048 closer to real zero + sar EBP, QMIXER_ZEROLEVELDECAY + sub ECX, EBP + mov [_judas_zerolevell], ECX + add EAX, ECX + + sar EAX, SIGNIFICANT_BITS_32 - 8 ;Shrink to 8 bit + cmp EAX, 127 + jg qmixer_s8bit_overflowl + cmp EAX, -128 + jl qmixer_s8bit_underflowl + add AL, 128 + mov byte [EDI], AL +qmixer_s8bit_cutdonel: + + ;right + mov EAX, [ESI+4] ;Get value from clipbuffer + mov ECX, [_judas_zerolevelr] + add ECX, [EBX+4] ;Add from zladdbuffer to zerolevel + mov EBP, ECX ;zerolevel gets 1/2048 closer to real zero + sar EBP, QMIXER_ZEROLEVELDECAY + sub ECX, EBP + mov [_judas_zerolevelr], ECX + add EAX, ECX + + sar EAX, SIGNIFICANT_BITS_32 - 8 ;Shrink to 8 bit + cmp EAX, 127 + jg qmixer_s8bit_overflowr + cmp EAX, -128 + jl qmixer_s8bit_underflowr + add AL, 128 + mov byte [EDI+1], AL +qmixer_s8bit_cutdoner: + + add ESI, 8 + add EBX, 8 + add EDI, 2 + dec dword [postproc] + jnz qmixer_s8bit_endphase_loop + ret +qmixer_s8bit_overflowl: + mov byte [EDI], 255 + shr EAX, 1 + cmp [_judas_clipped], AL + jae qmixer_s8bit_cutdonel + mov [_judas_clipped], AL + jmp qmixer_s8bit_cutdonel +qmixer_s8bit_underflowl: + mov byte [EDI], 0 + neg EAX + shr EAX, 1 + cmp [_judas_clipped], AL + jae qmixer_s8bit_cutdonel + mov [_judas_clipped], AL + jmp qmixer_s8bit_cutdonel +qmixer_s8bit_overflowr: + mov byte [EDI+1], 255 + shr EAX, 1 + cmp [_judas_clipped], AL + jae qmixer_s8bit_cutdoner + mov [_judas_clipped], AL + jmp qmixer_s8bit_cutdoner +qmixer_s8bit_underflowr: + mov byte [EDI+1], 0 + neg EAX + shr EAX, 1 + cmp [_judas_clipped], AL + jae qmixer_s8bit_cutdoner + mov [_judas_clipped], AL + jmp qmixer_s8bit_cutdoner +qmixer_nogusshit2: ;mono 8bit + + mov ESI, [_judas_clipbuffer] + mov EBX, [_judas_zladdbuffer] + mov EDI, [fptr] +qmixer_m8bit_endphase_loop: + + mov EAX, [ESI] ;Get value from clipbuffer + sar EAX, 1 + mov ECX, [ESI+4] ; + right + sar ECX, 1 + add EAX, ECX + + mov ECX, [_judas_zerolevell] ;left zerolevel + add ECX, [EBX] ;Add from zladdbuffer to zerolevel + mov EBP, ECX ;zerolevel gets 1/2048 closer to real zero + sar EBP, QMIXER_ZEROLEVELDECAY + sub ECX, EBP + mov [_judas_zerolevell], ECX + sar ECX, 1 + add EAX, ECX + mov ECX, [_judas_zerolevelr] ;right zerolevel + add ECX, [EBX+4] ;Add from zladdbuffer to zerolevel + mov EBP, ECX ;zerolevel gets 1/2048 closer to real zero + sar EBP, QMIXER_ZEROLEVELDECAY + sub ECX, EBP + mov [_judas_zerolevelr], ECX + sar ECX, 1 + add EAX, ECX + + sar EAX, SIGNIFICANT_BITS_32 - 8 ;Shrink to 8 bit + cmp EAX, 127 + jg qmixer_m8bit_overflow + cmp EAX, -128 + jl qmixer_m8bit_underflow + add AL, 128 + mov byte [EDI], AL +qmixer_m8bit_cutdone: + + add ESI, 8 + add EBX, 8 + inc EDI + dec dword [postproc] + jnz qmixer_m8bit_endphase_loop + ret +qmixer_m8bit_overflow: + mov byte [EDI], 255 + shr EAX, 1 + cmp [_judas_clipped], AL + jae qmixer_m8bit_cutdone + mov [_judas_clipped], AL + jmp qmixer_m8bit_cutdone +qmixer_m8bit_underflow: + mov byte [EDI], 0 + neg EAX + shr EAX, 1 + cmp [_judas_clipped], AL + jae qmixer_m8bit_cutdone + mov [_judas_clipped], AL + jmp qmixer_m8bit_cutdone + +qmixer_gus16s_endphase: + mov ESI, [_judas_clipbuffer] + mov EBX, [_judas_zladdbuffer] + mov EDI, [fptr] ;[EDI] = gus left + mov EDX, [_judas_bufferlength] + shr EDX, 1 + add EDX, EDI + add EDX, 32 ;[EDX] = gus right + +qmixer_gus16s_endphase_loop: + ;left + mov EAX, [ESI] ;Get value from clipbuffer + mov ECX, [_judas_zerolevell] + add ECX, [EBX] ;Add from zladdbuffer to zerolevel + mov EBP, ECX ;zerolevel gets 1/2048 closer to real zero + sar EBP, QMIXER_ZEROLEVELDECAY + sub ECX, EBP + mov [_judas_zerolevell], ECX + add EAX, ECX + + sar EAX, SIGNIFICANT_BITS_32 - 16 ;Shrink to 16 bit + cmp EAX, 32767 + jg qmixer_gus16s_overflowl + cmp EAX, -32768 + jl qmixer_gus16s_underflowl + mov [EDI], AX +qmixer_gus16s_cutdonel: + + ;right + mov EAX, [ESI+4] ;Get value from clipbuffer + mov ECX, [_judas_zerolevelr] + add ECX, [EBX+4] ;Add from zladdbuffer to zerolevel + mov EBP, ECX ;zerolevel gets 1/2048 closer to real zero + sar EBP, QMIXER_ZEROLEVELDECAY + sub ECX, EBP + mov [_judas_zerolevelr], ECX + add EAX, ECX + + sar EAX, SIGNIFICANT_BITS_32 - 16 ;Shrink to 16 bit + cmp EAX, 32767 + jg qmixer_gus16s_overflowr + cmp EAX, -32768 + jl qmixer_gus16s_underflowr + mov [EDX], AX +qmixer_gus16s_cutdoner: + + add ESI, 8 + add EBX, 8 + add EDI, 2 + add EDX, 2 + dec dword [postproc] + jnz qmixer_gus16s_endphase_loop + ret +qmixer_gus16s_overflowl: + mov word [EDI], 32767 + shr EAX, 9 + cmp [_judas_clipped], AL + jae qmixer_gus16s_cutdonel + mov [_judas_clipped], AL + jmp qmixer_gus16s_cutdonel +qmixer_gus16s_underflowl: + mov word [EDI], -32768 + neg EAX + shr EAX, 9 + cmp [_judas_clipped], AL + jae qmixer_gus16s_cutdonel + mov [_judas_clipped], AL + jmp qmixer_gus16s_cutdonel +qmixer_gus16s_overflowr: + mov word [EDX], 32767 + shr EAX, 9 + cmp [_judas_clipped], AL + jae qmixer_gus16s_cutdoner + mov [_judas_clipped], AL + jmp qmixer_gus16s_cutdoner +qmixer_gus16s_underflowr: + mov word [EDX], -32768 + neg EAX + shr EAX, 9 + cmp [_judas_clipped], AL + jae near qmixer_gus16s_cutdoner + mov [_judas_clipped], AL + jmp qmixer_gus16s_cutdoner + +qmixer_gus8s_endphase: + mov ESI, [_judas_clipbuffer] + mov EBX, [_judas_zladdbuffer] + mov EDI, [fptr] ;[EDI] = gus left + mov EDX, [_judas_bufferlength] + shr EDX, 1 + add EDX, EDI + add EDX, 32 ;[EDX] = gus right +qmixer_gus8s_endphase_loop: + ;left + mov EAX, [ESI] ;Get value from clipbuffer + mov ECX, [_judas_zerolevell] + add ECX, [EBX] ;Add from zladdbuffer to zerolevel + mov EBP, ECX ;zerolevel gets 1/2048 closer to real zero + sar EBP, QMIXER_ZEROLEVELDECAY + sub ECX, EBP + mov [_judas_zerolevell], ECX + add EAX, ECX + + sar EAX, SIGNIFICANT_BITS_32 - 8 ;Shrink to 8 bit + cmp EAX, 127 + jg qmixer_gus8s_overflowl + cmp EAX, -128 + jl qmixer_gus8s_underflowl + add AL, 128 + mov byte [EDI], AL +qmixer_gus8s_cutdonel: + + ;right + mov EAX, [ESI+4] ;Get value from clipbuffer + mov ECX, [_judas_zerolevelr] + add ECX, [EBX+4] ;Add from zladdbuffer to zerolevel + mov EBP, ECX ;zerolevel gets 1/2048 closer to real zero + sar EBP, QMIXER_ZEROLEVELDECAY + sub ECX, EBP + mov [_judas_zerolevelr], ECX + add EAX, ECX + + sar EAX, SIGNIFICANT_BITS_32 - 8 ;Shrink to 8 bit + cmp EAX, 127 + jg qmixer_gus8s_overflowr + cmp EAX, -128 + jl qmixer_gus8s_underflowr + add AL, 128 + mov byte [EDX], AL +qmixer_gus8s_cutdoner: + + add ESI, 8 + add EBX, 8 + inc EDI + inc EDX + dec dword [postproc] + jnz qmixer_gus8s_endphase_loop + ret +qmixer_gus8s_overflowl: + mov byte [EDI], 255 + shr EAX, 1 + cmp [_judas_clipped], AL + jae qmixer_gus8s_cutdonel + mov [_judas_clipped], AL + jmp qmixer_gus8s_cutdonel +qmixer_gus8s_underflowl: + mov byte [EDI], 0 + neg EAX + shr EAX, 1 + cmp [_judas_clipped], AL + jae qmixer_gus8s_cutdonel + mov [_judas_clipped], AL + jmp qmixer_gus8s_cutdonel +qmixer_gus8s_overflowr: + mov byte [EDX], 255 + shr EAX, 1 + cmp [_judas_clipped], AL + jae qmixer_gus8s_cutdoner + mov [_judas_clipped], AL + jmp qmixer_gus8s_cutdoner +qmixer_gus8s_underflowr: + mov byte [EDX], 0 + neg EAX + shr EAX, 1 + cmp [_judas_clipped], AL + jae qmixer_gus8s_cutdoner + mov [_judas_clipped], AL + jmp qmixer_gus8s_cutdoner +qmixer_quit: ret + +qmix_cubic_: +_qmix_cubic: + mov dword [cptr], offset _judas_channel +qmix_cubic_channelloop: + call qmixchannel_cubic + add dword [cptr], CHANNEL_size ;type CHANNEL in TASM + cmp dword [cptr], offset _judas_channel + CHANNELS * CHANNEL_size ;type CHANNEL in TASM + jne qmix_cubic_channelloop + ret + +qmix_linear_: +_qmix_linear: + mov dword [cptr], offset _judas_channel +qmix_linear_channelloop: + call qmixchannel_linear + add dword [cptr], CHANNEL_size ;type CHANNEL in TASM + cmp dword [cptr], offset _judas_channel + CHANNELS * CHANNEL_size ;type CHANNEL + jne qmix_cubic_channelloop + ret + +q_zerovolume: + ; Zerolevel stuff... + + mov EAX, [dptr] ;Get correct place at zladdbuf + sub EAX, [_judas_clipbuffer] ; + add EAX, [_judas_zladdbuffer] ; + mov EDX, [EBX + Chn_LastValL] + add [EAX], EDX + mov EDX, [EBX + Chn_LastValR] + mov dword [EBX + Chn_LastValL], 0 + add [EAX+4], EDX + mov dword [EBX + Chn_LastValR], 0 + + mov ESI, [samples] + mov EBP, [EBX + Chn_Pos] + mov ECX, EBP + shr EBP, 16 + shl ECX, 16 + mov CX, [EBX + Chn_FractPos] + mov EAX, [EBX + Chn_Freq] + mov EDX, EAX + shr EDX, 16 + shl EAX, 16 + div dword [_judas_mixrate] ;EAX = mixrate + test byte [EBX + Chn_VoiceMode], VM_16BIT ;If 16bit then double + jz q_zerovolume_not16bit ;the count + shl ESI, 1 +q_zerovolume_not16bit: + mul ESI ;EDX:EAX = pos. add + add ECX, EAX ;Add low part + adc EBP, EDX ;Add high part + mov [EBX + Chn_FractPos], CX ;Save fractpos + shl EBP, 16 ;(won't change now) + shr ECX, 16 ;Now shift back: ECX + or ECX, EBP ;is integer pos + test byte [EBX + Chn_VoiceMode], VM_16BIT ;Final adjust for 16bit + jz q_zerovolume_no16bitadjust + and ECX, 0fffffffeh +q_zerovolume_no16bitadjust: + test byte [EBX + Chn_VoiceMode], VM_LOOP ;Is it looped? + jnz q_zerovolume_looped + cmp ECX, [EBX + Chn_End] + jae q_zerovolume_oneshot_end +q_zerovolume_ready: + mov [EBX + Chn_Pos], ECX ;Store pos + ret +q_zerovolume_oneshot_end: + mov byte [EBX + Chn_VoiceMode], VM_OFF + ret +q_zerovolume_looped: + mov EAX, [EBX + Chn_End] + sub EAX, [EBX + Chn_Repeat] ;EAX = subtract value +q_zerovolume_looped_loop: + cmp ECX, [EBX + Chn_End] + jb q_zerovolume_ready + sub ECX, EAX + jmp q_zerovolume_looped_loop + + ;MACROS FOR QUALITY MIXER! + +%macro q_volpan14 0 + ;Volume! + xor EAX, EAX + mov AX, [EBX + Chn_Vol] + xor ECX, ECX + mov CL, [EBX + Chn_MasterVol] + imul EAX, ECX + shr EAX, 8 + mov EBP, EAX ;14 bit volume in AX + or AX, word [EBX + Chn_SmoothVolL+1] + or AX, word [EBX + Chn_SmoothVolR+1] + jz near q_zerovolume + ;Panning! + xor EAX, EAX + mov AL, [EBX + Chn_Panning] + mov ECX, EBP ;EBP = leftvol, ECX = rightvol + imul ECX, EAX + neg EAX ;EAX = 255-EAX + add EAX, 255 ; + imul EBP, EAX +%endmacro + +%macro q_mix8_stereo_l 0 + movsx EAX, byte [ESI] ; + movsx EBP, byte [ESI+1] ; + mov EBX, [rightvol] ;Smooth volume slide ;uv + sub EBP, EAX ;EBP = 16bit slidevalue + mov EDX, ECX ;EDX = fractpos + sub EBX, [SmoothVolR] ; + shr EDX, 17 ;32768x interpolation + add EDI, 8 ; + imul EBP, EDX ;EBP = interpolated value + sar EBX, QMIXER_VOLUMESMOOTH ;1/32 closer + mov EDX, [leftvol] ;Smooth volume sli. + add EBX, [SmoothVolR] ; + sub EDX, [SmoothVolL] ; + sal EAX, 8 ; + sar EDX, QMIXER_VOLUMESMOOTH ;1/32 closer + mov [SmoothVolR], EBX ; + shr EBX, 7 ; + add EDX, [SmoothVolL] ; + sar EBP, 7 ; + mov [SmoothVolL], EDX ; + shr EDX, 7 ; + add EBP, EAX ; + + add ECX, [fractadd] ;Sample pos fractional part + mov EAX, EBP ; + adc ESI, [integeradd] ;Sample pos integer part + + imul EBP, EBX ; + + imul EAX, EDX ; + + add [EDI-8], EAX ; + add [EDI-8+4], EBP ; +%endmacro + +%macro q_mix8_stereo_c 0 ;Here starts the pervert Hermite interpolation!!! + + movsx EBP, byte [ESI+IPMINUS1] + movsx EDX, byte [ESI+IP1] + movsx EBX, byte [ESI+IP2] + movsx EAX, byte [ESI+IP0] + sal EBX, 8 ; + sal EDX, 8 ; + mov dword [ip2], EBX ; + sal EAX, 8 ; + mov dword [ip1], EDX ; + mov EBX, EAX ; + sub EAX, EDX ; + sal EBP, 8 ; + + mov [ipminus1], EBP ; + lea EAX, [EAX*4+EDX] ; + mov EDX, ECX ; + sub EAX, EBX ; + shr EDX, 19 ;8192x interpolation + sub EAX, EBP ; + add EAX, [ip2] ; + lea EBP, [EBX*4+EBX] ; + + imul EAX, EDX ; + + sar EAX, 32-19+1 ; + add EBP, [ip2] ; + sar EBP, 1 ; + add EAX, [ip1] ; + add EAX, [ip1] ; + add EDI, 8 ; + sub EAX, EBP ; + mov EBP, [ip1] ; + add EAX, [ipminus1] ; + sub EBP, [ipminus1] ; + + imul EAX, EDX ; + + sar EBP, 1 ; + sar EAX, 32-19 ; + add ECX, [fractadd] ;Sample pos fract. + adc ESI, [integeradd] ;Sample pos integer + add EAX, EBP ; + + imul EAX, EDX ; + + sar EAX, 32-19 ; + mov EDX, [leftvol] ;Smooth vol.sl. + add EAX, EBX ; + mov EBX, [rightvol] ;Smooth vol.sl. + sub EDX, [SmoothVolL] ; + sub EBX, [SmoothVolR] ; + sar EBX, QMIXER_VOLUMESMOOTH ;1/32 closer + add EBX, [SmoothVolR] ; + sar EDX, QMIXER_VOLUMESMOOTH ;1/32 closer + mov [SmoothVolR], EBX ; + shr EBX, 7 ; + add EDX, [SmoothVolL] ; + mov [SmoothVolL], EDX ; + mov EBP, EAX ; + shr EDX, 7 ; + + imul EAX, EDX ; + + imul EBP, EBX ; + + add [EDI-8], EAX ; + add [EDI-8+4], EBP ; +%endmacro + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;pos adds are already defined. EBP contains volume for left channel +;and ECX for right. +%macro q_mixloop8 1 ; routine + mov [leftvol], EBP ;Save volumes + mov [rightvol], ECX ; + + mov ESI, [EBX + Chn_Pos] ;ESI:ECX = pos. ESI is integer + mov CX, [EBX + Chn_FractPos] ;part and ECX fraction. + shl ECX, 16 + + mov EAX, [EBX + Chn_End] ;Get sample end and repeat + mov [smpend], EAX ;positions. Calculate subtract + sub EAX, [EBX + Chn_Repeat] ;value. + mov [smpsubtract], EAX + + mov EDI, [dptr] ;Where to mix to? + mov EAX, [samples] ;How many samples to mix? + mov [loopcount], EAX + + ;Click removing for volume change and sample replace + + mov EAX, [EBX + Chn_Pos] ;Is it a new sound? + cmp EAX, [EBX + Chn_PrevPos] + je near %%soonmixloop ;jump if not +;New sound + mov EDX, [leftvol] +%IFDEF QMIXER_STARTMUTE + shr EDX, QMIXER_STARTMUTE ;Just to remove low frequency clicks +%ENDIF + mov [SmoothVolL], EDX + mov EDX, [rightvol] +%IFDEF QMIXER_STARTMUTE + shr EDX, QMIXER_STARTMUTE +%ENDIF + mov [SmoothVolR], EDX + mov [saved_reg], EBX + %1 ;routine (macro parameter) + mov EBX, [saved_reg] + mov EDX, [EBX + Chn_LastValL] + mov [saved_reg], EAX + sub EDX, EAX + mov EAX, [_judas_zladdbuffer] ;Get correct place at zladdbuf + sub EAX, [_judas_clipbuffer] ; + add [EAX+EDI-8], EDX + mov EDX, [EBX + Chn_LastValR] + sub EDX, EBP + add [EAX+EDI-8+4], EDX + mov EAX, [saved_reg] ;Maybe needed if sample ends now + mov [saved_reg], EBX + jmp near %%donezladjust + +%%soonmixloop: mov EDX, [EBX + Chn_SmoothVolL] + mov EBP, [EBX + Chn_SmoothVolR] + mov [SmoothVolL], EDX + mov [SmoothVolR], EBP + mov [saved_reg], EBX + + align 16 + +%%mixloop: %1 ; routine (macro parameter) - Mix one sample +%%donezladjust: + cmp ESI, [smpend] ;End of sample? + jae %%hitend + + dec dword [loopcount] ;Still shit to do? + jnz near %%mixloop + + mov EBX, [saved_reg] + mov [EBX + Chn_Pos], ESI ;No, all done. Save position + mov [EBX + Chn_PrevPos], ESI ;..and prevpos + shr ECX, 16 + mov [EBX + Chn_FractPos], CX + mov [EBX + Chn_LastValL], EAX ;Save Last values also + mov [EBX + Chn_LastValR], EBP ; + mov EAX, [SmoothVolL] ;Save volume + mov EBP, [SmoothVolR] ; + mov [EBX + Chn_SmoothVolL], EAX ; + mov [EBX + Chn_SmoothVolR], EBP ; + ret + +%%hitend: + mov EBX, [saved_reg] + test byte [EBX + Chn_VoiceMode], VM_LOOP ;Is it a looped sample? + jz %%oneshot +%%subloop: + sub ESI, [smpsubtract] ;Looped. Go to loop start. + cmp ESI, [smpend] + jae %%subloop ;Fucking shit, it got far beyond sample end. + + dec dword [loopcount] ;Still shit to do? + jnz near %%mixloop + + mov [EBX + Chn_Pos], ESI ;No, all done. Save position + shr ECX, 16 + mov [EBX + Chn_FractPos], CX + mov [EBX + Chn_PrevPos], ESI ;Save prevpos too + mov [EBX + Chn_LastValL], EAX ;...Last values also + mov [EBX + Chn_LastValR], EBP ; + mov EAX, [SmoothVolL] ;Save volume + mov EBP, [SmoothVolR] ; + mov [EBX + Chn_SmoothVolL], EAX ; + mov [EBX + Chn_SmoothVolR], EBP ; + ret + +%%oneshot: + mov byte [EBX + Chn_VoiceMode], VM_OFF ;Your time to die, sample! + + ;If sample doesn't end at zero, zerolevel must be adjusted. + mov dword [EBX + Chn_Pos], 0 + mov dword [EBX + Chn_LastValL], 0 + mov dword [EBX + Chn_LastValR], 0 + cmp dword [loopcount], 1 + jbe %%oneshot_lastsample + mov EDX, EDI ;Get correct place in zladdbuf + sub EDX, [_judas_clipbuffer] ; + add EDX, [_judas_zladdbuffer] ; + add [EDX], EAX + add [EDX+4], EBP + mov byte [EBX + Chn_PrevVM], VM_OFF ;No need to update zerolevel + ret ;at beginning of next chunk + +%%oneshot_lastsample: ;Last sample in mixed chunk + mov byte [EBX + Chn_PrevVM], VM_ON + ret +%endmacro + +%macro q_mix16_stereo_l 0 + movsx EAX, word [ESI*2] ; + movsx EBP, word [ESI*2+2] ; + mov EBX, [rightvol] ;Smooth volume slide ;uv + sub EBP, EAX ;EBP = 16bit slidevalue + mov EDX, ECX ;EDX = fractpos + sub EBX, [SmoothVolR] ; + shr EDX, 17 ;32768x interpolation + add EDI, 8 ; + imul EBP, EDX ;EBP = interpolated value + sar EBX, QMIXER_VOLUMESMOOTH ;1/32 closer + mov EDX, [leftvol] ;Smooth volume sli. + add EBX, [SmoothVolR] ; + sub EDX, [SmoothVolL] ; + sar EDX, QMIXER_VOLUMESMOOTH ;1/32 closer + mov [SmoothVolR], EBX ; + shr EBX, 7 ; + add EDX, [SmoothVolL] ; + sar EBP, 15 ; + mov [SmoothVolL], EDX ; + shr EDX, 7 ; + add EBP, EAX ; + + add ECX, [fractadd] ;Sample pos fractional part + mov EAX, EBP ; + adc ESI, [integeradd] ;Sample pos integer part + + imul EBP, EBX ; + + imul EAX, EDX ; + + add [EDI-8], EAX ; + add [EDI-8+4], EBP ; +%endmacro + +%macro q_mix16_stereo_c 0 ;Here starts the pervert Hermite interpolation!!! + movsx EBP, word [ESI*2+IPMINUS1*2] + movsx EAX, word [ESI*2+IP0*2] + movsx EBX, word [ESI*2+IP2*2] + movsx EDX, word [ESI*2+IP1*2] + mov [ip2], EBX ; + mov [ip1], EDX ; + mov EBX, EAX ; + + sub EAX, EDX ; + mov [ipminus1], EBP ; + lea EAX, [EAX*4+EDX] ; + mov EDX, ECX ; + sub EAX, EBX ; + shr EDX, 19 ;8192x interpolation + sub EAX, EBP ; + add EAX, [ip2] ; + lea EBP, [EBX*4+EBX] ; + + imul EAX, EDX ; + + sar EAX, 32-19+1 ; + add EBP, [ip2] ; + sar EBP, 1 ; + add EAX, [ip1] ; + add EAX, [ip1] ; + add EDI, 8 ; + sub EAX, EBP ; + mov EBP, [ip1] ; + add EAX, [ipminus1] ; + sub EBP, [ipminus1] ; + + imul EAX, EDX ; + + sar EBP, 1 ; + sar EAX, 32-19 ; + add ECX, [fractadd] ;Sample pos fract. + adc ESI, [integeradd] ;Sample pos integer + add EAX, EBP ; + + imul EAX, EDX ; + + sar EAX, 32-19 ; + mov EDX, [leftvol] ;Smooth vol.sl. + add EAX, EBX ; + mov EBX, [rightvol] ;Smooth vol.sl. + sub EDX, [SmoothVolL] ; + sub EBX, [SmoothVolR] ; + sar EBX, QMIXER_VOLUMESMOOTH ;1/32 closer + add EBX, [SmoothVolR] ; + sar EDX, QMIXER_VOLUMESMOOTH ;1/32 closer + mov [SmoothVolR], EBX ; + shr EBX, 7 ; + add EDX, [SmoothVolL] ; + mov [SmoothVolL], EDX ; + mov EBP, EAX ; + shr EDX, 7 ; + + imul EAX, EDX ; + + imul EBP, EBX ; + + add [EDI-8], EAX ; + add [EDI-8+4], EBP ; +%endmacro + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;pos adds are already defined. EBP contains volume for left channel +;and ECX for right. +%macro q_mixloop16 1 ; routine + mov [leftvol], EBP ;Save volumes + mov [rightvol], ECX ; + + mov ESI, [EBX + Chn_Pos] ;ESI:ECX = pos. ESI is integer + shr ESI, 1 + mov CX, [EBX + Chn_FractPos] ;part and ECX fraction. + shl ECX, 16 + + mov EAX, [EBX + Chn_End] ;Get sample end and repeat + shr EAX, 1 + mov [smpend], EAX ;positions. Calculate subtract + mov EAX, [EBX + Chn_End] + sub EAX, [EBX + Chn_Repeat] ;value. + shr EAX, 1 + mov [smpsubtract], EAX + + mov EDI, [dptr] ;Where to mix to? + mov EAX, [samples] ;How many samples to mix? + mov [loopcount], EAX + + ;Click removing for volume change and sample replace + + mov EAX, [EBX + Chn_Pos] ;Is it a new sound? + cmp EAX, [EBX + Chn_PrevPos] + je near %%soonmixloop ;jump if not +;New sound + mov EDX, [leftvol] +%IFDEF QMIXER_STARTMUTE + shr EDX, QMIXER_STARTMUTE ;Just to remove low frequency clicks +%ENDIF + mov [SmoothVolL], EDX + mov EDX, [rightvol] +%IFDEF QMIXER_STARTMUTE + shr EDX, QMIXER_STARTMUTE +%ENDIF + mov [SmoothVolR], EDX + mov [saved_reg], EBX + %1 ; routine macro parameter + mov EBX, [saved_reg] + mov EDX, [EBX + Chn_LastValL] + mov [saved_reg], EAX + sub EDX, EAX + mov EAX, [_judas_zladdbuffer] ;Get correct place at zladdbuf + sub EAX, [_judas_clipbuffer] ; + add [EAX+EDI-8], EDX + mov EDX, [EBX + Chn_LastValR] + sub EDX, EBP + add [EAX+EDI-8+4], EDX + mov EAX, [saved_reg] ;Maybe needed if sample ends now + mov [saved_reg], EBX + jmp near %%donezladjust + +%%soonmixloop: mov EDX, [EBX + Chn_SmoothVolL] + mov EBP, [EBX + Chn_SmoothVolR] + mov [SmoothVolL], EDX + mov [SmoothVolR], EBP + mov [saved_reg], EBX + + align 16 + +%%mixloop: %1 ; routine macro parameter - Mix one sample +%%donezladjust: + cmp ESI, [smpend] ;End of sample? + jae %%hitend + + dec dword [loopcount] ;Still shit to do? + jnz near %%mixloop + + mov EBX, [saved_reg] + shl ESI, 1 + mov [EBX + Chn_Pos], ESI ;No, all done. Save position + mov [EBX + Chn_PrevPos], ESI ;...and prevpos + shr ECX, 16 + mov [EBX + Chn_FractPos], CX + mov [EBX + Chn_LastValL], EAX ;Save Last values also + mov [EBX + Chn_LastValR], EBP ; + mov EAX, [SmoothVolL] ;Save volume + mov EBP, [SmoothVolR] ; + mov [EBX + Chn_SmoothVolL], EAX ; + mov [EBX + Chn_SmoothVolR], EBP ; + ret + +%%hitend: + mov EBX, [saved_reg] + test byte [EBX + Chn_VoiceMode], VM_LOOP ;Is it a looped sample? + jz %%oneshot +%%subloop: + sub ESI, [smpsubtract] ;Looped. Go to loop start. + cmp ESI, [smpend] + jae %%subloop ;Fucking shit, it got far beyond sample end. + + dec dword [loopcount] ;Still shit to do? + jnz near %%mixloop + + shl ESI, 1 + mov [EBX + Chn_Pos], ESI ;No, all done. Save position + mov [EBX + Chn_PrevPos], ESI ;Save prevpos too + shr ECX, 16 + mov [EBX + Chn_FractPos], CX + mov [EBX + Chn_LastValL], EAX ;...Last values also + mov [EBX + Chn_LastValR], EBP ; + mov EAX, [SmoothVolL] ;Save volume + mov EBP, [SmoothVolR] ; + mov [EBX + Chn_SmoothVolL], EAX ; + mov [EBX + Chn_SmoothVolR], EBP ; + ret + +%%oneshot: + mov byte [EBX + Chn_VoiceMode], VM_OFF ;Your time to die, sample! + + ;If sample doesn't end at zero, zerolevel must be adjusted. + mov dword [EBX + Chn_Pos], 0 + mov dword [EBX + Chn_LastValL], 0 + mov dword [EBX + Chn_LastValR], 0 + cmp dword [loopcount], 1 + jbe %%oneshot_lastsample + mov EDX, EDI ;Get correct place in zladdbuf + sub EDX, [_judas_clipbuffer] ; + add EDX, [_judas_zladdbuffer] ; + add [EDX], EAX + add [EDX+4], EBP + mov byte [EBX + Chn_PrevVM], VM_OFF ;No need to update zerolevel + ret ;at beginning of next chunk + +%%oneshot_lastsample: ;Last sample in mixed chunk + mov byte [EBX + Chn_PrevVM], VM_ON + ret +%endmacro + + ;Qualitymixes [samples] of channel [cptr] to buffer at [dptr]. Destroys + ;every register. LINEAR INTERPOLATION! + +qmixchannel_linear: + mov EBX, [cptr] + mov AL, [EBX + Chn_VoiceMode] + test AL, VM_ON + jnz qmixc_l_vm_on + cmp AL, [EBX + Chn_PrevVM] ;Sound discontinuity? + je near qmixc_l_quit ;no? + mov [EBX + Chn_PrevVM], AL ;yes... + mov EDX, [_judas_zladdbuffer] ;so must move zerolevel + add EDX, [dptr] + mov EAX, [EBX + Chn_LastValL] + sub EDX, [_judas_clipbuffer] + mov dword [EBX + Chn_LastValL], 0 + add [EDX], EAX + mov EAX, [EBX + Chn_LastValR] + mov dword [EBX + Chn_LastValR], 0 + add [EDX+4], EAX + jmp qmixc_l_quit +qmixc_l_vm_on: + mov EAX, [EBX + Chn_Freq] + cmp EAX, 535232 ;Highest linear freq + jbe qmixc_l_freqok + mov EAX, 535232 +qmixc_l_freqok: + mov EDX, EAX + shr EDX, 16 + shl EAX, 16 + div dword [_judas_mixrate] + mov word [fractadd + 2], AX + shr EAX, 16 + mov [integeradd], EAX + test byte [EBX + Chn_VoiceMode], VM_16BIT ;Is sampledata 16bit? + jnz near qmixc_l_16bit ;Jump if yes. + test byte [_judas_mixmode], STEREO ;No, 8bit. Stereo audio? + jz qmixc_l_mono_8bit ;Jump if mono. +qmixc_l_stereo_8bit: +qmixc_l_mono_8bit: ;Mix in stereo, even if mono output + q_volpan14 + q_mixloop8 q_mix8_stereo_l + ret +qmixc_l_16bit: + test byte [_judas_mixmode], STEREO ;It's 16bit. Stereo? + jz qmixc_l_mono_16bit +qmixc_l_stereo_16bit: +qmixc_l_mono_16bit: ;Mix in stereo, even if mono output + q_volpan14 + q_mixloop16 q_mix16_stereo_l +qmixc_l_quit: ret + + ;Qualitymixes [samples] of channel [cptr] to buffer at [dptr]. Destroys + ;every register. CUBIC INTERPOLATION! + +qmixchannel_cubic: + mov EBX, [cptr] + mov AL, [EBX + Chn_VoiceMode] + test AL, VM_ON + jnz qmixc_c_vm_on + cmp AL, [EBX + Chn_PrevVM] ;Sound discontinuity? + je near qmixc_c_quit ;no? + mov [EBX + Chn_PrevVM], AL ;yes... + mov EDX, [_judas_zladdbuffer] ;so must move zerolevel + add EDX, [dptr] + mov EAX, [EBX + Chn_LastValL] + sub EDX, [_judas_clipbuffer] + mov dword [EBX + Chn_LastValL], 0 + add [EDX], EAX + mov EAX, [EBX + Chn_LastValR] + mov dword [EBX + Chn_LastValR], 0 + add [EDX+4], EAX + jmp qmixc_c_quit +qmixc_c_vm_on: + mov [EBX + Chn_PrevVM], AL + mov EAX, [EBX + Chn_Freq] + cmp EAX, 535232 ;Highest linear freq + jbe qmixc_c_freqok + mov EAX, 535232 +qmixc_c_freqok: + mov EDX, EAX + shr EDX, 16 + shl EAX, 16 + div dword [_judas_mixrate] + mov word [fractadd + 2], AX + shr EAX, 16 + mov [integeradd], EAX + test byte [EBX + Chn_VoiceMode], VM_16BIT ;Is sampledata 16bit? + jnz near qmixc_c_16bit ;Jump if yes. + test byte [_judas_mixmode], STEREO ;No, 8bit. Stereo audio? + jz qmixc_c_mono_8bit ;Jump if mono. +qmixc_c_stereo_8bit: +qmixc_c_mono_8bit: ;Mix in stereo, even if mono output + q_volpan14 + q_mixloop8 q_mix8_stereo_c + ret +qmixc_c_16bit: + test byte [_judas_mixmode], STEREO ;It's 16bit. Stereo? + jz qmixc_c_mono_16bit +qmixc_c_stereo_16bit: +qmixc_c_mono_16bit: ;Mix in stereo, even if mono output + q_volpan14 + q_mixloop16 q_mix16_stereo_c +qmixc_c_quit: ret + +;Safety mixer for calls from c program + +safemixer_: +_safemixer: + cmp dword [_judas_mixersys], 0 + je safemixer_helldontcall + cmp byte [_judas_initialized], 0 + je safemixer_helldontcall + pushad + call dword [_judas_mixersys] + popad +safemixer_helldontcall: + ret + +judas_code_lock_end_: +_judas_code_lock_end: +; end diff --git a/JDAS210D.FRM b/JDAS210D.FRM new file mode 100644 index 0000000..2b46b31 --- /dev/null +++ b/JDAS210D.FRM @@ -0,0 +1,17 @@ +TITLE : Judas Sound System v2.10d +AUTHOR : Yehar + Cadaver + Black Spider(AC97/HDA code) +GROUP : +CATEGORY : players +EMAIL : ollinie@freenet.hut.fi (please read judas.doc file) + +--DESCRIPTION-BEGIN------------------------------------------------------------ +Judas v2.10d Apocalyptic Softwaremixing Soundsystem for Watcom 32bit flat mode. +Supports: SB/SBPRO/SB16/GUS/AC97/HDA/WAV-writer. Modules: XM/MOD/S3M. Samples: WAV/raw. +Quality mixer gives you the best sound quality for DOS you can get today, +clicker free (without touching sharp percussions), mixed with 32bit precision. +Cubic interpolation reproduces samples with good treble response and very low distortion. +Fast mixer is the ultra-fast alternative, suitable for games and demos. +A player, examples, documents and full sources are included. Judas is free. +New in this version: fixes, enhanced quality mixer, distortion indicator, +true volume meters, song looping settings, rewinding and forwarding, Watcom & DJGPP support. +--DESCRIPTION-END-------------------------------------------------------------- diff --git a/JDSWAV.ZIP b/JDSWAV.ZIP new file mode 100644 index 0000000000000000000000000000000000000000..1011dec7ea30851091c5e60b856f95724e4aec5f GIT binary patch literal 66734 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __DJGPP__ +#include +#include +#include +#include +/* declare flushall */ +#include +void flushall (void) +{ + /* Simulate `flushall' by only flushing standard streams */ + fflush (stdout); + fsync (fileno (stdout)); + fflush (stderr); + fsync (fileno (stderr)); +} +#else +/* WATCOM C++ graphics library & define djgpp base as absolute zero */ +#define __djgpp_conventional_base 0 +#include +#endif +#include "judas.h" +#include "timer.h" + +/* For direct video memory access. (Does this work at all with PMODE?) */ +#define SCREEN_AREA 0xb800 +#define SCREEN_LIN_ADDR ((SCREEN_AREA) << 4) +#define SCREEN_SIZE 80*25 + +#define VUMETERBOTTOM 17 +#define VUMETERXSIZE 80 +#define VUMETERLEFT 0 +#define VUMETERTOP 0 +#define VUMETERLINES (VUMETERBOTTOM-VUMETERTOP+1) + + +/* only for testing purposes */ +extern long hda_getbufpos (void); + + +/* Functions */ +void buildmainscreen(void); +void updatemainscreen(void); +int finishfilename(char *filename); +void wavizefilename(char *filename); +int main(int argc, char **argv); +static void handle_int(int a); + +/* Variables */ +int filewriter = 0; +int mixer = QUALITYMIXER; +int filetype = 0; +int interpolation = 1; +int songrounds = 1; +int channels = 80; +int vumeterwidth = 1; +int vumeterson = 1; +int mainscreenon = 1; +char filename[254]; +int mastervolume = 50; +int oldmastervolume; +char oldjudas_clipped; + +const char *const extensiontable[] = +{ + ".xm", + ".mod", + ".s3m", + NULL +}; + +/* + * Bright and dim versions of the 16 textmode colors. + */ +const char hilite[] = { 0,9,10,11,12,13,14,15,7,11,14,15,14,15,15,15 }; +const char lolite[] = { 0,1,2,8,4,5,8,8,8,1,2,3,4,5,6,7 }; + +#ifdef __WATCOMC__ +void jdsgotoxy(char x, char y); +/* + * Moves cursor to selected position. (Does this work at all with PMODE?) + */ +extern void jdsgotoxy(char x, char y); +#pragma aux jdsgotoxy = \ + "push bp" \ + "xor bh, bh" \ + "mov ah,2" \ + "int 10h" \ + "pop bp" \ + parm [dl] [dh] \ + modify [ah bh]; +#else +void jdsgotoxy(char x, char y) +{ + gotoxy(x+1, y+1); +} +#endif + + + +/* + * Builds the player screen. + */ +void buildmainscreen(void) +{ + #ifdef __DJGPP__ + /* use conio.h for DJGPP */ + textmode(C80); + #else + /* WATCOM C++ */ + _setvideomode(_TEXTC80); + #endif + jdsgotoxy(0,VUMETERBOTTOM+1); + printf("ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ JUDAS V2.10d player ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ" + "[ESC] Quit, [D] DOS shell, [<Ä>] Rewind/forward, [+-] Mastervol:%3d\n", mastervolume); + if (!filewriter) { + printf("%s, port %X IRQ %d DMA %d\n", + judas_devname[judas_device], judas_port, judas_irq, judas_dma); + } else { + printf("Writing WAV: %s\n", filename); + } + printf("%s %d Hz, %s, %s IP\n", + judas_mixmodename[judas_mixmode], judas_mixrate, + judas_mixername[mixer], judas_ipmodename[interpolation]); + + switch(filetype) + { + case 1: + channels = judas_getxmchannels(); + printf("XM name: %s\n", judas_getxmname()); + printf("Channels: %d\n", channels); + break; + + case 2: + channels = judas_getmodchannels(); + printf("MOD name: %s\n", judas_getmodname()); + printf("Channels: %d\n", channels); + break; + + case 3: + channels = judas_gets3mchannels(); + printf("S3M name: %s\n", judas_gets3mname()); + printf("Channels: %d\n", channels); + break; + } + vumeterwidth = VUMETERXSIZE/channels; +} + + +/* + * Updates the player screen. + */ +void updatemainscreen(void) +{ + if (oldmastervolume != mastervolume) { + jdsgotoxy(64,VUMETERBOTTOM+2); + printf("%3d",mastervolume); + oldmastervolume = mastervolume; + } + flushall(); + if (oldjudas_clipped != judas_clipped) { + jdsgotoxy(68, VUMETERBOTTOM+2); + if (judas_clipped) printf("Clipped:%3d%%", 1+(int)400*judas_clipped/256); else printf(" "); + oldjudas_clipped = judas_clipped; + } + flushall(); + +/* + jdsgotoxy(0,24); + // only for testing purposes + hda_getbufpos(); + flushall(); +*/ + + jdsgotoxy(0,24); + switch(filetype) + { + case 1: + printf("Pos:%3d Line:%3d Tick:%3d", judas_getxmpos(), judas_getxmline(), judas_getxmtick()); + break; + + case 2: + printf("Pos:%3d Line:%3d Tick:%3d", judas_getmodpos(), judas_getmodline(), judas_getmodtick()); + break; + + case 3: + printf("Pos:%3d Line:%3d Tick:%3d", judas_gets3mpos(), judas_gets3mline(), judas_gets3mtick()); + break; + } + flushall(); + + /* Draw vumeters... */ + if (vumeterson) { + short *screenmem = (short *)((unsigned char *)SCREEN_LIN_ADDR + __djgpp_conventional_base); + int count; + for (count = 0; count < channels; count++) + { + short *ptr = screenmem + 80*VUMETERTOP + count*vumeterwidth; + float floatvu = sqrt(judas_getvumeter(count)); + char color = (((unsigned int)judas_channel[count].end / 15) % 15) + 1; + int halves = floatvu *(VUMETERLINES*2+1); + int zeros; + int vu = halves/2; + halves -= vu*2; + zeros = VUMETERLINES-vu-halves; + + while (zeros) + { + int fuck = vumeterwidth; + while (fuck) + { + *ptr = 0; + ptr++; + fuck--; + } + ptr += (80-vumeterwidth); + zeros--; + } + if (halves) + { + int fuck = vumeterwidth-1; + if (fuck) { + *ptr = 220+256*hilite[(unsigned char)color]; + ptr++; + fuck--; + } + while (fuck) + { + *ptr = 220+256*color; + ptr++; + fuck--; + } + *ptr = 220+256*lolite[(unsigned char)color]; + ptr++; + ptr += (80-vumeterwidth); + } + while (vu) + { + int fuck = vumeterwidth-1; + if (fuck) { + *ptr = 219+256*hilite[(unsigned char)color]; + ptr++; + fuck--; + } + while (fuck) + { + *ptr = 219+256*color; + ptr++; + fuck--; + } + *ptr = 219+256*lolite[(unsigned char)color]; + ptr++; + ptr += (80-vumeterwidth); + vu--; + } + } + } +} + + +/* + * Tries some extensions. Returns 1 if finds the file, otherwise 0. + */ +int finishfilename(char *filename) +{ + /* Maybe the filename is already right... If, return 1. */ + { + int handle = judas_open(filename); + if (handle != -1) + { + judas_close(handle); + return 1; + } + } + { + int pos = strlen(filename)-1; + int pointpos = pos+1; + /* Is there already an extension in the filename? */ + { + while (filename[pos] != ':' && + filename[pos] != '\\' && + filename[pos] != '/' && + pos >= 0) + { + if (filename[pos] == '.') + { + /* No match! */ + return 0; + } + pos--; + } + /* Try with different extensions */ + { + int count; + + for (count = 0; extensiontable[count]; count++) { + int handle; + strcat(filename, extensiontable[count]); + handle = judas_open(filename); + if (handle != -1) { + judas_close(handle); + return 1; + } + filename[pointpos] = 0; + } + /* No match! */ + return 0; + } + } + } +} + +/* + * Removes path from the filename and changes extension to ".wav". + */ +void wavizefilename(char *filename) +{ + int pos = strlen(filename)-1; + while (filename[pos] != ':' && + filename[pos] != '\\' && + filename[pos] != '/' && + pos >= 0) + { + if (filename[pos] == '.') + { + filename[pos] = 0; + goto FOUNDEXTPOS; + } + pos--; + } + pos++; + if (pos == strlen(filename)-1) { + strcpy(filename, "output.wav"); + return; + } + FOUNDEXTPOS: + strcat(filename, ".wav"); + pos = strlen(filename)-1; + while (filename[pos] != ':' && + filename[pos] != '\\' && + filename[pos] != '/' && + pos >= 0) + { + pos--; + } + strcpy(filename, &filename[pos+1]); +} + +/* + * The player skeleton! + */ +int main(int argc, char **argv) +{ + int mixrate = 48000; + int mixmode = SIXTEENBIT | STEREO; + int wavhandle = 0; + + /* Set signal handlers */ + signal(SIGINT, handle_int); + #if defined(SIGBREAK) + signal(SIGBREAK, handle_int); + #endif + + #ifdef __DJGPP__ + if (__djgpp_nearptr_enable() == 0) { + printf("ERROR: Couldn't enable near pointers for DJGPP!\n"); + return 1; + } + /* Trick: Avoid re-setting DS selector limit on each memory allocation call */ + __djgpp_selector_limit = 0xffffffff; + #endif + + /* Print some shit */ + printf("ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ\n" + "JUDAS V2.10d player - plays XMs+MODs+S3Ms\n" + "ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ\n"); + + /* Check we have enough commandline parameters */ + if (argc == 1) + { + printf("Voluntary stuff marked with <>\n" + "\n" + "Usage: jp musicfile \n" + "Options: -rxxx Mixrate, in Hz\n" + " -vxxx Mastervolume, 0-255, default: 50\n" + " -m Output mono\n" + " -8 Output 8bit\n" + " -i Simpler interpolation\n" + " -f Fast mixer, default: Quality mixer\n" + " -l Loop song, play song xxx times, default: once.\n" + " If no xxx given or xxx zero, will play forever.\n" + " -w WAV writer, outputs audio into a WAV file named\n" + " after the musicfile, in the current directory.\n" + " -d Disable player screen\n" + " -dv Disable vumeters\n"); + return 1; + } + + /* Parse options */ + if (argc > 2) + { + int count = 2; + while (count < argc) + { + char *ptr = argv[count]; + if ((ptr[0] == '-') || (ptr[0] == '/')) + { + switch(tolower(ptr[1])) + { + case 'r': + sscanf(&ptr[2], "%d", &mixrate); + break; + + case 'v': + sscanf(&ptr[2], "%d", &mastervolume); + if (mastervolume < 0) mastervolume = 0; + if (mastervolume > 255) mastervolume = 255; + break; + + case 'm': + mixmode &= SIXTEENBIT; + break; + + case '8': + mixmode &= STEREO; + break; + + case 'i': + interpolation = 0; + break; + + case 'f': + mixer = FASTMIXER; + break; + + case 'l': + songrounds = 0; + sscanf(&ptr[2], "%d", &songrounds); + break; + + case 'w': + filewriter = 1; + judascfg_device = DEV_FILE; + break; + + case 'd': + if (tolower(ptr[2]) == 'v') vumeterson = 0; + if (!ptr[2]) mainscreenon = 0; + break; + } + } + count++; + } + } + + /* Get and finish filename */ + strcpy(filename, argv[1]); + if (!finishfilename(filename)) + { + printf("ERROR: Couldn't find musicfile!\n"); + return 1; + } + printf("LOADING FILE: %s\n",filename); + flushall(); + + /* Set uninit functions to be called at exit */ + atexit(judas_uninit); + + /* Autoconfigure by using enviroment */ + if (!filewriter) { + judas_config(); + } + + /* Try to init */ + if (!judas_init(mixrate, mixer, mixmode, interpolation)) goto ERROR; + + /* Exit if in no sound mode */ + if (judas_device == DEV_NOSOUND) + { + printf("ERROR: Sound card not detected!\n"); + return 1; + } + + /* Set mastervolume of channels */ + judas_setmusicmastervolume(CHANNELS, mastervolume); + + /* + * Try to load, first XM, then MOD, then S3M + */ + filetype = 1; + judas_loadxm(filename); + if (judas_error == JUDAS_OK) goto PLAYIT; + if (judas_error != JUDAS_WRONG_FORMAT) goto ERROR; + filetype = 2; + judas_loadmod(filename); + if (judas_error == JUDAS_OK) goto PLAYIT; + if (judas_error != JUDAS_WRONG_FORMAT) goto ERROR; + filetype = 3; + judas_loads3m(filename); + if (judas_error == JUDAS_OK) goto PLAYIT; + if (judas_error != JUDAS_WRONG_FORMAT) goto ERROR; + + ERROR: + printf("JUDAS ERROR: %s\n", judas_errortext[judas_error]); + return 1; + + PLAYIT: + printf("PLAYING "); + flushall(); + switch(filetype) + { + case 1: + printf("XM\n"); + judas_playxm(songrounds); + break; + + case 2: + printf("MOD\n"); + judas_playmod(songrounds); + break; + + case 3: + printf("S3M\n"); + judas_plays3m(songrounds); + break; + } + + /* Hook timer to update sound */ + if (!filewriter) { + timer_init(0x4300, judas_update); + atexit(timer_uninit); + } else { + wavizefilename(filename); + wavhandle = judas_wavwriter_open(filename); + if (wavhandle == -1) { + printf("WAV WRITER ERROR: Error creating the WAV file!"); + return 1; + }; + } + + if (mainscreenon) { + buildmainscreen(); + updatemainscreen(); + } else { + printf("[ESC] Quit, [D] DOS shell, [<Ä>] Rewind/forward, [+-] Mastervol\n"); + flushall(); + } + + for (;;) + { + char c; + + while (!kbhit() && judas_songisplaying()) + { + if (filewriter) { + if (judas_wavwriter_writesome(wavhandle) == -1) { + #ifdef __DJGPP__ + /* use conio.h for DJGPP */ + flushall(); + if (mainscreenon) textmode(LASTMODE); + #else + /* WATCOM C++ */ + flushall(); + if (mainscreenon) _setvideomode(_DEFAULTMODE); + #endif + printf("WAV WRITER ERROR: Error writing the WAV file!"); + return 1; + } + } + if (mainscreenon) updatemainscreen(); + } + + if (kbhit()) c = toupper(getch()); else c = 27; + switch (c) { + case 77: + { + switch(filetype) { + case 1: + judas_forwardxm(); + break; + + case 2: + judas_forwardmod(); + break; + + case 3: + judas_forwards3m(); + break; + } + } + break; + case 75: + { + switch(filetype) { + case 1: + judas_rewindxm(); + break; + + case 2: + judas_rewindmod(); + break; + + case 3: + judas_rewinds3m(); + break; + } + } + break; + case '+': + { + if (mastervolume < 20) mastervolume++; else mastervolume += 5; + if (mastervolume > 255) mastervolume = 255; + judas_setmusicmastervolume(CHANNELS, mastervolume); + judas_clipped = 0; + } + break; + case '-': + { + if (mastervolume < 21) mastervolume--; else mastervolume -= 5; + if (mastervolume < 0) mastervolume = 0; + judas_setmusicmastervolume(CHANNELS, mastervolume); + judas_clipped = 0; + } + break; + case 'D': + { + #ifdef __DJGPP__ + /* use conio.h for DJGPP */ + flushall(); + if (mainscreenon) textmode(LASTMODE); + #else + /* WATCOM C++ */ + flushall(); + if (mainscreenon) _setvideomode(_DEFAULTMODE); + #endif + printf("Type \"exit\" to return to JP..."); + #ifdef __DJGPP__ + flushall(); + #else + flushall(); + #endif + spawnl(P_WAIT, getenv("COMSPEC"), NULL); + if (!filewriter) if (!judas_init(mixrate, mixer, mixmode, interpolation)) goto ERROR; + if (mainscreenon) buildmainscreen(); + } + break; + case 27: + { + if (c == 0) getch(); + switch(filetype) + { + /* + * Freeing isn't necessary but we do it just + * to make sure it doesn't crash (page fault + * will occur when freeing memory if heap has + * been corrupted by the player!) + */ + case 1: + judas_freexm(); + break; + + case 2: + judas_freemod(); + break; + + case 3: + judas_frees3m(); + break; + } + goto BYEBYE; + } + break; + } + } + BYEBYE: + #ifdef __DJGPP__ + /* use conio.h for DJGPP */ + flushall(); + if (mainscreenon) textmode(LASTMODE); + #else + /* WATCOM C++ */ + flushall(); + if (mainscreenon) _setvideomode(_DEFAULTMODE); + #endif + if (filewriter) { + if (judas_wavwriter_close(wavhandle) == -1) { + printf("WAV WRITER ERROR: Error finishing the WAV file!"); + return 1; + }; + } + printf("Bye! Thank you for using Judas Sound System :)\n"); + if (judas_clipped) printf("Try playing the same song with mastervol %d to avoid clipping", (int)((float)100*mastervolume/(1+(float)400*judas_clipped/256))); + return 0; +} + +static void handle_int(int a) +{ + exit(0); /* Atexit functions will be called! */ +} diff --git a/JP.DJ b/JP.DJ new file mode 100644 index 0000000..f601b81 --- /dev/null +++ b/JP.DJ @@ -0,0 +1,43 @@ +# +# JUDAS library examples makefile for DJGPP v2.0 +# +# note : nasm compiles for coff output : +# nasm jasmdj.asm -ddjgpp -fcoff -E!nasm.err +# rem nasm jasmdj.asm -dwatcom -fobj -E!nasm.err +# + +CC = gcc +AS = nasm +AR = ar +RANLIB = ranlib +STRIP = strip +COFF2EXE = stubify +AFLAGS = -ddjgpp -fcoff +CFLAGS = -g -c -Wall -march=pentium -O2 +LFLAGS = -g + +OBJS = jp.o timer.o timerasm.o + +LIB = judaslib.a + +all : jp.exe clean + @echo done. + +# make the judas player jp.exe +jp.exe : $(OBJS) $(LIB) + $(CC) -o jp $(OBJS) $(LIB) $(LFLAGS) +# $(STRIP) jp + $(COFF2EXE) jp + @del jp > nul + +jp.o : jp.c + $(CC) $(CFLAGS) $< + +timer.o : timer.c + $(CC) $(CFLAGS) $< + +timerasm.o : timerasm.asm + $(AS) $(AFLAGS) $< + +clean : + @if exist *.o del *.o > nul diff --git a/JP.WAT b/JP.WAT new file mode 100644 index 0000000..6f4c77e --- /dev/null +++ b/JP.WAT @@ -0,0 +1,12 @@ +# JUDAS PLAYER makefile + +jp.exe: jp.obj timer.obj timerasm.obj +# wlink F jp,timer,timerasm N jp.exe L judas.lib SYS dos4g + wcl386 -d2 -ldos4g jp.c timer.c timerasm.obj judas.lib + +jp.obj: jp.c + wcc386 -d2 -w3 -zp4 jp.c +timer.obj: timer.c + wcc386 -d2 -w3 -zp4 timer.c +timerasm.obj: timerasm.asm + nasm -dwatcom -fobj timerasm.asm diff --git a/JUDAS.C b/JUDAS.C new file mode 100644 index 0000000..a1a1f94 --- /dev/null +++ b/JUDAS.C @@ -0,0 +1,3004 @@ +/* + * JUDAS Apocalyptic Softwaremixing Sound System V2.10d + * original version 2.06y by Faust & Yehar + * extended version 2.10b with Intel ICH AC97/HDA support by Black Spider + * Please be aware that HDA support in this version falls under the GNU/GPL + * license. The HDA code was written after analyzing various HDA linux related + * sources, the drivers from ALSA project made by Takashi Iwai, the OSS sound + * system project and others. Although this code is different and pretty unique + * some similarities especially in the HDA nodes access strategy can be found. + * That is why I decided to publicly release this version of Judas under the + * GNU/GPL license. + * + * Supported: + * - SOUND BLASTER (8bit, mono, up to 22050 Hz) + * - SOUND BLASTER PRO (8bit, stereo, up to 22050 Hz or mono up to 44100 Hz) + * - SOUND BLASTER 16 (16bit, stereo, up to 44100 Hz) + * - ULTRASOUND (16bit, stereo, up to 44100 Hz) + * - INTEL ICH0/ICH/ICH2/ICH3/ICH4 AC97 CODEC (16bit, stereo, up to 48000 Hz) + * - High Definition Audio codecs (downgrade to 16bit stereo at 48000 Hz max) + * - WAV file output + * - XMs, MODs, S3Ms, WAVs, raw samples + * + * Other features: + * - Clipping of sound output + * - Interpolation options: None, linear, cubic + * - Quality mixer: Click removing, high quality, 32 bit, clipping indicator + * + * This is the main module, where soundcards & mixer are initialized. It's not + * very good coding style, but who cares! + * + * Changes: + * V2.01 Doesn't fuck up if soundcard variables contain XXX-shit! + * V2.04y Added selectable quality mixer. Old mixer is now called fast mixer + * Added file writer + * V2.06y Wrote "char *volatile pos" instead of old "volatile char *pos"! + * Removed file writer and made the WAV writer in JUDASWAV.C + * V2.07a Support for Intel ICH0/ICH/ICH2/ICH3 and compatible AC97 Codecs + * added by Black Spider + * (Read JUDAS.DOC for complete history) + * V2.07y Improved Intel ICH0/ICH/ICH2/ICH3 AC97 code + * Added ICH4 controller support + * V2.08y Improved Intel ICH4 controller support + * (Read JUDAS.DOC for complete history) + * V2.09a Added support for SIS7012 - now it works :) + * V2.09b Corrected ICH4 and above chips support + * V2.09c dev version + * V2.09d corrected dev version with full WATCOM C++ & DJGPP support + * V2.09e all AC97 devices now use memory mapped IO if available + * V2.09f WAV library builder added to JUDAS + * V2.10a Intel HDA codecs support added + * basing on info from ALSA sources and other interesting HDA resources + * on the web i was finally able to add High Definition audio codecs support + * for Judas Sound System (this is not a public release). + * V2.10b judas_setwavlib function added - first public release with HDA code + * licensed under the GNU/GPL + * V2.10d HDA codecs support update for intel 5, 6, 7 series test by RayeR + */ + +/* + * Borland / Watcom REGS structure compatibility + */ +#ifdef __DJGPP__ +#define _BORLAND_DOS_REGS +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "judasdma.h" +#include "judasmem.h" +#include "judascfg.h" +#include "judasgus.h" +#include "judaserr.h" +#include "judasac.h" + +#ifdef __DJGPP__ +#include +#include +#include +#define HANDLE_PRAGMA_PACK_PUSH_POP 1 +/* define as nothing nothing */ +#define interrupt +/* make port access compatible with Watcom */ +#define inp(P) inportb(P) +#define inpw(P) inportw(P) +#define inpd(P) inportl(P) +#define outp(P,V) outportb(P,V) +#define outpw(P,V) outportw(P,V) +#define outpd(P,V) outportl(P,V) +#else +#define __djgpp_conventional_base 0 +#endif + +/* + Debug or retail release (only for AC97/HDA related code) +*/ +// #define AC97_DEBUG +// #define HDA_DEBUG + +/* + * Sound device numbers + */ +#define DEV_NOSOUND 0 +#define DEV_SB 1 +#define DEV_SBPRO 2 +#define DEV_SB16 3 +#define DEV_GUS 4 +#define DEV_AC97 5 +#define DEV_HDA 6 +#define DEV_FILE 7 + +/* + * Interrupt controller ports + */ +#define PICPORT1 0x20 +#define PICPORT2 0xa0 +#define PICMASK1 0x21 +#define PICMASK2 0xa1 + +/* + * Mixer numbers + */ +#define FASTMIXER 1 +#define QUALITYMIXER 2 + +/* + * Mixmode bits + */ +#define MONO 0 +#define STEREO 1 +#define EIGHTBIT 0 +#define SIXTEENBIT 2 + +/* + * Voicemode bits + */ +#define VM_OFF 0 +#define VM_ON 1 +#define VM_LOOP 2 +#define VM_16BIT 4 + +/* + * Sample & channel structures + */ +typedef struct +{ + char *start; + char *repeat; + char *end; + char *vuprofile; + unsigned char voicemode; +} SAMPLE; + +#pragma pack(push,1) +typedef struct +{ + char *volatile pos; + char *repeat; + char *end; + SAMPLE *volatile sample; + unsigned freq; + volatile unsigned short fractpos; + unsigned char mastervol; + unsigned char panning; + signed short vol; + volatile unsigned char voicemode; + + /* For quality mixer */ + volatile char prevvm; + char *volatile prevpos; + volatile int lastvalleft; + volatile int lastvalright; + volatile int smoothvoll; + volatile int smoothvolr; +} CHANNEL; +#pragma pack(pop) + + +/* + * Prototypes + */ +void judas_config(void); +int judas_init(unsigned mixrate, unsigned mixer, unsigned mixmode, int interpolation); +void judas_uninit(void); +int judas_songisplaying(void); + +static int judas_lock(void); +static int initmixer(void); +static void sb_delay(void); +static void sb_write(unsigned char value); +static unsigned char sb_read(void); +static int sb_reset(void); +static void sb_getversion(void); + +static int gus_detect(void); +static void gus_reset(void); +static void gus_delay(void); +static void gus_setupchannels(void); +static void gus_setupchannel(unsigned char channel, unsigned start, unsigned length); +static void gus_stopchannel(unsigned char channel); + +/* debug and log functions for ac97 code - internal to judas.c */ +static void ac97_set_error_message(char *message); +static void ac97_display_error_message(void); +static void ac97_clear_error_message(void); +static void logmessage(const char *text,...); + +/* pci stuff for ich ac97 compatible codecs */ +static BYTE pci_config_read_byte(int index); +static WORD pci_config_read_word(int index); +static DWORD pci_config_read_dword(int index); +static void pci_config_write_byte(int index, BYTE data); +static void pci_config_write_word(int index, WORD data); +static void pci_config_write_dword(int index, DWORD data); +static BOOL pci_check_bios(void); +static BOOL pci_find_device(); +static void pci_enable_io_access(); +static void pci_enable_memory_access(); +static void pci_enable_busmaster(); +static BOOL detect_windows(void); +static BOOL detect_os2(void); +static BOOL detect_linux(void); + +/* ac97 audio mixer registers access helper functions */ +static BOOL ac97_codec_semaphore(void); +static WORD ac97_read_codec(BYTE index); +static void ac97_write_codec(BYTE index, WORD data); +static void ac97_stop(void); +static int ac97_reset(void); +static unsigned int ich_INL (int base, unsigned int a); +static unsigned short ich_INW (int base, unsigned short a); +static unsigned char ich_INB (int base, unsigned char a); +static void ich_OUTL (unsigned int d, int base, unsigned int a); +static void ich_OUTW (unsigned short d, int base, unsigned short a); +static void ich_OUTB (unsigned char d, int base, unsigned char a); + +/* HDA audio mixer registers access helper functions */ +static unsigned int hda_calc_stream_format (int mixrate); + +/* HDA register access */ +static unsigned int hda_INL (unsigned int a); +static unsigned short hda_INW (unsigned int a); +static unsigned char hda_INB (unsigned int a); +static void hda_OUTL (unsigned int a, unsigned int d); +static void hda_OUTW (unsigned int a, unsigned short d); +static void hda_OUTB (unsigned int a, unsigned char d); + +/* HDA Basic read/write to codecs - Single immediate command instead of CORB/RIRB buffers for simplicity */ +static BOOL hda_single_send_cmd (unsigned short nid, unsigned int direct, unsigned int verb, unsigned int param); +static unsigned int hda_single_get_response (void); +static unsigned int hda_codec_read (unsigned short nid, unsigned int direct, unsigned int verb, unsigned int param); +static unsigned int hda_param_read (unsigned short nid ,unsigned int param); +static int hda_codec_write (unsigned short nid, unsigned int direct, unsigned int verb, unsigned int param); + +/* HDA main functions */ +static void hda_codec_stop (void); +static void hda_codec_start (void); +extern long hda_getbufpos (void); +static BOOL hda_reset (void); + +/* HDA mixer functions */ +static unsigned int hda_mixer_init (void); +static void hda_set_volume (unsigned long reg, unsigned long val); +static unsigned long hda_get_volume (unsigned long reg); + +/* nodes identification prototypes */ +static unsigned int hda_get_sub_nodes (unsigned short nid, unsigned short *start_node); +static void hda_search_audio_node (void); +static int hda_get_connections (unsigned short nid, unsigned short *conn_list, int max_conns); +static int hda_add_new_node (struct hda_node *node, unsigned short nid); +static struct hda_node *hda_get_node (unsigned short nid); + +/* nodes functions prototypes */ +static void hda_set_vol_mute (unsigned short nid, int ch, int direction, int index, int val); +static unsigned int hda_get_vol_mute (unsigned short nid, int ch, int direction, int index); +static int hda_codec_amp_update (unsigned short nid, int ch, int direction, int idx, int mask, int val); +static void hda_unmute_output (struct hda_node *node); +static void hda_unmute_input (struct hda_node *node, unsigned int index); +static int hda_select_input_connection (struct hda_node *node, unsigned int index); +static void hda_clear_check_flags (void); + +/* output path - select connections */ +static int hda_parse_output_path (struct hda_node *node, int dac_idx); +static struct hda_node *hda_parse_output_jack (int jack_type); +static int hda_parse_output (void); + + +/* + * Assembler functions in JUDASASM.ASM / JASMDJ.ASM + */ +void judas_update(void); +void interrupt sb_handler(void); +void interrupt sb_aihandler(void); +void interrupt sb16_handler(void); +void interrupt gus_handler(void); + +void gus_poke(unsigned location, unsigned char data); +unsigned char gus_peek(unsigned location); +void gus_startchannels(void); +void gus_dmaprogram(unsigned pos, unsigned length); +void gus_dmainit(void); +void gus_dmawait(void); + +void ac97_poke(unsigned location, unsigned char data); +unsigned char ac97_peek(unsigned location); +void ac97_startchannels(void); +void ac97_dmaprogram(unsigned pos, unsigned length); +void ac97_dmainit(void); +void ac97_dmawait(void); + +void fmixer(void *address, int length); +void qmixer(void *address, int length); +void safemixer(void *address, int length); +void normalmix(void); +void ipmix(void); +void qmix_linear(void); +void qmix_cubic(void); +void judas_code_lock_start(void); +void judas_code_lock_end(void); +unsigned short judas_get_ds(void); + + +/* + * Variables + */ +int judas_error = JUDAS_OK; +void (*judas_player)(void) = NULL; +void (*judas_mixroutine)(void) = &normalmix; +void (*judas_mixersys)(void *address, int length) = &qmixer; + +#ifdef __DJGPP__ +/* DJGPP version + _go32_dpmi_seginfo structure contains ( unsigned long size; unsigned long pm_offset; unsigned short pm_selector; unsigned short rm_offset; unsigned short rm_segment; ) */ +static _go32_dpmi_seginfo judas_oldvect; +static _go32_dpmi_seginfo judas_newvect; + +#else +/* WATCOM C++ version */ +static void (__interrupt __far *judas_oldvect)(); +static void (__interrupt __far *judas_newvect)(); +#endif + +unsigned judascfg_device = DEV_NOSOUND; +unsigned judascfg_port = -1; +unsigned judascfg_irq = -1; +unsigned judascfg_dma1 = -1; +unsigned judascfg_dma2 = -1; +unsigned judas_irqcount = 0; +unsigned judas_device; +unsigned judas_port; +unsigned judas_irq; +unsigned judas_dma; +unsigned judas_int; +unsigned judas_mixrate; +unsigned judas_mixpos; +unsigned judas_bufferlength; +unsigned judas_buffermask; +unsigned judas_bpmcount; +int *judas_clipbuffer; +int *judas_zladdbuffer; /* Alustukset, „„li”! *** */ +int judas_zerolevell = 0; +int judas_zerolevelr = 0; +short *judas_cliptable; +int *judas_volumetable; +unsigned short judas_ds; +static unsigned short dsp_version; +static unsigned char mixer_firsttime = 1; +static unsigned char judas_locked = 0; +static unsigned char judas_oldpicmask1; +static unsigned char judas_oldpicmask2; +unsigned char judas_initialized = 0; +unsigned char judas_mixer; +unsigned char judas_mixmode; +unsigned char judas_bpmtempo; +unsigned char judas_samplesize; +CHANNEL judas_channel[CHANNELS]; +char judas_clipped = 0; +char *filewriterbuffer; +int filewriterbuffersize = 65536; +AUDIO_PCI_DEV audio_pci = {0}; /* Pci device structure for AC97/HDA */ +unsigned long hda_civ = 0; +unsigned long hda_lpib = 0; + + +/* + * JUDAS device names + */ +char *judas_devname[] = +{ + "No Sound", + "Sound Blaster", + "Sound Blaster Pro", + "Sound Blaster 16", + "UltraSound", + "AC97 Audio Codec", + "HDA Audio Codec", + "File Writer" +}; + +/* + * Mixer names + */ +char *judas_mixername[] = +{ + "(No mixer, big shit will happen)", + "Fast Mixer", + "Quality Mixer", +}; + +/* + * Mixmode names + */ +char *judas_mixmodename[] = +{ + "8-bit mono", + "8-bit stereo", + "16-bit mono", + "16-bit stereo" +}; + +/* + * Interpolation mode names + */ +char *judas_ipmodename[] = +{ + "(?)", + "(?)" +}; + +/* + * JUDAS error texts + */ +char *judas_errortext[] = +{ + "Everything OK", + "Couldn't open file", + "Couldn't read file", + "Incorrect file format", + "Out of memory", + "Hardware init failure", + "Configuration incorrect", + "Out of channels" +}; + +static unsigned char gus_dmalatch[] = +{ + 0x40, 0x41, 0x40, 0x42, 0x40, 0x43, 0x44, 0x45 +}; + +static unsigned char gus_irqlatch[] = +{ + 0x40, 0x40, 0x40, 0x43, 0x40, 0x42, 0x40, 0x44, + 0x40, 0x41, 0x40, 0x45, 0x46, 0x40, 0x40, 0x47 +}; + +/* + * Fake sample returned by the routines when in NOSOUND mode + */ +SAMPLE fakesample = {NULL, NULL, NULL, NULL, VM_OFF}; + +int judas_songisplaying(void) { + return (judas_player != NULL)? 1:0; +} + + +/******************************************************************** + * Error and debug functions for AC97 (internal to judas.c) + ********************************************************************/ + +#define AC97_ERROR_MESSAGE_BUFFER_SIZE 256 +static char ac97_error_message[AC97_ERROR_MESSAGE_BUFFER_SIZE] = {0}; + +static void ac97_set_error_message(char *message) +{ + int count = 0; + char cvalue; + + while (count < AC97_ERROR_MESSAGE_BUFFER_SIZE - 3) { + cvalue = message[count]; + if (!cvalue) break; + ac97_error_message[count] = cvalue; + count++; + } + ac97_error_message[count] = '\n'; + ac97_error_message[count + 1] = '\0'; +} + +static void ac97_display_error_message(void) +{ + printf(">>> %s", &ac97_error_message[0]); +} + +static void ac97_clear_error_message(void) +{ + ac97_error_message[0] = '\0'; +} + +static void logmessage(const char *text,...) +{ + va_list arg; + + va_start(arg, text); + vfprintf(stdout, text, arg); + va_end(arg); +} + + +/******************************************************************** + * Intel PCI BIOS helper funtions + ********************************************************************/ + +#ifndef PCI_ANY_ID +#define PCI_ANY_ID ((WORD)(~0)) +#endif + +static BYTE pci_config_read_byte(int index) +{ + union REGS r; + + memset(&r, 0, sizeof(r)); + r.x.eax = 0x0000B108; // config read byte + r.x.ebx = (DWORD)audio_pci.device_bus_number; + r.x.edi = (DWORD)index; + int386(0x1a, &r, &r); + if (r.h.ah != 0) { + #ifdef AC97_DEBUG + logmessage("Error : PCI read config byte failed\n"); + #endif + r.x.ecx = 0; + } + return (BYTE)r.x.ecx; +} + +static WORD pci_config_read_word(int index) +{ + union REGS r; + + memset(&r, 0, sizeof(r)); + r.x.eax = 0x0000B109; // config read word + r.x.ebx = (DWORD)audio_pci.device_bus_number; + r.x.edi = (DWORD)index; + int386(0x1a, &r, &r); + if (r.h.ah != 0 ){ + #ifdef AC97_DEBUG + logmessage("Error : PCI read config word failed\n"); + #endif + r.x.ecx = 0; + } + return (WORD)r.x.ecx; +} + +static DWORD pci_config_read_dword(int index) +{ + union REGS r; + + memset(&r, 0, sizeof(r)); + r.x.eax = 0x0000B10A; // config read dword + r.x.ebx = (DWORD)audio_pci.device_bus_number; + r.x.edi = (DWORD)index; + int386(0x1a, &r, &r); + if (r.h.ah != 0 ){ + #ifdef AC97_DEBUG + logmessage("Error : PCI read config dword failed\n"); + #endif + r.x.ecx = 0; + } + return (DWORD)r.x.ecx; +} + +static void pci_config_write_byte(int index, BYTE data) +{ + union REGS r; + + memset(&r, 0, sizeof(r)); + r.x.eax = 0x0000B10B; // config write byte + r.x.ebx = (DWORD)audio_pci.device_bus_number; + r.x.ecx = (DWORD)data; + r.x.edi = (DWORD)index; + int386(0x1a, &r, &r); + if (r.h.ah != 0 ){ + #ifdef AC97_DEBUG + logmessage("Error : PCI write config byte failed\n"); + #endif + } +} + +static void pci_config_write_word(int index, WORD data) +{ + union REGS r; + + memset(&r, 0, sizeof(r)); + r.x.eax = 0x0000B10C; // config write word + r.x.ebx = (DWORD)audio_pci.device_bus_number; + r.x.ecx = (DWORD)data; + r.x.edi = (DWORD)index; + int386(0x1a, &r, &r); + if (r.h.ah != 0 ){ + #ifdef AC97_DEBUG + logmessage("Error : PCI write config word failed\n"); + #endif + } +} + +static void pci_config_write_dword(int index, DWORD data) +{ + union REGS r; + + memset(&r, 0, sizeof(r)); + r.x.eax = 0x0000B10D; // config write dword + r.x.ebx = (DWORD)audio_pci.device_bus_number; + r.x.ecx = (DWORD)data; + r.x.edi = (DWORD)index; + int386(0x1a, &r, &r); + if (r.h.ah != 0 ){ + #ifdef AC97_DEBUG + logmessage("Error : PCI write config dword failed\n"); + #endif + } +} + +static BOOL pci_check_bios(void) +{ + union REGS r; + + memset(&r, 0, sizeof(r)); + r.x.eax = 0x0000B101; // PCI BIOS - installation check + r.x.edi = 0x00000000; + int386(0x1a, &r, &r); + if (r.x.edx != 0x20494350) return FALSE; // ' ICP' identifier found ? + return TRUE; +} + +static BOOL pci_find_device() +{ + union REGS r; + + memset(&r, 0, sizeof(r)); + r.x.eax = 0x0000B102; // PCI BIOS - find PCI device + r.x.ecx = audio_pci.device_id; // device ID + r.x.edx = audio_pci.vender_id; // vender ID + r.x.esi = 0x00000000; // device index + int386(0x1a, &r, &r); + if (r.h.ah != 0 ) return FALSE; // device not found + audio_pci.device_bus_number = r.w.bx; // save device & bus/funct number + if(audio_pci.sub_vender_id != PCI_ANY_ID){ + // check subsystem vender id + if(pci_config_read_word(0x2C) != audio_pci.sub_vender_id) return FALSE; + } + if(audio_pci.sub_device_id != PCI_ANY_ID){ + // check subsystem device id + if(pci_config_read_word(0x2E) != audio_pci.sub_device_id) return FALSE; + } + return TRUE; // device found +} + +static void pci_enable_io_access() +{ + pci_config_write_word(0x04, pci_config_read_word(0x04) | BIT0); +} + +static void pci_enable_memory_access() +{ + pci_config_write_word(0x04, pci_config_read_word(0x04) | BIT1); +} + +static void pci_enable_busmaster() +{ + pci_config_write_word(0x04, pci_config_read_word(0x04) | BIT2); +} + + +/******************************************************************** + * Windows/OS2/Linux detection helper functions + ********************************************************************/ + +static BOOL detect_windows(void) // Win 3.1+ detection +{ + union REGS r; + + memset(&r, 0, sizeof(r)); + r.x.eax = 0x1600; + int386(0x2F, &r, &r); + if ((r.h.al & 0x7F) != 0) return TRUE; + return FALSE; +} + +static BOOL detect_os2(void) // OS2 2.0+ detection +{ + union REGS r; + + memset(&r, 0, sizeof(r)); + r.x.eax = 0x4010; + int386(0x2F, &r, &r); + if (r.w.ax == 0) return TRUE; + return FALSE; +} + +static BOOL detect_linux(void) // Linux DOSEMU detection ??? +{ + union REGS r; + + memset(&r, 0, sizeof(r)); + r.x.eax = 0x0200; + r.x.ebx = 0x00E6; + int386(0x31, &r, &r); // segment of this vector should be 0xF000 + if (r.w.cx != 0xF000) return FALSE; + + memset(&r, 0, sizeof(r)); + r.x.eax = 0; + int386(0xE6, &r, &r); + if (r.w.ax == 0xAA55) return TRUE; + return FALSE; +} + + +/******************************************************************** + * Judas config and init procedures + ********************************************************************/ + +void judas_config(void) +{ + char *envstr; + judascfg_device = DEV_NOSOUND; + + + /* + * Try to find BLASTER enviroment variable + */ + envstr = getenv("BLASTER"); + if (envstr) + { + judascfg_device = DEV_SB; + judascfg_port = -1; + judascfg_irq = -1; + judascfg_dma1 = -1; + judascfg_dma2 = -1; + while (*envstr) + { + unsigned sb_type = 0; + if ((*envstr == 'A') || (*envstr == 'a')) sscanf(envstr + 1, "%x", &judascfg_port); + if ((*envstr == 'I') || (*envstr == 'i')) sscanf(envstr + 1, "%d", &judascfg_irq); + if ((*envstr == 'D') || (*envstr == 'd')) + { + sscanf(envstr + 1, "%d", &judascfg_dma1); + } + if ((*envstr == 'T') || (*envstr == 't')) + { + sscanf(envstr + 1, "%d", &sb_type); + if ((sb_type == 2) || (sb_type == 4)) judascfg_device = DEV_SBPRO; + } + if ((*envstr == 'H') || (*envstr == 'h')) + { + sscanf(envstr + 1, "%d", &judascfg_dma2); + judascfg_device = DEV_SB16; + } + envstr++; + } + } + + /* + * Try to find ULTRASND enviroment variable + */ + envstr = getenv("ULTRASND"); + if (envstr) + { + unsigned irq2; + judascfg_device = DEV_GUS; + judascfg_port = -1; + judascfg_irq = -1; + judascfg_dma1 = -1; + judascfg_dma2 = -1; + sscanf(envstr, "%x,%d,%d,%d,%d", &judascfg_port, &judascfg_dma1, + &judascfg_dma2, &judascfg_irq, &irq2); + /* If MIDI IRQ is lower then use it (for DOS4G) */ + if (irq2 < judascfg_irq) judascfg_irq = irq2; + } + + /* + * Try to detect Intel ICH AC97 via PCI access + */ + if (judascfg_device == DEV_NOSOUND) + { + int device; + if (detect_windows() == TRUE) { + ac97_set_error_message("Error : AC97 Audio Codec driver works only in pure DOS.\n"); + return; + } + if (detect_os2() == TRUE) { + ac97_set_error_message("Error : AC97 Audio Codec driver works only in pure DOS.\n"); + return; + } + if (detect_linux() == TRUE) { + ac97_set_error_message("Error : AC97 Audio Codec driver works only in pure DOS.\n"); + return; + } + if (pci_check_bios() == FALSE) { + ac97_set_error_message("Error : PCI BIOS not found.\n"); + return; + } + device = 0; + while (audio_dev_list[device].vender_id != 0x0000) { + audio_pci.vender_id = audio_dev_list[device].vender_id; + audio_pci.device_id = audio_dev_list[device].device_id; + audio_pci.sub_vender_id = audio_dev_list[device].sub_vender_id; + audio_pci.sub_device_id = audio_dev_list[device].sub_device_id; + if (pci_find_device() == TRUE){ + #ifdef AC97_DEBUG + logmessage("%s found.\n", audio_dev_list[device].string); + #endif + break; + } + device++; + } + if (audio_dev_list[device].vender_id == NULL) { + ac97_set_error_message("Error : AC97/HDA Audio Codec compatible device could not be found.\n"); + return; + } + + // save device info + audio_pci.device_type = audio_dev_list[device].type; + strcpy (audio_pci.device_name, audio_dev_list[device].string); + + // read pci configuration + audio_pci.command = pci_config_read_word (PCI_COMMAND); + audio_pci.irq = pci_config_read_byte (PCI_INTERRUPT_LINE); + audio_pci.pin = pci_config_read_byte (PCI_INT_LINE); + audio_pci.base0 = pci_config_read_dword (PCI_BASE_ADDRESS_0); // NAMBAR + audio_pci.base1 = pci_config_read_dword (PCI_BASE_ADDRESS_1); // NABMBAR + + #ifdef AC97_DEBUG + logmessage("AC97/HDA Audio Codec PCI IRQ at %d\n", audio_pci.irq); + logmessage("AC97/HDA Audio Codec IRQ PIN nr %d\n", audio_pci.pin); + #endif + + // if memory type IO is enabled then use memory type IO (other manufacturers) + if (audio_pci.command & PCI_COMMAND_MEMORY) audio_pci.mem_mode = 1; + + // start configuring devices + switch (audio_pci.device_type) { + case DEVICE_INTEL_ICH4: + // try to go for memory type IO as default for ICH4+ Intel controllers even if disabled in PCI command config + audio_pci.mem_mode = 1; + break; + case DEVICE_NFORCE: + pci_config_write_dword (0x4c, pci_config_read_dword (0x4c) | 0x1000000); + break; + + case DEVICE_HDA_INTEL: + audio_pci.hda_mode = 1; + audio_pci.mem_mode = 1; + break; + case DEVICE_HDA_ATI: + audio_pci.hda_mode = 1; + audio_pci.mem_mode = 1; + // enable snoop for ATI SB450 Azalia HD Audio + pci_config_write_byte (0x42, (pci_config_read_byte (0x42) & 0xf8) | 0x2); + break; + case DEVICE_HDA_NVIDIA: + audio_pci.hda_mode = 1; + audio_pci.mem_mode = 1; + // enable snoop for nVidia Azalia HD Audio + pci_config_write_byte (0x4e, (pci_config_read_byte (0x4e) & 0xf0) | 0x0f); + break; + case DEVICE_HDA_SIS: + audio_pci.hda_mode = 1; + audio_pci.mem_mode = 1; + break; + case DEVICE_HDA_ULI: + audio_pci.hda_mode = 1; + audio_pci.mem_mode = 1; + pci_config_write_word (0x40, pci_config_read_word (0x40) | 0x10); + pci_config_write_dword (PCI_MEM_BASE_ADDRESS_1, 0); + break; + case DEVICE_HDA_VIA: + audio_pci.hda_mode = 1; + audio_pci.mem_mode = 1; + break; + + default: + break; + } + + // HDA configuration + if (audio_pci.hda_mode) { + audio_pci.base0 = pci_config_read_dword (PCI_MEM_BASE_ADDRESS_0); // AZBAR + audio_pci.base0 &= ~7; + #ifdef AC97_DEBUG + logmessage("HDA Audio Codec PCI BASE0 at I/O %04X\n", audio_pci.base0); + #endif + + // map linear memory - convert physical address to linear address + if (!DPMI_MapMemory ((unsigned long *)&audio_pci.base0, (unsigned long *)&audio_pci.base0, 0x4000)) audio_pci.mem_mode = 0; + + // enable memory, IO and bus master - activate the device + audio_pci.command |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY; + pci_config_write_word (PCI_COMMAND, audio_pci.command); + + // Clear bits 0-2 of PCI register TCSEL (Traffic Class Select Register at offset 0x44) + // Ensuring these bits are 0 clears playback static on some Azalia HD Audio codecs + pci_config_write_byte (0x44, pci_config_read_byte (0x44) & 0xf8); + + // HDA config done now we need to initialize the codec + + // AC97 configuration + } else { + // get I/O base0 port - this is the Audio Mixer base port + audio_pci.base0 = pci_config_read_dword (PCI_BASE_ADDRESS_0); // NAMBAR + // Remove I/O space marker in bit 0 + audio_pci.base0 &= ~0xf; + #ifdef AC97_DEBUG + logmessage("AC97 Audio Codec PCI BASE0 at I/O %04X\n", audio_pci.base0); + #endif + + // get I/O base1 port - this is the Bus Master base port + audio_pci.base1 = pci_config_read_dword (PCI_BASE_ADDRESS_1); // NABMBAR + // Remove I/O space marker in bit 0 + audio_pci.base1 &= ~0xf; + judascfg_port = audio_pci.base1; + #ifdef AC97_DEBUG + logmessage("AC97 Audio Codec PCI BASE1 at I/O %04X\n", audio_pci.base1); + #endif + + // get memory mapped IO for ICH4+ and other new chips + if ((audio_pci.base0 == 0) || (audio_pci.mem_mode)) { + audio_pci.base2 = pci_config_read_dword (PCI_MEM_BASE_ADDRESS_2); // NAMBAR + audio_pci.base3 = pci_config_read_dword (PCI_MEM_BASE_ADDRESS_3); // NABMBAR + + // map linear memory - convery physical address to linear address + if (!DPMI_MapMemory ((unsigned long *)&audio_pci.base2, (unsigned long *)&audio_pci.base2, 0x1000)) audio_pci.mem_mode = FALSE; + if (!DPMI_MapMemory ((unsigned long *)&audio_pci.base3, (unsigned long *)&audio_pci.base3, 0x1000)) audio_pci.mem_mode = FALSE; + + // for information purposes only + if (audio_pci.mem_mode) judascfg_port = audio_pci.base3; + } + + // legacy I/O access + if (audio_pci.mem_mode == FALSE) { + if (audio_pci.device_type == DEVICE_INTEL_ICH4) { + // enable the IOSE bit in 0x41 for legacy mode for ICH4/ICH5 + pci_config_write_byte (PCI_ICH4_CFG_REG, 1); + // Set the secondary codec ID + pci_config_write_byte (PCI_COMMAND_PARITY, 0x39); + } + + // enable IO and bus master + audio_pci.command |= PCI_COMMAND_MASTER | PCI_COMMAND_IO; + pci_config_write_word (PCI_COMMAND, audio_pci.command); + + } else { + // enable port IO access compatibility even though we're going for memory mapped IO !!! + if (audio_pci.device_type == DEVICE_INTEL_ICH4) { + // enable the IOSE bit in 0x41 for legacy mode for ICH4/ICH5 + pci_config_write_byte (PCI_ICH4_CFG_REG, 1); + // Set the secondary codec ID + pci_config_write_byte (PCI_COMMAND_PARITY, 0x39); + } + + // enable memory, IO and bus master + audio_pci.command |= PCI_COMMAND_MASTER | PCI_COMMAND_IO | PCI_COMMAND_MEMORY; + pci_config_write_word (PCI_COMMAND, audio_pci.command); + } + + // AC97 config done now we need to initialize the codec + } + + // test if both I/O range and memory mapped range are NULL + // note : memory range address will be NULL if DPMI_MapMemory function fails for some reason + if ((audio_pci.base0 == 0) && (audio_pci.mem_mode == 0)) { + ac97_set_error_message("Error : AC97/HDA normal I/O and memory mapped I/O access failed!\n"); + if (audio_pci.base0) DPMI_UnmapMemory ((unsigned long *)&audio_pci.base0); + if (audio_pci.base2) DPMI_UnmapMemory ((unsigned long *)&audio_pci.base2); + if (audio_pci.base3) DPMI_UnmapMemory ((unsigned long *)&audio_pci.base3); + return; // still DEV_NOSOUND at this point + } + + if ((audio_pci.base0 == 0) && (audio_pci.base2 == 0)) { + ac97_set_error_message("Error : AC97/HDA Audio Codec device found, but disabled.\n"); + if (audio_pci.base0) DPMI_UnmapMemory ((unsigned long *)&audio_pci.base0); + if (audio_pci.base2) DPMI_UnmapMemory ((unsigned long *)&audio_pci.base2); + if (audio_pci.base3) DPMI_UnmapMemory ((unsigned long *)&audio_pci.base3); + return; // still DEV_NOSOUND at this point + } + + if (audio_pci.hda_mode) judascfg_device = DEV_HDA; + else judascfg_device = DEV_AC97; + } +} + +int judas_init (unsigned mixrate, unsigned mixer, unsigned mixmode, int interpolation) +{ + int sbrate = 0; + + /* + enable near pointers for DJGPP + some DPMI hosts might reject this call + */ + judas_error = JUDAS_ILLEGAL_CONFIG; + #ifdef __DJGPP__ + if (__djgpp_nearptr_enable() == 0) return 0; + /* Trick: Avoid re-setting DS selector limit on each memory allocation call */ + __djgpp_selector_limit = 0xffffffff; + #endif + + /* + * Check for illegal values + */ + judas_error = JUDAS_ILLEGAL_CONFIG; + if (judascfg_device > DEV_FILE) return 0; + if (judascfg_device != DEV_NOSOUND && judascfg_device != DEV_AC97 && judascfg_device != DEV_HDA && judascfg_device != DEV_FILE) + { + if (judascfg_port < 0x200) return 0; + if (judascfg_port > 0x2ff) return 0; + if (judascfg_irq < 2) return 0; + if (judascfg_irq > 15) return 0; + if (judascfg_dma1 > 7) return 0; + if (judascfg_device == DEV_SB16) + { + if (judascfg_dma2 > 7) return 0; + } + if (judascfg_irq == 9) judascfg_irq = 2; + } + if (mixrate < 5000) mixrate = 5000; + if (mixrate > 44100 && judascfg_device != DEV_AC97 && judascfg_device != DEV_HDA) mixrate = 44100; + if ((mixer != FASTMIXER) && (mixer != QUALITYMIXER)) return 0; + + /* enable pci bus master and i/o access for AC97 */ + if (judascfg_device == DEV_AC97) + { + pci_enable_busmaster(); + pci_enable_io_access(); + if (audio_pci.mem_mode) pci_enable_memory_access(); + } + + /* enable pci bus master and mem access for HDA */ + if (judascfg_device == DEV_HDA) + { + pci_enable_busmaster(); + if (audio_pci.mem_mode) pci_enable_memory_access(); + } + + /* + * If user wants to re-initialize, shutdown first + */ + if (judas_initialized) judas_uninit(); + judas_mixrate = mixrate; + judas_mixmode = mixmode & (SIXTEENBIT | STEREO); + judas_mixer = mixer; + + /* + * Copy the config to currently used values + */ + judas_device = judascfg_device; + judas_port = judascfg_port; + judas_irq = judascfg_irq; + judas_dma = judascfg_dma1; + if (mixer == QUALITYMIXER) { + judas_mixersys = &qmixer; + if (interpolation) judas_mixroutine = &qmix_cubic; + else judas_mixroutine = &qmix_linear; + judas_ipmodename[0] = "Linear"; + judas_ipmodename[1] = "Cubic"; + } else { + judas_ipmodename[0] = "No"; + judas_ipmodename[1] = "Linear"; + judas_mixersys = &fmixer; + if (interpolation) judas_mixroutine = &ipmix; + else judas_mixroutine = &normalmix; + } + + /* + * If it's NOSOUND, don't go further + */ + if (judas_device == DEV_NOSOUND) + { + judas_error = JUDAS_OK; + return 1; + } + + /* + * Detect soundcard + */ + judas_error = JUDAS_HARDWARE_ERROR; + switch (judas_device) + { + case DEV_SB: + { + sbrate = 256 - 1000000 / judas_mixrate; + if (sbrate > 210) sbrate = 210; + judas_mixrate = 1000000 / (256 - sbrate); + } + if (!sb_reset()) return 0; + sb_getversion(); + + #ifdef __DJGPP__ + /* DJGPP version */ + if (dsp_version > 0x200) judas_newvect.pm_offset = (unsigned long) sb_aihandler; + else judas_newvect.pm_offset = (unsigned long) sb_handler; + judas_newvect.pm_selector = (unsigned short) _go32_my_cs(); + + #else + /* WATCOM C++ version */ + if (dsp_version > 0x200) judas_newvect = &sb_aihandler; + else judas_newvect = &sb_handler; + #endif + + judas_mixmode = EIGHTBIT | MONO; + break; + + case DEV_SBPRO: + if (judas_mixmode & STEREO) + { + sbrate = 256 - 500000 / judas_mixrate; + if (sbrate > 233) sbrate = 233; + judas_mixrate = 500000 / (256 - sbrate); + } + else + { + sbrate = 256 - 1000000 / judas_mixrate; + if (sbrate > 233) sbrate = 233; + judas_mixrate = 1000000 / (256 - sbrate); + } + if (!sb_reset()) return 0; + sb_getversion(); + if (dsp_version < 0x300) return 0; + + #ifdef __DJGPP__ + /* DJGPP version */ + judas_newvect.pm_offset = (unsigned long) sb_aihandler; + judas_newvect.pm_selector = (unsigned short) _go32_my_cs(); + + #else + /* WATCOM C++ version */ + judas_newvect = &sb_aihandler; + #endif + + judas_mixmode &= STEREO; + break; + + case DEV_SB16: + if (!sb_reset()) return 0; + sb_getversion(); + if (dsp_version < 0x400) return 0; + + #ifdef __DJGPP__ + /* DJGPP version */ + if (judas_mixmode & SIXTEENBIT) + { + judas_dma = judascfg_dma2; + judas_newvect.pm_offset = (unsigned long) sb16_handler; + } + else judas_newvect.pm_offset = (unsigned long) sb_aihandler; + judas_newvect.pm_selector = (unsigned short) _go32_my_cs(); + + #else + /* WATCOM C++ version */ + if (judas_mixmode & SIXTEENBIT) + { + judas_dma = judascfg_dma2; + judas_newvect = &sb16_handler; + } + else judas_newvect = &sb_aihandler; + #endif + break; + + case DEV_GUS: + if (!gus_detect()) return 0; + gus_reset(); + gus_dmainit(); + { + unsigned gus_rate; + + /* + * This stupidity is needed to keep mixrate above 5000 + */ + if (judas_mixrate < 5100) judas_mixrate = 5100; + gus_rate = (judas_mixrate << 9) / 44100; + judas_mixrate = (gus_rate * 44100) >> 9; + } + + #ifdef __DJGPP__ + /* DJGPP version */ + judas_newvect.pm_offset = (unsigned long) gus_handler; + judas_newvect.pm_selector = (unsigned short) _go32_my_cs(); + + #else + /* WATCOM C++ version */ + judas_newvect = &gus_handler; + #endif + break; + + case DEV_AC97: + if (!ac97_reset()) return 0; + /* only 7 rates supported by AC97 - 48000Hz included */ + if (judas_mixrate < 11025) judas_mixrate = 8000; + else if (judas_mixrate < 16000) judas_mixrate = 11025; + else if (judas_mixrate < 22050) judas_mixrate = 16000; + else if (judas_mixrate < 32000) judas_mixrate = 22050; + else if (judas_mixrate < 44100) judas_mixrate = 32000; + else if (judas_mixrate < 48000) judas_mixrate = 44100; + else if (judas_mixrate > 48000) judas_mixrate = 48000; + if (!audio_pci.ac97_vra_supported) judas_mixrate = 48000; + /* force 16-bit stereo output */ + judas_mixmode = STEREO | SIXTEENBIT; + break; + + case DEV_HDA: + /* reset, initialize the HDA and setup output node */ + if (!hda_reset()) return 0; + /* only 7 rates supported by HDA - 48000Hz included */ + if (judas_mixrate < 11025) judas_mixrate = 8000; + else if (judas_mixrate < 16000) judas_mixrate = 11025; + else if (judas_mixrate < 22050) judas_mixrate = 16000; + else if (judas_mixrate < 32000) judas_mixrate = 22050; + else if (judas_mixrate < 44100) judas_mixrate = 32000; + else if (judas_mixrate < 48000) judas_mixrate = 44100; + else if (judas_mixrate > 48000) judas_mixrate = 48000; + /* force 16-bit stereo output */ + judas_mixmode = STEREO | SIXTEENBIT; + break; + } + + /* + * Calculate sample size & buffer length, set initial mixing pos. + */ + judas_samplesize = 1; + judas_buffermask = 0xfffffff8; + if (judas_mixmode & STEREO) + { + judas_samplesize <<= 1; + judas_buffermask <<= 1; + } + if (judas_mixmode & SIXTEENBIT) + { + judas_samplesize <<= 1; + judas_buffermask <<= 1; + } + /* + * For Standard GUS, mask is always 64 bytes to ensure proper DMA + * transfer alignment even with 16-bit DMA + stereo output. + */ + if (judas_device == DEV_GUS) + { + judas_buffermask = 0xffffffc0; + } + if (judas_device == DEV_HDA) + { + judas_buffermask = 0xffffff00; + } + judas_bufferlength = judas_mixrate / PER_SECOND * judas_samplesize; + if (judas_bufferlength < 512) judas_bufferlength = 512; + if (judas_bufferlength > (DMA_MAXSIZE - 64)) judas_bufferlength = DMA_MAXSIZE - 64; + judas_bufferlength &= judas_buffermask; + judas_mixpos = 0; + + /* + * Reserve dma buffer, initialize mixer tables and lock JUDAS code & + * data, each of them must be successful + */ + judas_error = JUDAS_OUT_OF_MEMORY; + if (judas_device != DEV_FILE && judas_device != DEV_AC97 && judas_device != DEV_HDA) { + if (!dma_reserve(44100 / PER_SECOND * 4 + 64)) return 0; + } + + if (judas_device == DEV_AC97) { + int count; + audio_pci.pcmout_buffer0 = (DWORD *)dos_malloc(judas_bufferlength); + audio_pci.pcmout_buffer1 = (DWORD *)dos_malloc(judas_bufferlength); + if (!audio_pci.pcmout_buffer0 || !audio_pci.pcmout_buffer1) return 0; + audio_pci.bdl_buffer = (DWORD *)dos_malloc (256); // allocate memory for BDL (256 bytes) + if (!audio_pci.bdl_buffer) return 0; + count = 0; + while (count < 64){ + // in low DOS memory + *(audio_pci.bdl_buffer + count + (__djgpp_conventional_base >> 2)) = (DWORD) audio_pci.pcmout_buffer0; + *(audio_pci.bdl_buffer + 2 + count + (__djgpp_conventional_base >> 2)) = (DWORD) audio_pci.pcmout_buffer1; + if(audio_pci.device_type == DEVICE_SIS){ // bit 30 - BUP enabled + *(audio_pci.bdl_buffer + 1 + count + (__djgpp_conventional_base >> 2)) = BIT30 + judas_bufferlength; // in bytes + *(audio_pci.bdl_buffer + 3 + count + (__djgpp_conventional_base >> 2)) = BIT30 + judas_bufferlength; // in bytes + }else{ + *(audio_pci.bdl_buffer + 1 + count + (__djgpp_conventional_base >> 2)) = BIT30 + (judas_bufferlength >> 1); // in samples (16bit stereo) + *(audio_pci.bdl_buffer + 3 + count + (__djgpp_conventional_base >> 2)) = BIT30 + (judas_bufferlength >> 1); // in samples (16bit stereo) + } + count += 4; + } + ich_OUTL ((DWORD)audio_pci.bdl_buffer, CTL_BASE, ICH_REG_PO_BDBAR); // set BDL PCM OUT address (low DOS memory) + ich_OUTB (0x1F, CTL_BASE, ICH_REG_PO_LVI); // set LVI to 31 (last index) + } + + if (judas_device == DEV_HDA) { + int count; + unsigned int mem_aligned; + + // clear the BDL info in HDA codec (just in case) + int timeout; + unsigned char val; + unsigned long uval; + + // stop DMA engine if playing and disable interrupts (Interrupt on Completion, FIFO Error Interrupt, Descriptor Error Interrupt) - RUN bit set to 0 + hda_OUTB (HDA_SDO0CTL, hda_INB (HDA_SDO0CTL) & ~(SD_CTL_DMA_START | SD_INT_MASK)); + delay (1); + + // Software must read a 0 from the DMA run bit before modifying related control registers or restarting the DMA engine + timeout = 300; + while (timeout--) { + if (!(hda_INB (HDA_SDO0CTL) & SD_CTL_DMA_START)) break; + delay (1); + } + + // reset the HDA stream 0 + hda_OUTB (HDA_SDO0CTL, hda_INB (HDA_SDO0CTL) | SD_CTL_STREAM_RESET); + delay (1); + + timeout = 300; + while (!((val = hda_INB (HDA_SDO0CTL)) & SD_CTL_STREAM_RESET) && --timeout) delay(1); + val &= ~SD_CTL_STREAM_RESET; + hda_OUTB (HDA_SDO0CTL, val); + delay (1); + + // wait for out of reset confirmation from hardware + timeout = 300; + while (((val = hda_INB (HDA_SDO0CTL)) & SD_CTL_STREAM_RESET) && --timeout) delay(1); + + // clear stream 0 data since we're not using other output streams anyway + hda_OUTL (HDA_DPLBASE, 0); // DMA Position Lower Base Address + hda_OUTL (HDA_DPUBASE, 0); // DMA Position Upper Base Address + hda_OUTL (HDA_SDO0BDLPL, 0); // SD0 outpuy BDL - lower + hda_OUTL (HDA_SDO0BDLPU, 0); // SD0 output BDL - upper + // hda_OUTL ( HDA_SDO0CTL, 0); // SD0 output stream descriptor control + + // prepare BDL in low DOS memory - we will have at least 32 entries in the BDL (but allocate for 256 entries) + // perform 4096 bytes boundary alignement - page align + audio_pci.hda_buffer = (DWORD *)dos_malloc ((judas_bufferlength * 2) + 8192 + (4096 * 3)); + if (!audio_pci.hda_buffer) return 0; + // set buffer 0 + mem_aligned = (((unsigned int)audio_pci.hda_buffer + 4095) & (~4095)); + audio_pci.pcmout_buffer0 = (unsigned int *) mem_aligned; + + // set buffer 1 + mem_aligned += judas_bufferlength; + mem_aligned = ((mem_aligned + 4095) & (~4095)); + audio_pci.pcmout_buffer1 = (unsigned int *) mem_aligned; + + // set BDL and clear it + mem_aligned += judas_bufferlength; + mem_aligned = ((mem_aligned + 4095) & (~4095)); + audio_pci.bdl_buffer = (unsigned int *) mem_aligned; + memset (audio_pci.bdl_buffer, 0, 8192); + + count = 0; + while (count < 128) { + // in low DOS memory (address lower 32bits, address upper 32bits, size field of BDL entry, IOC flag) + unsigned int off = count << 3; /* 8 dword step */ + // 4 DWORD steps + *(audio_pci.bdl_buffer + 0 + off + (__djgpp_conventional_base >> 2)) = (DWORD) audio_pci.pcmout_buffer0; + *(audio_pci.bdl_buffer + 1 + off + (__djgpp_conventional_base >> 2)) = 0; + *(audio_pci.bdl_buffer + 2 + off + (__djgpp_conventional_base >> 2)) = judas_bufferlength; + *(audio_pci.bdl_buffer + 3 + off + (__djgpp_conventional_base >> 2)) = 0; // no IOC + // 4 DWORD steps + *(audio_pci.bdl_buffer + 4 + off + (__djgpp_conventional_base >> 2)) = (DWORD) audio_pci.pcmout_buffer1; + *(audio_pci.bdl_buffer + 5 + off + (__djgpp_conventional_base >> 2)) = 0; + *(audio_pci.bdl_buffer + 6 + off + (__djgpp_conventional_base >> 2)) = judas_bufferlength; + *(audio_pci.bdl_buffer + 7 + off + (__djgpp_conventional_base >> 2)) = 0; // no IOC + count++; + } + + // program the stream tag - bits 20 - 23 of SD_CTL register + audio_pci.stream_tag = 1; + hda_OUTL (HDA_SDO0CTL, (hda_INL (HDA_SDO0CTL) & ~SD_CTL_STREAM_TAG_MASK) | (audio_pci.stream_tag << SD_CTL_STREAM_TAG_SHIFT)); + delay (1); + uval = hda_INL (HDA_SDO0CTL); + + // program the length of samples in cyclic buffer + hda_OUTL (HDA_SDO0CBL, judas_bufferlength); + delay (1); + uval = hda_INL (HDA_SDO0CBL); + + // program the stream format on the controller - this value needs to be the same as the one programmed later for output widget (16bits PCM) + audio_pci.dacout_num_bits = 16; + audio_pci.dacout_num_channels = 2; + audio_pci.format_val = hda_calc_stream_format (judas_mixrate); + hda_OUTW (HDA_SDO0FORMAT, audio_pci.format_val); + delay (1); + uval = hda_INW (HDA_SDO0FORMAT); + + // program the stream LVI (last valid index) of the BDL - set it to 31 + hda_OUTW (HDA_SDO0LVI, 31); + delay (1); + uval = hda_INW (HDA_SDO0LVI); + + // program the BDL address (lower 32bits and upper 32bits) + hda_OUTL (HDA_SDO0BDLPL, (DWORD) audio_pci.bdl_buffer); + delay (1); + uval = hda_INL (HDA_SDO0BDLPL); + hda_OUTL (HDA_SDO0BDLPU, 0); + delay (1); + uval = hda_INL (HDA_SDO0BDLPU); + + // enable the DMA position buffer to have info on current DMA position which is updated every frame by hardware (not initialized for JUDAS at this stage) + // if (!(hda_INL ( HDA_DPLBASE) & HDA_DPLBASE_ENABLE)) hda_OUTL ( HDA_DPLBASE, audio_pci.pcmout_dma_pos_ptr | HDA_DPLBASE_ENABLE); + // disable the DMA position buffer + hda_OUTL (HDA_DPLBASE, hda_INL (HDA_DPLBASE) & ~HDA_DPLBASE_ENABLE); + delay (1); + uval = hda_INL (HDA_DPLBASE); + + // setup stream 1 - link it to our output widget programmed earlier (channel ID is 0) and program the same format as on the controller (16bits PCM) + if (audio_pci.dac_node[0]) { + hda_codec_write (audio_pci.dac_node[0]->nid, 0, AC_VERB_SET_CHANNEL_STREAMID, (audio_pci.stream_tag << 4) | 0); + delay (150); + hda_codec_write (audio_pci.dac_node[0]->nid, 0, AC_VERB_SET_STREAM_FORMAT, audio_pci.format_val); + } + if (audio_pci.dac_node[1]) { + hda_codec_write (audio_pci.dac_node[0]->nid, 0, AC_VERB_SET_CHANNEL_STREAMID, (audio_pci.stream_tag << 4) | 0); + delay (150); + hda_codec_write (audio_pci.dac_node[0]->nid, 0, AC_VERB_SET_STREAM_FORMAT, audio_pci.format_val); + } + } + + if (!initmixer()) return 0; + if (!judas_lock()) return 0; + judas_ds = judas_get_ds(); + + /* + * Unmask IRQ & set vector + */ + if (judas_device != DEV_FILE && judas_device != DEV_AC97 && judas_device != DEV_HDA) { + judas_oldpicmask1 = inp(PICMASK1); + judas_oldpicmask2 = inp(PICMASK2); + if (judas_irq < 8) + { + outp(PICMASK1, judas_oldpicmask1 & ~(1 << judas_irq)); + judas_int = judas_irq + 0x8; + } + else + { + outp(PICMASK1, judas_oldpicmask1 & 0xfb); + outp(PICMASK2, judas_oldpicmask2 & ~(1 << (judas_irq & 3))); + judas_int = judas_irq + 0x68; + } + + #ifdef __DJGPP__ + /* DJGPP version */ + _go32_dpmi_get_protected_mode_interrupt_vector(judas_int, &judas_oldvect); + printf("Old handler at %04X:%08X\n", judas_oldvect.pm_selector, (unsigned int)judas_oldvect.pm_offset); + printf("New handler at %04X:%08X\n", judas_newvect.pm_selector, (unsigned int)judas_newvect.pm_offset); +// _go32_dpmi_allocate_iret_wrapper(&judas_newvect); +// printf("New wrapper at %04X:%08X\n", judas_newvect.pm_selector, (unsigned int)judas_newvect.pm_offset); + _go32_dpmi_set_protected_mode_interrupt_vector(judas_int, &judas_newvect); + + #else + /* WATCOM C++ version */ + judas_oldvect = _dos_getvect(judas_int); + _dos_setvect(judas_int, judas_newvect); + #endif + } + + /* + * Everything is prepared. Now just set up the soundcard for output! + */ + switch (judas_device) + { + case DEV_SB: + memset((char *)dma_address + __djgpp_conventional_base, 0x80, judas_bufferlength); + dma_program(DMA_WRITE_LOOP, 0, judas_bufferlength); + sb_write(0xd1); + sb_write(0x40); + sb_write(sbrate); + if (dsp_version <= 0x200) + { + sb_write(0x14); + sb_write(0xf0); + sb_write(0xff); + } + else + { + sb_write(0x48); + sb_write(0xf0); + sb_write(0xff); + sb_write(0x1c); + } + break; + + case DEV_SBPRO: + memset((char *)dma_address + __djgpp_conventional_base, 0x80, judas_bufferlength); + if (judas_mixmode & STEREO) + { + int timeout = 0xfffff; + + outp(judas_port + 4, 0xe); + outp(judas_port + 5, inp(judas_port + 5) | 0x2); + /* + * To make left & right correct, send one silent + * byte with singlecycle output, and wait for the + * transfer to complete. + */ + judas_irqcount = 0; + dma_program(DMA_WRITE_ONESHOT, 0, 1); + sb_write(0x14); + sb_write(0x0); + sb_write(0x0); + while (!judas_irqcount) + { + timeout--; + if (!timeout) break; + } + } + else + { + outp(judas_port + 4, 0xe); + outp(judas_port + 5, inp(judas_port + 5) & 0xfd); + } + dma_program(DMA_WRITE_LOOP, 0, judas_bufferlength); + sb_write(0xd1); + sb_write(0x40); + sb_write(sbrate); + sb_write(0x48); + sb_write(0xf0); + sb_write(0xff); + /* Use highspeed mode if timeconstant > 210 */ + if (sbrate > 210) sb_write(0x90); + else sb_write(0x1c); + break; + + case DEV_SB16: + sb_write(0x41); + sb_write(judas_mixrate >> 8); + sb_write(judas_mixrate & 0xff); + if (judas_mixmode & SIXTEENBIT) memset((char *)dma_address + __djgpp_conventional_base, 0, judas_bufferlength); + else memset((char *)dma_address + __djgpp_conventional_base, 0x80, judas_bufferlength); + dma_program(DMA_WRITE_LOOP, 0, judas_bufferlength); + if (judas_mixmode & SIXTEENBIT) + { + if (judas_mixmode & STEREO) + { + sb_write(0xb6); + sb_write(0x30); + sb_write(0xf0); + sb_write(0xff); + } + else + { + sb_write(0xb6); + sb_write(0x10); + sb_write(0xf0); + sb_write(0xff); + } + } + else + { + if (judas_mixmode & STEREO) + { + sb_write(0xc6); + sb_write(0x20); + sb_write(0xf0); + sb_write(0xff); + } + else + { + sb_write(0xc6); + sb_write(0x00); + sb_write(0xf0); + sb_write(0xff); + } + } + break; + + case DEV_GUS: + if (judas_mixmode & SIXTEENBIT) memset((char *)dma_address + __djgpp_conventional_base, 0, judas_bufferlength + 64); + else memset((char *)dma_address + __djgpp_conventional_base, 0x80, judas_bufferlength + 64); + gus_dmaprogram(0, judas_bufferlength + 64); + gus_dmawait(); + gus_setupchannels(); + gus_startchannels(); + break; + + case DEV_AC97: + /* clear for 8 and 16bit - although now ac97 will only work with 16bit */ + if (judas_mixmode & SIXTEENBIT) memset((char *)audio_pci.pcmout_buffer0 + __djgpp_conventional_base, 0, judas_bufferlength); + else memset((char *)audio_pci.pcmout_buffer0 + __djgpp_conventional_base, 0x80, judas_bufferlength); + if (judas_mixmode & SIXTEENBIT) memset((char *)audio_pci.pcmout_buffer1 + __djgpp_conventional_base, 0, judas_bufferlength); + else memset((char *)audio_pci.pcmout_buffer1 + __djgpp_conventional_base, 0x80, judas_bufferlength); + /* 16bit PCM, stereo sound */ + { + DWORD data; + data = ich_INL (CTL_BASE, ICH_REG_GLOB_CNT); + switch (audio_pci.device_type) { + case DEVICE_SIS: + data = data & ~(ICH_SIS_PCM_246_MASK | ICH_20_BIT | ICH_24_BIT); + data = data & ~ICH_GIE; + break; + + /* as for DEVICE_INTEL */ + default: + data = data & ~(ICH_PCM_246_MASK | ICH_20_BIT | ICH_24_BIT); + data = data & ~ICH_GIE; + break; + } + + ich_OUTL (data, CTL_BASE, ICH_REG_GLOB_CNT); + if (audio_pci.ac97_vra_supported) ac97_write_codec (AC97_PCM_FRONT_DAC_RATE, judas_mixrate); + delay(100); + + /* start the Bus Master DMA engine */ + ich_OUTB (ICH_STARTBM, CTL_BASE, ICH_REG_PO_CR); + } + break; + + case DEV_HDA: + /* clear for 8 and 16bit - although now ac97 will only work with 16bit */ + if (judas_mixmode & SIXTEENBIT) memset((char *)audio_pci.pcmout_buffer0 + __djgpp_conventional_base, 0, judas_bufferlength); + else memset((char *)audio_pci.pcmout_buffer0 + __djgpp_conventional_base, 0x80, judas_bufferlength); + if (judas_mixmode & SIXTEENBIT) memset((char *)audio_pci.pcmout_buffer1 + __djgpp_conventional_base, 0, judas_bufferlength); + else memset((char *)audio_pci.pcmout_buffer1 + __djgpp_conventional_base, 0x80, judas_bufferlength); + + /* start the Bus Master DMA engine with 16bit PCM stereo sound programmed earlier */ +// hda_OUTB (HDA_SDO0CTL, hda_INB (HDA_SDO0CTL) | SD_CTL_DMA_START); + break; + + } + judas_initialized = 1; + judas_clipped = 0; + judas_error = JUDAS_OK; + return 1; +} + + +void judas_uninit(void) +{ + judas_error = JUDAS_OK; + if (!judas_initialized) return; + judas_initialized = 0; + /* + * Soundblaster is best shut down by doing a DSP reset twice. For SBPRO, + * we also deactivate the stereo mode. GUS is shut down by simply + * stopping the channels. + */ + switch (judas_device) + { + case DEV_SB: + case DEV_SB16: + sb_reset(); + sb_reset(); + break; + + case DEV_SBPRO: + sb_reset(); + sb_reset(); + outp(judas_port + 4, 0xe); + outp(judas_port + 5, inp(judas_port + 5) & 0xfd); + break; + + case DEV_GUS: + gus_stopchannel(0); + gus_stopchannel(1); + break; + + case DEV_AC97: + ac97_stop(); + dos_free(audio_pci.bdl_buffer); + dos_free(audio_pci.pcmout_buffer1); + dos_free(audio_pci.pcmout_buffer0); + break; + + case DEV_HDA: + hda_codec_stop(); + if (audio_pci.hda_buffer) dos_free (audio_pci.hda_buffer); + if (audio_pci.afg_nodes) locked_free (audio_pci.afg_nodes); + break; + } + /* + * Then restore the PIC mask and IRQ vector. + */ + if (judas_device != DEV_FILE && judas_device != DEV_AC97 && judas_device != DEV_HDA) { + outp(PICMASK1, judas_oldpicmask1); + outp(PICMASK2, judas_oldpicmask2); + #ifdef __DJGPP__ + /* DJGPP version */ + _go32_dpmi_set_protected_mode_interrupt_vector(judas_int, &judas_oldvect); +// _go32_dpmi_free_iret_wrapper(&judas_newvect); + #else + /* WATCOM C++ version */ + _dos_setvect(judas_int, judas_oldvect); + #endif + } +} + + +/***************************************************************************** + Sound Blaster stuff +*****************************************************************************/ + +static void sb_delay(void) +{ + unsigned char temp; + char counter = 15; + + while (counter--) temp = inp(judas_port + 6); +} + +static void sb_write(unsigned char value) +{ + int timeout = 0xfffff; + while (inp(judas_port + 12) & 0x80) + { + timeout--; + if (!timeout) return; + } + outp(judas_port + 12, value); +} + +static unsigned char sb_read(void) +{ + int timeout = 0xfffff; + while (!(inp(judas_port + 14) & 0x80)) + { + timeout--; + if (!timeout) return 0; + } + return inp(judas_port + 10); +} + +static int sb_reset(void) +{ + outp(judas_port + 6, 1); + sb_delay(); + outp(judas_port + 6, 0); + if (sb_read() == 0xaa) return 1; + return 0; +} + +static void sb_getversion(void) +{ + sb_write(0xe1); + dsp_version = sb_read() << 8; + dsp_version += sb_read(); +} + + +/***************************************************************************** + GUS stuff +*****************************************************************************/ + +static void gus_delay(void) +{ + int count = 70; + unsigned char temp; + while (count--) temp = inp(judas_port); +} + +static int gus_detect(void) +{ + outp(judas_port + GF1_REG_SELECT, MASTER_RESET); + outp(judas_port + GF1_DATA_HI, 0x0); + gus_delay(); + outp(judas_port + GF1_REG_SELECT, MASTER_RESET); + outp(judas_port + GF1_DATA_HI, GF1_MASTER_RESET); + gus_delay(); + gus_poke(0, 0xaa); + gus_poke(1, 0x55); + if (gus_peek(0) != 0xaa) return 0; + return 1; +} + +static void gus_reset(void) +{ + unsigned char temp; + + outp(judas_port + 0xf, 0x5); + outp(judas_port, ENABLE_LINE_IN | ENABLE_OUTPUT); + outp(judas_port + GF1_IRQ_CTRL, 0x0); + outp(judas_port + 0xf, 0x0); + outp(judas_port, ENABLE_LINE_IN | ENABLE_OUTPUT); + outp(judas_port + GF1_IRQ_CTRL, gus_dmalatch[judas_dma] | 0x80); + outp(judas_port, ENABLE_LINE_IN | ENABLE_OUTPUT | SELECT_GF1_REG); + outp(judas_port + GF1_IRQ_CTRL, gus_irqlatch[judas_irq]); + outp(judas_port, ENABLE_LINE_IN | ENABLE_OUTPUT); + outp(judas_port + GF1_IRQ_CTRL, gus_dmalatch[judas_dma] | 0x80); + outp(judas_port, ENABLE_LINE_IN | ENABLE_OUTPUT | SELECT_GF1_REG); + outp(judas_port + GF1_IRQ_CTRL, gus_irqlatch[judas_irq]); + outp(judas_port + GF1_PAGE, 0x0); + outp(judas_port, ENABLE_LINE_IN | ENABLE_GF1_IRQ); + outp(judas_port + GF1_REG_SELECT, DMA_CONTROL); + outp(judas_port + GF1_DATA_HI, 0x0); + outp(judas_port + GF1_REG_SELECT, TIMER_CONTROL); + outp(judas_port + GF1_DATA_HI, 0x0); + outp(judas_port + GF1_REG_SELECT, SAMPLE_CONTROL); + outp(judas_port + GF1_DATA_HI, 0x0); + outp(judas_port + GF1_REG_SELECT, SET_VOICES); + outp(judas_port + GF1_DATA_HI, 13 | 0xc0); + temp = inp(judas_port + GF1_IRQ_STAT); + outp(judas_port + GF1_REG_SELECT, DMA_CONTROL); + temp = inp(judas_port + GF1_DATA_HI); + outp(judas_port + GF1_REG_SELECT, SAMPLE_CONTROL); + temp = inp(judas_port + GF1_DATA_HI); + outp(judas_port + GF1_REG_SELECT, GET_IRQV); + temp = inp(judas_port + GF1_DATA_HI); + for (temp = 0; temp < 32; temp++) + { + outp(judas_port + GF1_PAGE, temp); + outp(judas_port + GF1_REG_SELECT, SET_CONTROL); + outp(judas_port + GF1_DATA_HI, VOICE_STOPPED | STOP_VOICE); + gus_delay(); + outp(judas_port + GF1_DATA_HI, VOICE_STOPPED | STOP_VOICE); + outp(judas_port + GF1_REG_SELECT, SET_VOLUME_CONTROL); + outp(judas_port + GF1_DATA_HI, VOLUME_STOPPED | STOP_VOLUME); + gus_delay(); + outp(judas_port + GF1_DATA_HI, VOLUME_STOPPED | STOP_VOLUME); + outp(judas_port + GF1_REG_SELECT, SET_VOLUME); + outpw(judas_port + GF1_DATA_LOW, 0x0); + outp(judas_port + GF1_REG_SELECT, SET_START_HIGH); + outpw(judas_port + GF1_DATA_LOW, 0x0); + outp(judas_port + GF1_REG_SELECT, SET_START_LOW); + outpw(judas_port + GF1_DATA_LOW, 0x0); + outp(judas_port + GF1_REG_SELECT, SET_END_HIGH); + outpw(judas_port + GF1_DATA_LOW, 0x0); + outp(judas_port + GF1_REG_SELECT, SET_END_LOW); + outpw(judas_port + GF1_DATA_LOW, 0x0); + outp(judas_port + GF1_REG_SELECT, SET_ACC_HIGH); + outpw(judas_port + GF1_DATA_LOW, 0x0); + outp(judas_port + GF1_REG_SELECT, SET_ACC_LOW); + outpw(judas_port + GF1_DATA_LOW, 0x0); + } + temp = inp(judas_port + GF1_IRQ_STAT); + outp(judas_port + GF1_REG_SELECT, DMA_CONTROL); + temp = inp(judas_port + GF1_DATA_HI); + outp(judas_port + GF1_REG_SELECT, SAMPLE_CONTROL); + temp = inp(judas_port + GF1_DATA_HI); + outp(judas_port + GF1_REG_SELECT, GET_IRQV); + temp = inp(judas_port + GF1_DATA_HI); + outp(judas_port + GF1_REG_SELECT, MASTER_RESET); + outp(judas_port + GF1_DATA_HI, GF1_MASTER_RESET | GF1_OUTPUT_ENABLE | GF1_MASTER_IRQ); +} + +static void gus_setupchannels(void) +{ + if (judas_mixmode & SIXTEENBIT) + { + if (judas_mixmode & STEREO) + { + gus_setupchannel(0, 0, judas_bufferlength >> 2); + gus_setupchannel(1, (judas_bufferlength >> 2) + 16, judas_bufferlength >> 2); + } + else + { + gus_setupchannel(0, 0, judas_bufferlength >> 1); + gus_setupchannel(1, 0, judas_bufferlength >> 1); + } + } + else + { + if (judas_mixmode & STEREO) + { + gus_setupchannel(0, 0, judas_bufferlength >> 1); + gus_setupchannel(1, (judas_bufferlength >> 1) + 32, judas_bufferlength >> 1); + } + else + { + gus_setupchannel(0, 0, judas_bufferlength); + gus_setupchannel(1, 0, judas_bufferlength); + } + } +} + +static void gus_setupchannel(unsigned char channel, unsigned start, unsigned length) +{ + length += start; /* Length is actually end address */ + outp(judas_port + GF1_PAGE, channel); + outp(judas_port + GF1_REG_SELECT, SET_BALANCE); + outp(judas_port + GF1_DATA_HI, channel * 15); + outp(judas_port + GF1_REG_SELECT, SET_START_HIGH); + outpw(judas_port + GF1_DATA_LOW, start >> 7); + outp(judas_port + GF1_REG_SELECT, SET_START_LOW); + outpw(judas_port + GF1_DATA_LOW, start << 9); + outp(judas_port + GF1_REG_SELECT, SET_END_HIGH); + outpw(judas_port + GF1_DATA_LOW, length >> 7); + outp(judas_port + GF1_REG_SELECT, SET_END_LOW); + outpw(judas_port + GF1_DATA_LOW, length << 9); + outp(judas_port + GF1_REG_SELECT, SET_ACC_HIGH); + outpw(judas_port + GF1_DATA_LOW, start >> 7); + outp(judas_port + GF1_REG_SELECT, SET_ACC_LOW); + outpw(judas_port + GF1_DATA_LOW, start << 9); + outp(judas_port + GF1_REG_SELECT, SET_FREQUENCY); + outpw(judas_port + GF1_DATA_LOW, (((judas_mixrate << 9) + 22050) / 44100) << 1); + outp(judas_port + GF1_REG_SELECT, SET_VOLUME); + outpw(judas_port + GF1_DATA_LOW, 0xf800); +} + +static void gus_stopchannel(unsigned char channel) +{ + outp(judas_port + GF1_PAGE, channel); + outp(judas_port + GF1_REG_SELECT, SET_VOLUME); + outpw(judas_port + GF1_DATA_LOW, 0x0); + outp(judas_port + GF1_REG_SELECT, SET_CONTROL); + outp(judas_port + GF1_DATA_HI, VOICE_STOPPED | STOP_VOICE); + gus_delay(); + outp(judas_port + GF1_DATA_HI, VOICE_STOPPED | STOP_VOICE); +} + + +/***************************************************************************** + AC97 audio mixer registers access helper functions +******************************************************************************/ + +static BOOL ac97_codec_semaphore(void) +{ + DWORD status; + int limit; + + // check if primary codec ready + status = ich_INL (CTL_BASE, ICH_REG_GLOB_STA); // 30h global status register at NABMBAR + if (!(status & ICH_PCR)) return FALSE; // not ready (not important here) + + // wait until codec ready - fail if wait limit exceeded + limit = 100; + while (limit) + { + status = ich_INB (CTL_BASE, ICH_REG_ACC_SEMA); // 34h codec write semaphore register at NABMBAR + if (!(status & ICH_CAS)) return TRUE; + delay(10); + limit--; + } + + // clear semaphore flag + ich_INW (MIXER_BASE, AC97_RESET); // register reset the codec (0) + + return FALSE; // busy (not important here) +} + +static WORD ac97_read_codec(BYTE index) +{ + index &= 0xff; + ac97_codec_semaphore(); + return ich_INW (MIXER_BASE, index); +} + +static void ac97_write_codec(BYTE index, WORD data) +{ + index &= 0xff; + ac97_codec_semaphore(); + ich_OUTW (data, MIXER_BASE, index); +} + +static void ac97_stop(void) +{ + // stop all PCM out data + ich_OUTB (0, CTL_BASE, ICH_REG_PO_CR); // control register at NABMBAR + delay (100); // 100ms delay + + // reset PCM out registers + ich_OUTB (ICH_RESETREGS, CTL_BASE, ICH_REG_PO_CR); // control register at NABMBAR + delay (50); +} + +static int ac97_reset(void) +{ + DWORD data; + WORD wdata; + + // ICH4+ may fail when busmastering is enabled and memory mapped IO is on. Continue and ignore PCR status. + if (!audio_pci.mem_mode) { + // exit if primary codec is not ready + if ((ich_INL (CTL_BASE, ICH_REG_GLOB_STA) & ICH_PCR) == 0) return 0; + } + + // opt for 2 channels with 16bit samples (Global Control Register) + // ACLink is open because we already have the primary codec ready status + data = ich_INL (CTL_BASE, ICH_REG_GLOB_CNT); + switch (audio_pci.device_type) { + case DEVICE_SIS: + data = data & ~(ICH_SIS_PCM_246_MASK | ICH_20_BIT | ICH_24_BIT); + data = data & ~ICH_GIE; + break; + + /* as for DEVICE_INTEL */ + default: + data = data & ~(ICH_PCM_246_MASK | ICH_20_BIT | ICH_24_BIT); + data = data & ~ICH_GIE; + break; + } + ich_OUTL (data, CTL_BASE, ICH_REG_GLOB_CNT); + + // clear semaphore flag by reading Audio Mixer reset register + ich_INW (MIXER_BASE, AC97_RESET); + + // disable interrupts + ich_OUTB (0, CTL_BASE, ICH_REG_PI_CR); // 0Bh control register at NABMBAR + ich_OUTB (0, CTL_BASE, ICH_REG_PO_CR); // 1Bh control register at NABMBAR + ich_OUTB (0, CTL_BASE, ICH_REG_MC_CR); // 2Bh control register at NABMBAR + + // reset channels + ich_OUTB (ICH_RESETREGS, CTL_BASE, ICH_REG_PI_CR); // 0Bh control register at NABMBAR + ich_OUTB (ICH_RESETREGS, CTL_BASE, ICH_REG_PO_CR); // 1Bh control register at NABMBAR + ich_OUTB (ICH_RESETREGS, CTL_BASE, ICH_REG_MC_CR); // 2Bh control register at NABMBAR + + // enable VRA (if supported) and set default dacrate to 44100Hz + if (ac97_read_codec (AC97_EXTENDED_ID) & AC97_EA_VRA) { + ac97_write_codec (AC97_EXTENDED_STATUS, AC97_EA_VRA | ac97_read_codec(AC97_EXTENDED_STATUS)); + ac97_write_codec (AC97_PCM_FRONT_DAC_RATE, 44100); + audio_pci.ac97_vra_supported = TRUE; + } else audio_pci.ac97_vra_supported = FALSE; + + // disable DRA (if supported) + if (ac97_read_codec (AC97_EXTENDED_ID) & AC97_EA_DRA) { + wdata = ac97_read_codec (AC97_EXTENDED_STATUS); + wdata &= ~(AC97_EA_DRA); + ac97_write_codec (AC97_EXTENDED_STATUS, wdata); + } + + return 1; +} + +unsigned int ich_INL (int base, unsigned int a) +{ + if (audio_pci.mem_mode == 1) { + if (base == CTL_BASE) return *(unsigned int *) (audio_pci.base3 + (a)); // bus master base memory + else return *(unsigned int *) (audio_pci.base2 + (a)); // audio mixer base memory + } else { + if (base == CTL_BASE) return inpd (audio_pci.base1 + a); // bus master base IO + else return inpd (audio_pci.base0 + a); // audio mixer base IO + } +} + +unsigned short ich_INW (int base, unsigned short a) +{ + if (audio_pci.mem_mode == 1) { + if (base == CTL_BASE) return *(unsigned short *) (audio_pci.base3 + (a)); // bus master base memory + else return *(unsigned short *) (audio_pci.base2 + (a)); // audio mixer base memory + } else { + if (base == CTL_BASE) return inpw (audio_pci.base1 + a); // bus master base IO + else return inpw (audio_pci.base0 + a); // audio mixer base IO + } +} + +unsigned char ich_INB (int base, unsigned char a) +{ + if (audio_pci.mem_mode == 1) { + if (base == CTL_BASE) return *(unsigned char *) (audio_pci.base3 + (a)); // bus master base memory + else return *(unsigned char *) (audio_pci.base2 + (a)); // audio mixer base memory + } else { + if (base == CTL_BASE) return inp (audio_pci.base1 + a); // bus master base IO + else return inp (audio_pci.base0 + a); // audio mixer base IO + } +} + +void ich_OUTL (unsigned int d, int base, unsigned int a) +{ + if (audio_pci.mem_mode == 1) { + if (base == CTL_BASE) *(unsigned int *) (audio_pci.base3 + (a)) = d; // bus master base memory + else *(unsigned int *) (audio_pci.base2 + (a)) = d; // audio mixer base memory + } else { + if (base == CTL_BASE) outpd (audio_pci.base1 + a, d); // bus master base IO + else outpd (audio_pci.base0 + a, d); // audio mixer base IO + } +} + +void ich_OUTW (unsigned short d, int base, unsigned short a) +{ + if (audio_pci.mem_mode == 1) { + if (base == CTL_BASE) *(unsigned short *) (audio_pci.base3 + (a)) = d; // bus master base memory + else *(unsigned short *) (audio_pci.base2 + (a)) = d; // audio mixer base memory + } else { + if (base == CTL_BASE) outpw (audio_pci.base1 + a, d); // bus master base IO + else outpw (audio_pci.base0 + a, d); // audio mixer base IO + } +} + +void ich_OUTB (unsigned char d, int base, unsigned char a) +{ + if (audio_pci.mem_mode == 1) { + if (base == CTL_BASE) *(unsigned char *) (audio_pci.base3 + (a)) = d; // bus master base memory + else *(unsigned char *) (audio_pci.base2 + (a)) = d; // audio mixer base memory + } else { + if (base == CTL_BASE) outp (audio_pci.base1 + a, d); // bus master base IO + else outp (audio_pci.base0 + a, d); // audio mixer base IO + } +} + + + + + +/***************************************************************************** + HDA audio mixer registers access helper functions + + HDA code falls under the GNU/GPL license + + This code is based on info from ALSA sources and other + GNU/GLP sources like Open Sound System. Although it differs + much from those projects I decided to put it on the same + license as the above mentioned sources - the GNU/GPL license. + +******************************************************************************/ +static unsigned int hda_calc_stream_format (int mixrate) +{ + unsigned int value = 0; + + // we only support 7 rates for HDA - 48000Hz included + if (mixrate < 11025) value = BIT8 + BIT10; // 8000 (48000 / 6) + else if (mixrate < 16000) value = BIT14 + BIT8 + BIT9; // 11025 (44000 / 4) + else if (mixrate < 22050) value = BIT9; // 16000 (48000 / 3) + else if (mixrate < 32000) value = BIT14 + BIT8; // 22050 (44000 / 2) + else if (mixrate < 44100) value = BIT11 + BIT9; // 32000 (96000 / 3) + else if (mixrate < 48000) value = BIT14; // 44100 + else value = 0; // 48000 + + // 8bit not supported - we force 16bits PCM anyway + switch (audio_pci.dacout_num_bits) { + case 16: + value |= BIT4; + break; + case 20: + value |= BIT5; + break; + case 24: + value |= (BIT4 + BIT5); + break; + case 32: + value |= BIT6; + break; + default: + value |= BIT4; + break; + } + + value += (audio_pci.dacout_num_channels - 1); + + return value; +} + +/************************************* + * HDA low level register access + *************************************/ +// AZBAR is base0 only +static unsigned int hda_INL (unsigned int a) +{ + if (audio_pci.hda_mode == 1) return *(unsigned int *) (audio_pci.base0 + (a)); + else return 0; +} + +static unsigned short hda_INW (unsigned int a) +{ + if (audio_pci.hda_mode == 1) return *(unsigned short *) (audio_pci.base0 + (a)); + else return 0; +} + +static unsigned char hda_INB (unsigned int a) +{ + if (audio_pci.hda_mode == 1) return *(unsigned char *) (audio_pci.base0 + (a)); + else return 0; +} + +static void hda_OUTL (unsigned int a, unsigned int d) +{ + if (audio_pci.hda_mode == 1) *(unsigned int *) (audio_pci.base0 + (a)) = d; +} + +static void hda_OUTW (unsigned int a, unsigned short d) +{ + if (audio_pci.hda_mode == 1) *(unsigned short *) (audio_pci.base0 + (a)) = d; +} + +static void hda_OUTB (unsigned int a, unsigned char d) +{ + if (audio_pci.hda_mode == 1) *(unsigned char *) (audio_pci.base0 + (a)) = d; +} + +/********************************************************************************************* + * HDA Basic read/write to codecs - Single immediate command instead of CORB/RIRB buffers for simplicity + *********************************************************************************************/ +static BOOL hda_single_send_cmd (unsigned short nid, unsigned int direct, unsigned int verb, unsigned int param) +{ + int timeout = 1000; + unsigned int value; + + value = (unsigned int) (audio_pci.codec_index & 0x0f) << 28; // CaD - codec address + value |= (unsigned int) direct << 27; // 0 = direct NID reference, 1 = indirect NID reference + value |= (unsigned int) nid << 20; // node ID + value |= verb << 8; // verb + value |= param; // parameter + + while (timeout--) { + if (!(hda_INW (HDA_IRS) & IRS_BUSY)) { + hda_OUTW (HDA_IRS, hda_INW (HDA_IRS) | IRS_VALID); + hda_OUTL (HDA_IC, value); + hda_OUTW (HDA_IRS, hda_INW (HDA_IRS) | IRS_BUSY); + return TRUE; + } + } + return FALSE; +} + +static unsigned int hda_single_get_response (void) +{ + int timeout = 1000; + + while (timeout--) { + if (hda_INW (HDA_IRS) & IRS_VALID) return hda_INL (HDA_IR); + } + return 0; +} + +// standard codec read +static unsigned int hda_codec_read (unsigned short nid, unsigned int direct, unsigned int verb, unsigned int param) +{ + if (hda_codec_write (nid, direct, verb, param) == TRUE) return hda_single_get_response(); + return 0; +} + +// simplified codec read parameters - direct NID reference +static unsigned int hda_param_read (unsigned short nid ,unsigned int param) +{ + return hda_codec_read (nid, 0, AC_VERB_PARAMETERS, param); +} + +// standard codec write +static int hda_codec_write (unsigned short nid, unsigned int direct, unsigned int verb, unsigned int param) +{ + return hda_single_send_cmd (nid, direct, verb, param); +} + +/************************************************************* + * Main HDA functions + *************************************************************/ +// stop and close all output azalia hd audio operations if currently playing +static void hda_codec_stop (void) +{ + int timeout = 300; + + // hda codec stop - stop DMA engine for output and disable interrupts (Interrupt on Completion, FIFO Error Interrupt, Descriptor Error Interrupt) + hda_OUTB (HDA_SDO0CTL, hda_INB (HDA_SDO0CTL) & ~(SD_CTL_DMA_START | SD_INT_MASK)); + + // Software must read a 0 from the DMA run bit before modifying related control registers or restarting the DMA engine + while (timeout--) { + if (!(hda_INB (HDA_SDO0CTL) & SD_CTL_DMA_START)) break; + delay(1); + } + + // hda codec close - reset stream 0 data since we're not using other output streams anyway + hda_OUTL (HDA_DPLBASE, 0); // DMA Position Lower Base Address + hda_OUTL (HDA_DPUBASE, 0); // DMA Position Upper Base Address + hda_OUTL (HDA_SDO0BDLPL, 0); // SD0 outpuy BDL - lower + hda_OUTL (HDA_SDO0BDLPU, 0); // SD0 output BDL - upper + hda_OUTL (HDA_SDO0CTL, 0); // SD0 output stream descriptor control + + // zero out HDA start values in ASM module (current pos and CIV) + hda_civ = 0; + hda_lpib = 0; +} + +// start the DMA engine with currently allocated buffers (only for playback) - obsolete - start is done in ASM module +static void hda_codec_start (void) +{ + // start SD0 output DMA engine + hda_OUTB (HDA_SDO0CTL, hda_INB (HDA_SDO0CTL) | SD_CTL_DMA_START); +} + +// read Link Position in Current Buffer - obsolete - used for testing purposes +extern long hda_getbufpos (void) +{ + unsigned long bufpos; + + // temporary + audio_pci.pcmout_dmasize = judas_bufferlength; + audio_pci.pcmout_bdl_entries = 32; + audio_pci.pcmout_bdl_size = (4 * sizeof(DWORD)) * audio_pci.pcmout_bdl_entries; + + // read Link Position in Current Buffer + bufpos = hda_INL (HDA_SDO0LPIB); + + #ifdef HDA_DEBUG + logmessage ("ctl:%8.8X sts:%8.8X cbl:%d ds:%d ps:%d pn:%d bufpos:%5d", + hda_INB (HDA_SDO0CTL), hda_INL (HDA_SDO0STS), hda_INL (HDA_SDO0CBL), audio_pci.pcmout_dmasize, audio_pci.pcmout_bdl_size, audio_pci.pcmout_bdl_entries, bufpos); + #endif + + if (bufpos < audio_pci.pcmout_dmasize) audio_pci.pcmout_dma_lastgoodpos = bufpos; + + return audio_pci.pcmout_dma_lastgoodpos; +} + +// enable codec, unmute stuff, set output to desired rate +// in = desired sample rate +// out = true or false +static BOOL hda_reset (void) +{ + DWORD flags = 0; + int i = 0; + + // stop the codec if currently playing + hda_codec_stop (); + + // disable global controller interrupts CIE and GIE if enabled + hda_OUTL (HDA_INTCTL, hda_INL (HDA_INTCTL) & ~(HDA_INT_CTRL_EN | HDA_INT_GLOBAL_EN)); + + // --------------------- + // reset controller + // --------------------- + flags = hda_INL (HDA_GCTL); // AZBAR + HDA_GCTL + flags &= ~CRST; + hda_OUTL (HDA_GCTL, flags); // reset bit0 of GCTL + + i = 50; + while (hda_INL (HDA_GCTL) && --i) delay (1); // 1 ms delay + delay(500); // must read 0 to verify the controller is in reset + + // bring controller out of reset + flags = hda_INL (HDA_GCTL); // AZBAR + HDA_GCTL + flags |= CRST; + hda_OUTL (HDA_GCTL, flags); // set bit0 of GCTL + + i = 50; + while (!hda_INL (HDA_GCTL) && --i) delay (1); // 1 ms delay + delay(500); // must read 1 before accessing controller registers + + if (!hda_INL (HDA_GCTL)) return FALSE; // controller not ready - exit with error + + // disable unsolicited responses + hda_OUTL (HDA_GCTL, (hda_INL (HDA_GCTL) & (~UREN)) ); + delay(1); + + // detect codecs + if (!audio_pci.codec_mask) { + audio_pci.codec_mask = hda_INW (HDA_STATESTS); + } + + // ------------------------------------ + // hardware init the controller + //------------------------------------- + // clear interrupt status for stream 0 - set bits in Buffer Completion Interrupt, FIFO Error, Descriptor Error + hda_OUTB (HDA_SDO0STS, SD_INT_MASK); + // enable interrupts for stream 0 - I do not use AC97/HDA interrupts in INCHINIT or JUDAS so skip this part of initialization + // hda_OUTB ( HDA_SDO0CTL, SD_INT_MASK); + // clear STATESTS - Flag bits that indicate which SDI signal(s) received a state change event. The bits are cleared by writing 1’s to them. + hda_OUTW (HDA_STATESTS, STATESTS_INT_MASK); + // clear RIRB status - Response Interrupt, Response Overrun Interrupt Status - not used in ICHINIT and JUDAS either + hda_OUTB (HDA_RIRBSTS, RIRB_INT_MASK); + // clear global interrupt status + hda_OUTL (HDA_INTSTS, HDA_INT_CTRL_EN | HDA_INT_ALL_STREAM); + // enable global controller interrupts CIE and GIE - I do not use AC97/HDA interrupts in INCHINIT or JUDAS so skip this part of initialization + // hda_OUTL ( HDA_INTCTL, hda_INL ( HDA_INTCTL) | HDA_INT_CTRL_EN | HDA_INT_GLOBAL_EN); + + // reset the position buffer (program the position buffer when starting playback) + hda_OUTL (HDA_DPLBASE, 0); + hda_OUTL (HDA_DPUBASE, 0); + + // hardware init mixer for first detected codec (bits 0, 1, 2, etc) + for (i = 0; i < HDA_MAX_CODECS; i++) { + if (audio_pci.codec_mask & (1 << i)) { + audio_pci.codec_index = i; + if (hda_mixer_init ()) break; + } + } + return TRUE; +} + +/************************** + HDA mixer init functions +***************************/ +// initialize the HDA mixer +static unsigned int hda_mixer_init (void) +{ + unsigned int i; + unsigned short nid; + unsigned long hda_codec_vender_id; + + // get vender and device IDs then save it to our audio_pci structure + hda_codec_vender_id = hda_param_read (AC_NODE_ROOT, AC_PAR_VENDOR_ID); + if (hda_codec_vender_id == 0) hda_codec_vender_id = hda_param_read (AC_NODE_ROOT, AC_PAR_VENDOR_ID); + audio_pci.codec_id1 = (WORD) (hda_codec_vender_id >> 16); // HDA codec vender ID + audio_pci.codec_id2 = (WORD) (hda_codec_vender_id & 0xffff); // HDA codec device ID + + // search for audio function group root node, exit if not found + hda_search_audio_node (); + if (!audio_pci.afg_root_nodenum) goto error_exit_mixinit; + + // check number of nodes for AFG, nid is the starting AFG node - can not be 0 + // hda_get_sub_nodes returns -1 if more then max connections defined in header file are present + audio_pci.afg_num_nodes = hda_get_sub_nodes (audio_pci.afg_root_nodenum, &nid); + if ((audio_pci.afg_num_nodes <= 0) || !nid) goto error_exit_mixinit; + + // read default capabilities from main audio function group node + audio_pci.def_amp_out_caps = hda_param_read (audio_pci.afg_root_nodenum, AC_PAR_AMP_OUT_CAP); + audio_pci.def_amp_in_caps = hda_param_read (audio_pci.afg_root_nodenum, AC_PAR_AMP_IN_CAP); + + // allocate memory for all AFG nodes + if (audio_pci.afg_nodes) locked_free (audio_pci.afg_nodes); + audio_pci.afg_nodes = (struct hda_node *) locked_malloc (audio_pci.afg_num_nodes * sizeof (struct hda_node)); + if (!audio_pci.afg_nodes) goto error_exit_mixinit; + + // add all AFG nodes to our structure with hda_add_new_node function + for (i = 0; i < audio_pci.afg_num_nodes; i++, nid++) hda_add_new_node (&audio_pci.afg_nodes[i], nid); + + // determine output path + if (!hda_parse_output()) goto error_exit_mixinit; + + // determine the supported audio format from dac node - first output node + if (audio_pci.dac_node[0]){ + audio_pci.supported_formats = audio_pci.dac_node[0]->supported_formats; + if (!audio_pci.supported_formats) audio_pci.supported_formats = 0xffffffff; // then enter fixed max freq + // fixed - we only support 48khz with 16bit PCM stereo max anyway + audio_pci.supported_max_freq = 48000; + audio_pci.supported_max_bits = 16; + // audio_pci.supported_max_freq = hda_get_max_freq (); + // audio_pci.supported_max_bits = hda_get_max_bits (); + } + return 1; + + error_exit_mixinit: + // free nodes memory on error + if (audio_pci.afg_nodes){ + locked_free (audio_pci.afg_nodes); + audio_pci.afg_nodes = NULL; + } + return 0; +} + +/********************************* +HDA functions volume update/unmute +**********************************/ +// set the volume or mute the requested amplifier +static void hda_set_vol_mute (unsigned short nid, int ch, int direction, int index, int val) +{ + unsigned int param; + + param = (ch) ? AC_AMP_SET_RIGHT : AC_AMP_SET_LEFT; + param |= (direction == HDA_OUTPUT) ? AC_AMP_SET_OUTPUT : AC_AMP_SET_INPUT; + param |= index << 8; // index is bits 8 - 11 for set payload + param |= val; + hda_codec_write (nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, param); +} + +// get the volume or mute status of the requested amplifier +static unsigned int hda_get_vol_mute (unsigned short nid, int ch, int direction, int index) +{ + unsigned int val, param; + + param = (ch) ? AC_AMP_GET_RIGHT : AC_AMP_GET_LEFT; + param |= (direction == HDA_OUTPUT) ? AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT; + param |= index; // index is bits 0 - 3 for get payload + val = hda_codec_read (nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, param); + return (val & 0xff); // bits 8 - 31 are ignored as they should be 0 +} + +// update the requested amplifier +static int hda_codec_amp_update (unsigned short nid, int ch, int direction, int idx, int mask, int val) +{ + val &= mask; + val |= hda_get_vol_mute (nid, ch, direction, idx) & ~mask; + hda_set_vol_mute (nid, ch, direction, idx, val); + return 1; +} + +// unmute output and set max volume for the output amplifier +static void hda_unmute_output (struct hda_node *node) +{ + unsigned int val; + val = (node->amp_out_caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT; + hda_codec_amp_update (node->nid, 0, HDA_OUTPUT, 0, 0xff, val); + hda_codec_amp_update (node->nid, 1, HDA_OUTPUT, 0, 0xff, val); +} + +// unmute input and set max volume for the input amplifier +static void hda_unmute_input (struct hda_node *node, unsigned int index) +{ + unsigned int val; + val = (node->amp_in_caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT; + hda_codec_amp_update (node->nid, 0, HDA_OUTPUT, 0, 0xff, val); + hda_codec_amp_update (node->nid, 1, HDA_OUTPUT, 0, 0xff, val); +} + +/********************************** +HDA Nodes identification functions +***********************************/ +// Subordinate Node Count as in Intel specs +static unsigned int hda_get_sub_nodes (unsigned short nid, unsigned short *start_node) +{ + unsigned int param; // the parameter value - response is 32bit + + param = hda_param_read (nid, AC_PAR_NODE_COUNT); + *start_node = (param >> 16) & 0xff; // starting node number bits 16 - 23 + return (param & 0xff); // total number of nodes bits 0 - 7 +} + +// Search audio node from Function Group Types +static void hda_search_audio_node (void) +{ + int i, total_nodes; + unsigned short nid; // starting function group node + + // get total function group nodes and save starting function group node + total_nodes = hda_get_sub_nodes (AC_NODE_ROOT, &nid); + + // bits 0 - 7 of response from parameter read specify the node type + // search and save audio function group node id + for (i = 0; i < total_nodes; i++, nid++) { + if ((hda_param_read (nid, AC_PAR_FUNCTION_TYPE) & 0xff) == AC_GRP_AUDIO_FUNCTION) { + audio_pci.afg_root_nodenum = nid; + break; + } + } +} + +// retrieve connections for the specified node +// 0 if none +// -1 if more than max_conns +// positive on success with array of conn_list filled +static int hda_get_connections (unsigned short nid, unsigned short *conn_list, int max_conns) +{ + unsigned int param; + unsigned int shift, num_elements, mask; + int conn_length, conns, i; + unsigned short prev_nid; + unsigned short val, n; + + // get Connection List Length for nid + param = hda_param_read (nid, AC_PAR_CONNLIST_LEN); + + // if long form connections list - each entry has 16 bits (2 entries) + if (param & BIT7) { + shift = 16; + num_elements = 2; + + // mask is 0x7f for short and 0x7fff for long entries + // the highest bit (7 or 15) specifies whether connection is an independent NID or a range of NIDs + mask = 0x7fff; + + // if short form connections list - each entry has 8 bits (4 entries) + } else { + shift = 8; + num_elements = 4; + + // mask is 0x7f for short and 0x7fff for long entries + // the highest bit (7 or 15) specifies whether connection is an independent NID or a range of NIDs + mask = 0x7f; + } + + // Connection List Length bits 0 - 6 (exit if no connections) + conn_length = param & 0x7f; + if (!conn_length) return 0; + + // if only 1 connection - get it and exit + if (conn_length == 1) { + param = hda_codec_read (nid, 0, AC_VERB_GET_CONNECT_LIST, 0); + conn_list[0] = param & mask; + return 1; + } + + // if more connections + conns = 0; + prev_nid = 0; + for (i = 0; i < conn_length; i++) { + + // get 4 entries for short from and 2 entries for long form connections list + if (i % num_elements == 0) param = hda_codec_read (nid, 0, AC_VERB_GET_CONNECT_LIST, i); + + // current connection + val = param & mask; + + // if range of NIDs (highest bit set - 7 or 15 for long entries) + if (param & (mask + 1)) { + if (!prev_nid || prev_nid >= val) continue; // ignore for first entry or if previous NID was equal/higher than current + for (n = prev_nid + 1; n <= val; n++) { // start at (prev_nid + 1) because prev_nid is already saved as single NID + if (conns >= max_conns) return -1; + conn_list[conns++] = n; + } + + // if single NID (highest bit clear) + } else { + if (conns >= max_conns) return -1; + conn_list[conns++] = val; + } + + // save this NID as previous NID in case we have a range of NIDs in next entry + prev_nid = val; + + // pass on to next connection entry + param >>= shift; + } + return conns; +} + +// add a new node to our audio structure +static int hda_add_new_node (struct hda_node *node, unsigned short nid) +{ + int nconns; + + node->nid = nid; + nconns = hda_get_connections (nid, &node->conn_list[0], HDA_MAX_CONNECTIONS); + + // when we have connections + if (nconns >= 0) { + node->nconns = nconns; + + // get Audio Widget Capabilities + node->wid_caps = hda_param_read (nid, AC_PAR_AUDIO_WIDGET_CAP); + + // bits 20 - 23 define the node type + // AC_WID_AUD_OUT, AC_WID_AUD_IN, AC_WID_AUD_MIX, AC_WID_AUD_SEL, AC_WID_PIN, AC_WID_POWER, AC_WID_VOL_KNB, AC_WID_BEEP, AC_WID_VENDOR + node->type = (node->wid_caps & (BIT20 | BIT21 | BIT22 | BIT23)) >> 20; + + // if pin complex + if (node->type == AC_WID_PIN){ + // get Pin Capabilities (output capable, input capable, vref, etc) + node->pin_caps = hda_param_read (node->nid, AC_PAR_PIN_CAP); + + // get Pin Widget Control status (out enable, in enable, vref enable, etc) + node->pin_ctl = hda_codec_read (node->nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + + // get default config + node->def_config = hda_codec_read (node->nid, 0, AC_VERB_GET_CONFIG_DEFAULT, 0); + } + + // if out amplifier present + if (node->wid_caps & AC_WCAP_OUT_AMP) { + // If Amp Param Override is a 1, the widget contains its own amplifier parameters. If this bit is a 0, + // then the Audio Function node must contain default amplifier parameters, and they should be used + // to define all amplifier parameters (both input and output) in this widget. + + // The “Amplifier Properties” parameters return the parameters for the input or the output amplifier + // on a node. In the case of a Pin Widget, the terms input and output are relative to the codec itself; + // for all other widgets, these terms are relative to the node. The amplifier capabilities are indicated by + // the step size of the amplifier, the number of steps, the offset of the range with respect to 0 dB, and + // whether the amplifier supports mute. + if (node->wid_caps & AC_WCAP_AMP_OVRD) node->amp_out_caps = hda_param_read (node->nid, AC_PAR_AMP_OUT_CAP); + if (!node->amp_out_caps) node->amp_out_caps = audio_pci.def_amp_out_caps; + } + + // if in amplifier present + if (node->wid_caps & AC_WCAP_IN_AMP) { + // If Amp Param Override is a 1, the widget contains its own amplifier parameters. If this bit is a 0, + // then the Audio Function node must contain default amplifier parameters, and they should be used + // to define all amplifier parameters (both input and output) in this widget. + + // The “Amplifier Properties” parameters return the parameters for the input or the output amplifier + // on a node. In the case of a Pin Widget, the terms input and output are relative to the codec itself; + // for all other widgets, these terms are relative to the node. The amplifier capabilities are indicated by + // the step size of the amplifier, the number of steps, the offset of the range with respect to 0 dB, and + // whether the amplifier supports mute. + if (node->wid_caps & AC_WCAP_AMP_OVRD) node->amp_in_caps = hda_param_read (node->nid, AC_PAR_AMP_IN_CAP); + if (!node->amp_in_caps) node->amp_in_caps = audio_pci.def_amp_in_caps; + } + + // read supported PCM formats - return 32bit dword with format data as in HDA specification + // for AFG, AI converter and AO converter group + node->supported_formats = hda_param_read (node->nid, AC_PAR_PCM); + } + + return nconns; +} + +// get the node data pointed by NID from our device structure +static struct hda_node *hda_get_node (unsigned short nid) +{ + struct hda_node *node = audio_pci.afg_nodes; + unsigned int i; + + for (i = 0; i < audio_pci.afg_num_nodes; i++, node++) + if (node->nid == nid) return node; + + return NULL; +} + +/******************************************************************************************** + HDA output nodes - search path + output path nodes parser written basing on ALSA project with major modifications for JUDAS support + some more improvements will be made in the near future + *********************************************************************************************/ +// select the input connection of the given node +static int hda_select_input_connection (struct hda_node *node, unsigned int index) +{ + return hda_codec_write (node->nid, 0, AC_VERB_SET_CONNECT_SEL, index); +} + +// clear checked nodes flags for different jack types +static void hda_clear_check_flags (void) +{ + struct hda_node *node = audio_pci.afg_nodes; + unsigned int i; + + for (i = 0; i < audio_pci.afg_num_nodes; i++, node++) node->checked = 0; +} + +// Parse output path until we reach an audio output widget. +// returns 0 if not found, 1 if found +static int hda_parse_output_path (struct hda_node *node, int dac_idx) +{ + int i; + struct hda_node *child; + + // exit if node already checked + if (node->checked) return 0; + + // mark this node as checked + node->checked = 1; + // if we have an Audio Out widget type + if (node->type == AC_WID_AUD_OUT) { + // skip DIGITAL OUT node + if (node->wid_caps & AC_WCAP_DIGITAL) return 0; + // DAC node is assigned already, just unmute and connect + if (audio_pci.dac_node[dac_idx]) return node == audio_pci.dac_node[dac_idx]; + // assign DAC node + audio_pci.dac_node[dac_idx] = node; + // if out amplifier present save node to pcm_vols + if((node->wid_caps & AC_WCAP_OUT_AMP) && (audio_pci.pcm_num_vols < HDA_MAX_PCM_VOLS)) { + audio_pci.pcm_vols[audio_pci.pcm_num_vols].node = node; + audio_pci.pcm_vols[audio_pci.pcm_num_vols].index = 0; + audio_pci.pcm_num_vols++; + } + // found + return 1; + } + + // also parse child nodes for the main node using the same function + for (i = 0; i < node->nconns; i++) { + child = hda_get_node (node->conn_list[i]); + if (!child) continue; + // child node found, parse it's output + if (hda_parse_output_path (child, dac_idx)) { + // found - select the path, unmute both input and output + if (node->nconns > 1) hda_select_input_connection (node, i); + hda_unmute_input (node, i); + hda_unmute_output (node); + if (audio_pci.dac_node[dac_idx] && (audio_pci.pcm_num_vols < HDA_MAX_PCM_VOLS) && !(audio_pci.dac_node[dac_idx]->wid_caps & AC_WCAP_OUT_AMP)) { + if((node->wid_caps & AC_WCAP_IN_AMP) || (node->wid_caps & AC_WCAP_OUT_AMP)) { + int n = audio_pci.pcm_num_vols; + audio_pci.pcm_vols[n].node = node; + audio_pci.pcm_vols[n].index = i; + audio_pci.pcm_num_vols++; + } + } + return 1; + } + } + return 0; +} + +// Look for the output PIN widget with the given jack type +// and parse the output path to that PIN. +// Returns the PIN node when the path to DAC is established. +// -1 to parse first output +static struct hda_node *hda_parse_output_jack (int jack_type) +{ + struct hda_node *node = audio_pci.afg_nodes; + int err, i; + + // we test each node from our afg_nodes structure which contains all nodes connected to root AFG node + for (i = 0 ; i < audio_pci.afg_num_nodes; i++, node++) { + // pin widget node ? + if (node->type != AC_WID_PIN) continue; + // output capable ? + if (!(node->pin_caps & AC_PINCAP_OUT)) continue; + // unconnected ? + if (defconfig_port_conn (node) == AC_JACK_PORT_NONE) continue; + // parse defined outputs + if (jack_type >= 0) { + if (jack_type != defconfig_type (node)) continue; + // if SPDIF skip + if (node->wid_caps & AC_WCAP_DIGITAL) continue; + // else parse 1st output + } else { + // output as default ? + if (!(node->pin_ctl & AC_PINCTL_OUT_EN)) continue; + } + // clear checked nodes flags for different jack types + hda_clear_check_flags (); + + // try primary dac + err = hda_parse_output_path (node, 0); + + // try secondary dac + if (!err && audio_pci.out_pin_node[0]) { + err = hda_parse_output_path (node, 1); + } + if (err) { + // unmute the PIN output + hda_unmute_output (node); + // set PIN-Out enable + hda_codec_write (node->nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN | + ((node->pin_caps & AC_PINCAP_HP_DRV) ? AC_PINCTL_HP_EN : 0)); + return node; + } + } + return NULL; +} + +static int hda_parse_output (void) +{ + struct hda_node *node; + + // Look for the output PIN widget. First, look for the line-out pin + node = hda_parse_output_jack (AC_JACK_LINE_OUT); + + // Found, remember the PIN node + if (node) audio_pci.out_pin_node[0] = node; + // If no line-out is found, try speaker out + else { + node = hda_parse_output_jack (AC_JACK_SPEAKER); + if (node) audio_pci.out_pin_node[0] = node; + } + + // Look for the HP-out pin + node = hda_parse_output_jack (AC_JACK_HP_OUT); + // If we have no line-out/speaker pin then select hp-out as default output + // Otherwaise select hp-out as secondary output + if (node) { + if (!audio_pci.out_pin_node[0]) audio_pci.out_pin_node[0] = node; + else audio_pci.out_pin_node[1] = node; + } + + // When no line-out or HP pins found choose the first output pin + if (!audio_pci.out_pin_node[0]) { + audio_pci.out_pin_node[0] = hda_parse_output_jack (-1); // parse 1st output + if (!audio_pci.out_pin_node[0]) { + #ifdef HDA_DEBUG + logmessage ("Error : no proper output path found\n"); + #endif + return 0; + } + } + + return 1; +} + + +/***************************************************************************** + + END OF CODE FALLING UNDER THE GNU/GPL LICENSE + +*****************************************************************************/ + + + + +/***************************************************************************** + General stuff +******************************************************************************/ + +int initmixer(void) +{ + int v, s, sv; + + /* + * If this is the first time we are initializing, we must allocate the + * lookup tables, clipbuffer & zladdbuffer and lock them as well. + * Volume table needs to be calculated only once. + * + */ + if (mixer_firsttime) + { + int *volptr; + CHANNEL *chptr; + int buffersize; + + judas_cliptable = locked_malloc(65536 * sizeof(short)); + if (!judas_cliptable) + { + return 0; + } + + if (judas_device == DEV_FILE) { + buffersize = (int) filewriterbuffersize * 8; + } else if (judas_device == DEV_AC97 || judas_device == DEV_HDA) { + buffersize = 48000 / PER_SECOND * 8; + } else buffersize = 44100 / PER_SECOND * 8; + judas_clipbuffer = locked_malloc(buffersize); + if (!judas_clipbuffer) + { + locked_free(judas_cliptable); + return 0; + } + judas_zladdbuffer = locked_malloc(buffersize); + if (!judas_zladdbuffer) + { + return 0; + } + judas_volumetable = locked_malloc(256 * 256 * sizeof(int) + 1024); + if (!judas_volumetable) + { + locked_free(judas_cliptable); + locked_free(judas_clipbuffer); + locked_free(judas_zladdbuffer); + return 0; + } + /* + * Adjust the volumetable to begin on a 1024 byte boundary; + * the mixing routines need this! + */ + judas_volumetable = (int *)((((unsigned)judas_volumetable) + 1023) & 0xfffffc00); + volptr = &judas_volumetable[0]; + /* + * Note: although there is an optimized routine for zero volume, + * we need the zero volume table because in stereo mixing the + * other channel's volume could be zero. + */ + for (v = 0; v < 256; v++) + { + for (s = 0; s < 256; s++) + { + sv = s; + if (sv > 127) sv -= 256; + sv *= v; + sv >>= (16 - SIGNIFICANT_BITS_16); + *volptr = sv; + volptr++; + } + } + /* + * The mixing routines need the address shifted, and since we + * don't need the pointer anymore... + */ + judas_volumetable = (int *)((unsigned)judas_volumetable >> 2); + + chptr = &judas_channel[0]; + + /* + * Init all channels (no sound played, no sample, mastervolume 64) + * + */ + for (s = CHANNELS; s > 0; s--) + { + chptr->voicemode = VM_OFF; + chptr->sample = NULL; + chptr->mastervol = 64; + chptr++; + } + mixer_firsttime = 0; + } + + if (judas_mixmode & SIXTEENBIT) + { + short *clipptr = &judas_cliptable[0]; + + for (s = 0; s < 65536; s++) + { + sv = s; + if (sv > 32767) sv -= 65536; + sv <<= (16 - SIGNIFICANT_BITS_16); + if (sv < -32768) sv = -32768; + if (sv > 32767) sv = 32767; + *clipptr = sv; + clipptr++; + } + } + else + { + unsigned char *clipptr = (unsigned char *)&judas_cliptable[0]; + + for (s = 0; s < 65536; s++) + { + int sv = s; + if (sv > 32767) sv = s - 65536; + sv <<= (16 - SIGNIFICANT_BITS_16); + if (sv < -32768) sv = -32768; + if (sv > 32767) sv = 32767; + *clipptr = (sv >> 8) + 128; + clipptr++; + } + } + return 1; +} + +static int judas_lock(void) +{ + if (judas_locked) return 1; + if (!judas_memlock(&judas_code_lock_start, (int)&judas_code_lock_end - (int)&judas_code_lock_start)) return 0; + if (!judas_memlock(&judas_device, sizeof judas_device)) return 0; + if (!judas_memlock(&judas_mixroutine, sizeof judas_mixroutine)) return 0; + if (!judas_memlock(&judas_mixersys, sizeof judas_mixersys)) return 0; + if (!judas_memlock(&judas_initialized, sizeof judas_initialized)) return 0; + if (!judas_memlock(&judas_ds, sizeof judas_ds)) return 0; + if (!judas_memlock(&judas_irq, sizeof judas_irq)) return 0; + if (!judas_memlock(&judas_int, sizeof judas_int)) return 0; + if (!judas_memlock(&judas_dma, sizeof judas_dma)) return 0; + if (!judas_memlock(&judas_port, sizeof judas_port)) return 0; + if (!judas_memlock(&judas_irqcount, sizeof judas_irqcount)) return 0; + if (!judas_memlock(&dsp_version, sizeof dsp_version)) return 0; + if (!judas_memlock(&judas_mixrate, sizeof judas_mixrate)) return 0; + if (!judas_memlock(&judas_mixmode, sizeof judas_mixmode)) return 0; + if (!judas_memlock(&judas_bufferlength, sizeof judas_bufferlength)) return 0; + if (!judas_memlock(&judas_buffermask, sizeof judas_buffermask)) return 0; + if (!judas_memlock(&judas_clipbuffer, sizeof judas_clipbuffer)) return 0; + if (!judas_memlock(&judas_zladdbuffer, sizeof judas_zladdbuffer)) return 0; + if (!judas_memlock(&judas_cliptable, sizeof judas_cliptable)) return 0; + if (!judas_memlock(&judas_volumetable, sizeof judas_volumetable)) return 0; + if (!judas_memlock(&judas_mixpos, sizeof judas_mixpos)) return 0; + if (!judas_memlock(&judas_samplesize, sizeof judas_samplesize)) return 0; + if (!judas_memlock(&judas_player, sizeof judas_player)) return 0; + if (!judas_memlock(&judas_bpmcount, sizeof judas_bpmcount)) return 0; + if (!judas_memlock(&judas_bpmtempo, sizeof judas_bpmtempo)) return 0; + if (!judas_memlock(&judas_channel, sizeof judas_channel * CHANNELS)) return 0; + if (!judas_memlock(&judas_zerolevell, sizeof judas_zerolevell)) return 0; + if (!judas_memlock(&judas_zerolevelr, sizeof judas_zerolevelr)) return 0; + if (!judas_memlock(&judas_clipped, sizeof judas_clipped)) return 0; + if (!judas_memlock(&filewriterbuffer, sizeof filewriterbuffer)) return 0; + if (!judas_memlock(&filewriterbuffersize, sizeof filewriterbuffersize)) return 0; + judas_locked = 1; + return 1; +} + diff --git a/JUDAS.CMD b/JUDAS.CMD new file mode 100644 index 0000000..0dd71af --- /dev/null +++ b/JUDAS.CMD @@ -0,0 +1 @@ ++judas +judassmp +judasraw +judaswav +judastbl +judasxm +judasmod +judass3m +judasio +judasasm +judasdma +judasmem diff --git a/JUDAS.DOC b/JUDAS.DOC new file mode 100644 index 0000000..2808b95 --- /dev/null +++ b/JUDAS.DOC @@ -0,0 +1,804 @@ + Û ßÜ ÜÜ ÜÜÜÜÜ + ÜÜÛÝ ÜÜÜ ßÜÜÜÜ ÛÛÜÜ ÜÜÛßßßßßßÛÛ ÜÛÛß ßßÜ + ß ÞÛÜ ßßÛ ÞÛ ßß ÞÛßßßßÛÜ ÞÛÛ ÛÝ Ûß Û + ßÛÛ ÛÝ ÞÛ ÛÝ ßÛÜ ÞÛÛ ÛÛ ÞÛ ßÜß + ÞÛÝ ÛÝ ÛÝ ÞÛ ßÛÜ ÞÛÛ ÞÛÝ ÛÛÜ + ÛÛ ÞÛ ÛÝ ÞÛÝ ÛÛÜ ÞÛÝ ÞÛÝ ßßÛÛÜÜ + ÛÛ ÞÛ ÞÛ ÞÛÝ ßÛÝ ÛÛÝ ÜÜÜÜÜ ÞÛÝ ßÛÛÜ + ÛÛ ÞÛÝ ÞÛ ÞÛÛ ÛÝ ÛÛÛßßßßßßßßÛÛÛÜ ßÛÛ + ÞÛÛ ÛÝ ÞÛÝ ÛÛ Û ÛÛ ÛÛ Û ÛÛ + ÜÜ ÞÛÝ ÛÛ ÛÛ ÞÛÝ ÛÝ ÞÛÝ ÛÛ ÜÜ ÞÛÝ + Û ÞÛ ÛÛ ÛÛ ÛÛÝ ÛÛ ÛÛ ÞÛ ÛÛ Üß ß ÛÛ + ß Û ÞÛÝ ÞÛÝ ÜÛÛÛ ÞÛÝ ÜÜÛÛ ÛÛ ÛÛ ÛÜ ÞÛÝ + ÞÝ ÛÛ ÛÛ ÜÜÛÛÛÛß ÛÛÛÛÛÛÛÛÛß ÜÛÛÛÜ ÜÛÛÛÜ ÛÛ ÜÛÛ + ÛÛÜ ÜÛÛß ÞÛÛ ÜÛÛÛßß ÛÛßßß Üß ß ÛÛÜ ÜÛÛß + ßÛÛÛÛßß ßÛÛÛß Üß ßÛÛÛÛßß + + Apocalyptic Softwaremixing Soundsystem Version 2.10d + + + "Black Spider's April 2008 Release" + with HDA support under the GNU/GPL license + + + DOCUMENTATION + ------------- + + Contents: + + 1. INTRODUCTION + 2. FEATURES + 3. HOW TO USE + 4. CONFIGURING + 5. HISTORY + 6. FINAL WORDS + + +--------------- +1. INTRODUCTION +--------------- + +This is version 2.10d of JUDAS Apocalyptic Softwaremixing Soundsystem. +System+soundcard code & original (buggy) players by Cadaver. +Most bugfixes, additions and quality mixer by Yehar. +Intel ICH AC97 and HDA codecs support by Black Spider. + +JUDAS can be used to create devastating digital virtual music & fx +abominations with the most popular soundcards. It is mainly intended for games, +demos and musicdisks. + +JUDAS is exclusively intended to be used with Watcom 32bit flat protected +mode DOS programs, with either DOS4GW or any other complatible DOS extender. +With DOS4GW soundcard IRQs above 7 don't work, though. + +Previous versions of JUDAS were blasphemy-ware. This means that: + * Use at own risk - authors take no responsibility for anything! + * Documentation & attitude aren't "serious" or "professional", if + you don't like this then look elsewhere! + * Contains strictly non-portable DOS code + * Full sources are included + * Judas is free, it would be nice though + if you include the authors in the credits. + * Kutulu will always be watching you + * Listen to True Nordic Black Metal while programming, preferably + Burzum. Or listen to Supreme Teutonic Power Metal :D + +This version however (all the HDA codec functions only) is released under the +GNU/GPL license. The full text of this license can be found in the file gpl20.txt +The HDA code was written after analyzing various HDA linux related sources, +the drivers from ALSA project made by Takashi Iwai, the OSS sound system project +and others. Although this code is different and pretty unique some similarities +especially in the HDA nodes access strategy can be found. That is why I decided +to publicly release this version of Judas under the GNU/GPL license. + + + +----------- +2. FEATURES +----------- + +Soundcards supported: + + * SOUND BLASTER + - 8bit sound + - mono + - autoinit mode for DSP versions > 2.00 + - mixrate up to 22050 Hz + * SOUND BLASTER PRO + - 8bit sound + - mono/stereo + - mixrate up to 22050 Hz (stereo) or 44100 Hz (mono) + * SOUND BLASTER 16 & + GRAVIS ULTRASOUND + - 8bit/16bit sound + - mono/stereo + - mixrate up to 44100 Hz + * INTEL ICH AC97 CODEC (and compatibles) + - 16bit sound + - stereo + - mixrate up to 48000 Hz + + + ATTENTION: + ---------- + >> GRAVIS ULTRASOUND support is slightly more complicated than SB + support. Sound must be transferred to the card's DRAM before it's + played (it cannot play the DMA buffer directly. GUSMAX could, using + the codec chip but as a matter of principle it won't be used!) This + transfer uses a very high speed (650 kHz) which slows down the CPU a + bit. Using a 16-bit DMA channel helps much. + GUS support will work at least with Classic & MAX & PnP. + + + >> INTEL ICH AC97/HDA CODECS are only supported in pure DOS. Under Windows, + a SOUND BLASTER compatible device will be detected. If all supported + devices SB/GUS/AC97/HDA are present (or emulated) in the system, + the configuration procedure will choose the GRAVIS ULTRASOUND + (by looking at the ULTRASND enviroment variable) as the best option. + When only a SOUND BLASTER compatible card and an AC97/HDA codec + is present, the config procedure will always chose the SOUND BLASTER + device as default (by looking at the BLASTER enviroment variable). + THE AC97/HDA code does not make use of any ISA DMA channel and does + not need any IRQ (the AC97/HDA interrupt number is given for debug + purposes only). The PCI Bus Master DMA engine is started once at + the begining (init procedure) and stopped at the uninit procedure. + From the programmer point of view, the AC97/HDA codec should be + considered just like an SB comptible music card (it still needs a + call to the judas update procedure, preferably at 50 - 70Hz to stay + compatible with other supported devices). + + NOTE : The user may need to run the ichinit.com utility that comes + with this package in order to unlock or adjust/decrease the AC97 + codec volume under pure DOS. The best way out would be to put + ichinit.com in autoexec.bat. Should work (although there is + absolutely no guarantee it will ;-) with AC97 integrated on + i810/i810E/i810E2/i815/i815E/i815EG/i815EP/i815G/i815P/i820/i820E/ + i830MP/i840/i845/i845E/i845G/i845GL/i850/i850E/i860/440MX/E7500/SIS735 + and some AMD/nVidia chipsets. High Definition Audio codecs do not + require ICHINIT to work properly. In theory all HDA platforms are + supported. + + NOTE: AC97 codecs must support VRA (Variable Rate Audio) in order + to change the sample rate to something else than 48000 Hz. + + + >> Judas also has a WAV writer. + + +Fileformats supported: + + * XMs + * MODs + * S3Ms + * WAVs + * Raw samples + +Software mixer features: + + * 32 channels (can be increased, if you need more!) + * Unlimited amount of samples (dynamically allocated) + * Clipping + * True channel volume meters + + Fast mixer: + * Optional linear interpolation + * It's extremely fast! (read SPEED.DOC) + + Quality mixer: + * 32 bit mixing + * Click removal system + * Either cubic (Hermite) or linear interpolation + * Enough volume levels for even the faintest sounds + * Makes Judas the best sounding sound system! (Hi Maz!) High-end audio + freaks: Look at JUDASW.GIF and JUVSCPW.GIF for interpolation test + results. The plots are spectrum analyses of sampled white noise + played at a low frequency. + +Multitasking compatibility stuff: + + * Developed almost exclusively in a WIN95 dos box, without problems! + * Doesn't use Virtual DMA or any strange shit, just allocates + the DMA buffer from conventional memory and everything works fine! + * Soundcards work under WINDOWS, providing the sound driver doesn't + fuck up. However, playback can take a LOT more time (especially on a + GUS) than under pure DOS. Some known problems: + - Old WIN3.1 GUS driver causes the machine to lock up if a DOS + program uses the GUS. + - The WIN95 driver of Avance SB16 clone causes the machine to lock + up if a DOS program tries to use 16bit DMA. + - Some WIN3.1 SB drivers might not work. + * All player/mixer code & sound/music data is locked via the DPMI + memory locking functions. Feel free to call judas_update() from your + IRQ0 handler (remember to lock your own IRQ code/data though!) + * If memory can't be locked (not enough physical memory), the situation + will be treated as an out-of-memory error. + + +------------- +3. HOW TO USE +------------- + +Here follows an explanation how to compile and link the soundsystem to your +programs. Later on all JUDAS functions are explained. + + * MAKEFILE builds the JUDAS library. Simply run wmake to compile. + + * You'll need NASM to re-compile the mixing routines in JUDASASM.ASM/JASMDJ.ASM. + + * MAKEEX.BAT will compile the JUDAS example programs. By default + they'll be linked with WDOSX. MAKEJP.BAT and MAKEANAL.BAT compile + the example programs separately. + + * To use JUDAS in your own programs, you must have the header files + JUDAS.H, JUDASCFG.H & JUDASERR.H in your work directory. Put the + following line in your program: + + #include "judas.h" + + When linking, remember to tell the linker to use the JUDAS.LIB file. + For example: + + wlink F massacre N massacre.exe L judas.lib SYS wdosx + +And now to the functions...but first an explanation of JUDAS error handling. +For functions that return a value, 1 is success and 0 is failure (except the +JUDAS file functions, they work like those defined in IO.H, e.g failed file +open returns -1.) If a function returns failure, you can check the reason from +the variable judas_error. Error codes are defined in JUDASERR.H, and there +are also text strings for them, so it's easy to print the error. + +GENERAL FUNCTIONS +----------------- + +void judas_config(void); + + This reads the soundcard type, port, IRQ & DMA from the enviroment + variables (BLASTER & ULTRASND.) If none of them is found, it tries + to detect an Intel ICH AC97 compatible codec by querying the PCI bus. + If that fails too, it assumes there is no soundcard. + The sound configuration can also be manually set, it consists of + these variables (all this shit is defined in JUDAS.H) + + extern unsigned judascfg_device; + extern unsigned judascfg_port; + extern unsigned judascfg_irq; + extern unsigned judascfg_dma1; + extern unsigned judascfg_dma2; /* SB16 high DMA */ + + Device must be DEV_NOSOUND, DEV_SB, DEV_SBPRO, DEV_SB16 or DEV_GUS. + judascfg_dma2 is only needed for SB16. These variables can be changed + anytime, for example to let the user try different settings. + + ATTENTION: In NOSOUND mode samples/modules aren't loaded/played, to + conserve memory. There is no "simulated" playing. This will cause + problems if you need to synchronize your main program to the music + (demos!) But, it's not impossible to overcome this. Simply use a + secondary timing method (for example, a counter increased by IRQ0) + if in NOSOUND mode. + +int judas_init(unsigned mixrate, unsigned mixer, unsigned mixmode, int interpolation); + + Tries to init the soundcard according to the sound configuration. + If the soundcard doesn't respond, returns failure. + When calling this function for the first time, it must also reserve + the DMA buffer & the mixing internal buffers, so it might also fail + if there's not enough (physical) memory. + If everything is successful, the sound output is started and you can + begin to call the judas_update() routine. + + Mixrate is the number of audio samples produced per second. It is auto- + corrected if soundcard doesn't support a certain mixrate. Remember: CPU + time usage is directly proportional to the mixrate, as it has to + produce more audio data at higher rates! + + Mixer specifies the mixer used. You can select either QUALITYMIXER or + FASTMIXER. If you use quality mixer, you'll get better sound quality, + but mixing will take much more CPU time. Quality mixer improvements in + comparison to fast mixer: 32 bit mixing, click removal, smooth volume + slides, 16 bit interpolation of 8 bit samples, more precise volume, + cubic interpolation. A simple guideline: If you are making a demo or a + game and want to show off with the amazingly fast gfx, choose fast + mixer. If you are making a music disk, choose quality mixer. You can + also let the user decide! For a sizelimited intro, Judas is a bit too + bloated. + + Mixmode can be one of these, and is also auto-corrected according to + soundcard limitations. Stereo sound takes more processing power, + especially if the channels aren't panned to extreme left/right or + to middle. 16bitness shouldn't affect the CPU time usage much. Quality + mixer won't get any faster even if you use mono sound (lazy code). + + MONO | EIGHTBIT + STEREO | EIGHTBIT + MONO | SIXTEENBIT + STEREO | SIXTEENBIT + + With fast mixer, any nonzero value in the interpolation parameter turns + interpolation on. It will make the sound MUCH better and takes only 50% + more CPU time than normal mixing. Quality mixer always uses + interpolation, and the interpolation parameter switches between cubic + and linear interpolation. Zero makes it linear, any other value cubic. + + Here's a table that shows how the mixer and interpolation modes affect + CPU time usage when playing a 16-bit test xm on a Pentium/166: + + IP: off linear cubic + quality mixer - 34% 36% + fast mixer 13% 19% - + + If you want to change soundcard, mixer, mixmode, mixrate or anything, + just call judas_init() again. There will be one empty DMA buffer + played, which means a short pause in the sound. + +void judas_update(void); + + This will mix new sound data to the DMA buffer. Must be called 20 or + more times per second. This can be done either in the main program + or in some interrupt (IRQ0 is good, reprogram the PIT clock to + something like 50-70 Hz to reduce the amount of work done at a time. + This means smoother CPU time usage. Send also the end-of-interrupt + before mixing to allow other interrupts meanwhile, this is especially + important for Original Sound Blaster & Ultrasound because they need + as fast as possible response to the sound card IRQ.) + + NOTE: the interval at which you call judas_update() WILL NOT affect + music tempo, but if you don't call it often enough the sound gets + choppy as the old DMA buffer contents get played again. + + WEIRD SHIT ALERT: judas_update() has to query either the DMA controller + or in case of Ultrasound, the soundcard itself, where the sound is + currently playing. If judas_update() is called too often (many hundred + times in a second), the hardware gives sometimes wrong positions. + There is code to compensate that, but in case that you have a Pentium, + clicks may still occur in the sound. To avoid this, it's best to wait + for a retrace or do something other time-consuming task in between + judas_update() calls. And of course, the easiest thing is, as mentioned + above, to program the timer to update the sound. 70 Hz is an absolutely + safe calling frequency. + +void judas_uninit(void); + + This is the most important function, as it stops the sound and restores + the soundcard IRQ vector. Make sure this is called at the end of the + program, even if user presses Ctrl-C or Ctrl-Break. This is done by + setting up signal handlers: + + int main(int argc, char **argv) + { + /* Set judas_uninit() to be called at exit */ + atexit(judas_uninit); + + /* Set signal handlers */ + signal(SIGINT, handle_int); + signal(SIGBREAK, handle_int); + + judas_config(); + judas_init(); + + ... + } + + static void handle_int(int a) + { + exit(0); /* Atexit functions will be called! */ + } + +int judas_songisplaying(void); + + Returns 1 if a song is currently being played, otherwise returns 0. + Use this function if you need to know if the song has reached its end + and stopped. + +int judas_wavwriter_open(char *name); +int judas_wavwriter_writesome(int handle); +int judas_wavwriter_close(int handle); + + WAV writer functions. You can use them to convert your songs into WAVs. + Instructions: Initialize Judas, load a song, start playing it (do + not call judas_update() in any phase), call judas_wavwriter_open() with + the WAV file name as the parameter, the function returns a file handle + for the opened WAV file, keep calling judas_wavwriter_writesome() with + the handle as the parameter until the song is finished (indicated by + judas_songisplaying()), call judas_wavwriter_close() with the handle + as the parameter to finish the WAV writing process. + + These functions return -1 on error. + +SAMPLE & CHANNEL FUNCTIONS +-------------------------- + +SAMPLE *judas_allocsample(int length); + + This allocates a new sample with the given length of bytes. In normal + use you don't need to call this but if you want to play long streams + of audio or want to do some strange synthetic abominations this might + be for you. + + If you want to play a long audiostream, you must allocate a suitably + long buffer with this function, stuff data into it, start playing and + then spy the pos field of the channel structure to know where in the + buffer to stuff new data. Actually this is a lot like the DMA buffer + updating. I didn't want to make functions for this because I wouldn't + need them myself and everyone has their own way to make this. + + judas_allocsample() also allocates the volume profile of the sample. + The volume profile takes about 0.002x memory compared to the sampledata + itself. It is used by the realtime volume meter routines. + +void judas_ipcorrect(SAMPLE *smp); + + In normal use you really don't need this, but if you have allocated + a sample with judas_allocsample(), then each time you change the + contents of your sample data, you must call this function. + + It updates a 3,5K safety buffer directly after the sample data. This is + needed because of mixing routine optimizations. Ipcorrect originally + meant interpolation-correction, when the optimized routines in fast + mixer weren't in use yet. + + It also calculates the volume profile of the sample, for later use by + the realtime volume meter routines. + + When judas_allocsample() is called, the safety buffer is automatically + added to the sample length, and enough memory for the volume profile is + allocated, so you don't have to worry about those more. + + WARNING: You must set the end (and also repeat if you're using looping) + fields of the sample structure before calling judas_ipcorrect(), or + it won't do anything! + +void judas_freesample(SAMPLE *smp); + + Frees a sample, no matter if it's made by yourself, or loaded with + some of the functions. You can even free a sample which is running, + and no crash! + + NOTE: you can't obtain the samplepointers used by the music players. + This is for everyone's safety. And please don't call this function with + some random shit as the sample pointer, it'll just fuck up! + +void judas_playsample(SAMPLE *smp, unsigned chnum, unsigned frequency, + unsigned char volume, unsigned char panning); + + This function has many parameters, but they're really self-explaining. + Channel numbers go from 0 to CHANNELS - 1. Volume must be in range 0 to + 64*256. Give the sampling frequency of your sample in the frequency + parameter if you want it to be played at the original frequency. + + IMPORTANT: The music players use channels 0 to MOD_CHANNELS - 1. So + if you're playing a 8-channel tune, 8 is the lowest free channel. + Playing a sample on a music channel while music is on won't fuck up + badly, but you will get strange sounds. + +void judas_stopsample(unsigned chnum); + + Stops sound on channel chnum by turning the voicemode to VM_OFF. + +void judas_preventdistortion(unsigned active_channels); + + This will set mastervolume on all channels to 256 / active_channels. + This will totally prevent clipping distortion, but may cause the + sound to be too quiet. Experiment by yourself. + +void judas_setmastervolume(unsigned chnum, unsigned char mastervol); + + Sets mastervolume on channel chnum. + +void judas_setmusicmastervolume(unsigned musicchannels, unsigned char mastervol); + + Sets mastervolume on channels 0 to musicchannels - 1. + +void judas_setsfxmastervolume(unsigned musicchannels, unsigned char mastervol); + + Sets mastervolume on channels musicchannels to CHANNELS - 1, i.e + on the channels that are free for sound fx. + +SAMPLE *judas_loadrawsample(char *name, int repeat, int end, unsigned char voicemode); + + Loads a raw sample, and sets the repeat, end, and voicemode as you + wish. Here the repeat and end aren't pointers, but offsets from sample + start. Set end to 0 and it will be automatically set to the end of the + sample. + +SAMPLE *judas_loadwav(char *name); + + Loads a WAV file. WAVs are always one-shot. Stereo WAVs are converted + to mono. The function should be able to skip properly the crap-chunks + added by various Windows audio recording programs. In addition, + starting from version 2.09f WAV files can also be loaded from + a library. The default WAV library the system will check is the file + JDSWAV.LIB. Please check jdswav.zip package for more details on how + to create such a library. + +void judas_setwavlib (char *name); + + Overrides the default library name for WAV files. + +float judas_getvumeter(unsigned chnum); + + Estimates and returns the true volume on the specified channel. The + range is 0..1, but not 1. The values you get don't depend on master + volume or mixer. + + +MUSIC LOADING/PLAYING FUNCTIONS +------------------------------- + +There are three function sets for XM, MOD and S3M format. Only those you use +will be linked to your program. The function behaviour is same in every set. +Here are the XM functions listed & explained: + +int judas_loadxm(char *name); + + Loads an XM file. May fail because of various things, like file not + found, read error, incorrect format or out-of-memory. + +void judas_freexm(void); + + Frees an XM file, if one has been loaded. It isn't really necessary + to do this, because judas_loadxm() will automatically free the previous + XM. This is however a good function for testing purposes: if the heap + has been corrupted by the player/mixer routines it will page-fault! + (I haven't had a page-fault for many days now...) + +void judas_playxm(void); +void judas_stopxm(void); +void judas_forwardxm(void); +void judas_rewindxm(void); +unsigned char judas_getxmpos(void); +unsigned char judas_getxmline(void); +unsigned char judas_getxmtick(void); +unsigned char judas_getxmchannels(void); +char *judas_getxmname(void); + + Should all explain themselves. You cannot hang the machine by calling + the functions before an XM has been loaded! + +JUDAS IO FUNCTIONS +------------------ + +int judas_open(char *name); +int judas_seek(int handle, int bytes, int whence); +int judas_read(int handle, void *buffer, int size); +void judas_close(int handle); + + These work really like the standard IO functions defined in IO.H. The + idea of them is following: if you want to implement a datafile system + (compressed, maybe) you need only to change these routines. + +DPMI MEMORY-LOCKING FUNCTIONS +----------------------------- + +int judas_memlock(void *start, unsigned size); +int judas_memunlock(void *start, unsigned size); +void *locked_malloc(int size); +void locked_free(void *address); +void *dos_malloc(int size); +void dos_free(void *address); + + + These should be self-explanatory. According to the JUDAS "standard", + judas_memlock() & judas_memunlock() return 1 on success and 0 on + failure. + + locked_malloc() and locked_free() function just like the normal + malloc() & free() functions, they'll just lock and unlock the memory + automatically. locked_malloc() returns NULL either if: + * there is not enough memory available + * the memory cannot be locked + + dos_malloc() and dos_free() functions allocate and free low DOS memory + + For multitasking compatibility, you MUST lock all memory (code, data, + variables) touched by your IRQ routines. If you do this, Kutulu will be + very very proud of you. + +OTHER THINGS +------------ + +In JUDAS.H there are definitions for the config currently used as well as many +textstring arrays. They can be used to easily print soundcard & audiomode info, +as well as errors. Look at JP.C for a good(?) example. + + /* + * Device, port, IRQ, DMA, mixrate & mixmode currently in use. + * Don't change them! + */ + extern unsigned judas_device; + extern unsigned judas_port; + extern unsigned judas_irq; + extern unsigned judas_dma; + extern unsigned judas_mixrate; + extern unsigned char judas_mixmode; + + /* + * Mixer names + */ + extern char *judas_mixername[]; + + /* + * Sound device names + */ + extern char *judas_devname[]; + + /* + * Mixmode names + */ + extern char *judas_mixmodename[]; + + /* + * Interpolation mode names + */ + extern char *judas_ipmodename[]; + + /* + * Error code + */ + extern int judas_error; + + /* + * Text strings for the error codes + */ + extern char *judas_errortext[]; + + /* + * Clipping indicator (quality mixer only) indicates the hardest + * clipping occurred. 0 = has not clipped, 64 = Very faint clipping, + * 128 = half volume wouldn't clip, 255 = 1/4 volume wouldn't clip + * Does not give values in range 1..63. + */ + extern volatile char judas_clipped; + +-------------- +4. CONFIGURING +-------------- + +In JUDASCFG.H there are a couple of interesting defines. Look at it for an +explanation & instructions. + +---------- +5. HISTORY +---------- + +V2.0 - Original release + +V2.01 - Corrected S3M arpeggio (if zero, must use previous infobyte) + - Corrected XXX-fuckup in soundcard enviroment variables + - Corrected player bounds checking in Anal Invaders + +V2.02 - Removed error checking from sample loading in MODs & S3Ms, to make + corrupted modules work. In the XM loader I didn't dare to do this, + because XMs contain various headers in between sampledata, and if + they're not in their correct position...BIG SHIT HAPPENS! + - Added a check to MOD loader that loop end must not be beyond + sample length. This makes those weird shittunes like DALLAS.MOD work + without crashing. + - Corrected the spelling of Vintersemestre in the hails section of this + document. + +V2.03 - Added a keyboard handler to Anal Invaders, now it's easier to play! + - Added support for empty patterns in XMs + - Corrected the "inaudible-bassline-bug" of HERRA.S3M + - Corrected volumeslide "both-parameters-nonzero" bug in XMs & MODs + +V2.04 - Added handling of mono S3Ms and "panning magic" to S3M loader + - Added undocumented panning commands to MOD and S3M players, now songs + by Khyron will be played correctly! + - Corrected S3M vibrato "lose-old-speed-bug" + - Removed some checking in S3M loader + - Corrected S3M frequency limits + - Corrected S3M portamento up & down to use the same previous value + - Scaled default pannings by 50% to prevent "earache" + - Corrected S3M sample offset command to remember old value + - Corrected XM global volume limits + - Corrected MOD instrumentnumber/note alone bug + - XM toneportamento must not change the instrument/samplenumber + (a very stupid bug!) + +V2.04y - Added selectable quality mixer and made required changes to the + example programs + - Added simple file writer + +V2.05 - Corrected behaviour of retrig command in both XMs & MODs. Now retrig + in XMs behaves just like in FT2 and in MODs like in Cubic Player + (confusing!!!) After doing this, Cadaver is on the verge of total mind + meltdown... + - In MODs, a notedelay without a note mustn't play anything. + - There might be 256 patterns in a mod, so mt_patterns variable must + be bigger than 8 bits. + - If GUS DMA-transfer cache fills up (some IRQ missed maybe) then cache + is cleared manually. Sometimes sound stuck under WIN95, hopefully + this corrects it! + - Made JPLAY's default volume louder + - Added some little things to the documentation + - December has begun, cold frostvoid everywhere... + - This will probably be the last version of Judas of which you can get + Cadaver's version also. + +V2.06y - Corrected XM & MOD handling of two pattern breaks on the same row. + The problem still exists in the S3M player, because the channels + should be sorted somehow accordingly to the default panning, and + that's just too much shit for us! + - Corrected XM, MOD & S3M track sine vibrato phase. (Hi Dagoon!) + - XM & S3M note cut now works! + - Made song end autodetection & looping control. Now you can set how + many times the song plays before it stops by itself! Infinite looping + is also possible. + - Killed a bug that caused S3M player to sometimes crash at song end. + - Increased the number of volume levels. (Audible with Quality Mixer + only!) If you have used the sample playing functions of older versions + of Judas, and want to use the latest version of Judas in your program, + just multiply the volumes that go to judas_playsample() by 256. + - Renamed JPLAY to JP to make it faster to type. Retrain your fingers! + - Made the clip removing routines detect and handle sudden VM_OFFs! Now + S3Ms with noteoffs sound smoother. + - Added channel volume meters. Don't worry! They won't slow down the + mixing at all when you don't use them! + - Rewrote the file writer. Now it writes WAV files! + - Made Judas WATCOM 11.0 compatible. (Structure packing was the problem) + - Optimized mixing routines a bit. + - Added quality mixer clipping indicator. + - Added song rewinding and forwarding. + - Released at the Abduction'98 demoparty!!! + +V2.07a - Added pure DOS support for INTEL ICH0/ICH/ICH2/ICH3 and compatible + AC97 integrated audio codecs (judasac.h and judasac.inc defines). + - Added low DOS memory functions dos_malloc() and dos_free() + - Advanced error tracking and debug functions for AC97 support code + located in judas.c file. + - Added a separate ichinit DOS utility for AC97 codecs. + (Black Spider's February 2003 release). + +V2.07y - Improved pure DOS support for INTEL ICH0/ICH/ICH2/ICH3 and compatible + AC97 integrated audio codecs (judasac.h and judasac.inc defines). + - Added ICH4 controller support. + - Example programs now use the WDOSX DOS extender instead of PMODE/W + (Black Spider's March 2003 release). + +V2.08y - Improved pure DOS support for INTEL ICH4 AC97 integrated audio codecs. + (Black Spider's March 2003 release). + +V2.09a - Added support for SIS7012 - now it works :) + +V2.09b - Corrected ICH4 and above chips support - now it works :) + +V2.09c - Dev version (all tasm code converted to nasm) + +V2.09d - Dev version with full support for WATCOM C++ and DJGPP near pointers + +V2.09e - All AC97 audio devices now use memory mapped IO if available + +V2.09f - WAV library builder added to JUDAS. See JDSWAV.ZIP package for details + +v2.10a - High Definition Audio codecs support added - supported up to ICH10 (!) + +v2.10b - judas_setwavlib function added + +v2.10c - High Definition Audio codecs support update for intel 5 and 6 series test by RayeR + +v2.10d - High Definition Audio codecs support update for intel 7 series test by RayeR + +-------------- +6. FINAL WORDS +-------------- + +JUDAS was written because Cadaver got bored of digital sound systems which were +full of restrictions (register and you'll get sound fx support!), didn't +interpolate, were made in Pascal or refused to work if they ran out of +Ultrasound memory. It was programmed in the realms of northern icy void +from March 1997 onwards. + +Hail from Cadaver to these supreme artists: Impaled Nazarene, Mustan Kuun +Lapset, Vintersemestre, Horna, Troll, Burzum, Immortal, Mercyful Fate. + +Hail to all perverts who know us! + +email Cadaver: loorni@student.oulu.fi +Cadaver's homepage: http://www.student.oulu.fi/~loorni + +email Yehar: ollinie@freenet.hut.fi +Absorb free ambient techno: http://www.sublevel3.org + +NOTE: Judas is developed as a sound system, not as a player. So, don't ask us +to program Cubic-Player-like fileselectors or such. It's YOUR job, right? :) + +------------------------------------------------------------------------------ + +NOTE: Judas sound system for DOS is no longer developed. However this +sound engine is now integrated in the BME package, which is available +at Cadaver's web site. The last original 2.06y version of Judas for DOS +was released in 1998. +This version (2.10b - Black Spider's release) only adds AC97/HDA +codecs support for the DOS version of Judas sound system. There are +also a few docs changes. Just enough to explain AC97/HDA behaviour. +Many thanks to Matthias Goeke for providing the ICH5 as well as the HDA +mainboard for testing purposes. + +Greetz to all those who release their coding stuff with source code. +Hail to the ones who know me, and of coz to Cadaver and Yehar +for makeing such a good DOS sound engine. + +email to Piotr Ulaszewski (aka BSpider) for AC97/HDA support problems: +(don't forget to specify the type of hardware you use :-) +piotrkn22@poczta.onet.pl +http://www.piotrkn22.republika.pl diff --git a/JUDAS.H b/JUDAS.H new file mode 100644 index 0000000..6d5dbcc --- /dev/null +++ b/JUDAS.H @@ -0,0 +1,267 @@ + +#ifdef __DJGPP__ +#define HANDLE_PRAGMA_PACK_PUSH_POP 1 +#endif + +/* + * JUDAS main header file + */ +#include "judascfg.h" +#include "judaserr.h" + +/* + * Mixer numbers + */ +#define FASTMIXER 1 +#define QUALITYMIXER 2 + +/* + * Mixmode bits + */ +#define MONO 0 +#define STEREO 1 +#define EIGHTBIT 0 +#define SIXTEENBIT 2 + +/* + * Sound device numbers + */ +#define DEV_NOSOUND 0 +#define DEV_SB 1 +#define DEV_SBPRO 2 +#define DEV_SB16 3 +#define DEV_GUS 4 +#define DEV_AC97 5 +#define DEV_HDA 6 +#define DEV_FILE 7 + +/* + * Voicemode bits + */ +#define VM_OFF 0 +#define VM_ON 1 +#define VM_ONESHOT 0 +#define VM_LOOP 2 +#define VM_16BIT 4 + +/* + * Panning values (these specific panning positions are mixed with optimized + * routines which greatly reduce processing time if fast mixer is used!) + */ +#define LEFT 0 +#define MIDDLE 128 +#define RIGHT 255 + +/* + * Sample structure + */ +typedef struct +{ + char *start; + char *repeat; + char *end; + char *vuprofile; + unsigned char voicemode; +} SAMPLE; + +/* + * Channel structure + */ +#pragma pack(push,1) +typedef struct +{ + char *volatile pos; /* Position which is currently played */ + char *repeat; /* Goes here after hitting end */ + char *end; /* Position of last sample + 1 */ + SAMPLE *volatile smp; /* Pointer to sample currently used */ + unsigned freq; /* Playing frequency in hertz */ + volatile unsigned short fractpos; /* 16bit fractional pos */ + unsigned char mastervol; /* 255 = whole volume range of output */ + unsigned char panning; /* 0 left, 128 middle, 255 right */ + signed short vol; /* Note volume, ranges from 0 to 64*256 */ + volatile unsigned char voicemode; /* See the bit definitions above */ + + /* Quality mixer internal variables (No need to understand) */ + volatile char prevvm; /* voicemode != prevvm if discontinuity in the sound */ + char *volatile prevpos; /* Pos != prevpos if discontinuity in the sound */ + volatile int lastvalleft; /* Last value from prev. mixed chunk (after volume scaling) */ + volatile int lastvalright; + volatile int smoothvoll; /* Final volume. Smoothly slided towards given volume */ + volatile int smoothvolr; +} CHANNEL; +#pragma pack(pop) + +/* + * General functions + */ +void judas_config(void); +int judas_init(unsigned mixrate, unsigned mixer, unsigned mixmode, int interpolation); +void judas_update(void); +void judas_uninit(void); +int judas_songisplaying(void); + +/* + * Sample & channel functions + */ +SAMPLE *judas_allocsample(int length); +void judas_ipcorrect(SAMPLE *smp); +void judas_freesample(SAMPLE *smp); +void judas_playsample(SAMPLE *smp, unsigned chnum, unsigned frequency, unsigned short volume, unsigned char panning); +void judas_stopsample(unsigned chnum); +void judas_preventdistortion(unsigned active_channels); +void judas_setmastervolume(unsigned chnum, unsigned char mastervol); +void judas_setmusicmastervolume(unsigned musicchannels, unsigned char mastervol); +void judas_setsfxmastervolume(unsigned musicchannels, unsigned char mastervol); +float judas_getvumeter(unsigned chnum); /* Returns 0..1 */ + +/* + * RAW sample & WAV functions + */ +SAMPLE *judas_loadrawsample(char *name, int repeat, int end, unsigned char voicemode); +SAMPLE *judas_loadwav(char *name); +void judas_setwavlib(char *name); + +/* + * WAV writer functions + */ +int judas_wavwriter_open(char *name); +int judas_wavwriter_writesome(int handle); +int judas_wavwriter_close(int handle); + +/* + * XM player functions + */ +int judas_loadxm(char *name); +void judas_freexm(void); +void judas_playxm(int rounds); +void judas_stopxm(void); +void judas_forwardxm(void); +void judas_rewindxm(void); +unsigned char judas_getxmpos(void); +unsigned char judas_getxmline(void); +unsigned char judas_getxmtick(void); +unsigned char judas_getxmchannels(void); +char *judas_getxmname(void); + +/* + * MOD player functions + */ +int judas_loadmod(char *name); +void judas_freemod(void); +void judas_playmod(int rounds); +void judas_stopmod(void); +void judas_forwardmod(void); +void judas_rewindmod(void); +unsigned char judas_getmodpos(void); +unsigned char judas_getmodline(void); +unsigned char judas_getmodtick(void); +unsigned char judas_getmodchannels(void); +char *judas_getmodname(void); + +/* + * S3M player functions + */ +int judas_loads3m(char *name); +void judas_frees3m(void); +void judas_plays3m(int rounds); +void judas_stops3m(void); +void judas_forwards3m(void); +void judas_rewinds3m(void); +unsigned char judas_gets3mpos(void); +unsigned char judas_gets3mline(void); +unsigned char judas_gets3mtick(void); +unsigned char judas_gets3mchannels(void); +char *judas_gets3mname(void); + +/* + * JUDAS IO functions + */ +#define SEEK_SET 0 +#define SEEK_CUR 1 +#define SEEK_END 2 +int judas_open(char *name); +int judas_seek(int handle, int bytes, int whence); +int judas_read(int handle, void *buffer, int size); +void judas_close(int handle); + +/* + * DPMI memory locking functions (use them, and no more crashes under + * multitaskers!) + */ +int judas_memlock(void *start, unsigned size); +int judas_memunlock(void *start, unsigned size); +void *locked_malloc(int size); +void locked_free(void *address); +void *dos_malloc(int size); +void dos_free(void *address); +int DPMI_MapMemory (unsigned long *physaddress, unsigned long *linaddress, unsigned long size); +int DPMI_UnmapMemory (unsigned long *linaddress); + +/* + * Sound configuration (may be changed anytime, used only by judas_init()) + */ +extern unsigned judascfg_device; +extern unsigned judascfg_port; +extern unsigned judascfg_irq; +extern unsigned judascfg_dma1; +extern unsigned judascfg_dma2; /* SB High DMA, not needed for GUS */ + +/* + * Device, port, IRQ, DMA, mixrate & mixmode currently in use. + * Don't change them! + */ +extern unsigned judas_device; +extern unsigned judas_port; +extern unsigned judas_irq; +extern unsigned judas_dma; +extern unsigned judas_mixrate; +extern unsigned char judas_mixmode; + +/* + * Sound device names + */ +extern char *judas_devname[]; + +/* + * Mixer names + */ +extern char *judas_mixername[]; + +/* + * Mixmode names + */ +extern char *judas_mixmodename[]; + +/* + * Interpolation mode names + */ +extern char *judas_ipmodename[]; + +/* + * Error code + */ +extern int judas_error; + +/* + * Text strings for the error codes + */ +extern char *judas_errortext[]; + +/* + * Channel array + */ +extern CHANNEL judas_channel[CHANNELS]; + +/* + * Fake sample returned by the routines when in NOSOUND mode + */ +extern SAMPLE fakesample; + +/* + * Clipping indicator (quality mixer only) indicates the hardest + * clipping occurred. 0 = has not clipped, 64 = clipping starts, + * 128 = half volume wouldn't clip, 255 = 1/4 volume wouldn't clip + * Does not give values in range 1..63. + */ +extern volatile char judas_clipped; + diff --git a/JUDASAC.H b/JUDASAC.H new file mode 100644 index 0000000..6570919 --- /dev/null +++ b/JUDASAC.H @@ -0,0 +1,1235 @@ + +/* + * Internal header file: simplified definitions (used by AC97/HDA code) + * Some parts of the header file are based on ALSA HDA defines, some on common + * Intel docs HDA defines. Almost everything has been rewritten, the whole + * stuff was simplified and adapted to support the Judas library. + * Please be aware that HDA support functions fall uder the GNU/GPL license. + * + * by Piotr Ulaszewski (PETERS) + * March 2008 rev 2.1/2,2 + */ + +#define FALSE 0 +#define TRUE 1 +#ifdef NULL // override NULL to 0 +#undef NULL +#endif +#define NULL 0 +typedef int bool; +typedef int BOOL; +typedef unsigned char BYTE; +typedef unsigned short WORD; +typedef unsigned int DWORD; +typedef unsigned char UCHAR; +typedef unsigned short USHORT; +typedef unsigned int UINT; +typedef unsigned long ULONG; +typedef DWORD dword; +typedef WORD word; +typedef BYTE byte; +#define LOWORD(l) ((WORD)((DWORD)(l))) +#define HIWORD(l) ((WORD)(((DWORD)(l)>>16)&0xFFFF)) +#define LOBYTE(w) ((BYTE)(w)) +#define HIBYTE(w) ((BYTE)(((WORD)(w)>>8)&0xFF)) + + +#define BIT0 1 +#define BIT1 2 +#define BIT2 4 +#define BIT3 8 +#define BIT4 0x10 +#define BIT5 0x20 +#define BIT6 0x40 +#define BIT7 0x80 +#define BIT8 0x100 +#define BIT9 0x200 +#define BIT10 0x400 +#define BIT11 0x800 +#define BIT12 0x1000 +#define BIT13 0x2000 +#define BIT14 0x4000 +#define BIT15 0x8000 +#define BIT16 0x10000 +#define BIT17 0x20000 +#define BIT18 0x40000 +#define BIT19 0x80000 +#define BIT20 0x100000 +#define BIT21 0x200000 +#define BIT22 0x400000 +#define BIT23 0x800000 +#define BIT24 0x1000000 +#define BIT25 0x2000000 +#define BIT26 0x4000000 +#define BIT27 0x8000000 +#define BIT28 0x10000000 +#define BIT29 0x20000000 +#define BIT30 0x40000000 +#define BIT31 0x80000000 + + +/***************************************************************************** + * General PCI static defines + ******************************************************************************/ + +#define PCI_CLASS_MULTIMEDIA_AUDIO 0x0401 +#define PCI_CLASS_MULTIMEDIA_OTHER 0x0480 +#define PCI_VENDER_ID 0x00 +#define PCI_REVISION_ID 0x08 +#define PCI_COMMAND 0x04 +#define PCI_DEVICE_ID 0x02 +#define PCI_INTERRUPT_LINE 0x3c +#define PCI_INT_LINE 0x3d + +#define PCI_MEM_BASE_ADDRESS_0 0x10 +#define PCI_MEM_BASE_ADDRESS_1 0x14 +#define PCI_MEM_BASE_ADDRESS_2 0x18 +#define PCI_MEM_BASE_ADDRESS_3 0x1c + +#define PCI_BASE_ADDRESS_0 0x10 +#define PCI_BASE_ADDRESS_1 0x14 +#define PCI_BASE_ADDRESS_2 0x18 +#define PCI_BASE_ADDRESS_3 0x1c +#define PCI_BASE_ADDRESS_4 0x20 +#define PCI_BASE_ADDRESS_5 0x24 + +#define PCI_COMMAND_IO 0x01 +#define PCI_COMMAND_MEMORY 0x02 +#define PCI_COMMAND_MASTER 0x04 +#define PCI_COMMAND_PARITY 0x40 +#define PCI_ICH4_CFG_REG 0x41 +#define PCI_COMMAND_SERR 0x100 + +#define PCI_STATUS 0x06 +#define PCI_SUBSYSTEM_VENDER_ID 0x2c +#define PCI_SUBSYSTEM_ID 0x2e + +/* Ac97 general defines */ +#define CTL_BASE 0 /* addressing controller regs */ +#define MIXER_BASE 1 /* addressing mixer regs */ + +/***************************************************************************** + * PCI space extended defines HDA specific + ******************************************************************************/ + +/* PCI space */ +#define HDA_PCIREG_TCSEL 0x44 + +/* Defines for ATI HD Audio support in SB450 south bridge */ +#define ATI_SB450_HDAUDIO_MISC_CNTR2_ADDR 0x42 +#define ATI_SB450_HDAUDIO_ENABLE_SNOOP 0x02 + +/* Defines for Nvidia HDA support */ +#define NVIDIA_HDA_TRANSREG_ADDR 0x4e +#define NVIDIA_HDA_ENABLE_COHBITS 0x0f + +/* HDA general defines */ +#define HDA_MAX_DEV 16 +#define HDA_MAX_CODECS 4 +#define HDA_MAX_CODEC_ADDRESS 0x0f + +/* max. connections to a widget */ +#define HDA_MAX_CONNECTIONS 32 +#define HDA_MAX_PCM_VOLS 2 +#define HDA_MAX_NUM_INPUTS 16 + + +/******************************************************* + * Internal header file: Intel ICH AC97/HDA and compatibles. + * (AC97/HDA pci device structure and defines) + *******************************************************/ + +typedef struct { + WORD vender_id; + WORD device_id; + WORD sub_vender_id; + WORD sub_device_id; + int type; + char *string; +} AUDIO_DEVICE_LIST; + +typedef struct { + char *string; +} AUDIO_STEREO_TECHNOLOGY; + +typedef struct { + WORD vender_id1; + WORD vender_id2; + char *string; +} AUDIO_CODEC_LIST; + + +/**********************************************************/ +/* The main AC97/HDA Audio structure */ +/**********************************************************/ + +#pragma pack(push,1) +// 1 HDA node with it's data +struct hda_node { + WORD nid; /* NID of this widget */ + WORD nconns; /* number of input connections */ + WORD conn_list[HDA_MAX_CONNECTIONS]; + DWORD wid_caps; /* widget capabilities */ + DWORD type; /* node/widget type - bits 20 - 23 of wid_caps */ + DWORD pin_caps; /* pin widget capabilities */ + DWORD pin_ctl; /* pin controls */ + DWORD def_config; /* default configuration */ + DWORD amp_out_caps; /* AMP out capabilities override over default */ + DWORD amp_in_caps; /* AMP in capabilities override over default */ + DWORD supported_formats; /* supported formats value */ + BYTE checked; /* flag indicator that node is already parsed */ +}; + +struct pcm_vol_str { + struct hda_node *node; /* Node for PCM volume */ + DWORD index; /* connection of PCM volume */ +}; + + typedef struct { + // PCI device IDs + WORD vender_id; + WORD device_id; + WORD sub_vender_id; + WORD sub_device_id; + WORD device_bus_number; + BYTE irq; //PCI IRQ + BYTE pin; // PCI IRQ PIN + WORD command; // PCI command reg + DWORD base0; // PCI BAR for mixer registers NAMBAR_REG + DWORD base1; // PCI BAR for bus master registers NABMBAR_REG + DWORD base2; + DWORD base3; + DWORD base4; + DWORD base5; + DWORD device_type; // any + + // driver type flags + int mem_mode; // 0 for IO access, 1 for memory access + int hda_mode; // 0 for AC97 mode, 1 for HDA mode + + // memory allocated for BDL and PCM buffers + DWORD *bdl_buffer; // Buffer Descriptor List for AC97 - 256 bytes / HDA - 8192 bytes + DWORD *pcmout_buffer0; // Low DOS memory AC97/HDA buffer 0 for Bus Master DMA + DWORD *pcmout_buffer1; // Low DOS memory AC97/HDA buffer 1 for Bus Master DMA + DWORD *hda_buffer; // base of HDA allocated memory non aligned + DWORD pcmout_bufsize; // size of PCM out buffer - obsolete + DWORD pcmout_bdl_entries; // number of BDL entries - obsolete + DWORD pcmout_bdl_size; // single BDL size - obsolete + DWORD pcmout_dmasize; // size of 1 BDL entry - obsolete + DWORD pcmout_dma_lastgoodpos; // last good position in DMA - obsolete + DWORD pcmout_dma_pos_ptr; // buffer for DMA position (not used in Judas) - obsolete + + // ac97 only properties + int ac97_vra_supported; // False by default + + // HDA only properties + unsigned long codec_mask; // mask for all available codecs + unsigned int codec_index; // 1st codec that passed hardware init + + unsigned short afg_root_nodenum; // Audio Function Group root node + int afg_num_nodes; // number of subordinate nodes connected to AFG root node + struct hda_node *afg_nodes; // all nodes connected to root AFG node + unsigned int def_amp_out_caps; // default out amplifier capabilities + unsigned int def_amp_in_caps; // default in amplifier capabilities + + struct hda_node *dac_node[2]; // DAC nodes + struct hda_node *out_pin_node[2]; // Output pin nodes - all (Line-Out/hp-out, etc...) + struct hda_node *adc_node[2]; // ADC nodes + struct hda_node *in_pin_node[2]; // Input pin nodes - all (CD-In, etc...) - obsolete + unsigned int input_items; // Input items for capture + unsigned int pcm_num_vols; // number of PCM volumes + struct pcm_vol_str pcm_vols[HDA_MAX_PCM_VOLS]; // PCM volume nodes + + unsigned int format_val; // stream type + unsigned int dacout_num_bits; // bits for playback (16 bits by default) + unsigned int dacout_num_channels; // channels for playback (2 = stereo by default) + unsigned int stream_tag; // stream associated with our SD (1 by default) + unsigned long supported_formats; + unsigned long supported_max_freq; + unsigned int supported_max_bits; + + unsigned long freq_card; // current frequency 44100 by default + unsigned int chan_card; + unsigned int bits_card; + + // codec IDs and names + WORD codec_id1; // codec vender id + WORD codec_id2; // codec device id + char device_name[128]; // controller name string + char codec_name[128]; // codec name string +} AUDIO_PCI_DEV; +#pragma pack(pop) + + +/**********************************************************/ +/* AC97 base0 commands */ +/* registers accessed via NAMBAR - base0 - Audio Mixer registers */ +/* */ +/**********************************************************/ + +#define AC97_RESET 0x00 // Reset register +#define AC97_MASTER 0x02 // Master Volume +#define AC97_HEADPHONE 0x04 // Headphone Volume (optional) +#define AC97_MASTER_MONO 0x06 +#define AC97_MASTER_TONE 0x08 +#define AC97_PCBEEP_VOL 0x0a +#define AC97_PHONE_VOL 0x0c +#define AC97_MIC_VOL 0x0e +#define AC97_LINE_IN_VOL 0x10 +#define AC97_CD_VOL 0x12 +#define AC97_VID_VOL 0x14 +#define AC97_AUX_VOL 0x16 +#define AC97_PCM 0x18 // PCM OUT Volume +#define AC97_RECORD_SELECT 0x1a +#define AC97_RECORD_VOL 0x1c +#define AC97_RECORD_MIC_VOL 0x1e +#define AC97_GP_REG 0x20 // general purpose +#define AC97_3D_CONTROL_REG 0x22 // 3D control + // 24h is reserved +#define AC97_POWER_CTRL 0x26 // powerdown control +#define AC97_EXTENDED_ID 0x28 // Extended Audio ID +#define AC97_EXTENDED_STATUS 0x2a // Extended Audio Status (control) +#define AC97_PCM_FRONT_DAC_RATE 0x2c // PCM OUT Front DAC Rate +#define AC97_PCM_SURND_DAC_RATE 0x2e // surround sound sample rate +#define AC97_PCM_LFE_DAC_RATE 0x30 // LFE samplerate +#define AC97_LR_ADC_DAC_RATE 0x32 // pcm in sample rate +#define AC97_MIC_ADC_RATE 0x34 // mic in sample rate + //registers 36-7a are reserved on the ICH +#define AC97_VENDERID1_REG 0x7c // codec vender ID 1 +#define AC97_VENDERID2_REG 0x7e // codec vender ID 2 + +// When 2 codecs are present in the system, use BIT7 to access the 2nd set of registers, ie 80h-feh + +#define PRIMARY_CODEC 0 // 0-7F for primary codec +#define SECONDARY_CODEC BIT7 // 80-8f registers for 2ndary + +#define AC97_EA_VRA BIT0 /* Variable Rate Audio enable bit */ +#define AC97_EA_DRA BIT1 /* Double Rate Audio enable bit */ + + +/* registers accessed via NABMBAR - base1 - Bus Master registers */ + +/* capture block - PCM IN */ +#define ICH_REG_PI_BDBAR 0x00 /* dword - Buffer Descriptor list base address */ +#define ICH_REG_PI_CIV 0x04 /* byte - Current Index Value */ +#define ICH_REG_PI_LVI 0x05 /* byte - Last Valid Index */ +#define ICH_REG_PI_SR 0x06 /* byte - Status Register */ +#define ICH_REG_PI_PICB 0x08 /* word - Position In Current Buffer */ +#define ICH_REG_PI_PIV 0x0a /* byte - Prefetched Index Value */ +#define ICH_REG_PI_CR 0x0b /* byte - Control Register */ + + +/* playback block - PCM OUT */ +#define ICH_REG_PO_BDBAR 0x10 /* dword - Buffer Descriptor list base address */ +#define ICH_REG_PO_CIV 0x14 /* byte - Current Index Value */ +#define ICH_REG_PO_LVI 0x15 /* byte - Last Valid Index */ +#define ICH_REG_PO_SR 0x16 /* byte - Status Register */ +#define ICH_REG_PO_PICB 0x18 /* word - Position In Current Buffer */ +#define ICH_REG_PO_PIV 0x1a /* byte - Prefetched Index Value */ +#define ICH_REG_PO_CR 0x1b /* byte - Control Register */ + + +/* mic capture block - MIC IN */ +#define ICH_REG_MC_BDBAR 0x20 /* dword - Buffer Descriptor list base address */ +#define ICH_REG_MC_CIV 0x24 /* byte - Current Index Value */ +#define ICH_REG_MC_LVI 0x25 /* byte - Last Valid Index */ +#define ICH_REG_MC_SR 0x26 /* byte - Status Register */ +#define ICH_REG_MC_PICB 0x28 /* word - Position In Current Buffer */ +#define ICH_REG_MC_PIV 0x2a /* byte - Prefetched Index Value */ +#define ICH_REG_MC_CR 0x2b /* byte - Control Register */ + + +/* bitfields for (PCM IN/PCM OUT/MIC IN) */ +#define ICH_REG_LVI_MASK 0x1f /* LVI range mask -> 0 - 31 */ +#define ICH_REG_PIV_MASK 0x1f /* PIV range mask -> 0 - 31 */ + +/* status register bitfields */ +#define ICH_FIFOE 0x10 /* FIFO error (overrun/underrun) */ +#define ICH_BCIS 0x08 /* Buffer Completion Interrupt Status */ +#define ICH_LVBCI 0x04 /* Last Valid Buffer Completion Interrupt */ +#define ICH_CELV 0x02 /* Current Equals Last Valid */ +#define ICH_DCH 0x01 /* DMA Controller Halted */ + +/* control register bitfields */ +#define ICH_IOCE 0x10 /* Interrupt On Completion Enable */ +#define ICH_FEIE 0x08 /* Fifo Error Interrupt Enable */ +#define ICH_LVBIE 0x04 /* Last Valid Buffer Interrupt Enable */ +#define ICH_RESETREGS 0x02 /* Reset busmaster Registers */ +#define ICH_STARTBM 0x01 /* Start Busmaster operation */ + + +/* global block - control/status */ +#define ICH_REG_GLOB_CNT 0x2c /* dword - global control register */ +#define ICH_24_BIT BIT23 /* 24 bit samples */ +#define ICH_20_BIT BIT22 /* 20 bit samples */ +#define ICH_PCM_246_MASK BIT20 | BIT21 /* 6 channels (not all chips) */ +#define ICH_PCM_6 BIT21 /* 6 channels (not all chips) */ +#define ICH_PCM_4 BIT20 /* 4 channels (not all chips) */ +#define ICH_PCM_2 0 /* 2 channels (stereo) same as ICH_SIS */ +#define ICH_SIS_PCM_246_MASK BIT6 | BIT7 /* 6 channels (not all chips) */ +#define ICH_SIS_PCM_6 BIT7 /* 6 channels (not all chips) */ +#define ICH_SIS_PCM_4 BIT6 /* 4 channels (not all chips) */ +#define ICH_SIS_PCM_2 0 /* 2 channels (stereo) same as ICH */ +#define ICH_SRIE BIT5 /* Secondary Resume Interrupt Enable */ +#define ICH_PRIE BIT4 /* Primary Resume Interrupt Enable */ +#define ICH_ACLINK BIT3 /* AClink shut off */ +#define ICH_AC97WARM BIT2 /* AC97 Warm reset */ +#define ICH_AC97COLD BIT1 /* AC97 Cold reset */ +#define ICH_GIE BIT0 /* GPI Interrupt Enable */ + +#define ICH_REG_GLOB_STA 0x30 /* dword - global status register */ +#define ICH_24_BIT_CAP BIT23 /* 24 bit sample support */ +#define ICH_20_BIT_CAP BIT22 /* 20 bit sample support */ +#define ICH_PCM_6_CAP BIT21 /* 6 channels capability */ +#define ICH_PCM_4_CAP BIT20 /* 4 channels capability */ +#define ICH_MD3 BIT17 /* Modem power Down semaphore (status) */ +#define ICH_AD3 BIT16 /* Audio power Down semaphore (status) */ +#define ICH_RCS BIT15 /* Read Completion Status (0=normal) */ +#define ICH_BIT3 BIT14 /* shadowed status of bit 3 in slot 12 */ +#define ICH_BIT2 BIT13 /* shadowed status of bit 2 in slot 12 */ +#define ICH_BIT1 BIT12 /* shadowed status of bit 1 in slot 12 */ +#define ICH_SRI BIT11 /* Secondary codec Resume Interrupt */ +#define ICH_PRI BIT10 /* Primary codec Resume Interrupt */ +#define ICH_SCR BIT9 /* Secondary Codec Ready */ +#define ICH_PCR BIT8 /* Primary Codec Ready */ +#define ICH_MCINT BIT7 /* MIC In Interrupt - mic capture */ +#define ICH_POINT BIT6 /* PCM Out Interrupt - pcm playback */ +#define ICH_PIINT BIT5 /* PCM In Interrupt - pcm capture */ +#define ICH_MOINT BIT2 /* Modem Out Interrupt - modem playback */ +#define ICH_MIINT BIT1 /* Modem In Interrupt - modem capture */ +#define ICH_GSCI BIT0 /* GPI Status Change Interrupt */ + +#define ICH_REG_ACC_SEMA 0x34 /* byte - codec write semaphore register */ +#define ICH_CAS BIT0 /* Codec Access Semaphore */ + + +/***************************************************************************** + * Internal header file: Intel ICH HDA and compatibles. + * (HDA pci device structure and defines as in Intel docs) + ******************************************************************************/ + +/* + * nodes + */ +#define AC_NODE_ROOT 0x00 + +/* + * function group types + */ +enum { + AC_GRP_AUDIO_FUNCTION = 0x01, + AC_GRP_MODEM_FUNCTION = 0x02, +}; + +/* + * widget types + */ +enum { + AC_WID_AUD_OUT, /* Audio Out */ + AC_WID_AUD_IN, /* Audio In */ + AC_WID_AUD_MIX, /* Audio Mixer */ + AC_WID_AUD_SEL, /* Audio Selector */ + AC_WID_PIN, /* Pin Complex */ + AC_WID_POWER, /* Power */ + AC_WID_VOL_KNB, /* Volume Knob */ + AC_WID_BEEP, /* Beep Generator */ + AC_WID_VENDOR = 0x0f /* Vendor specific */ +}; + +/* + * GET verbs + */ +#define AC_VERB_GET_STREAM_FORMAT 0x0a00 +#define AC_VERB_GET_AMP_GAIN_MUTE 0x0b00 +#define AC_VERB_GET_PROC_COEF 0x0c00 +#define AC_VERB_GET_COEF_INDEX 0x0d00 +#define AC_VERB_PARAMETERS 0x0f00 +#define AC_VERB_GET_CONNECT_SEL 0x0f01 +#define AC_VERB_GET_CONNECT_LIST 0x0f02 +#define AC_VERB_GET_PROC_STATE 0x0f03 +#define AC_VERB_GET_SDI_SELECT 0x0f04 +#define AC_VERB_GET_POWER_STATE 0x0f05 +#define AC_VERB_GET_CONV 0x0f06 +#define AC_VERB_GET_PIN_WIDGET_CONTROL 0x0f07 +#define AC_VERB_GET_UNSOLICITED_RESPONSE 0x0f08 +#define AC_VERB_GET_PIN_SENSE 0x0f09 +#define AC_VERB_GET_BEEP_CONTROL 0x0f0a +#define AC_VERB_GET_EAPD_BTLENABLE 0x0f0c +#define AC_VERB_GET_DIGI_CONVERT 0x0f0d +#define AC_VERB_GET_VOLUME_KNOB_CONTROL 0x0f0f +#define AC_VERB_GET_GPIO_DATA 0x0f15 +#define AC_VERB_GET_GPIO_MASK 0x0f16 +#define AC_VERB_GET_GPIO_DIRECTION 0x0f17 +#define AC_VERB_GET_CONFIG_DEFAULT 0x0f1c +#define AC_VERB_GET_SUBSYSTEM_ID 0x0f20 + +/* + * SET verbs + */ +#define AC_VERB_SET_STREAM_FORMAT 0x200 +#define AC_VERB_SET_AMP_GAIN_MUTE 0x300 +#define AC_VERB_SET_PROC_COEF 0x400 +#define AC_VERB_SET_COEF_INDEX 0x500 +#define AC_VERB_SET_CONNECT_SEL 0x701 +#define AC_VERB_SET_PROC_STATE 0x703 +#define AC_VERB_SET_SDI_SELECT 0x704 +#define AC_VERB_SET_POWER_STATE 0x705 +#define AC_VERB_SET_CHANNEL_STREAMID 0x706 +#define AC_VERB_SET_PIN_WIDGET_CONTROL 0x707 +#define AC_VERB_SET_UNSOLICITED_ENABLE 0x708 +#define AC_VERB_SET_PIN_SENSE 0x709 +#define AC_VERB_SET_BEEP_CONTROL 0x70a +#define AC_VERB_SET_EAPD_BTLENABLE 0x70c +#define AC_VERB_SET_DIGI_CONVERT_1 0x70d +#define AC_VERB_SET_DIGI_CONVERT_2 0x70e +#define AC_VERB_SET_VOLUME_KNOB_CONTROL 0x70f +#define AC_VERB_SET_GPIO_DATA 0x715 +#define AC_VERB_SET_GPIO_MASK 0x716 +#define AC_VERB_SET_GPIO_DIRECTION 0x717 +#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_0 0x71c +#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_1 0x71d +#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_2 0x71e +#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_3 0x71f +#define AC_VERB_SET_CODEC_RESET 0x7ff + +/* + * Parameter IDs + */ +#define AC_PAR_VENDOR_ID 0x00 +#define AC_PAR_SUBSYSTEM_ID 0x01 +#define AC_PAR_REV_ID 0x02 +#define AC_PAR_NODE_COUNT 0x04 +#define AC_PAR_FUNCTION_TYPE 0x05 +#define AC_PAR_AUDIO_FG_CAP 0x08 +#define AC_PAR_AUDIO_WIDGET_CAP 0x09 +#define AC_PAR_PCM 0x0a +#define AC_PAR_STREAM 0x0b +#define AC_PAR_PIN_CAP 0x0c +#define AC_PAR_AMP_IN_CAP 0x0d +#define AC_PAR_CONNLIST_LEN 0x0e +#define AC_PAR_POWER_STATE 0x0f +#define AC_PAR_PROC_CAP 0x10 +#define AC_PAR_GPIO_CAP 0x11 +#define AC_PAR_AMP_OUT_CAP 0x12 + +/* + * AC_VERB_PARAMETERS results (32bit) + */ + +/* Function Group Type */ +#define AC_FGT_TYPE 0xff +#define AC_FGT_TYPE_SHIFT 0 +#define AC_FGT_UNSOL_CAP BIT8 + +/* Audio Function Group Capabilities */ +#define AC_AFG_OUT_DELAY 0xf +#define AC_AFG_IN_DELAY (0xf << 8) +#define AC_AFG_BEEP_GEN BIT16 + +/* Audio Widget Capabilities */ +#define AC_WCAP_STEREO BIT0 /* stereo I/O */ +#define AC_WCAP_IN_AMP BIT1 /* AMP-in present */ +#define AC_WCAP_OUT_AMP BIT2 /* AMP-out present */ +#define AC_WCAP_AMP_OVRD BIT3 /* AMP-parameter override */ +#define AC_WCAP_FORMAT_OVRD BIT4 /* format override */ +#define AC_WCAP_STRIPE BIT5 /* stripe */ +#define AC_WCAP_PROC_WID BIT6 /* Proc Widget */ +#define AC_WCAP_UNSOL_CAP BIT7 /* Unsol capable */ +#define AC_WCAP_CONN_LIST BIT8 /* connection list */ +#define AC_WCAP_DIGITAL BIT9 /* digital I/O */ +#define AC_WCAP_POWER BIT10 /* power control */ +#define AC_WCAP_LR_SWAP BIT11 /* L/R swap */ +#define AC_WCAP_DELAY (0xf << 16) +#define AC_WCAP_DELAY_SHIFT 16 + +/* supported PCM rates and bits */ +#define AC_SUPPCM_RATES 0xfff +#define AC_SUPPCM_BITS_8 BIT16 +#define AC_SUPPCM_BITS_16 BIT17 +#define AC_SUPPCM_BITS_20 BIT18 +#define AC_SUPPCM_BITS_24 BIT19 +#define AC_SUPPCM_BITS_32 BIT20 + +/* supported PCM stream format */ +#define AC_SUPFMT_PCM BIT0 +#define AC_SUPFMT_FLOAT32 BIT1 +#define AC_SUPFMT_AC3 BIT2 + +/* Pin widget capabilies */ +#define AC_PINCAP_IMP_SENSE BIT0 /* impedance sense capable */ +#define AC_PINCAP_TRIG_REQ BIT1 /* trigger required */ +#define AC_PINCAP_PRES_DETECT BIT2 /* presence detect capable */ +#define AC_PINCAP_HP_DRV BIT3 /* headphone drive capable */ +#define AC_PINCAP_OUT BIT4 /* output capable */ +#define AC_PINCAP_IN BIT5 /* input capable */ +#define AC_PINCAP_BALANCE BIT6 /* balanced I/O capable */ +#define AC_PINCAP_VREF (0x37 << 8) +#define AC_PINCAP_VREF_SHIFT 8 +#define AC_PINCAP_EAPD BIT16 /* EAPD capable */ + +/* Vref status (used in pin cap) */ +#define AC_PINCAP_VREF_HIZ BIT0 /* Hi-Z */ +#define AC_PINCAP_VREF_50 BIT1 /* 50% */ +#define AC_PINCAP_VREF_GRD BIT2 /* ground */ +#define AC_PINCAP_VREF_80 BIT3 /* 80% */ +#define AC_PINCAP_VREF_100 BIT5 /* 100% */ + +/* Amplifier capabilities */ +#define AC_AMPCAP_OFFSET (0x7f << 0) /* 0dB offset */ +#define AC_AMPCAP_OFFSET_SHIFT 0 +#define AC_AMPCAP_NUM_STEPS (0x7f << 8) /* number of steps */ +#define AC_AMPCAP_NUM_STEPS_SHIFT 8 +#define AC_AMPCAP_STEP_SIZE (0x7f << 16) /* step size 0-32dB in 0.25dB */ +#define AC_AMPCAP_STEP_SIZE_SHIFT 16 +#define AC_AMPCAP_MUTE BIT31 /* mute capable */ +#define AC_AMPCAP_MUTE_SHIFT 31 + +/* Supported power status */ +#define AC_PWRST_D0SUP BIT0 +#define AC_PWRST_D1SUP BIT1 +#define AC_PWRST_D2SUP BIT2 +#define AC_PWRST_D3SUP BIT3 + +/* Power state values */ +#define AC_PWRST_D0 0x00 +#define AC_PWRST_D1 0x01 +#define AC_PWRST_D2 0x02 +#define AC_PWRST_D3 0x03 + +/* Processing capabilies */ +#define AC_PCAP_BENIGN BIT0 +#define AC_PCAP_NUM_COEF (0xff << 8) + +/* Volume knobs capabilities */ +#define AC_KNBCAP_NUM_STEPS 0x7f +#define AC_KNBCAP_DELTA BIT8 + +/* + * Control Parameters + */ + +/* Amplifier gain/mute */ +#define AC_AMP_MUTE BIT7 +#define AC_AMP_GAIN BIT0 | BIT1 | BIT2 | BIT3 | BIT4 | BIT5 | BIT6 +#define AC_AMP_GET_INDEX BIT0 | BIT1 | BIT2 | BIT3 + +#define AC_AMP_GET_LEFT BIT13 +#define AC_AMP_GET_RIGHT 0 /* bit13 clear */ +#define AC_AMP_GET_OUTPUT BIT15 +#define AC_AMP_GET_INPUT 0 /* bit15 clear */ + +#define AC_AMP_SET_INDEX BIT8 | BIT9 | BIT10 | BIT11 +#define AC_AMP_SET_RIGHT BIT12 +#define AC_AMP_SET_LEFT BIT13 +#define AC_AMP_SET_INPUT BIT14 +#define AC_AMP_SET_OUTPUT BIT15 + +/* DIGITAL1 bits */ +#define AC_DIG1_ENABLE BIT0 +#define AC_DIG1_V BIT1 +#define AC_DIG1_VCFG BIT2 +#define AC_DIG1_EMPHASIS BIT3 +#define AC_DIG1_COPYRIGHT BIT4 +#define AC_DIG1_NONAUDIO BIT5 +#define AC_DIG1_PROFESSIONAL BIT6 +#define AC_DIG1_LEVEL BIT7 + +/* Pin widget control - 8bit */ +#define AC_PINCTL_VREFEN 0x7 +#define AC_PINCTL_VREF_HIZ 0 /* Hi-Z */ +#define AC_PINCTL_VREF_50 1 /* 50% */ +#define AC_PINCTL_VREF_GRD 2 /* ground */ +#define AC_PINCTL_VREF_80 4 /* 80% */ +#define AC_PINCTL_VREF_100 5 /* 100% */ +#define AC_PINCTL_IN_EN BIT5 +#define AC_PINCTL_OUT_EN BIT6 +#define AC_PINCTL_HP_EN BIT7 + +/* Unsolicited response - 8bit */ +#define AC_USRSP_EN BIT7 + +/* configuration default - 32bit */ +#define AC_DEFCFG_SEQUENCE (0xf << 0) +#define AC_DEFCFG_DEF_ASSOC (0xf << 4) +#define AC_DEFCFG_ASSOC_SHIFT 4 +#define AC_DEFCFG_MISC (0xf << 8) +#define AC_DEFCFG_MISC_SHIFT 8 +#define AC_DEFCFG_COLOR (0xf << 12) +#define AC_DEFCFG_COLOR_SHIFT 12 +#define AC_DEFCFG_CONN_TYPE (0xf << 16) +#define AC_DEFCFG_CONN_TYPE_SHIFT 16 +#define AC_DEFCFG_DEVICE (0xf << 20) +#define AC_DEFCFG_DEVICE_SHIFT 20 +#define AC_DEFCFG_LOCATION (0x3f << 24) +#define AC_DEFCFG_LOCATION_SHIFT 24 +#define AC_DEFCFG_PORT_CONN (0x3 << 30) +#define AC_DEFCFG_PORT_CONN_SHIFT 30 + +/* device types (0x0 - 0xf) */ +enum { + AC_JACK_LINE_OUT, + AC_JACK_SPEAKER, + AC_JACK_HP_OUT, + AC_JACK_CD, + AC_JACK_SPDIF_OUT, + AC_JACK_DIG_OTHER_OUT, + AC_JACK_MODEM_LINE_SIDE, + AC_JACK_MODEM_HAND_SIDE, + AC_JACK_LINE_IN, + AC_JACK_AUX, + AC_JACK_MIC_IN, + AC_JACK_TELEPHONY, + AC_JACK_SPDIF_IN, + AC_JACK_DIG_OTHER_IN, + AC_JACK_OTHER = 0xf, +}; + +/* jack connection types (0x0 - 0xf) */ +enum { + AC_JACK_CONN_UNKNOWN, + AC_JACK_CONN_1_8, + AC_JACK_CONN_1_4, + AC_JACK_CONN_ATAPI, + AC_JACK_CONN_RCA, + AC_JACK_CONN_OPTICAL, + AC_JACK_CONN_OTHER_DIGITAL, + AC_JACK_CONN_OTHER_ANALOG, + AC_JACK_CONN_DIN, + AC_JACK_CONN_XLR, + AC_JACK_CONN_RJ11, + AC_JACK_CONN_COMB, + AC_JACK_CONN_OTHER = 0xf, +}; + +/* jack colors (0x0 - 0xf) */ +enum { + AC_JACK_COLOR_UNKNOWN, + AC_JACK_COLOR_BLACK, + AC_JACK_COLOR_GREY, + AC_JACK_COLOR_BLUE, + AC_JACK_COLOR_GREEN, + AC_JACK_COLOR_RED, + AC_JACK_COLOR_ORANGE, + AC_JACK_COLOR_YELLOW, + AC_JACK_COLOR_PURPLE, + AC_JACK_COLOR_PINK, + AC_JACK_COLOR_WHITE = 0xe, + AC_JACK_COLOR_OTHER, +}; + +/* Jack location (0x0 - 0x3f) */ +enum { + AC_JACK_LOC_NONE, + AC_JACK_LOC_REAR, + AC_JACK_LOC_FRONT, + AC_JACK_LOC_LEFT, + AC_JACK_LOC_RIGHT, + AC_JACK_LOC_TOP, + AC_JACK_LOC_BOTTOM, +}; + +/* bits 4-5 */ +enum { + AC_JACK_LOC_EXTERNAL = 0x00, + AC_JACK_LOC_INTERNAL = 0x10, + AC_JACK_LOC_SEPARATE = 0x20, + AC_JACK_LOC_OTHER = 0x30, +}; + +enum { + /* external on primary chasis */ + AC_JACK_LOC_REAR_PANEL = 0x07, + AC_JACK_LOC_DRIVE_BAY, + /* internal */ + AC_JACK_LOC_RISER = 0x17, + AC_JACK_LOC_HDMI, + AC_JACK_LOC_ATAPI, + /* others */ + AC_JACK_LOC_MOBILE_IN = 0x37, + AC_JACK_LOC_MOBILE_OUT, +}; + +/* Port connectivity (0-3) */ +enum { + AC_JACK_PORT_COMPLEX, + AC_JACK_PORT_NONE, + AC_JACK_PORT_FIXED, + AC_JACK_PORT_BOTH, +}; + +/* 0 = input, 1 = output */ +enum { + HDA_INPUT, HDA_OUTPUT +}; + +/* amp values */ +#define AMP_IN_MUTE(idx) (0x7080 | ((idx)<< 8)) +#define AMP_IN_UNMUTE(idx) (0x7000 | ((idx)<< 8)) +#define AMP_OUT_MUTE 0xb080 +#define AMP_OUT_UNMUTE 0xb000 +#define AMP_OUT_ZERO 0xb000 + +/* pinctl values */ +#define PIN_IN 0x20 +#define PIN_VREF80 0x24 +#define PIN_VREF50 0x21 +#define PIN_OUT 0x40 +#define PIN_HP 0xc0 +#define PIN_HP_AMP 0x80 + +#define defconfig_type(node) (((node)->def_config & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT) +#define defconfig_location(node) (((node)->def_config & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT) +#define defconfig_port_conn(node) (((node)->def_config & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT) + + +/**********************************************************/ +/* Azalia base0 commands */ +/* registers accessed via AZBAR - base0 - AZALIA registers */ +/* */ +/**********************************************************/ + +#define HDA_GCAP 0x00 /* Global Capabilities */ +#define HDA_VMIN 0x02 /* Minor Version */ +#define HDA_VMAJ 0x03 /* Major Version */ +#define HDA_OUTPAY 0x04 /* Output Payload Capability */ +#define HDA_INPAY 0x06 /* Input Payload Capability */ +#define HDA_GCTL 0x08 /* Global Control */ +#define CRST BIT0 /* Controller reset */ +#define UREN BIT8 /* Unsolicited responses */ +#define HDA_WAKEEN 0x0C /* Wake Enable */ +#define HDA_STATESTS 0x0E /* Wake Status */ +#define HDA_GSTST 0x10 /* Global Status */ +#define HDA_ECAP 0x14 /* Extended capabilities - mobile only */ +#define HDA_OUTSTRMPAY 0X18 /* Output stream payload capability */ +#define HDA_INSTRMPAY 0x1A /* Input stream payload capability */ + +#define HDA_INTCTL 0x20 /* Interrupt Control */ +#define HDA_INT_ALL_STREAM 0xff /* all stream interrupts mask on IOC */ +#define HDA_INT_CTRL_EN BIT30 /* controller interrupt enable bit */ +#define HDA_INT_GLOBAL_EN BIT31 /* global interrupt enable bit */ + +#define HDA_INTSTS 0x24 /* Interrupt Status */ +#define REG_WALCLK 0x30 /* Wall Clock Counter */ +#define HDA_SSYNC 0x34 /* Stream Synchronization */ + +#define HDA_CORBLBASE 0x40 /* CORB Lower Base Address */ +#define HDA_CORBUBASE 0x44 /* CORB Upper Base Address */ +#define HDA_CORBWP 0x48 /* CORB Write Pointer */ +#define HDA_CORBRP 0x4A /* CORB Read Pointer */ +#define HDA_CORBCTL 0x4C /* CORB Control */ +#define HDA_CORBSTS 0x4D /* CORB Status */ +#define HDA_CORBSIZE 0x4E /* CORB Size */ + +#define HDA_RIRBLBASE 0x50 /* RIRB Lower Base Address */ +#define HDA_RIRBUBASE 0x54 /* RIRB Upper Base Address */ +#define HDA_RIRBWP 0x58 /* RIRB Write Pointer */ +#define HDA_RINTCNT 0x5A /* Response Interrupt Count */ +#define HDA_RIRBCTL 0x5C /* RIRB Control */ +#define HDA_RIRBSTS 0x5D /* RIRB Status */ +#define HDA_RIRBSIZE 0x5E /* RIRB Size */ + +#define HDA_IC 0x60 /* Immediate Command */ +#define HDA_IR 0x64 /* Immediate Response */ +#define HDA_IRS 0x68 /* Immediate Command Status */ +#define IRS_BUSY BIT0 /* immediate command busy */ +#define IRS_VALID BIT1 /* immediate command valid */ +#define HDA_DPLBASE 0x70 /* DMA Position Lower Base Address */ +#define HDA_DPUBASE 0x74 /* DMA Position Upper Base Address */ +#define HDA_DPLBASE_ENABLE 0x1 /* Enable position buffer */ + +#define HDA_SD_CTL 0x0 /* stream register offsets from stream base - not used */ +#define HDA_SD_STS 0x3 +#define HDA_SD_LPIB 0x4 +#define HDA_SD_CBL 0x8 +#define HDA_SD_LVI 0xC +#define HDA_SD_FIFOW 0xE +#define HDA_SD_FIFOSIZE 0x10 +#define HDA_SD_FORMAT 0x12 +#define HDA_SD_BDLPL 0x18 +#define HDA_SD_BDLPU 0x1C +#define HDA_SD_LPIBA 0x2004 + +/* SDCTL - Stream Descriptor Control Register bits */ +#define SD_CTL_STREAM_RESET BIT0 /* stream reset bit */ +#define SD_CTL_DMA_START BIT1 /* stream DMA start bit */ +#define SD_CTL_STREAM_TAG_MASK (0xf << 20) /* set bits 20 - 23 of SD_CTL register */ +#define SD_CTL_STREAM_TAG_SHIFT 20 + +/* SDSTS - Stream Descriptor Status Register bits */ +#define SD_INT_COMPLETE BIT2 /* completion interrupt */ +#define SD_INT_FIFO_ERR BIT3 /* FIFO error interrupt */ +#define SD_INT_DESC_ERR BIT4 /* descriptor error interrupt */ +#define RIRB_INT_MASK (BIT0 | BIT2) +#define SD_INT_MASK (SD_INT_DESC_ERR | SD_INT_FIFO_ERR | SD_INT_COMPLETE) +#define STATESTS_INT_MASK (BIT0 | BIT1 | BIT2) + +#define HDA_SDI0CTL 0x80 /* Stream Descriptor Control */ +#define HDA_SDI0STS 0x83 /* Stream Descriptor Status */ +#define HDA_SDI0LPIB 0x84 /* Link Position in Current Buffer */ +#define HDA_SDI0CBL 0x88 /* Cyclic Buffer Length */ +#define HDA_SDI0LVI 0x8C /* Last Valid Index */ +#define HDA_SDI0FIFOW 0x8E /* FIFO watermark */ +#define HDA_SDI0FIFOSIZE 0x90 /* FIFO Size */ +#define HDA_SDI0FORMAT 0x92 /* Format */ +#define HDA_SDI0BDLPL 0x98 /* List Pointer - Lower */ +#define HDA_SDI0BDLPU 0x9C /* List Pointer - Upper */ +#define HDA_SDI0LPIBA 0x2084 /* Link Posiiton in Buffer n Alias */ + +#define HDA_SDI1CTL 0xA0 /* Stream Descriptor Control */ +#define HDA_SDI1STS 0xA3 /* Stream Descriptor Status */ +#define HDA_SDI1LPIB 0xA4 /* Link Position in Current Buffer */ +#define HDA_SDI1CBL 0xA8 /* Cyclic Buffer Length */ +#define HDA_SDI1LVI 0xAC /* Last Valid Index */ +#define HDA_SDI1FIFOW 0xAE /* FIFO watermark */ +#define HDA_SDI1FIFOSIZE 0xB0 /* FIFO Size */ +#define HDA_SDI1FORMAT 0xB2 /* Format */ +#define HDA_SDI1BDLPL 0xB8 /* List Pointer - Lower */ +#define HDA_SDI1BDLPU 0xBC /* List Pointer - Upper */ +#define HDA_SDI1LPIBA 0x20A4 /* Link Posiiton in Buffer n Alias */ + +#define HDA_SDI2CTL 0xC0 /* Stream Descriptor Control */ +#define HDA_SDI2STS 0xC3 /* Stream Descriptor Status */ +#define HDA_SDI2LPIB 0xC4 /* Link Position in Current Buffer */ +#define HDA_SDI2CBL 0xC8 /* Cyclic Buffer Length */ +#define HDA_SDI2LVI 0xCC /* Last Valid Index */ +#define HDA_SDI2FIFOW 0xCE /* FIFO watermark */ +#define HDA_SDI2FIFOSIZ 0xD0 /* FIFO Size */ +#define HDA_SDI2FORMAT 0xD2 /* Format */ +#define HDA_SDI2BDLPL 0xD8 /* List Pointer - Lower */ +#define HDA_SDI2BDLPU 0xDC /* List Pointer - Upper */ +#define HDA_SDI2LPIBA 0x20D4 /* Link Posiiton in Buffer n Alias */ + +#define HDA_SDI3CTL 0xE0 /* Stream Descriptor Control */ +#define HDA_SDI3STS 0xE3 /* Stream Descriptor Status */ +#define HDA_SDI3LPIB 0xE4 /* Link Position in Current Buffer */ +#define HDA_SDI3CBL 0xE8 /* Cyclic Buffer Length */ +#define HDA_SDI3LVI 0xEC /* Last Valid Index */ +#define HDA_SDI3FIFOW 0xFE /* FIFO watermark */ +#define HDA_SDI3FIFOSIZE 0xF0 /* FIFO Size */ +#define HDA_SDI3FORMAT 0xF2 /* Format */ +#define HDA_SDI3BDLPL 0xF8 /* List Pointer - Lower */ +#define HDA_SDI3BDLPU 0xFC /* List Pointer - Upper */ +#define HDA_SDI3LPIBA 0x20E4 /* Link Posiiton in Buffer n Alias */ + +#define HDA_SDO0CTL 0x100 /* Stream Descriptor Control */ +#define HDA_SDO0STS 0x103 /* Stream Descriptor Status */ +#define HDA_SDO0LPIB 0x104 /* Link Position in Current Buffer */ +#define HDA_SDO0CBL 0x108 /* Cyclic Buffer Length */ +#define HDA_SDO0LVI 0x10C /* Last Valid Index */ +#define HDA_SDO0FIFOW 0x10E /* FIFO watermark */ +#define HDA_SDO0FIFOSIZE 0x110 /* FIFO Size */ +#define HDA_SDO0FORMAT 0x112 /* Format */ +#define HDA_SDO0BDLPL 0x118 /* List Pointer - Lower */ +#define HDA_SDO0BDLPU 0x11C /* List Pointer - Upper */ +#define HDA_SDO0LPIBA 0x2104 /* Link Posiiton in Buffer n Alias */ + +#define HDA_SDO1CTL 0x120 /* Stream Descriptor Control */ +#define HDA_SDO1STS 0x123 /* Stream Descriptor Status */ +#define HDA_SDO1LPIB 0x124 /* Link Position in Current Buffer */ +#define HDA_SDO1CBL 0x128 /* Cyclic Buffer Length */ +#define HDA_SDO1LVI 0x12C /* Last Valid Index */ +#define HDA_SDO1FIFOW 0x12E /* FIFO watermark */ +#define HDA_SDO1FIFOSIZE 0x130 /* FIFO Size */ +#define HDA_SDO1FORMAT 0x132 /* Format */ +#define HDA_SDO1BDLPL 0x138 /* List Pointer - Lower */ +#define HDA_SDO1BDLPU 0x13C /* List Pointer - Upper */ +#define HDA_SDO1LPIBA 0x2124 /* Link Posiiton in Buffer n Alias */ + +#define HDA_SDO2CTL 0x140 /* Stream Descriptor Control */ +#define HDA_SDO2STS 0x143 /* Stream Descriptor Status */ +#define HDA_SDO2LPIB 0x144 /* Link Position in Current Buffer */ +#define HDA_SDO2CBL 0x148 /* Cyclic Buffer Length */ +#define HDA_SDO2LVI 0x14C /* Last Valid Index */ +#define HDA_SDO2FIFOW 0x14E /* FIFO watermark */ +#define HDA_SDO2FIFOSIZE 0x150 /* FIFO Size */ +#define HDA_SDO2FORMAT 0x152 /* Format */ +#define HDA_SDO2BDLPL 0x158 /* List Pointer - Lower */ +#define HDA_SDO2BDLPU 0x15C /* List Pointer - Upper */ +#define HDA_SDO2LPIBA 0x2144 /* Link Posiiton in Buffer n Alias */ + +#define HDA_SDO3CTL 0x160 /* Stream Descriptor Control */ +#define HDA_SDO3STS 0x163 /* Stream Descriptor Status */ +#define HDA_SDO3LPIB 0x164 /* Link Position in Current Buffer */ +#define HDA_SDO3CBL 0x168 /* Cyclic Buffer Length */ +#define HDA_SDO3LVI 0x16C /* Last Valid Index */ +#define HDA_SDO3FIFOW 0x16E /* FIFO watermark */ +#define HDA_SDO3FIFOSIZE 0x170 /* FIFO Size */ +#define HDA_SDO3FORMAT 0x172 /* Format */ +#define HDA_SDO3BDLPL 0x178 /* List Pointer - Lower */ +#define HDA_SDO3BDLPU 0x17C /* List Pointer - Upper */ + + +/* AC97/HDA supported controller types */ +#define DEVICE_INTEL 0 /* AC97 device Intel ICH compatible */ +#define DEVICE_SIS 1 /* AC97 device SIS compatible */ +#define DEVICE_INTEL_ICH4 2 /* AC97 device Intel ICH4 compatible */ +#define DEVICE_NFORCE 3 /* AC97 device nForce compatible */ +#define DEVICE_HDA_INTEL 4 /* HDA audio device for Intel and others */ +#define DEVICE_HDA_ATI 5 +#define DEVICE_HDA_ATIHDMI 6 +#define DEVICE_HDA_NVIDIA 7 +#define DEVICE_HDA_SIS 8 +#define DEVICE_HDA_ULI 9 +#define DEVICE_HDA_VIA 10 + +#define PCI_ANY_ID ((WORD)(~0)) + +static AUDIO_DEVICE_LIST audio_dev_list[] = { + // supported controllers AC97 INTEL + { 0x8086, 0x2415, PCI_ANY_ID, PCI_ANY_ID, DEVICE_INTEL, "Intel 82801AA (ICH) integrated AC97 audio codec" }, + { 0x8086, 0x2425, PCI_ANY_ID, PCI_ANY_ID, DEVICE_INTEL, "Intel 82801AB (ICH0) integrated AC97 audio codec" }, + { 0x8086, 0x2445, PCI_ANY_ID, PCI_ANY_ID, DEVICE_INTEL, "Intel 82801BA (ICH2) integrated AC97 audio codec" }, + { 0x8086, 0x2485, PCI_ANY_ID, PCI_ANY_ID, DEVICE_INTEL, "Intel 82801CA (ICH3) integrated AC97 audio codec" }, + { 0x8086, 0x24c5, PCI_ANY_ID, PCI_ANY_ID, DEVICE_INTEL_ICH4, "Intel 82801DB (ICH4) integrated AC97 audio codec" }, + { 0x8086, 0x24d5, PCI_ANY_ID, PCI_ANY_ID, DEVICE_INTEL_ICH4, "Intel 82801EB/ER (ICH5/ICH5R) integrated AC97 audio codec" }, + { 0x8086, 0x25a6, PCI_ANY_ID, PCI_ANY_ID, DEVICE_INTEL_ICH4, "Intel 6300ESB integrated AC97 audio codec" }, + { 0x8086, 0x266e, PCI_ANY_ID, PCI_ANY_ID, DEVICE_INTEL_ICH4, "Intel 82801FB (ICH6) integrated AC97 audio codec" }, + { 0x8086, 0x27de, PCI_ANY_ID, PCI_ANY_ID, DEVICE_INTEL_ICH4, "Intel 82801GB (ICH7) integrated AC97 audio codec" }, + { 0x8086, 0x7195, PCI_ANY_ID, PCI_ANY_ID, DEVICE_INTEL, "Intel 82443MX integrated AC97 audio codec" }, + + // supported controllers AC97 other (AMD/NVIDIA/SIS) + { 0x1022, 0x7445, PCI_ANY_ID, PCI_ANY_ID, DEVICE_INTEL, "AMD 768 integrated AC97 audio codec" }, + { 0x1022, 0x746d, PCI_ANY_ID, PCI_ANY_ID, DEVICE_INTEL, "AMD 8111 integrated AC97 audio codec" }, + + { 0x10de, 0x01b1, PCI_ANY_ID, PCI_ANY_ID, DEVICE_NFORCE, "Nvidia nForce integrated AC97 audio codec" }, + { 0x10de, 0x006a, PCI_ANY_ID, PCI_ANY_ID, DEVICE_NFORCE, "Nvidia nForce2 integrated AC97 audio codec" }, + { 0x10de, 0x008a, PCI_ANY_ID, PCI_ANY_ID, DEVICE_NFORCE, "Nvidia CK8 (nForce compatible) integrated AC97 audio codec" }, + { 0x10de, 0x00da, PCI_ANY_ID, PCI_ANY_ID, DEVICE_NFORCE, "Nvidia nForce3 integrated AC97 audio codec" }, + { 0x10de, 0x00ea, PCI_ANY_ID, PCI_ANY_ID, DEVICE_NFORCE, "Nvidia CK8S (nForce compatible) integrated AC97 audio codec" }, + { 0x10de, 0x0059, PCI_ANY_ID, PCI_ANY_ID, DEVICE_NFORCE, "Nvidia nForce4 integrated AC97 audio codec" }, + { 0x10de, 0x026b, PCI_ANY_ID, PCI_ANY_ID, DEVICE_NFORCE, "Nvidia nForce MCP51 integrated AC97 audio codec" }, + { 0x10de, 0x003a, PCI_ANY_ID, PCI_ANY_ID, DEVICE_NFORCE, "Nvidia nForce MCP04 integrated AC97 audio codec" }, + { 0x10de, 0x006b, PCI_ANY_ID, PCI_ANY_ID, DEVICE_NFORCE, "Nvidia nForce MCP integrated AC97 audio codec" }, + + { 0x1039, 0x7012, PCI_ANY_ID, PCI_ANY_ID, DEVICE_SIS, "SiS SI7012 integrated AC97 audio codec" }, + + // supported controllers HDA INTEL + { 0x8086, 0x2668, PCI_ANY_ID, PCI_ANY_ID, DEVICE_HDA_INTEL, "Intel 82801F (ICH6) integrated High Definition Audio controller" }, + { 0x8086, 0x27d8, PCI_ANY_ID, PCI_ANY_ID, DEVICE_HDA_INTEL, "Intel 82801G (ICH7) integrated High Definition Audio controller" }, + { 0x8086, 0x269a, PCI_ANY_ID, PCI_ANY_ID, DEVICE_HDA_INTEL, "Intel ESB2 integrated High Definition Audio controller" }, + { 0x8086, 0x284b, PCI_ANY_ID, PCI_ANY_ID, DEVICE_HDA_INTEL, "Intel 82801H (ICH8) integrated High Definition Audio controller" }, + { 0x8086, 0x293e, PCI_ANY_ID, PCI_ANY_ID, DEVICE_HDA_INTEL, "Intel 82801I (ICH9) integrated High Definition Audio controller" }, + { 0x8086, 0x293f, PCI_ANY_ID, PCI_ANY_ID, DEVICE_HDA_INTEL, "Intel 82801I (ICH9) integrated High Definition Audio controller" }, + { 0x8086, 0x3a3e, PCI_ANY_ID, PCI_ANY_ID, DEVICE_HDA_INTEL, "Intel 82801J (ICH10R) integrated High Definition Audio controller" }, + { 0x8086, 0x3a6e, PCI_ANY_ID, PCI_ANY_ID, DEVICE_HDA_INTEL, "Intel 82801J (ICH10) integrated High Definition Audio controller" }, + { 0x8086, 0x3b56, PCI_ANY_ID, PCI_ANY_ID, DEVICE_HDA_INTEL, "Intel 5-series integrated High Definition Audio controller" }, + { 0x8086, 0x1c20, PCI_ANY_ID, PCI_ANY_ID, DEVICE_HDA_INTEL, "Intel 6-series integrated High Definition Audio controller" }, + { 0x8086, 0x1d20, PCI_ANY_ID, PCI_ANY_ID, DEVICE_HDA_INTEL, "Intel 7-series integrated High Definition Audio controller" }, + + // supported controllers HDA other (ATI/NVIDIA/SIS/ULI/VIA) + { 0x1002, 0x437b, PCI_ANY_ID, PCI_ANY_ID, DEVICE_HDA_ATI, "ATI Technologies SB450 integrated High Definition Audio controller" }, + { 0x1002, 0x4383, PCI_ANY_ID, PCI_ANY_ID, DEVICE_HDA_ATI, "ATI Technologies SB600 integrated High Definition Audio controller" }, + + { 0x1002, 0x793b, PCI_ANY_ID, PCI_ANY_ID, DEVICE_HDA_ATIHDMI, "ATI Technologies RS600 integrated High Definition Audio controller",}, + { 0x1002, 0x7919, PCI_ANY_ID, PCI_ANY_ID, DEVICE_HDA_ATIHDMI, "ATI Technologies RS690 integrated High Definition Audio controller",}, + { 0x1002, 0x960c, PCI_ANY_ID, PCI_ANY_ID, DEVICE_HDA_ATIHDMI, "ATI Technologies RS780 integrated High Definition Audio controller",}, + { 0x1002, 0xaa00, PCI_ANY_ID, PCI_ANY_ID, DEVICE_HDA_ATIHDMI, "ATI Technologies R600 integrated High Definition Audio controller",}, + + { 0x10de, 0x026c, PCI_ANY_ID, PCI_ANY_ID, DEVICE_HDA_NVIDIA, "Nvidia nForce MCP51 integrated High Definition Audio controller" }, + { 0x10de, 0x0371, PCI_ANY_ID, PCI_ANY_ID, DEVICE_HDA_NVIDIA, "Nvidia nForce MCP55 integrated High Definition Audio controller" }, + { 0x10de, 0x03e4, PCI_ANY_ID, PCI_ANY_ID, DEVICE_HDA_NVIDIA, "Nvidia nForce MCP61a integrated High Definition Audio controller" }, + { 0x10de, 0x03f0, PCI_ANY_ID, PCI_ANY_ID, DEVICE_HDA_NVIDIA, "Nvidia nForce MCP61b integrated High Definition Audio controller" }, + { 0x10de, 0x044a, PCI_ANY_ID, PCI_ANY_ID, DEVICE_HDA_NVIDIA, "Nvidia nForce MCP65a integrated High Definition Audio controller" }, + { 0x10de, 0x044b, PCI_ANY_ID, PCI_ANY_ID, DEVICE_HDA_NVIDIA, "Nvidia nForce MCP65b integrated High Definition Audio controller" }, + { 0x10de, 0x055c, PCI_ANY_ID, PCI_ANY_ID, DEVICE_HDA_NVIDIA, "Nvidia nForce MCP67a integrated High Definition Audio controller" }, + { 0x10de, 0x055d, PCI_ANY_ID, PCI_ANY_ID, DEVICE_HDA_NVIDIA, "Nvidia nForce MCP67b integrated High Definition Audio controller" }, + { 0x10de, 0x07fc, PCI_ANY_ID, PCI_ANY_ID, DEVICE_HDA_NVIDIA, "Nvidia nForce MCP73a integrated High Definition Audio controller" }, + { 0x10de, 0x07fd, PCI_ANY_ID, PCI_ANY_ID, DEVICE_HDA_NVIDIA, "Nvidia nForce MCP73b integrated High Definition Audio controller" }, + { 0x10de, 0x0774, PCI_ANY_ID, PCI_ANY_ID, DEVICE_HDA_NVIDIA, "Nvidia nForce MCP77a integrated High Definition Audio controller" }, + { 0x10de, 0x0775, PCI_ANY_ID, PCI_ANY_ID, DEVICE_HDA_NVIDIA, "Nvidia nForce MCP77b integrated High Definition Audio controller" }, + { 0x10de, 0x0776, PCI_ANY_ID, PCI_ANY_ID, DEVICE_HDA_NVIDIA, "Nvidia nForce MCP77c integrated High Definition Audio controller" }, + { 0x10de, 0x0777, PCI_ANY_ID, PCI_ANY_ID, DEVICE_HDA_NVIDIA, "Nvidia nForce MCP77d integrated High Definition Audio controller" }, + { 0x10de, 0x0ac0, PCI_ANY_ID, PCI_ANY_ID, DEVICE_HDA_NVIDIA, "Nvidia nForce MCP79a integrated High Definition Audio controller" }, + { 0x10de, 0x0ac1, PCI_ANY_ID, PCI_ANY_ID, DEVICE_HDA_NVIDIA, "Nvidia nForce MCP79b integrated High Definition Audio controller" }, + { 0x10de, 0x0ac2, PCI_ANY_ID, PCI_ANY_ID, DEVICE_HDA_NVIDIA, "Nvidia nForce MCP79c integrated High Definition Audio controller" }, + { 0x10de, 0x0ac3, PCI_ANY_ID, PCI_ANY_ID, DEVICE_HDA_NVIDIA, "Nvidia nForce MCP79d integrated High Definition Audio controller" }, + + { 0x1039, 0x7502, PCI_ANY_ID, PCI_ANY_ID, DEVICE_HDA_SIS, "SIS 966 integrated High Definition Audio controller" }, + + { 0x10b9, 0x5461, PCI_ANY_ID, PCI_ANY_ID, DEVICE_HDA_ULI, "ULI M5461 integrated High Definition Audio controller" }, + + { 0x1106, 0x3288, PCI_ANY_ID, PCI_ANY_ID, DEVICE_HDA_VIA, "VIA 8251/8237 integrated High Definition Audio controller" }, + + // null entry + { 0x0000, 0x0000, PCI_ANY_ID, PCI_ANY_ID, 0, "" } +}; + +static AUDIO_STEREO_TECHNOLOGY audio_stereo_technology[] = { + {"No 3D Stereo Enhancement"}, + {"Analog Devices Phat Stereo"}, // 1 + {"Creative Stereo Enhancement"}, // 2 + {"National Semi 3D Stereo Enhancement"}, // 3 + {"YAMAHA Ymersion"}, // 4 + {"BBE 3D Stereo Enhancement"}, // 5 + {"Crystal Semi 3D Stereo Enhancement"}, // 6 + {"Qsound QXpander"}, // 7 + {"Spatializer 3D Stereo Enhancement"}, // 8 + {"SRS 3D Stereo Enhancement"}, // 9 + {"Platform Tech 3D Stereo Enhancement"}, // 10 + {"AKM 3D Audio"}, // 11 + {"Aureal Stereo Enhancement"}, // 12 + {"Aztech 3D Enhancement"}, // 13 + {"Binaura 3D Audio Enhancement"}, // 14 + {"ESS Technology Stereo Enhancement"}, // 15 + {"Harman International VMAx"}, // 16 + {"Nvidea 3D Stereo Enhancement"}, // 17 + {"Philips Incredible Sound"}, // 18 + {"Texas Instruments 3D Stereo Enhancement"}, // 19 + {"VLSI Technology 3D Stereo Enhancement"}, // 20 + {"TriTech 3D Stereo Enhancement"}, // 21 + {"Realtek 3D Stereo Enhancement"}, // 22 + {"Samsung 3D Stereo Enhancement"}, // 23 + {"Wolfson Microelectronics 3D Enhancement"}, // 24 + {"Delta Integration 3D Enhancement"}, // 25 + {"SigmaTel 3D Enhancement"}, // 26 + {"Winbond 3D Stereo Enhancement"}, // 27 + {"Rockwell 3D Stereo Enhancement"}, // 28 + {"Unknown 3D Stereo Enhancement"}, // 29 + {"Unknown 3D Stereo Enhancement"}, // 30 + {"Unknown 3D Stereo Enhancement"}, // 31 +}; + +// vender id, device id, string +static AUDIO_CODEC_LIST audio_codec_list[] = { + + // AC97 codecs + {0x4144, 0x5303, "Analog Devices AD1819"}, + {0x4144, 0x5340, "Analog Devices AD1881"}, + {0x4144, 0x5348, "Analog Devices AD1881A"}, + {0x4144, 0x5360, "Analog Devices AD1885"}, + {0x4144, 0x5361, "Analog Devices AD1886"}, + {0x4144, 0x5362, "Analog Devices AD1887"}, + {0x4144, 0x5363, "Analog Devices AD1886A"}, + {0x4144, 0x5370, "Analog Devices AD1980"}, + + {0x414B, 0x4D00, "Asahi Kasei AK4540"}, + {0x414B, 0x4D01, "Asahi Kasei AK4542"}, + {0x414B, 0x4D02, "Asahi Kasei AK4543"}, + + {0x414C, 0x4300, "ALC100"}, + {0x414C, 0x4326, "ALC100P"}, + {0x414C, 0x4710, "ALC200/200P"}, + {0x414C, 0x4720, "ALC650"}, + {0x414C, 0x4721, "ALC650D"}, + {0x414C, 0x4722, "ALC650E"}, + {0x414C, 0x4723, "ALC650F"}, + {0x414C, 0x4760, "ALC655"}, + {0x414C, 0x4780, "ALC658"}, + {0x414C, 0x4781, "ALC658D"}, + {0x414C, 0x4790, "ALC850"}, + + {0x434D, 0x4941, "CMedia CM9738"}, + {0x434D, 0x4942, "CMedia"}, + {0x434D, 0x4961, "CMedia CM9739"}, + {0x434D, 0x4978, "CMedia CM9761 rev A"}, + {0x434D, 0x4982, "CMedia CM9761 rev B"}, + {0x434D, 0x4983, "CMedia CM9761 rev C"}, + + {0x4352, 0x5900, "Cirrus Logic CS4297"}, + {0x4352, 0x5903, "Cirrus Logic CS4297"}, + {0x4352, 0x5910, "Cirrus Logic CS4297A"}, + {0x4352, 0x5913, "Cirrus Logic CS4297A rev A"}, + {0x4352, 0x5914, "Cirrus Logic CS4297A rev B"}, + {0x4352, 0x5923, "Cirrus Logic CS4298"}, + {0x4352, 0x592B, "Cirrus Logic CS4294"}, + {0x4352, 0x592D, "Cirrus Logic CS4294"}, + {0x4352, 0x5930, "Cirrus Logic CS4299"}, + {0x4352, 0x5931, "Cirrus Logic CS4299 rev A"}, + {0x4352, 0x5933, "Cirrus Logic CS4299 rev C"}, + {0x4352, 0x5934, "Cirrus Logic CS4299 rev D"}, + {0x4352, 0x5948, "Cirrus Logic CS4201"}, + {0x4352, 0x5958, "Cirrus Logic CS4205"}, + + {0x4358, 0x5442, "CXT66"}, + + {0x4454, 0x3031, "Diamond Technology DT0893"}, + + {0x4583, 0x8308, "ESS Allegro ES1988"}, + + {0x4943, 0x4511, "ICEnsemble ICE1232"}, + {0x4943, 0x4541, "VIA Vinyl"}, + {0x4943, 0x4551, "VIA Vinyl"}, + {0x4943, 0x4552, "VIA Vinyl"}, + {0x4943, 0x6010, "VIA Vinyl"}, + {0x4943, 0x6015, "VIA Vinyl"}, + {0x4943, 0x6017, "VIA Vinyl"}, + + {0x4e53, 0x4331, "National Semiconductor LM4549"}, + + {0x5349, 0x4c22, "Silicon Laboratory Si3036"}, + {0x5349, 0x4c23, "Silicon Laboratory Si3038"}, + + {0x5452, 0x00FF, "TriTech TR?????"}, + {0x5452, 0x4102, "TriTech TR28022"}, + {0x5452, 0x4103, "TriTech TR28023"}, + {0x5452, 0x4106, "TriTech TR28026"}, + {0x5452, 0x4108, "TriTech TR28028"}, + {0x5452, 0x4123, "TriTech TR A5"}, + + {0x5745, 0x4301, "Winbond 83971D"}, + + {0x574D, 0x4C00, "Wolfson WM9704"}, + {0x574D, 0x4C03, "WM9703/07/08/17"}, + {0x574D, 0x4C04, "WM9704M/WM9704Q"}, + {0x574D, 0x4C05, "Wolfson WM9705/WM9710"}, + + {0x594D, 0x4803, "Yamaha YMF753"}, + + {0x8384, 0x7600, "SigmaTel STAC9700"}, + {0x8384, 0x7604, "SigmaTel STAC9704"}, + {0x8384, 0x7605, "SigmaTel STAC9705"}, + {0x8384, 0x7608, "SigmaTel STAC9708"}, + {0x8384, 0x7609, "SigmaTel STAC9721/23"}, + {0x8384, 0x7644, "SigmaTel STAC9744/45"}, + {0x8384, 0x7656, "SigmaTel STAC9756/57"}, + {0x8384, 0x7666, "SigmaTel STAC9750T"}, + {0x8384, 0x7684, "SigmaTel STAC9783/84"}, + + // HDA codecs + {0x10ec, 0x0260, "ALC260 Realtek High Definition Audio"}, + {0x10ec, 0x0268, "ALC268 Realtek High Definition Audio"}, + {0x10ec, 0x0660, "ALC660 Realtek High Definition Audio"}, + {0x10ec, 0x0861, "ALC861 Realtek High Definition Audio"}, + {0x10ec, 0x0862, "ALC861VD Realtek High Definition Audio"}, + {0x10ec, 0x0880, "ALC880 Realtek High Definition Audio"}, + {0x10ec, 0x0882, "ALC882 Realtek High Definition Audio"}, + {0x10ec, 0x0883, "ALC883 Realtek High Definition Audio"}, + {0x10ec, 0x0885, "ALC885 Realtek High Definition Audio"}, + {0x10ec, 0x0888, "ALC888 Realtek High Definition Audio"}, + + {0x11d4, 0x1981, "AD1981HD Analog Devices High Definition Audio"}, + {0x11d4, 0x1983, "AD1983 Analog Devices High Definition Audio"}, + {0x11d4, 0x1984, "AD1984 Analog Devices High Definition Audio"}, + {0x11d4, 0x1986, "AD1986A Analog Devices High Definition Audio"}, + {0x11d4, 0x1988, "AD1988 Analog Devices High Definition Audio"}, + {0x11d4, 0x198b, "AD1988B Analog Devices High Definition Audio"}, + + {0x434d, 0x4980, "CMI9880 CMedia High Definition Audio"}, + + {0x8384, 0x7680, "STAC9221 Sigmatel High Definition Audio"}, + {0x8384, 0x7683, "STAC9221D Sigmatel High Definition Audio"}, + {0x8384, 0x7690, "STAC9220 Sigmatel High Definition Audio"}, + {0x8384, 0x7681, "STAC9220D Sigmatel High Definition Audio"}, + {0x8384, 0x7618, "STAC9227 Sigmatel High Definition Audio"}, + {0x8384, 0x7627, "STAC9271D Sigmatel High Definition Audio"}, + + {0x14f1, 0x5045, "CXVenice Conexant High Definition Audio"}, + {0x14f1, 0x5047, "CXWaikiki Conexant High Definition Audio"}, + + {0x1106, 0x1708, "VT1708 rev8 High Definition Audio"}, + {0x1106, 0x1709, "VT1708 rev9 High Definition Audio"}, + {0x1106, 0x170a, "VT1708 rev10 High Definition Audio"}, + {0x1106, 0x170b, "VT1708 rev11 High Definition Audio"}, + {0x1106, 0xe710, "VT1709 rev0 High Definition Audio"}, + {0x1106, 0xe711, "VT1709 rev1 High Definition Audio"}, + {0x1106, 0xe712, "VT1709 rev2 High Definition Audio"}, + {0x1106, 0xe713, "VT1709 rev3 High Definition Audio"}, + {0x1106, 0xe714, "VT1709 rev4 High Definition Audio"}, + {0x1106, 0xe715, "VT1709 rev5 High Definition Audio"}, + {0x1106, 0xe716, "VT1709 rev6 High Definition Audio"}, + {0x1106, 0xe717, "VT1709 rev7 High Definition Audio"}, + {0x1106, 0xe718, "VT1709 rev8 High Definition Audio"}, + {0x1106, 0xe719, "VT1709 rev9 High Definition Audio"}, + + {0x0000, 0x0000, "Unknown Device"}, +}; diff --git a/JUDASAC.INC b/JUDASAC.INC new file mode 100644 index 0000000..ca355bc --- /dev/null +++ b/JUDASAC.INC @@ -0,0 +1,523 @@ + +;***************************************************************************** +;* some asm constants to help us :-> +;***************************************************************************** + +%define TRUE 1 +%define FALSE 0 + +%define BIT0 1 +%define BIT1 2 +%define BIT2 4 +%define BIT3 8 +%define BIT4 10h +%define BIT5 20h +%define BIT6 40h +%define BIT7 80h +%define BIT8 100h +%define BIT9 200h +%define BIT10 400h +%define BIT11 800h +%define BIT12 1000h +%define BIT13 2000h +%define BIT14 4000h +%define BIT15 8000h +%define BIT16 10000h +%define BIT17 20000h +%define BIT18 40000h +%define BIT19 80000h +%define BIT20 100000h +%define BIT21 200000h +%define BIT22 400000h +%define BIT23 800000h +%define BIT24 1000000h +%define BIT25 2000000h +%define BIT26 4000000h +%define BIT27 8000000h +%define BIT28 10000000h +%define BIT29 20000000h +%define BIT30 40000000h +%define BIT31 80000000h + + + + +;***************************************************************************** +;* Intel ICH controller equates (based on Jeff Leyda AC97 wav player SDK :-) +;***************************************************************************** + +; supported controllers +%define INTEL_VID 8086h ; Intel's PCI vendor ID +%define SIS_VID 1039h ; SIS PCI vendor ID +%define NVIDIA_VID 10deh ; NVIDIA vendor ID +%define AMD_VID 1022h ; AMD vendor ID + +%define ICH_DID 2415h ; ICH device ID +%define ICH0_DID 2425h ; ICH0 +%define ICH2_DID 2445h ; ICH2 +%define ICH3_DID 2485h ; ICH3 +%define ICH4_DID 24c5h ; ICH4 +%define ICH5R_DID 24d5h ; ICH5R +%define ESB_DID 25a6h ; ESB (embedded - ICH4 comp) +%define ICH6_DID 266eh ; ICH6 +%define ICH6W_DID 2668h ; ICH6W +%define ESB2_DID 2698h ; ESB2 (embedded - ICH4 comp) +%define ICH7_DID 27deh ; ICH7 +%define ICH7W_DID 27d8h ; ICH7W +%define ICHHD_DID 284bh ; ICHHD +%define ICH9_DID 293eh ; ICH9 +%define ICH10_DID 3a6eh ; ICH10 +%define ICH10R_DID 3a3eh ; ICH10R + +%define MX440_DID 7195h ; 440MX +%define SI7012_DID 7012h ; SI7012 + +%define NFORCE_DID 01b1h ; NFORCE +%define NFORCE_MCP04_DID 003ah ; NFORCE MCP04 +%define NFORCE2_DID 006ah ; NFORCE2 +%define NFORCE2b_DID 006bh ; NFORCE2b (?) +%define NFORCE_CK8_DID 008ah ; NFORCE CK8 +%define NFORCE3_DID 00dah ; NFORCE3 +%define NFORCE_CK8S_DID 00eah ; NFORCE CK8S +%define NFORCE4_DID 0059h ; NFORCE4 +%define NFORCE410_MCP_DID 026bh ; NFORCE MCP 410 + +%define AMD8111_DID 764dh ; AMD8111 +%define AMD768_DID 7445h ; AMD768 + + +; PCI config space defines + +%define NAMBAR_REG 10h ; Native Audio Mixer BAR + %define NAM_SIZE 256 ; 256 bytes required for registers + +%define NABMBAR_REG 14h ; Native Audio Bus Mastering BAR + %define NABM_SIZE 64 ; 64 bytes required for registers + + +; BUS master registers, accessed via NABMBAR+offset +; ICH supports 3 different types of register sets for three types of things : +; +; PCM in (for recording) aka PI +; PCM out (for playback) aka PO +; MIC in (for recording) aka MC + +%define PI_BDBAR_REG 0 ; PCM in buffer descriptor BAR +%define PO_BDBAR_REG 10h ; PCM out buffer descriptor BAR +%define MC_BDBAR_REG 20h ; MIC in buffer descriptor BAR + + +; There are 32 entries in the BDL (256 bytes in total) +; Each entry is 8 bytes long. It holds a pointer to a data buffer, +; and the size (in bytes or samples) with execution flags for that buffer. + +%define BDL_SIZE 32*8 ; Buffer Descriptor List size +%define INDEX_MASK 31 ; indexes must be 0-31 + + +; 8bit read only +; The Current Index Value is a number of the buffer (0-31) which the codec +; is currently processing. Once this counter hits 31, it wraps back to 0. + +%define PI_CIV_REG 4 ; PCM in Current Index Value (RO) +%define PO_CIV_REG 14h ; PCM out Current Index Value (RO) +%define MC_CIV_REG 24h ; MIC in Current Index Value (RO) + + +; 8bit read/write +; The Last Valid Index is a number of the buffer (0-31) on which the codec +; will stop processing data. + +%define PI_LVI_REG 5 ; PCM in Last Valid Index +%define PO_LVI_REG 15h ; PCM out Last Valid Index +%define MC_LVI_REG 25h ; MIC in Last Valid Index + + +; 16bit read/write +; status registers. + +%define PI_SR_REG 6 ; PCM in Status Register +%define PO_SR_REG 16h ; PCM out Status Register +%define MC_SR_REG 26h ; MIC in Status Register + + +; status register bitfields + +%define FIFO_ERR BIT4 ; FIFO error (Over/Underrun) W1TC. +%define BCIS BIT3 ; Buffer Completion Interrupt Status. + ; Set whenever the last sample in ANY + ; buffer is finished. Bit is only + ; set when the Interrupt on Complete + ; (BIT4 of control register) is set. +%define LVBCI BIT2 ; Last Valid Buffer Completion Interrupt + ; Set whenever the codec has processed + ; the last buffer in the buffer list. + ; Will fire an interrupt if IOC bit is + ; set. Probably set after the last + ; sample in the last buffer is + ; processed. W1TC +%define CELV BIT1 ; Current Equals Last Valid + ; Bit is RO and remains set until LVI is + ; cleared. Probably set up the start + ; of processing for the last buffer. +%define DCH BIT0 ; DMA controller halted. + ; Set whenever audio stream is stopped + ; or something else goes wrong. + + +; 16bit read only +; Position In Current Buffer shows the number of dwords left to be +; processed in the current buffer. + +%define PI_PICB_REG 8 ; PCM in position in current buffer(RO) +%define PO_PICB_REG 18h ; PCM out position in current buffer(RO) +%define MC_PICB_REG 28h ; MIC in position in current buffer (RO) + + +; 8bit, read only +; Prefetched Index Value Register. +; Tells which buffer number (0-31) has been prefetched. +; This value probably follows the current index value fairly closely. (CIV+1) + +%define PI_PIV_REG 0ah ; PCM in Prefected index value +%define PO_PIV_REG 1ah ; PCM out Prefected index value +%define MC_PIV_REG 2ah ; MIC in Prefected index value + + +; 8bit +; Control register can only be accessed as an 8bit value. +; Control register. + +%define PI_CR_REG 0bh ; PCM in Control Register +%define PO_CR_REG 1bh ; PCM out Control Register +%define MC_CR_REG 2bh ; MIC in Control Register + + +; control register bitfields. + +%define IOCE BIT4 ; Interrupt On Complete Enable + ; Set this bit if you want an interrupt + ; to fire whenever LVBCI is set. +%define FEIFE BIT3 ; Fifo Error Interrupt Enable + ; Set if you want an interrupt to fire + ; whenever there is a FIFO (over or + ; under) error. +%define LVBIE BIT2 ; Last Valid Buffer Interrupt Enable + ; Set if you want an interrupt to fire + ; whenever the completion of the last + ; valid buffer. +%define RR BIT1 ; Reset busmaster Registers + ; Nukes all regs except bits 4:2 + ; of this register. + ; Only set this bit if BIT 0 is 0 +%define RPBM BIT0 ; Start Busmaster operation (Run/Pause) + ; set this bit to start the codec! + + +; Global Control Register with bitfields + +%define GLOB_CNT_REG 2ch ; Global control register +%define SMP_24_BIT BIT23 ; 24 bit samples +%define SMP_20_BIT BIT22 ; 20 bit samples +%define CHN_PCM_246_MASK 300000h ; 6 channels (not all chips) +%define CHN_PCM_6 BIT21 ; 6 channels (not all chips) +%define CHN_PCM_4 BIT20 ; 4 channels (not all chips) +%define CHN_PCM_2 0 ; 2 channels (stereo) +%define SEC_RES_EN BIT5 ; Secondary Codec Resume event + ; interrupt enable. +%define PRI_RES_EN BIT4 ; Primary Codec Resume event + ; interrupt enable. +%define ACLINK_OFF BIT3 ; Turn off the AC97 link +%define ACWARM_RESET BIT2 ; Awaken the AC97 link from sleep. + ; registers preserved, bit self clears +%define ACCOLD_RESET BIT1 ; Reset everything in the AC97 and + ; reset all registers. Not self clearing +%define GPIIE BIT0 ; GPI Interrupt enable. + ; set if you want an interrupt to + ; fire upon ANY of the bits in the + ; GPI changed? + + +; Global Status Register with bitfields + +%define GLOB_STS_REG 30h ; Global Status register (RO) +%define AUDIO_24_BIT BIT23 ; 24 bit sample support +%define AUDIO_20_BIT BIT22 ; 20 bit sample support +%define CHN_CAP_6 BIT21 ; 6 channels capability +%define CHN_CAP_4 BIT20 ; 4 channels capability +%define MD3 BIT17 ; Modem powerdown status (yawn) +%define AD3 BIT16 ; Audio powerdown status (yawn) +%define RD_COMPLETE_STS BIT15 ; Codec read timed out. 0=normal +%define BIT3SLOT12 BIT14 ; shadowed status of bit 3 in slot 12 +%define BIT2SLOT12 BIT13 ; shadowed status of bit 2 in slot 12 +%define BIT1SLOT12 BIT12 ; shadowed status of bit 1 in slot 12 +%define SEC_RESUME_STS BIT11 ; secondary codec has resumed (and irqed) +%define PRI_RESUME_STS BIT10 ; primary codec has resumed (and irqed) +%define SEC_CODEC_RDY BIT9 ; Secondary codec is ready for action +%define PRI_CODEC_RDY BIT8 ; Primary codec is ready for action + ; software must check these bits before + ; starting the codec! +%define MIC_IN_IRQ BIT7 ; MIC in caused an interrupt +%define PCM_OUT_IRQ BIT6 ; One of the PCM out channels IRQed +%define PCM_IN_IRQ BIT5 ; One of the PCM in channels IRQed +%define MODEM_OUT_IRQ BIT2 ; Modem out channel IRQed +%define MODEM_IN_IRQ BIT1 ; Modem in channel IRQed +%define GPI_STS_CHANGE BIT0 ; Set whenever GPI's have changed. + ; BIT0 of slot 12 also reflects this. + + +; Codec Write Semaphore Register + +%define ACC_SEMA_REG 34h ; Codec write semaphore register +%define CODEC_BUSY BIT0 ; codec register I/O is happening + ; self clearing + + + +; Buffer Descriptors List +; Each buffer descriptor list is a set of (up to) 32 descriptors. +; Each descriptor is 8 bytes long. Bytes 0-3 of a descriptor entry point +; to a chunk of memory to either play from or record to. Bytes 4-7 of an +; entry describe various control things detailed below. +; Buffer pointers must always be aligned on a dword boundary. + +%define IOC BIT31 ; Fire an interrupt whenever this + ; buffer is complete. +%define BUP BIT30 ; Buffer Underrun Policy. + ; If this buffer is the last buffer + ; in a playback, fill the remaining + ; samples with 0 (silence) or not. + +; Bits 15:0 contain the length of the buffer, in number of samples, +; (or in number of bytes for the SIS controller) which are 16 bits each, +; coupled in left and right pairs, or 32bits each. +; A value of 0 in these bits means play no samples. + + + + +;***************************************************************************** +;* AC97 Codec registers include (based on Jeff Leyda AC97 wav player SDK :-) +;***************************************************************************** + +; Not all codecs are created equal. Refer to the spec for your specific codec. +; All registers are 16bits wide. Access to codec registers over the AC97 link +; is defined by the OEM. +; Secondary codec's are accessed by ORing in BIT7 of all register accesses. + + +; each codec/mixer register is 16bits + +%define CODEC_RESET_REG 00 ; reset codec +%define CODEC_MASTER_VOL_REG 02 ; master volume +%define CODEC_HP_VOL_REG 04 ; headphone volume +%define CODEC_MASTER_MONO_VOL_REG 06 ; master mono volume +%define CODEC_MASTER_TONE_REG 08 ; master tone (R+L) +%define CODEC_PCBEEP_VOL_REG 0ah ; PC beep volume +%define CODEC_PHONE_VOL_REG 0ch ; phone volume +%define CODEC_MIC_VOL_REG 0eh ; MIC volume +%define CODEC_LINE_IN_VOL_REG 10h ; line input volume +%define CODEC_CD_VOL_REG 12h ; CD volume +%define CODEC_VID_VOL_REG 14h ; video volume +%define CODEC_AUX_VOL_REG 16h ; aux volume +%define CODEC_PCM_OUT_REG 18h ; PCM output volume +%define CODEC_RECORD_SELECT_REG 1ah ; record select input +%define CODEC_RECORD_VOL_REG 1ch ; record volume +%define CODEC_RECORD_MIC_VOL_REG 1eh ; record mic volume +%define CODEC_GP_REG 20h ; general purpose +%define CODEC_3D_CONTROL_REG 22h ; 3D control +; 24h is reserved +%define CODEC_POWER_CTRL_REG 26h ; powerdown control +%define CODEC_EXT_AUDIO_REG 28h ; extended audio +%define CODEC_EXT_AUDIO_CTRL_REG 2ah ; extended audio control +%define CODEC_PCM_FRONT_DACRATE_REG 2ch ; PCM out sample rate +%define CODEC_PCM_SURND_DACRATE_REG 2eh ; surround sound sample rate +%define CODEC_PCM_LFE_DACRATE_REG 30h ; LFE sample rate +%define CODEC_LR_ADCRATE_REG 32h ; PCM in sample rate +%define CODEC_MIC_ADCRATE_REG 34h ; mic in sample rate + + +; registers 36-7a are reserved on the ICH + +%define CODEC_VENDORID1_REG 7ch ; codec vendor ID 1 +%define CODEC_VENDORID2_REG 7eh ; codec vendor ID 2 + + +; When 2 codecs are present in the system, use BIT7 to access the 2nd +; set of registers, ie 80h-feh + +%define PRIMARY_CODEC 0 ; 0-7F for primary codec +%define SECONDARY_CODEC BIT7 ; 80-8f registers for 2ndary + + +; HDA defines (simplified info) + +%define HDA_GCAP 0x00 ; Global Capabilities +%define HDA_VMIN 0x02 ; Minor Version +%define HDA_VMAJ 0x03 ; Major Version +%define HDA_OUTPAY 0x04 ; Output Payload Capability +%define HDA_INPAY 0x06 ; Input Payload Capability +%define HDA_GCTL 0x08 ; Global Control +%define CRST BIT0 ; Controller reset +%define UREN BIT8 ; Unsolicited responses +%define HDA_WAKEEN 0x0C ; Wake Enable +%define HDA_STATESTS 0x0E ; Wake Status +%define HDA_GSTST 0x10 ; Global Status +%define HDA_ECAP 0x14 ; Extended capabilities - mobile only +%define HDA_OUTSTRMPAY 0X18 ; Output stream payload capability +%define HDA_INSTRMPAY 0x1A ; Input stream payload capability + +%define HDA_INTCTL 0x20 ; Interrupt Control +%define HDA_INT_ALL_STREAM 0xff ; all stream interrupts mask on IOC +%define HDA_INT_CTRL_EN BIT30 ; controller interrupt enable bit +%define HDA_INT_GLOBAL_EN BIT31 ; global interrupt enable bit + +%define HDA_INTSTS 0x24 ; Interrupt Status +%define REG_WALCLK 0x30 ; Wall Clock Counter +%define HDA_SSYNC 0x34 ; Stream Synchronization + +%define HDA_CORBLBASE 0x40 ; CORB Lower Base Address +%define HDA_CORBUBASE 0x44 ; CORB Upper Base Address +%define HDA_CORBWP 0x48 ; CORB Write Pointer +%define HDA_CORBRP 0x4A ; CORB Read Pointer +%define HDA_CORBCTL 0x4C ; CORB Control +%define HDA_CORBSTS 0x4D ; CORB Status +%define HDA_CORBSIZE 0x4E ; CORB Size + +%define HDA_RIRBLBASE 0x50 ; RIRB Lower Base Address +%define HDA_RIRBUBASE 0x54 ; RIRB Upper Base Address +%define HDA_RIRBWP 0x58 ; RIRB Write Pointer +%define HDA_RINTCNT 0x5A ; Response Interrupt Count +%define HDA_RIRBCTL 0x5C ; RIRB Control +%define HDA_RIRBSTS 0x5D ; RIRB Status +%define HDA_RIRBSIZE 0x5E ; RIRB Size + +%define HDA_IC 0x60 ; Immediate Command +%define HDA_IR 0x64 ; Immediate Response +%define HDA_IRS 0x68 ; Immediate Command Status +%define IRS_BUSY BIT0 ; immediate command busy +%define IRS_VALID BIT1 ; immediate command valid +%define HDA_DPLBASE 0x70 ; DMA Position Lower Base Address +%define HDA_DPUBASE 0x74 ; DMA Position Upper Base Address +%define HDA_DPLBASE_ENABLE 0x1 ; Enable position buffer + +%define HDA_SD_CTL 0x0 ; stream register offsets from stream base +%define HDA_SD_STS 0x3 +%define HDA_SD_LPIB 0x4 +%define HDA_SD_CBL 0x8 +%define HDA_SD_LVI 0xC +%define HDA_SD_FIFOW 0xE +%define HDA_SD_FIFOSIZE 0x10 +%define HDA_SD_FORMAT 0x12 +%define HDA_SD_BDLPL 0x18 +%define HDA_SD_BDLPU 0x1C +%define HDA_SD_LPIBA 0x2004 + +; SDCTL—Stream Descriptor Control Register bits +%define SD_CTL_STREAM_RESET BIT0 ; stream reset bit +%define SD_CTL_DMA_START BIT1 ; stream DMA start bit +%define SD_CTL_STREAM_TAG_MASK (BIT20 + BIT21 + BIT22 + BIT23) ; set bits 20 - 23 of SD_CTL register +%define SD_CTL_STREAM_TAG_SHIFT 20 + +; SDSTS—Stream Descriptor Status Register bits +%define SD_INT_COMPLETE BIT2 ; completion interrupt +%define SD_INT_FIFO_ERR BIT3 ; FIFO error interrupt +%define SD_INT_DESC_ERR BIT4 ; descriptor error interrupt +%define RIRB_INT_MASK (BIT0 + BIT2) +%define SD_INT_MASK (SD_INT_DESC_ERR + SD_INT_FIFO_ERR + SD_INT_COMPLETE) +%define STATESTS_INT_MASK (BIT0 + BIT1 + BIT2) + +%define HDA_SDI0CTL 0x80 ; Stream Descriptor Control +%define HDA_SDI0STS 0x83 ; Stream Descriptor Status +%define HDA_SDI0LPIB 0x84 ; Link Position in Current Buffer +%define HDA_SDI0CBL 0x88 ; Cyclic Buffer Length +%define HDA_SDI0LVI 0x8C ; Last Valid Index +%define HDA_SDI0FIFOW 0x8E ; FIFO watermark +%define HDA_SDI0FIFOSIZE 0x90 ; FIFO Size +%define HDA_SDI0FORMAT 0x92 ; Format +%define HDA_SDI0BDLPL 0x98 ; List Pointer - Lower +%define HDA_SDI0BDLPU 0x9C ; List Pointer - Upper +%define HDA_SDI0LPIBA 0x2084 ; Link Posiiton in Buffer n Alias + +%define HDA_SDI1CTL 0xA0 ; Stream Descriptor Control +%define HDA_SDI1STS 0xA3 ; Stream Descriptor Status +%define HDA_SDI1LPIB 0xA4 ; Link Position in Current Buffer +%define HDA_SDI1CBL 0xA8 ; Cyclic Buffer Length +%define HDA_SDI1LVI 0xAC ; Last Valid Index +%define HDA_SDI1FIFOW 0xAE ; FIFO watermark +%define HDA_SDI1FIFOSIZE 0xB0 ; FIFO Size +%define HDA_SDI1FORMAT 0xB2 ; Format +%define HDA_SDI1BDLPL 0xB8 ; List Pointer - Lower +%define HDA_SDI1BDLPU 0xBC ; List Pointer - Upper +%define HDA_SDI1LPIBA 0x20A4 ; Link Posiiton in Buffer n Alias + +%define HDA_SDI2CTL 0xC0 ; Stream Descriptor Control +%define HDA_SDI2STS 0xC3 ; Stream Descriptor Status +%define HDA_SDI2LPIB 0xC4 ; Link Position in Current Buffer +%define HDA_SDI2CBL 0xC8 ; Cyclic Buffer Length +%define HDA_SDI2LVI 0xCC ; Last Valid Index +%define HDA_SDI2FIFOW 0xCE ; FIFO watermark +%define HDA_SDI2FIFOSIZ 0xD0 ; FIFO Size +%define HDA_SDI2FORMAT 0xD2 ; Format +%define HDA_SDI2BDLPL 0xD8 ; List Pointer - Lower +%define HDA_SDI2BDLPU 0xDC ; List Pointer - Upper +%define HDA_SDI2LPIBA 0x20D4 ; Link Posiiton in Buffer n Alias + +%define HDA_SDI3CTL 0xE0 ; Stream Descriptor Control +%define HDA_SDI3STS 0xE3 ; Stream Descriptor Status +%define HDA_SDI3LPIB 0xE4 ; Link Position in Current Buffer +%define HDA_SDI3CBL 0xE8 ; Cyclic Buffer Length +%define HDA_SDI3LVI 0xEC ; Last Valid Index +%define HDA_SDI3FIFOW 0xFE ; FIFO watermark +%define HDA_SDI3FIFOSIZE 0xF0 ; FIFO Size +%define HDA_SDI3FORMAT 0xF2 ; Format +%define HDA_SDI3BDLPL 0xF8 ; List Pointer - Lower +%define HDA_SDI3BDLPU 0xFC ; List Pointer - Upper +%define HDA_SDI3LPIBA 0x20E4 ; Link Posiiton in Buffer n Alias + +%define HDA_SDO0CTL 0x100 ; Stream Descriptor Control +%define HDA_SDO0STS 0x103 ; Stream Descriptor Status +%define HDA_SDO0LPIB 0x104 ; Link Position in Current Buffer +%define HDA_SDO0CBL 0x108 ; Cyclic Buffer Length +%define HDA_SDO0LVI 0x10C ; Last Valid Index +%define HDA_SDO0FIFOW 0x10E ; FIFO watermark +%define HDA_SDO0FIFOSIZE 0x110 ; FIFO Size +%define HDA_SDO0FORMAT 0x112 ; Format +%define HDA_SDO0BDLPL 0x118 ; List Pointer - Lower +%define HDA_SDO0BDLPU 0x11C ; List Pointer - Upper +%define HDA_SDO0LPIBA 0x2104 ; Link Posiiton in Buffer n Alias + +%define HDA_SDO1CTL 0x120 ; Stream Descriptor Control +%define HDA_SDO1STS 0x123 ; Stream Descriptor Status +%define HDA_SDO1LPIB 0x124 ; Link Position in Current Buffer +%define HDA_SDO1CBL 0x128 ; Cyclic Buffer Length +%define HDA_SDO1LVI 0x12C ; Last Valid Index +%define HDA_SDO1FIFOW 0x12E ; FIFO watermark +%define HDA_SDO1FIFOSIZE 0x130 ; FIFO Size +%define HDA_SDO1FORMAT 0x132 ; Format +%define HDA_SDO1BDLPL 0x138 ; List Pointer - Lower +%define HDA_SDO1BDLPU 0x13C ; List Pointer - Upper +%define HDA_SDO1LPIBA 0x2124 ; Link Posiiton in Buffer n Alias + +%define HDA_SDO2CTL 0x140 ; Stream Descriptor Control +%define HDA_SDO2STS 0x143 ; Stream Descriptor Status +%define HDA_SDO2LPIB 0x144 ; Link Position in Current Buffer +%define HDA_SDO2CBL 0x148 ; Cyclic Buffer Length +%define HDA_SDO2LVI 0x14C ; Last Valid Index +%define HDA_SDO2FIFOW 0x14E ; FIFO watermark +%define HDA_SDO2FIFOSIZE 0x150 ; FIFO Size +%define HDA_SDO2FORMAT 0x152 ; Format +%define HDA_SDO2BDLPL 0x158 ; List Pointer - Lower +%define HDA_SDO2BDLPU 0x15C ; List Pointer - Upper +%define HDA_SDO2LPIBA 0x2144 ; Link Posiiton in Buffer n Alias + +%define HDA_SDO3CTL 0x160 ; Stream Descriptor Control +%define HDA_SDO3STS 0x163 ; Stream Descriptor Status +%define HDA_SDO3LPIB 0x164 ; Link Position in Current Buffer +%define HDA_SDO3CBL 0x168 ; Cyclic Buffer Length +%define HDA_SDO3LVI 0x16C ; Last Valid Index +%define HDA_SDO3FIFOW 0x16E ; FIFO watermark +%define HDA_SDO3FIFOSIZE 0x170 ; FIFO Size +%define HDA_SDO3FORMAT 0x172 ; Format +%define HDA_SDO3BDLPL 0x178 ; List Pointer - Lower +%define HDA_SDO3BDLPU 0x17C ; List Pointer - Upper diff --git a/JUDASASM.ASM b/JUDASASM.ASM new file mode 100644 index 0000000..decf00b --- /dev/null +++ b/JUDASASM.ASM @@ -0,0 +1,3626 @@ +; This module contains soundcard IRQ handlers, DMA routines, mixing routines +; as well as some time-critical GUS routines and AC97 access code. +; +; N„m„ ei mit„„n (kovin) optimisoituja rutiineja ole, PERKELE! +; +; Modified by BSpider for NASM on 5th Sep 2006 + + %idefine offset + %include "segments.inc" + %include "judascfg.inc" + %include "judasgus.inc" + %include "judasac.inc" + +%define MONO 0 +%define EIGHTBIT 0 +%define STEREO 1 +%define SIXTEENBIT 2 + +%define VM_OFF 0 +%define VM_ON 1 +%define VM_LOOP 2 +%define VM_16BIT 4 + +%define DEV_NOSOUND 0 +%define DEV_SB 1 +%define DEV_SBPRO 2 +%define DEV_SB16 3 +%define DEV_GUS 4 +%define DEV_AC97 5 +%define DEV_HDA 6 +%define DEV_FILE 7 + +%define CACHESLOTS 16 + +%define IPMINUS1 -1 +%define IP0 0 +%define IP1 1 +%define IP2 2 + +; predefined to 0 +struc CACHESLOT + GDC_Pos resd 1 + GDC_Length resd 1 +endstruc + +; predefined to 0 +struc DMACHANNEL + DMA_PagePort resw 1 + DMA_AdrPort resw 1 + DMA_LenPort resw 1 + DMA_MaskPort resw 1 + DMA_ModePort resw 1 + DMA_ClearPort resw 1 + DMA_Mask resb 1 + DMA_UnMask resb 1 + DMA_Unused resw 1 +endstruc + +; predefined to 0 +struc CHANNEL + Chn_Pos resd 1 + Chn_Repeat resd 1 + Chn_End resd 1 + Chn_Sample resd 1 + Chn_Freq resd 1 + Chn_FractPos resw 1 + Chn_MasterVol resb 1 + Chn_Panning resb 1 + Chn_Vol resw 1 + Chn_VoiceMode resb 1 + Chn_PrevVM resb 1 + Chn_PrevPos resd 1 + Chn_LastValL resd 1 + Chn_LastValR resd 1 + Chn_SmoothVolL resd 1 + Chn_SmoothVolR resd 1 +endstruc + + + +; not predefined +struc AUDIO_PCI_DEV + .vender_id resw 1 + .device_id resw 1 + .sub_vender_id resw 1 + .sub_device_id resw 1 + .device_bus_number resw 1 + .irq resb 1 + .pin resb 1 + .command resw 1 + .base0 resd 1 + .base1 resd 1 + .base2 resd 1 + .base3 resd 1 + .base4 resd 1 + .base5 resd 1 + .device_type resd 1 + + .mem_mode resd 1 + .hda_mode resd 1 + +; memory allocated for BDL and PCM buffers + .bdl_buffer resd 1 + .pcmout_buffer0 resd 1 + .pcmout_buffer1 resd 1 + .hda_buffer resd 1 + .pcmout_bufsize resd 1 + .pcmout_bdl_entries resd 1 + .pcmout_bdl_size resd 1 + .pcmout_dmasize resd 1 + .pcmout_dma_lastgoodpos resd 1 + .pcmout_dma_pos_ptr resd 1 + +; AC97 only properties + .ac97_vra_supported resd 1 + +; HDA modified structure will be placed here. + .codec_mask resd 1 + .codec_index resd 1 + + .afg_root_nodenum resw 1 + .afg_num_nodes resd 1 + .afg_nodes resd 1 + .def_amp_out_caps resd 1 + .def_amp_in_caps resd 1 + + .dac_node resd 1 + .out_pin_node resd 1 + .adc_node resd 1 + .in_pin_node resd 1 + .input_items resd 1 + .pcm_num_vols resd 1 + .pcm_vols resd 1 + + .format_val resd 1 + .dacout_num_bits resd 1 + .dacout_num_channels resd 1 + .stream_tag resd 1 + .supported_formats resd 1 + .supported_max_freq resd 1 + .supported_max_bits resd 1 + + .freq_card resd 1 + .chan_card resd 1 + .bits_card resd 1 + + .codec_id1 resw 1 + .codec_id2 resw 1 + .device_name resb 128 + .codec_name resb 128 +endstruc + +%define DEVICE_INTEL 0 ; AC97 device Intel ICH compatible +%define DEVICE_SIS 1 ; AC97 device SIS compatible +%define DEVICE_INTEL_ICH4 2 ; AC97 device Intel ICH4 compatible +%define DEVICE_NFORCE 3 ; AC97 device nForce compatible +%define DEVICE_HDA_INTEL 4 ; HDA audio device for Intel and others +%define DEVICE_HDA_ATI 5 +%define DEVICE_HDA_ATIHDMI 6 +%define DEVICE_HDA_NVIDIA 7 +%define DEVICE_HDA_SIS 8 +%define DEVICE_HDA_ULI 9 +%define DEVICE_HDA_VIA 10 + + ; register calling convention for WATCOM C++ + global judas_code_lock_start_ + global judas_code_lock_end_ + global judas_update_ + global judas_get_ds_ + global sb_handler_ + global sb_aihandler_ + global sb16_handler_ + global gus_handler_ + global gus_peek_ + global gus_poke_ + global gus_dmawait_ + global gus_dmainit_ + global gus_dmaprogram_ + global gus_startchannels_ + global fmixer_ + global qmixer_ + global safemixer_ + global normalmix_ + global ipmix_ + global qmix_linear_ + global qmix_cubic_ + global dma_program_ + ; stack calling convention for anything else + global _judas_code_lock_start + global _judas_code_lock_end + global _judas_update + global _judas_get_ds + global _sb_handler + global _sb_aihandler + global _sb16_handler + global _gus_handler + global _gus_peek + global _gus_poke + global _gus_dmawait + global _gus_dmainit + global _gus_dmaprogram + global _gus_startchannels + global _fmixer + global _qmixer + global _safemixer + global _normalmix + global _ipmix + global _qmix_linear + global _qmix_cubic + global _dma_program + + extern _judas_ds;word + extern _judas_initialized;byte + extern _judas_mixmode;byte + extern _judas_samplesize;byte + extern _judas_clipbuffer;dword + extern _judas_zladdbuffer;dword + extern _judas_zerolevell;dword + extern _judas_zerolevelr;dword + extern _judas_cliptable;dword + extern _judas_volumetable;dword + extern _judas_mixrate;dword + extern _judas_channel;dword + extern _judas_mixroutine;dword + extern _judas_mixersys;dword + extern _judas_device;dword + extern _judas_port;dword + extern _judas_irq;dword + extern _judas_dma;dword + extern _judas_irqcount;dword + extern _judas_bufferlength;dword + extern _judas_buffermask;dword + extern _judas_bpmcount;dword + extern _judas_bpmtempo;byte + extern _judas_player;dword + extern _judas_mixpos;dword + extern _dma_address;dword + extern _judas_clipped;byte + extern _audio_pci;AUDIO_PCI_DEV + extern _hda_civ ; dword + extern _hda_lpib ; dword + +%ifdef djgpp +section .text +%else +segment _TEXT +%endif + +judas_get_ds_: +_judas_get_ds: + mov AX, DS + mov [_judas_ds], AX + ret + +judas_code_lock_start_: +_judas_code_lock_start: +; this code is constant - TASM declaration of .const + + align 4 + +DMAChannels: + istruc DMACHANNEL + at DMA_PagePort, dw 87h + at DMA_AdrPort, dw 0h + at DMA_LenPort, dw 1h + at DMA_MaskPort, dw 0ah + at DMA_ModePort, dw 0bh + at DMA_ClearPort, dw 0ch + at DMA_Mask, db 4h + at DMA_UnMask, db 0h + at DMA_Unused, dw 0h + iend + + istruc DMACHANNEL + at DMA_PagePort, dw 83h + at DMA_AdrPort, dw 2h + at DMA_LenPort, dw 3h + at DMA_MaskPort, dw 0ah + at DMA_ModePort, dw 0bh + at DMA_ClearPort, dw 0ch + at DMA_Mask, db 5h + at DMA_UnMask, db 1h + at DMA_Unused, dw 0h + iend + + istruc DMACHANNEL + at DMA_PagePort, dw 81h + at DMA_AdrPort, dw 4h + at DMA_LenPort, dw 5h + at DMA_MaskPort, dw 0ah + at DMA_ModePort, dw 0bh + at DMA_ClearPort, dw 0ch + at DMA_Mask, db 6h + at DMA_UnMask, db 2h + at DMA_Unused, dw 0h + iend + + istruc DMACHANNEL + at DMA_PagePort, dw 82h + at DMA_AdrPort, dw 6h + at DMA_LenPort, dw 7h + at DMA_MaskPort, dw 0ah + at DMA_ModePort, dw 0bh + at DMA_ClearPort, dw 0ch + at DMA_Mask, db 7h + at DMA_UnMask, db 3h + at DMA_Unused, dw 0h + iend + + istruc DMACHANNEL + at DMA_PagePort, dw 8fh + at DMA_AdrPort, dw 0c0h + at DMA_LenPort, dw 0c2h + at DMA_MaskPort, dw 0d4h + at DMA_ModePort, dw 0d6h + at DMA_ClearPort, dw 0d8h + at DMA_Mask, db 4h + at DMA_UnMask, db 0h + at DMA_Unused, dw 0h + iend + + istruc DMACHANNEL + at DMA_PagePort, dw 8bh + at DMA_AdrPort, dw 0c4h + at DMA_LenPort, dw 0c6h + at DMA_MaskPort, dw 0d4h + at DMA_ModePort, dw 0d6h + at DMA_ClearPort, dw 0d8h + at DMA_Mask, db 5h + at DMA_UnMask, db 1h + at DMA_Unused, dw 0h + iend + + istruc DMACHANNEL + at DMA_PagePort, dw 89h + at DMA_AdrPort, dw 0c8h + at DMA_LenPort, dw 0cah + at DMA_MaskPort, dw 0d4h + at DMA_ModePort, dw 0d6h + at DMA_ClearPort, dw 0d8h + at DMA_Mask, db 6h + at DMA_UnMask, db 2h + at DMA_Unused, dw 0h + iend + + istruc DMACHANNEL + at DMA_PagePort, dw 8ah + at DMA_AdrPort, dw 0cch + at DMA_LenPort, dw 0ceh + at DMA_MaskPort, dw 0d4h + at DMA_ModePort, dw 0d6h + at DMA_ClearPort, dw 0d8h + at DMA_Mask, db 7h + at DMA_UnMask, db 3h + at DMA_Unused, dw 0h + iend + + align 4 + +shittable dd 0, 60, 56, 52, 48, 44, 40, 36 + dd 32, 28, 24, 20, 16, 12, 8, 4 + +%ifdef djgpp +section .data +%else +segment _DATA +%endif + + align 4 + +gdc: + %rep CACHESLOTS + istruc CACHESLOT + at GDC_Pos, dd 0 + at GDC_Length, dd 0 + iend + %endrep + + align 4 + +loopcount dd 0 +fractadd dd 0 +integeradd dd 0 +smpend dd 0 +smpsubtract dd 0 +samples dd 0 +totalwork dd 0 +postproc dd 0 +cptr dd 0 +dptr dd 0 +fptr dd 0 +ipminus1 dd 0 +ip0 dd 0 +ip1 dd 0 +ip2 dd 0 +leftvol dd 0 +rightvol dd 0 +SmoothVolL dd 0 +SmoothVolR dd 0 +saved_reg dd 0 + +mix_exec db 0 +gus_dmainprogress db 0 +ac97_buffer0_set db 0 +ac97_buffer1_set db 0 + +%ifdef djgpp +section .text +%else +segment _TEXT +%endif + + align 4 + + ;DMA functions. DMA polling is really fucked up: if reading the + ;position too often (> 100 Hz) one may get bogus values. This is + ;compensated by reading two values, and if their offset is too big or + ;they're outside the buffer, the position is read again. + ; + ;Actually GUS fucks up just in the same way when reading the channel + ;position. Shit, what is wrong with the hardware?! + ; + ;Previously I though that EMM386 causes these fuckups, but no, it + ;wasn't so. However, under DPMI there's no fuckups! + ; + ;It would be really nice & simple to just update one bufferhalf at a + ;time in the soundcard interrupt, but I think it's important to give + ;the user full control of the sound updating, even at the expense of + ;PAIN!!! + +dma_program_: +_dma_program: + push ESI + push EDI + push ECX + mov ECX, EAX ;ECX = mode + mov EDI, EDX ;EDI = offset + mov ESI, [_judas_dma] ;Get channel num + cmp ESI, 4 + jae dma16_program + shl ESI, 4 ;16 = dma struc len + add ESI, offset DMAChannels ;Ptr now ready + mov DX, [ESI + DMA_MaskPort] + mov AL, [ESI + DMA_Mask] + out DX, AL ;Mask the DMA channel + xor AL, AL + mov DX, [ESI + DMA_ClearPort] + out DX, AL ;Clear byte ptr. + mov DX, [ESI + DMA_ModePort] + mov AL, CL ;Get mode + or AL, [ESI + DMA_UnMask] ;Or with channel num + out DX, AL ;Set DMA mode + mov DX, [ESI + DMA_LenPort] + dec EBX ;EBX = length + mov AL, BL + out DX, AL ;Set length low and + mov AL, BH ;high bytes + out DX, AL + mov DX, [ESI + DMA_AdrPort] + mov EBX, [_dma_address] ;Get DMA buffer address + add EBX, EDI ;Add offset + mov AL, BL + out DX, AL ;Set offset + mov AL, BH + out DX, AL + mov DX, [ESI + DMA_PagePort] + shr EBX, 16 + mov AL, BL + out DX, AL ;Set page + mov DX, [ESI + DMA_MaskPort] + mov AL, [ESI + DMA_UnMask] + out DX, AL ;Unmask the DMA channel + pop ECX + pop EDI + pop ESI + ret +dma16_program: shl ESI, 4 ;16 = dma struc len + add ESI, offset DMAChannels ;Ptr now ready + mov DX, [ESI + DMA_MaskPort] + mov AL, [ESI + DMA_Mask] + out DX, AL ;Mask the DMA channel + xor AL, AL + mov DX, [ESI + DMA_ClearPort] + out DX, AL ;Clear byte ptr. + mov DX, [ESI + DMA_ModePort] + mov AL, CL ;Get mode + or AL, [ESI + DMA_UnMask] ;Or with channel num + out DX, AL ;Set DMA mode + mov DX, [ESI + DMA_LenPort] + shr EBX, 1 + dec EBX + mov AL, BL + out DX, AL ;Set length low and + mov AL, BH ;high bytes + out DX, AL + mov DX, [ESI + DMA_AdrPort] + mov EBX, [_dma_address] ;Get DMA buffer address + add EBX, EDI ;Add offset + shr EBX, 1 ;Because of 16-bitness + mov AL, BL + out DX, AL ;Set offset + mov AL, BH + out DX, AL + mov DX, [ESI + DMA_PagePort] + shr EBX, 15 + mov AL, BL + out DX, AL ;Set page + mov DX, [ESI + DMA_MaskPort] + mov AL, [ESI + DMA_UnMask] + out DX, AL ;Unmask the DMA channel + pop ECX + pop EDI + pop ESI + ret + +dma_query_: cli + push EBX + push ECX + push EDX + push ESI + mov ESI, [_judas_dma] + cmp ESI, 4 + jae dma16_query + shl ESI, 4 ;16 = dma struc len + add ESI, offset DMAChannels ;Ptr now ready + xor EAX, EAX + mov DX, [ESI + DMA_ClearPort] ;Clear flip-flop + out DX, AL + mov DX, [ESI + DMA_AdrPort] +dqloop1: xor EAX, EAX + in AL, DX + xchg AL, AH + in AL, DX + xchg AL, AH + sub AX, word [_dma_address] ;Subtract page offset + mov EBX, EAX ;EBX = position 1 + in AL, DX + xchg AL, AH + in AL, DX + xchg AL, AH + sub AX, word [_dma_address] ;Subtract page offset + mov ECX, EAX ;ECX = position 2 + cmp EBX, [_judas_bufferlength] ;Outside buffer? + jae dqloop1 + mov EAX, EBX + sub EAX, ECX + cmp EAX, 64 + jg dqloop1 + cmp EAX, -64 + jl dqloop1 + mov EAX, EBX + pop ESI + pop EDX + pop ECX + pop EBX + sti + ret +dma16_query: shl ESI, 4 ;16 = dma struc len + add ESI, offset DMAChannels ;Ptr now ready + mov DX, [ESI + DMA_ClearPort] ;Clear flip-flop + xor EAX, EAX + out DX, AL + mov DX, [ESI + DMA_AdrPort] + mov ESI, [_dma_address] + and ESI, 1ffffh +dqloop2: xor EAX, EAX + in AL, DX + xchg AL, AH + in AL, DX + xchg AL, AH + shl EAX, 1 + sub EAX, ESI ;Subtract page offset + mov EBX, EAX ;EBX = position 1 + xor EAX, EAX + in AL, DX + xchg AL, AH + in AL, DX + xchg AL, AH + shl EAX, 1 + sub EAX, ESI ;Subtract page offset + mov ECX, EAX ;ECX = position 2 + cmp EBX, [_judas_bufferlength] ;Outside buffer? + jae dqloop2 + mov EAX, EBX + sub EAX, ECX + cmp EAX, 64 + jg dqloop2 + cmp EAX, -64 + jl dqloop2 + mov EAX, EBX + pop ESI + pop EDX + pop ECX + pop EBX + sti + ret + + ;Generic send-EOI routine. + +send_eoi: inc dword [_judas_irqcount] + cmp dword [_judas_irq], 8 + jae highirq + mov AL, 20h + out 20h, AL + ret +highirq: mov AL, 20h + out 0a0h, AL + mov AL, 00001011b + out 0a0h, AL + in AL, 0a0h + or AL, AL + jnz sb_noeoi + mov AL, 20h + out 20h, AL +sb_noeoi: ret + + ;Soundblaster IRQ handlers, one for singlecycle, one for 8bit autoinit + ;and one for 16bit autoinit. + +sb_handler_: +_sb_handler: + pushad + push DS + mov AX, [CS:_judas_ds] + mov DS, AX + mov EDX, [_judas_port] + add EDX, 0eh + in AL, DX + sub EDX, 2h +sb_wait1: in AL, DX + or AL, AL + js sb_wait1 + mov AL, 14h + out DX, AL +sb_wait2: in AL, DX + or AL, AL + js sb_wait2 + mov AX, 0fff0h + out DX, AL +sb_wait3: in AL, DX + or AL, AL + js sb_wait3 + mov AL, AH + out DX, AL + sti + call send_eoi + pop DS + popad + iretd + +sb_aihandler_: +_sb_aihandler: + pushad + push DS + mov AX, [CS:_judas_ds] + mov DS, AX + mov EDX, [_judas_port] + add EDX, 0eh + in AL, DX + sti + call send_eoi + pop DS + popad + iretd + +sb16_handler_: +_sb16_handler: + pushad + push DS + mov AX, [CS:_judas_ds] + mov DS, AX + mov EDX, [_judas_port] + add EDX, 0fh + in AL, DX + sti + call send_eoi + pop DS + popad + iretd + + ;GUS IRQ handler + +gus_handler_: +_gus_handler: + pushad + push DS + mov AX, [CS:_judas_ds] + mov DS, AX +gus_irqloop: mov EDX, [_judas_port] + add EDX, GF1_IRQ_STAT + in AL, DX + test AL, DMA_TC_IRQ + jz near gus_irqdone + mov EDX, [_judas_port] ;Acknowledge the DMA + add EDX, GF1_REG_SELECT ;interrupt + mov AL, DMA_CONTROL + out DX, AL + mov EDX, [_judas_port] + add EDX, GF1_DATA_HI + in AL, DX + dec byte [gus_dmainprogress] + mov ESI, offset gdc + mov ECX, CACHESLOTS +gusirq_seekslot:cmp dword [ESI + GDC_Length], 0 + jnz gusirq_slotfound + add ESI, CACHESLOT_size ;type CACHESLOT in TASM + dec ECX + jnz gusirq_seekslot + jmp gus_irqloop +gusirq_slotfound: + mov EBX, [ESI + GDC_Pos] ;DMA offset + shr EBX, 4 + mov CL, DMA_ENABLE | DMA_R0 | DMA_TWOS_COMP | DMA_IRQ_ENABLE + test byte [_judas_mixmode], SIXTEENBIT + jz gus_dma_eight2 + mov CL, DMA_ENABLE | DMA_R0 | DMA_DATA_16 | DMA_IRQ_ENABLE +gus_dma_eight2: cmp dword [_judas_dma], 4 + jb gus_nohighdma2 + or CL, DMA_WIDTH_16 + shr EBX, 1 +gus_nohighdma2: mov EDX, [_judas_port] + add EDX, GF1_REG_SELECT + mov AL, SET_DMA_ADDRESS + out DX, AL + mov EDX, [_judas_port] + add EDX, GF1_DATA_LOW + mov AX, BX + out DX, AX + mov EDX, [_judas_port] + add EDX, GF1_REG_SELECT + mov AL, DMA_CONTROL + out DX, AL + mov EDX, [_judas_port] + add EDX, GF1_DATA_HI + mov AL, CL + out DX, AL + mov EBX, [ESI + GDC_Length] + mov dword [ESI + GDC_Length], 0 + mov EDX, [ESI + GDC_Pos] ;DMA offset + mov EAX, 48h ;DMA mode + call dma_program_ ;Program it! + jmp gus_irqloop +gus_irqdone: sti + call send_eoi + pop DS + popad + iretd + + ;Various GUS functions + +gus_peek_: +_gus_peek: + push EBX + mov EBX, EAX + mov AL, SET_DRAM_LOW + mov EDX, [_judas_port] + add EDX, GF1_REG_SELECT + out DX, AL + mov AX, BX + mov EDX, [_judas_port] + add EDX, GF1_DATA_LOW + out DX, AX + mov AL, SET_DRAM_HIGH + mov EDX, [_judas_port] + add EDX, GF1_REG_SELECT + out DX, AL + shr EBX, 16 + mov AL, BL + mov EDX, [_judas_port] + add EDX, GF1_DATA_HI + out DX, AL + mov EDX, [_judas_port] + add EDX, GF1_DRAM + in AL, DX + pop EBX + ret + +gus_poke_: +_gus_poke: + push EBX + push EDX + mov EBX, EAX + mov AL, SET_DRAM_LOW + mov EDX, [_judas_port] + add EDX, GF1_REG_SELECT + out DX, AL + mov AX, BX + mov EDX, [_judas_port] + add EDX, GF1_DATA_LOW + out DX, AX + mov AL, SET_DRAM_HIGH + mov EDX, [_judas_port] + add EDX, GF1_REG_SELECT + out DX, AL + shr EBX, 16 + mov AL, BL + mov EDX, [_judas_port] + add EDX, GF1_DATA_HI + out DX, AL + mov EDX, [_judas_port] + add EDX, GF1_DRAM + pop EAX + out DX, AL + pop EBX + ret + +gus_startchannels_: +_gus_startchannels: + push EBX ;This routine starts + push ECX ;the two channels + push EDX ;as quickly as possible. + mov EBX, [_judas_port] + add EBX, GF1_PAGE + mov ECX, [_judas_port] + add ECX, GF1_DATA_HI + mov EDX, [_judas_port] + add EDX, GF1_REG_SELECT + mov AL, SET_CONTROL + out DX, AL + test byte [_judas_mixmode], SIXTEENBIT + jz gus_start8 + mov EDX, EBX + mov AL, 0 + out DX, AL + mov EDX, ECX + mov AL, VC_LOOP_ENABLE | VC_DATA_TYPE + out DX, AL + mov EDX, EBX + mov AL, 1 + out DX, AL + mov EDX, ECX + mov AL, VC_LOOP_ENABLE | VC_DATA_TYPE + out DX, AL + pop EDX + pop ECX + pop EBX + ret +gus_start8: mov EDX, EBX + xor AL, AL + out DX, AL + mov EDX, ECX + mov AL, VC_LOOP_ENABLE + out DX, AL + mov EDX, EBX + mov AL, 1 + out DX, AL + mov EDX, ECX + mov AL, VC_LOOP_ENABLE + out DX, AL + pop EDX + pop ECX + pop EBX + ret + +gus_dmaprogram_: +_gus_dmaprogram: + or EDX, EDX ;Zero length fucks up! + jz gus_skipdma + pushad + cli + cmp byte [gus_dmainprogress], 0 ;Do we have to cache the + je gus_dontcache ;block? + mov EBX, offset gdc + mov ECX, CACHESLOTS +gus_seekslot: cmp dword [EBX + GDC_Length], 0 + je gus_slotfound + add EBX, CACHESLOT_size ;type CACHESLOT in TASM + dec ECX + jnz gus_seekslot + sti + popad +gus_skipdma: ret +gus_slotfound: mov [EBX + GDC_Pos], EAX + mov [EBX + GDC_Length], EDX + inc byte [gus_dmainprogress] + sti + popad + ret +gus_dontcache: sti + inc byte [gus_dmainprogress] + mov ESI, EAX + mov EDI, EDX + mov EBX, ESI ;DMA offset + shr EBX, 4 + mov CL, DMA_ENABLE | DMA_R0 | DMA_TWOS_COMP | DMA_IRQ_ENABLE + test byte [_judas_mixmode], SIXTEENBIT + jz gus_dma_eight + mov CL, DMA_ENABLE | DMA_R0 | DMA_DATA_16 | DMA_IRQ_ENABLE +gus_dma_eight: cmp dword [_judas_dma], 4 + jb gus_nohighdma + or CL, DMA_WIDTH_16 + shr EBX, 1 +gus_nohighdma: mov EDX, [_judas_port] + add EDX, GF1_REG_SELECT + mov AL, SET_DMA_ADDRESS + out DX, AL + mov EDX, [_judas_port] + add EDX, GF1_DATA_LOW + mov EAX, EBX + out DX, AX + mov EDX, [_judas_port] + add EDX, GF1_REG_SELECT + mov AL, DMA_CONTROL + out DX, AL + mov EDX, [_judas_port] + add EDX, GF1_DATA_HI + mov AL, CL + out DX, AL + mov EBX, EDI ;DMA length + mov EDX, ESI ;DMA offset + mov EAX, 48h ;DMA mode + call dma_program_ ;Program it! + popad + ret + +gus_dmainit_: +_gus_dmainit: + cli + mov byte [gus_dmainprogress], 0 + push EAX + push EDX + mov EDX, [_judas_port] ;Acknowledge the DMA + add EDX, GF1_REG_SELECT ;interrupt + mov AL, DMA_CONTROL + out DX, AL + mov EDX, [_judas_port] + add EDX, GF1_DATA_HI + in AL, DX + mov EAX, offset gdc +diloop: mov dword [EAX + GDC_Pos], 0 + mov dword [EAX + GDC_Length], 0 + add EAX, CACHESLOT_size ;type CACHESLOT + cmp EAX, offset gdc + CACHESLOTS * CACHESLOT_size ;type CACHESLOT in TASM + jne diloop + pop EDX + pop EAX + sti + ret + +gus_dmawait_: +_gus_dmawait: + mov EAX, 200000h ;Timeout counter +gus_dmawaitloop:cmp byte [gus_dmainprogress], 0 ;(might time out if + je gus_dmadone ;there is a DMA + dec EAX ;conflict.) This routine + jnz gus_dmawaitloop ;is used just for click +gus_dmadone: ret ;removal! + +gus_getpos: push EBX + push EDX + mov EDX, [_judas_port] ;Get the channel + add EDX, GF1_PAGE ;playing position to + xor AL, AL ;know where we'll mix + out DX, AL + mov EDX, [_judas_port] + add EDX, GF1_REG_SELECT + mov AL, GET_ACC_HIGH + out DX, AL + mov EDX, [_judas_port] + add EDX, GF1_DATA_LOW + in AX, DX + and EAX, 8191 + shl EAX, 7 + mov EBX, EAX + mov EDX, [_judas_port] + add EDX, GF1_REG_SELECT + mov AL, GET_ACC_LOW + out DX, AL + mov EDX, [_judas_port] + add EDX, GF1_DATA_LOW + in AX, DX + shr AX, 9 + or EAX, EBX + test byte [_judas_mixmode], SIXTEENBIT + jz ggp_not16 + shl EAX, 1 +ggp_not16: pop EDX + pop EBX + ret + + +;***************************************************************************** +; Intel ICH AC97 stuff +;***************************************************************************** +; When CIV == LVI, set LVI <> CIV to never run out of buffers to play. +ac97_updateLVI: + push eax + push edx + cmp dword [_audio_pci + AUDIO_PCI_DEV.mem_mode], 0 ; memory mapped IO? + jne ac97_updateLVI_mem + + mov edx, [_judas_port] + add edx, PO_CIV_REG ; PCM OUT Current Index Value + in ax, dx ; and Last Valid Index + and al, 01fh ; bits 0-5 only (important for SIS) + and ah, 01fh ; bits 0-5 only (important for SIS) + cmp al, ah ; CIV == LVI? + jnz ac97_updateLVI_ok ; no, don't change LVI + call ac97_setNewIndex ; set LVI to something else + jmp short ac97_updateLVI_ok +ac97_updateLVI_mem: + mov edx, [_audio_pci + AUDIO_PCI_DEV.base3] ; NABMBAR for memory mapped IO + add edx, PO_CIV_REG ; PCM OUT Current Index Value + mov ax, [edx] ; and Last Valid Index + and al, 01fh ; bits 0-5 only (important for SIS) + and ah, 01fh ; bits 0-5 only (important for SIS) + cmp al, ah ; CIV == LVI? + jnz ac97_updateLVI_ok ; no, don't change LVI + call ac97_setNewIndex ; set LVI to something else +ac97_updateLVI_ok: + pop edx + pop eax + ret + +; Set the Last Valid Index to 1 less than the Current Index Value, +; so that we never run out of buffers. +ac97_setNewIndex: + push eax + call ac97_getCurrentIndex ; get CIV + dec al ; make LVI != CIV + and al, INDEX_MASK ; make sure new value is 0-31 + call ac97_setLastValidIndex ; write new LVI + pop eax + ret + +; return AL = PCM OUT Current Index Value +ac97_getCurrentIndex: + push edx + cmp dword [_audio_pci + AUDIO_PCI_DEV.mem_mode], 0 ; memory mapped IO? + jne ac97_getCurrentIndex_mem + + mov edx, [_judas_port] + add edx, PO_CIV_REG + in al, dx + jmp short ac97_getCurrentIndex_ok +ac97_getCurrentIndex_mem: + mov edx, [_audio_pci + AUDIO_PCI_DEV.base3] ; NABMBAR for memory mapped IO + add edx, PO_CIV_REG ; PCM OUT Current Index Value + mov ax, [edx] +ac97_getCurrentIndex_ok: + pop edx + ret + +; input AL = PCM OUT Last Valid Index (index to stop on) +ac97_setLastValidIndex: + push edx + cmp dword [_audio_pci + AUDIO_PCI_DEV.mem_mode], 0 ; memory mapped IO? + jne ac97_setLastValidIndex_mem + + mov edx, [_judas_port] + add edx, PO_LVI_REG + out dx, al + jmp short ac97_setLastValidIndex_ok +ac97_setLastValidIndex_mem: + mov edx, [_audio_pci + AUDIO_PCI_DEV.base3] ; NABMBAR for memory mapped IO + add edx, PO_LVI_REG + mov [edx], al ; and Last Valid Index +ac97_setLastValidIndex_ok: + pop edx + ret + + +;***************************************************************************** +; Intel HDA stuff +;***************************************************************************** +hda_get_lpib: + + push edx + + mov edx, dword [_audio_pci + AUDIO_PCI_DEV.base0] + add edx, HDA_SDO0LPIB + mov eax, [edx] + + pop edx + ret + +hda_dma_start: + push edx + + mov edx, dword [_audio_pci + AUDIO_PCI_DEV.base0] + add edx, HDA_SDO0CTL + mov eax, [edx] + or eax, SD_CTL_DMA_START + mov [edx], eax + + pop edx + ret + + + ;General DMAbuffer update routine (call either from main program or + ;within your timer interrupt) + +judas_update_: +_judas_update: + cmp dword [_judas_device], DEV_NOSOUND + je near judas_gotohell + cmp dword [_judas_device], DEV_FILE + je near judas_gotohell + cmp byte [mix_exec], 0 + jne near judas_gotohell + cmp byte [_judas_initialized], 0 + je near judas_gotohell + inc byte [mix_exec] + pushad + cmp dword [_judas_device], DEV_GUS + je near updategus ;This is a different story + cmp dword [_judas_device], DEV_AC97 + je near updateac97 ; audio_pci update for AC97 + cmp dword [_judas_device], DEV_HDA + je near updatehda ; audio_pci update for HDA + call dma_query_ + ;Must be aligned on 8 + and EAX, [_judas_buffermask] ;samples (unrolling!) + mov EBX, [_judas_mixpos] ;This is the old pos + cmp EAX, EBX + je judas_donothing + jb judas_wrap +judas_normal: mov [_judas_mixpos], EAX + mov EDX, EAX + sub EDX, EBX ;EDX = length to mix + mov EAX, EBX ;EAX = pos. to mix + add EAX, [_dma_address] + call dword [_judas_mixersys] +judas_donothing:popad + dec byte [mix_exec] +judas_gotohell: ret +judas_wrap: mov [_judas_mixpos], EAX + mov EAX, EBX ;Mix to buffer end + mov EDX, [_judas_bufferlength] + sub EDX, EBX + add EAX, [_dma_address] + call dword [_judas_mixersys] + mov EAX, [_dma_address] ;Then to start + mov EDX, [_judas_mixpos] + or EDX, EDX + jz judas_donothing + call dword [_judas_mixersys] + jmp judas_donothing + + +updateac97: call ac97_updateLVI ; set CIV != LVI + call ac97_getCurrentIndex +update_hda_buffers: + test al, 1 ; check parity + jz ac97_playing_buffer0 + + ; playing buffer 1 -> refresh buffer 0 (Bus Master DMA) +ac97_playing_buffer1: + cmp byte [ac97_buffer0_set], 1 ; is buffer 0 + je judas_donothing ; already refreshed + mov eax, [_audio_pci + AUDIO_PCI_DEV.pcmout_buffer0] ; buffer 0 address + mov edx, [_judas_bufferlength] ; buffer 0 size + call dword [_judas_mixersys] + mov byte [ac97_buffer0_set], 1 ; set buffer 0 + mov byte [ac97_buffer1_set], 0 ; as refreshed + jmp judas_donothing + + ; playing buffer 0 -> refresh buffer 1 (Bus Master DMA) +ac97_playing_buffer0: + cmp byte [ac97_buffer1_set], 1 ; is buffer 1 + je near judas_donothing ; already refreshed + mov eax, [_audio_pci + AUDIO_PCI_DEV.pcmout_buffer1] ; buffer 1 address + mov edx, [_judas_bufferlength] ; buffer 1 size + call dword [_judas_mixersys] + mov byte [ac97_buffer1_set], 1 ; set buffer 1 + mov byte [ac97_buffer0_set], 0 ; as refreshed + jmp judas_donothing + + +updatehda: mov eax, [_hda_lpib] + or eax, eax + jnz hda_update_civ + mov eax, [_hda_civ] + or eax, eax + jnz hda_update_civ + call hda_dma_start ; 1st time run, start the DMA engine + +hda_update_civ: + call hda_get_lpib ; get LPIB + cmp eax, dword [_hda_lpib] ; compare wih last LPIB position + jae hda_skip_civ ; if no wrap around don't update CIV + + inc dword [_hda_civ] + cmp dword [_hda_civ], 32 + jne hda_skip_civ + mov dword [_hda_civ], 0 + +hda_skip_civ: + mov [_hda_lpib], eax + mov eax, [_hda_civ] + jmp update_hda_buffers ; same as AC97 on next step + + +updategus: cmp byte [gus_dmainprogress], CACHESLOTS - 4 ;Is there too many + jb ug_notstuck ;DMA's waiting? I mean, + call gus_dmainit_ ;maybe WIN95 has stuck + ;the card somehow. In + ;that case, release all + ;waiting DMAs manually! +ug_notstuck: cli + test byte [_judas_mixmode], STEREO + jz near updategus_mono +ipc_s: xor EAX, EAX + call gus_peek_ + mov DL, AL + mov EAX, [_judas_bufferlength] + shr EAX, 1 + call gus_poke_ + mov EAX, 1 + call gus_peek_ + mov DL, AL + mov EAX, [_judas_bufferlength] + shr EAX, 1 + inc EAX + call gus_poke_ + mov EAX, [_judas_bufferlength] + shr EAX, 1 + add EAX, 32 + call gus_peek_ + mov DL, AL + mov EAX, [_judas_bufferlength] + add EAX, 32 + call gus_poke_ + mov EAX, [_judas_bufferlength] + shr EAX, 1 + add EAX, 33 + call gus_peek_ + mov DL, AL + mov EAX, [_judas_bufferlength] + add EAX, 33 + call gus_poke_ + mov EDX, [_judas_bufferlength] + shr EDX, 1 +ugs_shitloop: call gus_getpos + mov EBX, EAX + call gus_getpos + mov ECX, EAX + cmp EBX, EDX + jae ugs_shitloop + mov EAX, EBX + sub EAX, ECX + cmp EAX, 64 + jg ugs_shitloop + cmp EAX, -64 + jl ugs_shitloop + mov EAX, EBX + sti + and EAX, [_judas_buffermask] + mov EBX, [_judas_mixpos] ;EBX = old mixpos + cmp EAX, EBX + je near judas_donothing + jb updategus_wrap + mov [_judas_mixpos], EAX ;New "oldpos" + mov EDX, EAX + sub EDX, EBX ;How much to mix + mov EAX, EBX ;Where to mix + push EAX + push EDX + shl EDX, 1 + add EAX, [_dma_address] + call dword [_judas_mixersys] + pop EDX + pop EAX + call gus_dmaprogram_ + add EAX, 32 + mov EBX, [_judas_bufferlength] + shr EBX, 1 + add EAX, EBX + call gus_dmaprogram_ + jmp judas_donothing +updategus_wrap: mov [_judas_mixpos], EAX ;Mix first to buffer + mov EAX, EBX ;end, then to start + mov EDX, [_judas_bufferlength] + shr EDX, 1 + sub EDX, EBX + push EAX + push EDX + shl EDX, 1 + add EAX, [_dma_address] + call dword [_judas_mixersys] + mov EAX, [_dma_address] + mov EDX, [_judas_mixpos] + shl EDX, 1 + call dword [_judas_mixersys] + pop EDX + pop EAX + call gus_dmaprogram_ + add EAX, 32 + mov EBX, [_judas_bufferlength] + shr EBX, 1 + add EAX, EBX + call gus_dmaprogram_ + xor EAX, EAX + mov EDX, [_judas_mixpos] + call gus_dmaprogram_ + add EAX, 32 + mov EBX, [_judas_bufferlength] + shr EBX, 1 + add EAX, EBX + call gus_dmaprogram_ + jmp judas_donothing + +updategus_mono: xor EAX, EAX + call gus_peek_ + mov DL, AL + mov EAX, [_judas_bufferlength] + call gus_poke_ + mov EAX, 1 + call gus_peek_ + mov DL, AL + mov EAX, [_judas_bufferlength] + inc EAX + call gus_poke_ + mov EDX, [_judas_bufferlength] +ugm_shitloop: call gus_getpos + mov EBX, EAX + call gus_getpos + mov ECX, EAX + cmp EBX, EDX + jae ugm_shitloop + mov EAX, EBX + sub EAX, ECX + cmp EAX, 64 + jg ugm_shitloop + cmp EAX, -64 + jl ugm_shitloop + mov EAX, EBX + sti + and EAX, [_judas_buffermask] + mov EBX, [_judas_mixpos] ;EBX = old mixpos + cmp EAX, EBX + je near judas_donothing + jb updategusm_wrap + mov [_judas_mixpos], EAX ;New "oldpos" + mov EDX, EAX + sub EDX, EBX ;How much to mix + mov EAX, EBX ;Where to mix + push EAX + push EDX + add EAX, [_dma_address] + call dword [_judas_mixersys] + pop EDX + pop EAX + call gus_dmaprogram_ + jmp judas_donothing +updategusm_wrap:mov [_judas_mixpos], EAX ;Mix first to buffer + mov EAX, EBX ;end + mov EDX, [_judas_bufferlength] + sub EDX, EBX + push EAX + push EDX + add EAX, [_dma_address] + call dword [_judas_mixersys] + mov EAX, [_dma_address] + mov EDX, [_judas_mixpos] + call dword [_judas_mixersys] + pop EDX + pop EAX + call gus_dmaprogram_ + xor EAX, EAX + mov EDX, [_judas_mixpos] + call gus_dmaprogram_ + jmp judas_donothing + + + + + + + + + +;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ +;³ Fast Mixer ³ +;³ ³ +;³ by Cadaver ³ +;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ + +;First macros... + +%macro getvolume 0 + xor EAX, EAX + mov AX, [ESI + Chn_Vol] + cmp EAX, 64*256 + jbe %%limit + mov EAX, 64*256 +%%limit: xor EBX, EBX + mov BL, [ESI + Chn_MasterVol] + imul EAX, EBX + shr EAX, 6+8 + or AL, AL + jz near zerovolume + mov EBX, EAX +%endmacro + +%macro stereoadjust 0 + shl EBX, 1 ;Stereo can have 2x vol + cmp EBX, 255 ;Check that volume is + jbe %%limit ;within volumetable + mov EBX, 255 ;limits though +%%limit: +%endmacro + +%macro smoothpanadjust 0 + xor EAX, EAX + mov AL, [ESI + Chn_Panning] + mov ECX, EBX + imul ECX, EAX + shr ECX, 7 + neg EAX + add EAX, 256 + imul EBX, EAX + shr EBX, 7 + cmp EBX, 255 + jbe %%limit1 + mov EBX, 255 +%%limit1: cmp ECX, 255 + jbe %%limit2 + mov ECX, 255 +%%limit2: +%endmacro + +%macro mix8_mono_n 1 + mov BL, [ESI] + add EDX, ECX + mov EAX, [EBX * 4] + adc ESI, EBP + add [EDI + 4 * %1], EAX +%endmacro + +%macro mix8_left_n 1 + mov BL, [ESI] + add EDX, ECX + mov EAX, [EBX * 4] + adc ESI, EBP + add [EDI + 8 * %1], EAX +%endmacro + +%macro mix8_middle_n 1 + mov BL, [ESI] + add EDX, ECX + mov EAX, [EBX * 4] + adc ESI, EBP + add [EDI + 8 * %1], EAX + add [EDI + 8 * %1 + 4], EAX +%endmacro + +%macro mix8_right_n 1 + mov BL, [ESI] + add EDX, ECX + mov EAX, [EBX * 4] + adc ESI, EBP + add [EDI + 8 * %1 + 4], EAX +%endmacro + +%macro mix8_smooth_n 1 + mov BL, [ESI] + add EDX, [fractadd] + mov EAX, [EBX * 4] + adc ESI, EBP + mov CL, BL + add [EDI + 8 * %1], EAX + mov EAX, [ECX * 4] + add [EDI + 8 * %1 + 4], EAX +%endmacro + +%macro mix8_mono_i 1 + movsx EAX, byte [ESI+1] + movsx ECX, byte [ESI] + sub EAX, ECX + movzx ECX, DH + imul AX, CX + mov BL, AH + add BL, [ESI] + add DX, word [fractadd + 2] + mov EAX, [EBX * 4] + adc ESI, EBP + add [EDI + 4 * %1], EAX +%endmacro + +%macro mix8_left_i 1 + movsx EAX, byte [ESI+1] + movsx ECX, byte [ESI] + sub EAX, ECX + movzx ECX, DH + imul AX, CX + mov BL, AH + add BL, [ESI] + add DX, word [fractadd + 2] + mov EAX, [EBX * 4] + adc ESI, EBP + add [EDI + 8 * %1], EAX +%endmacro + +%macro mix8_middle_i 1 + movsx EAX, byte [ESI+1] + movsx ECX, byte [ESI] + sub EAX, ECX + movzx ECX, DH + imul AX, CX + mov BL, AH + add BL, [ESI] + add DX, word [fractadd + 2] + mov EAX, [EBX * 4] + adc ESI, EBP + add [EDI + 8 * %1], EAX + add [EDI + 8 * %1 + 4], EAX +%endmacro + +%macro mix8_right_i 1 + movsx EAX, byte [ESI+1] + movsx ECX, byte [ESI] + sub EAX, ECX + movzx ECX, DH + imul AX, CX + mov BL, AH + add BL, [ESI] + add DX, word [fractadd + 2] + mov EAX, [EBX * 4] + adc ESI, EBP + add [EDI + 8 * %1 + 4], EAX +%endmacro + +%macro mix8_smooth_i 1 + movsx EAX, byte [ESI+1] + movsx EBP, byte [ESI] + sub EAX, EBP + movzx EBP, DH + imul AX, BP + mov BL, AH + add BL, [ESI] + add DX, word [fractadd + 2] + mov EAX, [EBX * 4] + adc ESI, [integeradd] + mov CL, BL + add [EDI + 8 * %1], EAX + mov EAX, [ECX * 4] + add [EDI + 8 * %1 + 4], EAX +%endmacro + +%macro mix16_mono_n 1 + movsx EAX, word [ESI * 2] + imul EAX, EBX + sar EAX, 8 + (16 - SIGNIFICANT_BITS_16) + add EDX, ECX + adc ESI, EBP + add [EDI + 4 * %1], EAX +%endmacro + +%macro mix16_left_n 1 + movsx EAX, word [ESI * 2] + imul EAX, EBX + sar EAX, 8 + (16 - SIGNIFICANT_BITS_16) + add EDX, ECX + adc ESI, EBP + add [EDI + 8 * %1], EAX +%endmacro + +%macro mix16_middle_n 1 + movsx EAX, word [ESI * 2] + imul EAX, EBX + sar EAX, 8 + (16 - SIGNIFICANT_BITS_16) + add EDX, ECX + adc ESI, EBP + add [EDI + 8 * %1], EAX + add [EDI + 8 * %1 + 4], EAX +%endmacro + +%macro mix16_right_n 1 + movsx EAX, word [ESI * 2] + imul EAX, EBX + sar EAX, 8 + (16 - SIGNIFICANT_BITS_16) + add EDX, ECX + adc ESI, EBP + add [EDI + 8 * %1 + 4], EAX +%endmacro + +%macro mix16_smooth_n 1 + movsx EAX, word [ESI * 2] + imul EAX, EBX + sar EAX, 8 + (16 - SIGNIFICANT_BITS_16) + add [EDI + 8 * %1], EAX + movsx EAX, word [ESI * 2] + imul EAX, ECX + sar EAX, 8 + (16 - SIGNIFICANT_BITS_16) + add EDX, [fractadd] + adc ESI, EBP + add [EDI + 8 * %1 + 4], EAX +%endmacro + +%macro mix16_mono_i 1 + movsx EAX, word [ESI * 2 + 2] + movsx ECX, word [ESI * 2] + sub EAX, ECX + movzx EBX, DH + imul EAX, EBX + sar EAX, 8 + add EAX, ECX + imul EAX, [leftvol] + sar EAX, 8 + (16 - SIGNIFICANT_BITS_16) + add DX, word [fractadd + 2] + adc ESI, EBP + add [EDI + 4 * %1], EAX +%endmacro + +%macro mix16_left_i 1 + movsx EAX, word [ESI * 2 + 2] + movsx ECX, word [ESI * 2] + sub EAX, ECX + movzx EBX, DH + imul EAX, EBX + sar EAX, 8 + add EAX, ECX + imul EAX, [leftvol] + sar EAX, 8 + (16 - SIGNIFICANT_BITS_16) + add DX, word [fractadd + 2] + adc ESI, EBP + add [EDI + 8 * %1], EAX +%endmacro + +%macro mix16_middle_i 1 + movsx EAX, word [ESI * 2 + 2] + movsx ECX, word [ESI * 2] + sub EAX, ECX + movzx EBX, DH + imul EAX, EBX + sar EAX, 8 + add EAX, ECX + imul EAX, [leftvol] + sar EAX, 8 + (16 - SIGNIFICANT_BITS_16) + add DX, word [fractadd + 2] + adc ESI, EBP + add [EDI + 8 * %1], EAX + add [EDI + 8 * %1 + 4], EAX +%endmacro + +%macro mix16_right_i 1 + movsx EAX, word [ESI * 2 + 2] + movsx ECX, word [ESI * 2] + sub EAX, ECX + movzx EBX, DH + imul EAX, EBX + sar EAX, 8 + add EAX, ECX + imul EAX, [leftvol] + sar EAX, 8 + (16 - SIGNIFICANT_BITS_16) + add DX, word [fractadd + 2] + adc ESI, EBP + add [EDI + 8 * %1 + 4], EAX +%endmacro + +%macro mix16_smooth_i 1 + movsx EAX, word [ESI * 2 + 2] + movsx ECX, word [ESI * 2] + sub EAX, ECX + movzx EBX, DH + imul EAX, EBX + sar EAX, 8 + add EAX, ECX + mov EBX, EAX + imul EAX, [leftvol] + sar EAX, 8 + (16 - SIGNIFICANT_BITS_16) + add [EDI + 8 * %1], EAX + mov EAX, EBX + imul EAX, [rightvol] + sar EAX, 8 + (16 - SIGNIFICANT_BITS_16) + add DX, word [fractadd + 2] + adc ESI, EBP + add [EDI + 8 * %1 + 4], EAX +%endmacro + +%macro mixloop8 3 ; routine, samplesize, ip + mov DX, [ESI + Chn_FractPos] ;Get fractional pos + %if (%3 == 0) ; if (ip == 0) + shl EDX, 16 + %endif + mov EAX, [ESI + Chn_End] ;Get end & endsubtract- + mov [smpend], EAX ;value + sub EAX, [ESI + Chn_Repeat] + mov [smpsubtract], EAX + mov ESI, [ESI + Chn_Pos] ;Get sample position + mov EDI, [dptr] ;Get bufferptr + mov EAX, [samples] ;Fix loopcount & + dec EAX + shr EAX, 4 ;jumpoffset & subtract + inc EAX ;EDI accordingly + mov [loopcount], EAX + mov EAX, [samples] + and EAX, 15 + mov EAX, [EAX * 4 + shittable] + %if (%2 == 4) ; if (samplesize == 4) + sub EDI, EAX + %else + sub EDI, EAX + sub EDI, EAX + %endif + add EAX, offset %%offsettable + jmp [EAX] + + align 16 + +%%offset0: + %1 0 ;routine 0 +%%offset1: + %1 1 ;routine 1 +%%offset2: + %1 2 ;routine 2 +%%offset3: + %1 3 ;routine 3 +%%offset4: + %1 4 ;routine 4 +%%offset5: + %1 5 ;routine 5 +%%offset6: + %1 6 ;routine 6 +%%offset7: + %1 7 ;routine 7 +%%offset8: + %1 8 ;routine 8 +%%offset9: + %1 9 ;routine 9 +%%offseta: + %1 10 ;routine 10 +%%offsetb: + %1 11 ;routine 11 +%%offsetc: + %1 12 ;routine 12 +%%offsetd: + %1 13 ;routine 13 +%%offsete: + %1 14 ;routine 14 +%%offsetf: + %1 15 ;routine 15 + add EDI, 16 * %2 ; samplesize + cmp ESI, [smpend] + jae %%hitend + dec dword [loopcount] + jnz near %%offset0 + mov EAX, [cptr] + mov [EAX + Chn_Pos], ESI + %if (%3 == 0) ; if (ip == 0) + shr EDX, 16 + %endif + mov [EAX + Chn_FractPos], DX + ret +%%hitend: mov EAX, [cptr] + test byte [EAX + Chn_VoiceMode], VM_LOOP + jz %%oneshot +%%subloop: sub ESI, [smpsubtract] + cmp ESI, [smpend] + jae %%subloop + dec dword [loopcount] + jnz near %%offset0 + mov [EAX + Chn_Pos], ESI + %if (%3 == 0) ;if (ip == 0) + shr EDX, 16 + %endif + mov [EAX + Chn_FractPos], DX + ret +%%oneshot: mov byte [EAX + Chn_VoiceMode], VM_OFF + ret + align 4 +%%offsettable: dd offset %%offset0 + dd offset %%offset1 + dd offset %%offset2 + dd offset %%offset3 + dd offset %%offset4 + dd offset %%offset5 + dd offset %%offset6 + dd offset %%offset7 + dd offset %%offset8 + dd offset %%offset9 + dd offset %%offseta + dd offset %%offsetb + dd offset %%offsetc + dd offset %%offsetd + dd offset %%offsete + dd offset %%offsetf +%endmacro + +%macro mixloop16 3 ; routine, samplesize, ip + mov DX, [ESI + Chn_FractPos] ;Get fractional pos + %if (%3 == 0) ; if (ip == 0) + shl EDX, 16 + %endif + mov EAX, [ESI + Chn_End] ;Get end & endsubtract- + shr EAX, 1 + mov [smpend], EAX ;value + mov EAX, [ESI + Chn_End] + sub EAX, [ESI + Chn_Repeat] + shr EAX, 1 + mov [smpsubtract], EAX + mov ESI, [ESI + Chn_Pos] ;Get sample position + shr ESI, 1 + mov EDI, [dptr] ;Get bufferptr + mov EAX, [samples] ;Fix loopcount & + dec EAX + shr EAX, 4 ;jumpoffset & subtract + inc EAX ;EDI accordingly + mov [loopcount], EAX + mov EAX, [samples] + and EAX, 15 + mov EAX, [EAX * 4 + shittable] + %if (%2 == 4) ; if (samplesize == 4) + sub EDI, EAX + %else + sub EDI, EAX + sub EDI, EAX + %endif + add EAX, offset %%offsettable + jmp [EAX] + + align 16 + +%%offset0: + %1 0 ;routine 0 +%%offset1: + %1 1 ;routine 1 +%%offset2: + %1 2 ;routine 2 +%%offset3: + %1 3 ;routine 3 +%%offset4: + %1 4 ;routine 4 +%%offset5: + %1 5 ;routine 5 +%%offset6: + %1 6 ;routine 6 +%%offset7: + %1 7 ;routine 7 +%%offset8: + %1 8 ;routine 8 +%%offset9: + %1 9 ;routine 9 +%%offseta: + %1 10 ;routine 10 +%%offsetb: + %1 11 ;routine 11 +%%offsetc: + %1 12 ;routine 12 +%%offsetd: + %1 13 ;routine 13 +%%offsete: + %1 14 ;routine 14 +%%offsetf: + %1 15 ;routine 15 + add EDI, 16 * %2 ; samplesize + cmp ESI, [smpend] + jae %%hitend + dec dword [loopcount] + jnz near %%offset0 + mov EAX, [cptr] + shl ESI, 1 + mov [EAX + Chn_Pos], ESI + %if (%3 == 0) ; if (ip == 0) + shr EDX, 16 + %endif + mov [EAX + Chn_FractPos], DX + ret +%%hitend: mov EAX, [cptr] + test byte [EAX + Chn_VoiceMode], VM_LOOP + jz %%oneshot +%%subloop: sub ESI, [smpsubtract] + cmp ESI, [smpend] + jae %%subloop + dec dword [loopcount] + jnz near %%offset0 + shl ESI, 1 + mov [EAX + Chn_Pos], ESI + %if (%3 == 0) ; if (ip == 0) + shr EDX, 16 + %endif + mov [EAX + Chn_FractPos], DX + ret +%%oneshot: mov byte [EAX + Chn_VoiceMode], VM_OFF + ret + align 4 +%%offsettable: dd offset %%offset0 + dd offset %%offset1 + dd offset %%offset2 + dd offset %%offset3 + dd offset %%offset4 + dd offset %%offset5 + dd offset %%offset6 + dd offset %%offset7 + dd offset %%offset8 + dd offset %%offset9 + dd offset %%offseta + dd offset %%offsetb + dd offset %%offsetc + dd offset %%offsetd + dd offset %%offsete + dd offset %%offsetf +%endmacro + + ;16bit fast mixer routines start here! + ;This is the main mixing routine, which mixes EDX bytes of sound into + ;address EAX, calling the music player at correct intervals. EDX must + ;be a multiply of (samplesize * 8) because there is an unrolled + ;postprocessing loop. + ;WARNING: This routine destroys every register! + + align 4 + +fmixer_: +_fmixer: + or EDX, EDX ;Check zero length + jz near mix_quit + mov ECX, EDX + test byte [_judas_mixmode], STEREO ;Stereo or mono? + jz mix_noshift1 + shr EDX, 1 +mix_noshift1: test byte [_judas_mixmode], SIXTEENBIT ;8- or 16bit? + jz mix_noshift2 + shr EDX, 1 + shr ECX, 1 +mix_noshift2: mov [samples], EDX ;Save number of samples + mov [totalwork], EDX ;"Total work" counter + mov [fptr], EAX ;Save final destination + shr ECX, 3 + mov [postproc], ECX ;Save clipbuffer size + mov EDI, [_judas_clipbuffer] ;Clear the clipbuffer + mov [dptr], EDI + xor EAX, EAX +mix_clearloop: mov [EDI], EAX + mov [EDI + 4], EAX + mov [EDI + 8], EAX + mov [EDI + 12], EAX + mov [EDI + 16], EAX + mov [EDI + 20], EAX + mov [EDI + 24], EAX + mov [EDI + 28], EAX + add EDI, 32 + dec ECX + jnz mix_clearloop + cmp dword [_judas_player], 0 + jne mix_hardwayloop + call dword [_judas_mixroutine] + jmp mix_firstphasedone +mix_hardwayloop:cmp dword [_judas_bpmcount], 0 ;Time to play? + jne mix_skipplaying + cmp dword [_judas_player], 0 ;Might change in the + je mix_fuckshitup ;middle of a loop + call dword [_judas_player] +mix_fuckshitup: mov EAX, [_judas_mixrate] + mov EBX, 5 + mul EBX + shr EAX, 1 + xor EDX, EDX + movzx EBX, byte [_judas_bpmtempo] + div EBX + mov [_judas_bpmcount], EAX +mix_skipplaying:mov EAX, [totalwork] + cmp EAX, [_judas_bpmcount] + jbe mix_nolimit + mov EAX, [_judas_bpmcount] +mix_nolimit: mov [samples], EAX + call dword [_judas_mixroutine] + mov EAX, [samples] + sub [_judas_bpmcount], EAX + mov EBX, EAX + shl EBX, 2 + test byte [_judas_mixmode], STEREO + jz mix_noshift3 + shl EBX, 1 +mix_noshift3: add [dptr], EBX + sub [totalwork], EAX + jnz near mix_hardwayloop +mix_firstphasedone: + test byte [_judas_mixmode], SIXTEENBIT + jz near mix_8bit_endphase +mix_16bit_endphase: + test byte [_judas_mixmode], STEREO + jz mix_nogusshit1 + cmp dword [_judas_device], DEV_GUS + je near mix_gus16_endphase +mix_nogusshit1: mov EDI, [fptr] + mov EBX, [postproc] + mov ESI, [_judas_clipbuffer] + mov ECX, [_judas_cliptable] + xor EAX, EAX +mix_16bit_endphase_loop: + mov AX, [ESI] + mov AX, [ECX + EAX * 2] + mov [EDI], AX + mov AX, [ESI + 4] + mov AX, [ECX + EAX * 2] + mov [EDI + 2], AX + mov AX, [ESI + 8] + mov AX, [ECX + EAX * 2] + mov [EDI + 4], AX + mov AX, [ESI + 12] + mov AX, [ECX + EAX * 2] + mov [EDI + 6], AX + mov AX, [ESI + 16] + mov AX, [ECX + EAX * 2] + mov [EDI + 8], AX + mov AX, [ESI + 20] + mov AX, [ECX + EAX * 2] + mov [EDI + 10], AX + mov AX, [ESI + 24] + mov AX, [ECX + EAX * 2] + mov [EDI + 12], AX + mov AX, [ESI + 28] + mov AX, [ECX + EAX * 2] + mov [EDI + 14], AX + add ESI, 32 + add EDI, 16 + dec EBX + jnz mix_16bit_endphase_loop +mix_quit: ret +mix_8bit_endphase: + test byte [_judas_mixmode], STEREO + jz mix_nogusshit2 + cmp dword [_judas_device], DEV_GUS + je near mix_gus8_endphase +mix_nogusshit2: mov EDI, [fptr] + mov EBX, [postproc] + mov ESI, [_judas_clipbuffer] + mov ECX, [_judas_cliptable] + xor EAX, EAX +mix_8bit_endphase_loop: + mov AX, [ESI] + mov AL, [ECX + EAX] + mov [EDI], AL + mov AX, [ESI + 4] + mov AL, [ECX + EAX] + mov [EDI + 1], AL + mov AX, [ESI + 8] + mov AL, [ECX + EAX] + mov [EDI + 2], AL + mov AX, [ESI + 12] + mov AL, [ECX + EAX] + mov [EDI + 3], AL + mov AX, [ESI + 16] + mov AL, [ECX + EAX] + mov [EDI + 4], AL + mov AX, [ESI + 20] + mov AL, [ECX + EAX] + mov [EDI + 5], AL + mov AX, [ESI + 24] + mov AL, [ECX + EAX] + mov [EDI + 6], AL + mov AX, [ESI + 28] + mov AL, [ECX + EAX] + mov [EDI + 7], AL + add ESI, 32 + add EDI, 8 + dec EBX + jnz mix_8bit_endphase_loop + jmp mix_quit +mix_gus16_endphase: + mov EDI, [fptr] + mov EBX, [postproc] + mov EDX, [_judas_bufferlength] + shr EDX, 1 + add EDX, EDI + add EDX, 32 + mov ESI, [_judas_clipbuffer] + mov ECX, [_judas_cliptable] + xor EAX, EAX +mix_gus16_endphase_loop: + mov AX, [ESI] + mov AX, [ECX + EAX * 2] + mov [EDI], AX + mov AX, [ESI + 4] + mov AX, [ECX + EAX * 2] + mov [EDX], AX + mov AX, [ESI + 8] + mov AX, [ECX + EAX * 2] + mov [EDI + 2], AX + mov AX, [ESI + 12] + mov AX, [ECX + EAX * 2] + mov [EDX + 2], AX + mov AX, [ESI + 16] + mov AX, [ECX + EAX * 2] + mov [EDI + 4], AX + mov AX, [ESI + 20] + mov AX, [ECX + EAX * 2] + mov [EDX + 4], AX + mov AX, [ESI + 24] + mov AX, [ECX + EAX * 2] + mov [EDI + 6], AX + mov AX, [ESI + 28] + mov AX, [ECX + EAX * 2] + mov [EDX + 6], AX + add ESI, 32 + add EDI, 8 + add EDX, 8 + dec EBX + jnz mix_gus16_endphase_loop + jmp mix_quit +mix_gus8_endphase: + mov EDI, [fptr] + mov EBX, [postproc] + mov EDX, [_judas_bufferlength] + shr EDX, 1 + add EDX, EDI + add EDX, 32 + mov ESI, [_judas_clipbuffer] + mov ECX, [_judas_cliptable] + xor EAX, EAX +mix_gus8_endphase_loop: + mov AX, [ESI] + mov AL, [ECX + EAX] + mov [EDI], AX + mov AX, [ESI + 4] + mov AL, [ECX + EAX] + mov [EDX], AX + mov AX, [ESI + 8] + mov AL, [ECX + EAX] + mov [EDI + 1], AX + mov AX, [ESI + 12] + mov AL, [ECX + EAX] + mov [EDX + 1], AX + mov AX, [ESI + 16] + mov AX, [ECX + EAX] + mov [EDI + 2], AX + mov AX, [ESI + 20] + mov AL, [ECX + EAX] + mov [EDX + 2], AX + mov AX, [ESI + 24] + mov AL, [ECX + EAX] + mov [EDI + 3], AX + mov AX, [ESI + 28] + mov AL, [ECX + EAX] + mov [EDX + 3], AX + add ESI, 32 + add EDI, 4 + add EDX, 4 + dec EBX + jnz mix_gus8_endphase_loop + jmp mix_quit + +normalmix_: +_normalmix: + mov dword [cptr], offset _judas_channel +normalmixloop: call mixchannel + add dword [cptr], CHANNEL_size ;type CHANNEL in TASM + cmp dword [cptr], offset _judas_channel + CHANNELS * CHANNEL_size ;type CHANNEL in TASM + jne normalmixloop + ret + +ipmix_: +_ipmix: + mov dword [cptr], offset _judas_channel +ipmixloop: call ipmixchannel + add dword [cptr], CHANNEL_size ;type CHANNEL in TASM + cmp dword [cptr], offset _judas_channel + CHANNELS * CHANNEL_size ;type CHANNEL + jne ipmixloop + ret + + ;Mixes [samples] of channel [cptr] to buffer at [dptr]. Destroys + ;every register. + +mixchannel: mov ESI, [cptr] + test byte [ESI + Chn_VoiceMode], VM_ON + jz near mixchannel_quit + mov EAX, [ESI + Chn_Freq] ;Get playing speed here + cmp EAX, 535232 ;Highest linear freq + jbe mixchannel_freqok + mov EAX, 535232 +mixchannel_freqok: + mov EDX, EAX ;Don't worry: overflow + shr EDX, 16 ;prevented by check + shl EAX, 16 ;above + div dword [_judas_mixrate] ;DIV is always + mov word [fractadd + 2], AX ;frightening!!! + shr EAX, 16 + mov [integeradd], EAX + test byte [ESI + Chn_VoiceMode], VM_16BIT ;16bit takes the branch + jnz near mixchannel16 ;because it's unusual + test byte [_judas_mixmode], STEREO ;Mono takes the branch + jz near mixchannel_mono ;because it's faster +mixchannel_stereo: + getvolume + cmp byte [ESI + Chn_Panning], 0 ;Left panning? + jne near mc8_notleft + stereoadjust + shl EBX, 8 ;Convert to volumetable + add EBX, [_judas_volumetable] ;ofs. + mov EBP, [integeradd] ;EBP = integeradd + mov ECX, [fractadd] ;ECX = fraction add + mixloop8 mix8_left_n, 8, 0 ;DO IT! +mc8_notleft: cmp byte [ESI + Chn_Panning], 128 ;Middle panning? + jne near mc8_notmiddle + shl EBX, 8 ;Convert to volumetable + add EBX, [_judas_volumetable] ;ofs. + mov EBP, [integeradd] ;EBP = integeradd + mov ECX, [fractadd] ;ECX = fraction add + mixloop8 mix8_middle_n, 8, 0 ;DO IT! +mc8_notmiddle: cmp byte [ESI + Chn_Panning], 255 ;Right panning? + jne near mc8_notright + stereoadjust + shl EBX, 8 ;Convert to volumetable + add EBX, [_judas_volumetable] ;ofs. + mov EBP, [integeradd] ;EBP = integeradd + mov ECX, [fractadd] ;ECX = fraction add + mixloop8 mix8_right_n, 8, 0 ;DO IT! +mc8_notright: smoothpanadjust ;Oh no, smooth panning! + shl EBX, 8 ;Convert to volumetable + add EBX, [_judas_volumetable] ;ofs. + shl ECX, 8 + add ECX, [_judas_volumetable] + mov EBP, [integeradd] ;ECX not available! + mixloop8 mix8_smooth_n, 8, 0 ;But yet we must do it.. +mixchannel_mono:getvolume + shl EBX, 8 ;Convert to volumetable + add EBX, [_judas_volumetable] ;ofs. + mov EBP, [integeradd] ;EBP = integeradd + mov ECX, [fractadd] ;ECX = fraction add + mixloop8 mix8_mono_n, 4, 0 ;DO IT! +mixchannel_quit:ret +mixchannel16: test byte [_judas_mixmode], STEREO ;Mono takes the branch + jz near mixchannel16_mono ;because it's faster +mixchannel16_stereo: + getvolume + cmp byte [ESI + Chn_Panning], 0 ;Left panning? + jne near mc16_notleft + stereoadjust + mov ECX, [fractadd] ;ECX = fraction add + mov EBP, [integeradd] ;EBP = integeradd + mixloop16 mix16_left_n, 8, 0 ;DO IT! +mc16_notleft: cmp byte [ESI + Chn_Panning], 128 ;Middle panning? + jne near mc16_notmiddle + mov ECX, [fractadd] ;ECX = fraction add + mov EBP, [integeradd] ;EBP = integeradd + mixloop16 mix16_middle_n, 8, 0 ;DO IT! +mc16_notmiddle: cmp byte [ESI + Chn_Panning], 255 ;Right panning? + jne near mc16_notright + stereoadjust + mov ECX, [fractadd] ;ECX = fraction add + mov EBP, [integeradd] ;EBP = integeradd + mixloop16 mix16_right_n, 8, 0 ;DO IT! +mc16_notright: smoothpanadjust ;Oh no, smooth panning! + mov EBP, [integeradd] + mixloop16 mix16_smooth_n, 8, 0 ;But yet we must do it.. +mixchannel16_mono: + getvolume + mov ECX, [fractadd] ;ECX = fraction add + mov EBP, [integeradd] ;EBP = integeradd + mixloop16 mix16_mono_n, 4, 0 ;DO IT! +mixchannel16_quit: + ret + + ;Mixes [samples] of channel [cptr] to buffer at [dptr] with + ;interpolation. Destroys every register. + +ipmixchannel: mov ESI, [cptr] + test byte [ESI + Chn_VoiceMode], VM_ON + jz near ipmixchannel_quit + mov EAX, [ESI + Chn_Freq] ;Get playing speed here + cmp EAX, 535232 ;Highest linear freq + jbe ipmixchannel_freqok + mov EAX, 535232 +ipmixchannel_freqok: + mov EDX, EAX + shr EDX, 16 + shl EAX, 16 + div dword [_judas_mixrate] + mov word [fractadd + 2], AX + shr EAX, 16 + mov [integeradd], EAX + test byte [ESI + Chn_VoiceMode], VM_16BIT ;16bit takes the branch + jnz near ipmixchannel16 ;because it's unusual + test byte [_judas_mixmode], STEREO ;Mono takes the branch + jz near ipmixchannel_mono ;because it's faster +ipmixchannel_stereo: + getvolume + cmp byte [ESI + Chn_Panning], 0 ;Left panning? + jne near imc8_notleft + stereoadjust + shl EBX, 8 ;Convert to volumetable + add EBX, [_judas_volumetable] ;ofs. + mov EBP, [integeradd] ;EBP = integeradd + mixloop8 mix8_left_i, 8, 1 ;DO IT! +imc8_notleft: cmp byte [ESI + Chn_Panning], 128 ;Middle panning? + jne near imc8_notmiddle + shl EBX, 8 ;Convert to volumetable + add EBX, [_judas_volumetable] ;ofs. + mov EBP, [integeradd] ;EBP = integeradd + mixloop8 mix8_middle_i, 8, 1 ;DO IT! +imc8_notmiddle: cmp byte [ESI + Chn_Panning], 255 ;Right panning? + jne near imc8_notright + stereoadjust + shl EBX, 8 ;Convert to volumetable + add EBX, [_judas_volumetable] ;ofs. + mov EBP, [integeradd] ;EBP = integeradd + mixloop8 mix8_right_i, 8, 1 ;DO IT! +imc8_notright: smoothpanadjust ;Oh no, smooth panning! + shl EBX, 8 ;Convert to volumetable + add EBX, [_judas_volumetable] ;ofs. + shl ECX, 8 + add ECX, [_judas_volumetable] + mixloop8 mix8_smooth_i, 8, 1 +ipmixchannel_mono:getvolume + shl EBX, 8 ;Convert to volumetable + add EBX, [_judas_volumetable] ;ofs. + mov EBP, [integeradd] ;EBP = integeradd + mixloop8 mix8_mono_i, 4, 1 ;DO IT! +ipmixchannel_quit:ret +ipmixchannel16: test byte [_judas_mixmode], STEREO ;Mono takes the branch + jz near ipmixchannel16_mono ;because it's faster +ipmixchannel16_stereo: + getvolume + cmp byte [ESI + Chn_Panning], 0 ;Left panning? + jne near imc16_notleft + stereoadjust + mov [leftvol], EBX + mov EBP, [integeradd] ;EBP = integeradd + mixloop16 mix16_left_i, 8, 1 ;DO IT! +imc16_notleft: cmp byte [ESI + Chn_Panning], 128 ;Middle panning? + jne near imc16_notmiddle + mov [leftvol], EBX + mov EBP, [integeradd] ;EBP = integeradd + mixloop16 mix16_middle_i, 8, 1 ;DO IT! +imc16_notmiddle:cmp byte [ESI + Chn_Panning], 255 ;Right panning? + jne near imc16_notright + stereoadjust + mov [leftvol], EBX + mov EBP, [integeradd] ;EBP = integeradd + mixloop16 mix16_right_i, 8, 1 ;DO IT! +imc16_notright: smoothpanadjust ;Oh no, smooth panning! + mov [leftvol], EBX + mov [rightvol], ECX + mov EBP, [integeradd] + mixloop16 mix16_smooth_i, 8, 1 ;But yet we must do it.. +ipmixchannel16_mono: + getvolume + mov [leftvol], EBX + mov EBP, [integeradd] ;EBP = integeradd + mixloop16 mix16_mono_i, 4, 1 ;DO IT! +ipmixchannel16_quit: + ret + +zerovolume: mov EBP, [samples] + mov EBX, [ESI + Chn_Pos] + mov ECX, EBX + shr EBX, 16 + shl ECX, 16 + mov CX, [ESI + Chn_FractPos] + mov EAX, [ESI + Chn_Freq] + mov EDX, EAX + shr EDX, 16 + shl EAX, 16 + div dword [_judas_mixrate] ;EAX = mixrate + test byte [ESI + Chn_VoiceMode], VM_16BIT ;If 16bit then double + jz zerovolume_not16bit ;the count + shl EBP, 1 +zerovolume_not16bit: + mul EBP ;EDX:EAX = pos. add + add ECX, EAX ;Add low part + adc EBX, EDX ;Add high part + mov [ESI + Chn_FractPos], CX ;Save fractpos + shl EBX, 16 ;(won't change now) + shr ECX, 16 ;Now shift back: ECX + or ECX, EBX ;is integer pos + test byte [ESI + Chn_VoiceMode], VM_16BIT ;Final adjust for 16bit + jz zerovolume_no16bitadjust + and ECX, 0fffffffeh +zerovolume_no16bitadjust: + test byte [ESI + Chn_VoiceMode], VM_LOOP ;Is it looped? + jnz zerovolume_looped + cmp ECX, [ESI + Chn_End] + jae zerovolume_oneshot_end +zerovolume_ready: + mov [ESI + Chn_Pos], ECX ;Store pos + ret +zerovolume_oneshot_end: + mov byte [ESI + Chn_VoiceMode], VM_OFF + ret +zerovolume_looped: + mov EAX, [ESI + Chn_End] + sub EAX, [ESI + Chn_Repeat] ;EAX = subtract value +zerovolume_looped_loop: + cmp ECX, [ESI + Chn_End] + jb zerovolume_ready + sub ECX, EAX + jmp zerovolume_looped_loop + + + + + + + + + +;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ +;³ Quality Mixer ³ +;³ ³ +;³ by Yehar ³ +;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ +; +;32 bit mixing with 30 significant bits. 16 bit interpolation for both 8 and 16 +;bit samples. Cubic (8192x) and linear (32768x) interpolation aka oversampling. +;14 bit volume. Zero-level reposition & volume change smoothing click removal. +; +;Don't try to read for educational purposes =)))) It's just too shitty & lazy! + + ;32bit quality mixer main routine starts here. This is separated from + ;the usual mixer because of different postprocessing etc. This is an + ;alternative main mixing routine, which mixes EDX bytes of sound into + ;address EAX, calling the music player at correct intervals. + ;WARNING: This routine destroys every register! + +qmixer_: +_qmixer: + test EDX, EDX ;Buffer size is zero -> nothing to do + jz near qmixer_quit + test byte [_judas_mixmode], SIXTEENBIT ;16/8bit? 16 means half + jz qmixer_noshift2 ;the number of samples. + shr EDX, 1 ;It is 16 bit! +qmixer_noshift2: + test byte [_judas_mixmode], STEREO ;Stereo/mono? Stereo means half + jz qmixer_noshift1 ;the number of samples. + shr EDX, 1 ;It is stereo! +qmixer_noshift1: + mov [fptr], EAX ;Save pointer to final buffer. + mov [samples], EDX ;Save number of samples. + mov [totalwork], EDX ;Save "total samples left" counter. + mov [postproc], EDX ;Number of samples to postprocess + mov EDI, [_judas_clipbuffer] + mov [dptr], EDI ;Dptr says to mixroutine: "Mix to clipbuffer!" + mov EBX, [_judas_zladdbuffer] + xor EAX, EAX + mov ECX, EDX + shl ECX, 1 +qmixer_clearloop: ;Clear clipbuffer & zeroleveladdbuffer + mov [EDI], EAX + mov [EBX], EAX + add EDI, 4 + add EBX, 4 + dec ECX + jnz qmixer_clearloop + + ;Player calling shit... + + cmp dword [_judas_player], 0 ;Is there a player? + jne qmixer_hardwayloop + call dword [_judas_mixroutine] ;No, just mix the sfx and + jmp qmixer_firstphasedone ;postprocess +qmixer_hardwayloop: + cmp dword [_judas_bpmcount], 0 ;Yes, check if it has to be + jne qmixer_skipplaying ;called. + cmp dword [_judas_player], 0 ;It has to, but player might + je qmixer_fuckshitup ;have disappeared. + call dword [_judas_player] ;Call if it hasn't. +qmixer_fuckshitup: + mov EAX, [_judas_mixrate] + mov EBP, 5 + mul EBP + shr EAX, 1 + xor EDX, EDX + movzx EBP, byte [_judas_bpmtempo] + div EBP + mov [_judas_bpmcount], EAX +qmixer_skipplaying: + mov EAX, [totalwork] + cmp EAX, [_judas_bpmcount] + jbe qmixer_nolimit + mov EAX, [_judas_bpmcount] +qmixer_nolimit: + mov [samples], EAX + call dword [_judas_mixroutine] + mov EAX, [samples] + sub [_judas_bpmcount], EAX + mov EBP, EAX + shl EBP, 3 + add [dptr], EBP + sub [totalwork], EAX + jnz qmixer_hardwayloop + + ;Postprocessing... + +qmixer_firstphasedone: + test byte [_judas_mixmode], SIXTEENBIT + jz near qmixer_8bit_endphase +qmixer_16bit_endphase: + test byte [_judas_mixmode], STEREO + jz near qmixer_nogusshit1 + cmp dword [_judas_device], DEV_GUS + je near qmixer_gus16s_endphase + ;stereo + mov ESI, [_judas_clipbuffer] + mov EBX, [_judas_zladdbuffer] + mov EDI, [fptr] +qmixer_s16bit_endphase_loop: + ;left + mov EAX, [ESI] ;Get value from clipbuffer + mov ECX, [_judas_zerolevell] + add ECX, [EBX] ;Add from zladdbuffer to zerolevel + mov EBP, ECX ;zerolevel gets 1/2048 closer to real zero + sar EBP, QMIXER_ZEROLEVELDECAY + sub ECX, EBP + mov [_judas_zerolevell], ECX + add EAX, ECX + + sar EAX, SIGNIFICANT_BITS_32 - 16 ;Shrink to 16 bit + cmp EAX, 32767 + jg qmixer_s16bit_overflowl + cmp EAX, -32768 + jl qmixer_s16bit_underflowl + mov [EDI], AX +qmixer_s16bit_cutdonel: + + ;right + mov EAX, [ESI+4] ;Get value from clipbuffer + mov ECX, [_judas_zerolevelr] + add ECX, [EBX+4] ;Add from zladdbuffer to zerolevel + mov EBP, ECX ;zerolevel gets 1/2048 closer to real zero + sar EBP, QMIXER_ZEROLEVELDECAY + sub ECX, EBP + mov [_judas_zerolevelr], ECX + add EAX, ECX + + sar EAX, SIGNIFICANT_BITS_32 - 16 ;Shrink to 16 bit + cmp EAX, 32767 + jg qmixer_s16bit_overflowr + cmp EAX, -32768 + jl qmixer_s16bit_underflowr + mov [EDI+2], AX +qmixer_s16bit_cutdoner: + + add ESI, 8 + add EBX, 8 + add EDI, 4 + dec dword [postproc] + jnz qmixer_s16bit_endphase_loop + ret +qmixer_s16bit_overflowl: + mov word [EDI], 32767 + shr EAX, 9 + cmp [_judas_clipped], AL + jae qmixer_s16bit_cutdonel + mov [_judas_clipped], AL + jmp qmixer_s16bit_cutdonel +qmixer_s16bit_underflowl: + mov word [EDI], -32768 + neg EAX + shr EAX, 9 + cmp [_judas_clipped], AL + jae qmixer_s16bit_cutdonel + mov [_judas_clipped], AL + jmp qmixer_s16bit_cutdonel +qmixer_s16bit_overflowr: + mov word [EDI+2], 32767 + shr EAX, 9 + cmp [_judas_clipped], AL + jae qmixer_s16bit_cutdoner + mov [_judas_clipped], AL + jmp qmixer_s16bit_cutdoner +qmixer_s16bit_underflowr: + mov word [EDI+2], -32768 + neg EAX + shr EAX, 9 + cmp [_judas_clipped], AL + jae qmixer_s16bit_cutdoner + mov [_judas_clipped], AL + jmp qmixer_s16bit_cutdoner +qmixer_nogusshit1: ;mono 16bit + mov ESI, [_judas_clipbuffer] + mov EBX, [_judas_zladdbuffer] + mov EDI, [fptr] +qmixer_m16bit_endphase_loop: + + mov EAX, [ESI] ;Get value from clipbuffer + sar EAX, 1 + mov ECX, [ESI+4] ; + right + sar ECX, 1 + add EAX, ECX + + mov ECX, [_judas_zerolevell] ;left zerolevel + add ECX, [EBX] ;Add from zladdbuffer to zerolevel + mov EBP, ECX ;zerolevel gets 1/2048 closer to real zero + sar EBP, QMIXER_ZEROLEVELDECAY + sub ECX, EBP + mov [_judas_zerolevell], ECX + sar ECX, 1 + add EAX, ECX + mov ECX, [_judas_zerolevelr] ;right zerolevel + add ECX, [EBX+4] ;Add from zladdbuffer to zerolevel + mov EBP, ECX ;zerolevel gets 1/2048 closer to real zero + sar EBP, QMIXER_ZEROLEVELDECAY + sub ECX, EBP + mov [_judas_zerolevelr], ECX + sar ECX, 1 + add EAX, ECX + + sar EAX, SIGNIFICANT_BITS_32 - 16 ;Shrink to 16 bit + cmp EAX, 32767 + jg qmixer_m16bit_overflow + cmp EAX, -32768 + jl qmixer_m16bit_underflow + mov [EDI], AX +qmixer_m16bit_cutdone: + + add ESI, 8 + add EBX, 8 + add EDI, 2 + dec dword [postproc] + jnz qmixer_m16bit_endphase_loop + ret +qmixer_m16bit_overflow: + mov word [EDI], 32767 + shr EAX, 9 + cmp [_judas_clipped], AL + jae qmixer_m16bit_cutdone + mov [_judas_clipped], AL + jmp qmixer_m16bit_cutdone +qmixer_m16bit_underflow: + mov word [EDI], -32768 + neg EAX + shr EAX, 9 + cmp [_judas_clipped], AL + jae qmixer_m16bit_cutdone + mov [_judas_clipped], AL + jmp qmixer_m16bit_cutdone + +qmixer_8bit_endphase: + test byte [_judas_mixmode], STEREO + jz near qmixer_nogusshit2 + cmp dword [_judas_device], DEV_GUS + je near qmixer_gus8s_endphase + ;stereo + mov ESI, [_judas_clipbuffer] + mov EBX, [_judas_zladdbuffer] + mov EDI, [fptr] +qmixer_s8bit_endphase_loop: + ;left + mov EAX, [ESI] ;Get value from clipbuffer + mov ECX, [_judas_zerolevell] + add ECX, [EBX] ;Add from zladdbuffer to zerolevel + mov EBP, ECX ;zerolevel gets 1/2048 closer to real zero + sar EBP, QMIXER_ZEROLEVELDECAY + sub ECX, EBP + mov [_judas_zerolevell], ECX + add EAX, ECX + + sar EAX, SIGNIFICANT_BITS_32 - 8 ;Shrink to 8 bit + cmp EAX, 127 + jg qmixer_s8bit_overflowl + cmp EAX, -128 + jl qmixer_s8bit_underflowl + add AL, 128 + mov byte [EDI], AL +qmixer_s8bit_cutdonel: + + ;right + mov EAX, [ESI+4] ;Get value from clipbuffer + mov ECX, [_judas_zerolevelr] + add ECX, [EBX+4] ;Add from zladdbuffer to zerolevel + mov EBP, ECX ;zerolevel gets 1/2048 closer to real zero + sar EBP, QMIXER_ZEROLEVELDECAY + sub ECX, EBP + mov [_judas_zerolevelr], ECX + add EAX, ECX + + sar EAX, SIGNIFICANT_BITS_32 - 8 ;Shrink to 8 bit + cmp EAX, 127 + jg qmixer_s8bit_overflowr + cmp EAX, -128 + jl qmixer_s8bit_underflowr + add AL, 128 + mov byte [EDI+1], AL +qmixer_s8bit_cutdoner: + + add ESI, 8 + add EBX, 8 + add EDI, 2 + dec dword [postproc] + jnz qmixer_s8bit_endphase_loop + ret +qmixer_s8bit_overflowl: + mov byte [EDI], 255 + shr EAX, 1 + cmp [_judas_clipped], AL + jae qmixer_s8bit_cutdonel + mov [_judas_clipped], AL + jmp qmixer_s8bit_cutdonel +qmixer_s8bit_underflowl: + mov byte [EDI], 0 + neg EAX + shr EAX, 1 + cmp [_judas_clipped], AL + jae qmixer_s8bit_cutdonel + mov [_judas_clipped], AL + jmp qmixer_s8bit_cutdonel +qmixer_s8bit_overflowr: + mov byte [EDI+1], 255 + shr EAX, 1 + cmp [_judas_clipped], AL + jae qmixer_s8bit_cutdoner + mov [_judas_clipped], AL + jmp qmixer_s8bit_cutdoner +qmixer_s8bit_underflowr: + mov byte [EDI+1], 0 + neg EAX + shr EAX, 1 + cmp [_judas_clipped], AL + jae qmixer_s8bit_cutdoner + mov [_judas_clipped], AL + jmp qmixer_s8bit_cutdoner +qmixer_nogusshit2: ;mono 8bit + + mov ESI, [_judas_clipbuffer] + mov EBX, [_judas_zladdbuffer] + mov EDI, [fptr] +qmixer_m8bit_endphase_loop: + + mov EAX, [ESI] ;Get value from clipbuffer + sar EAX, 1 + mov ECX, [ESI+4] ; + right + sar ECX, 1 + add EAX, ECX + + mov ECX, [_judas_zerolevell] ;left zerolevel + add ECX, [EBX] ;Add from zladdbuffer to zerolevel + mov EBP, ECX ;zerolevel gets 1/2048 closer to real zero + sar EBP, QMIXER_ZEROLEVELDECAY + sub ECX, EBP + mov [_judas_zerolevell], ECX + sar ECX, 1 + add EAX, ECX + mov ECX, [_judas_zerolevelr] ;right zerolevel + add ECX, [EBX+4] ;Add from zladdbuffer to zerolevel + mov EBP, ECX ;zerolevel gets 1/2048 closer to real zero + sar EBP, QMIXER_ZEROLEVELDECAY + sub ECX, EBP + mov [_judas_zerolevelr], ECX + sar ECX, 1 + add EAX, ECX + + sar EAX, SIGNIFICANT_BITS_32 - 8 ;Shrink to 8 bit + cmp EAX, 127 + jg qmixer_m8bit_overflow + cmp EAX, -128 + jl qmixer_m8bit_underflow + add AL, 128 + mov byte [EDI], AL +qmixer_m8bit_cutdone: + + add ESI, 8 + add EBX, 8 + inc EDI + dec dword [postproc] + jnz qmixer_m8bit_endphase_loop + ret +qmixer_m8bit_overflow: + mov byte [EDI], 255 + shr EAX, 1 + cmp [_judas_clipped], AL + jae qmixer_m8bit_cutdone + mov [_judas_clipped], AL + jmp qmixer_m8bit_cutdone +qmixer_m8bit_underflow: + mov byte [EDI], 0 + neg EAX + shr EAX, 1 + cmp [_judas_clipped], AL + jae qmixer_m8bit_cutdone + mov [_judas_clipped], AL + jmp qmixer_m8bit_cutdone + +qmixer_gus16s_endphase: + mov ESI, [_judas_clipbuffer] + mov EBX, [_judas_zladdbuffer] + mov EDI, [fptr] ;[EDI] = gus left + mov EDX, [_judas_bufferlength] + shr EDX, 1 + add EDX, EDI + add EDX, 32 ;[EDX] = gus right + +qmixer_gus16s_endphase_loop: + ;left + mov EAX, [ESI] ;Get value from clipbuffer + mov ECX, [_judas_zerolevell] + add ECX, [EBX] ;Add from zladdbuffer to zerolevel + mov EBP, ECX ;zerolevel gets 1/2048 closer to real zero + sar EBP, QMIXER_ZEROLEVELDECAY + sub ECX, EBP + mov [_judas_zerolevell], ECX + add EAX, ECX + + sar EAX, SIGNIFICANT_BITS_32 - 16 ;Shrink to 16 bit + cmp EAX, 32767 + jg qmixer_gus16s_overflowl + cmp EAX, -32768 + jl qmixer_gus16s_underflowl + mov [EDI], AX +qmixer_gus16s_cutdonel: + + ;right + mov EAX, [ESI+4] ;Get value from clipbuffer + mov ECX, [_judas_zerolevelr] + add ECX, [EBX+4] ;Add from zladdbuffer to zerolevel + mov EBP, ECX ;zerolevel gets 1/2048 closer to real zero + sar EBP, QMIXER_ZEROLEVELDECAY + sub ECX, EBP + mov [_judas_zerolevelr], ECX + add EAX, ECX + + sar EAX, SIGNIFICANT_BITS_32 - 16 ;Shrink to 16 bit + cmp EAX, 32767 + jg qmixer_gus16s_overflowr + cmp EAX, -32768 + jl qmixer_gus16s_underflowr + mov [EDX], AX +qmixer_gus16s_cutdoner: + + add ESI, 8 + add EBX, 8 + add EDI, 2 + add EDX, 2 + dec dword [postproc] + jnz qmixer_gus16s_endphase_loop + ret +qmixer_gus16s_overflowl: + mov word [EDI], 32767 + shr EAX, 9 + cmp [_judas_clipped], AL + jae qmixer_gus16s_cutdonel + mov [_judas_clipped], AL + jmp qmixer_gus16s_cutdonel +qmixer_gus16s_underflowl: + mov word [EDI], -32768 + neg EAX + shr EAX, 9 + cmp [_judas_clipped], AL + jae qmixer_gus16s_cutdonel + mov [_judas_clipped], AL + jmp qmixer_gus16s_cutdonel +qmixer_gus16s_overflowr: + mov word [EDX], 32767 + shr EAX, 9 + cmp [_judas_clipped], AL + jae qmixer_gus16s_cutdoner + mov [_judas_clipped], AL + jmp qmixer_gus16s_cutdoner +qmixer_gus16s_underflowr: + mov word [EDX], -32768 + neg EAX + shr EAX, 9 + cmp [_judas_clipped], AL + jae near qmixer_gus16s_cutdoner + mov [_judas_clipped], AL + jmp qmixer_gus16s_cutdoner + +qmixer_gus8s_endphase: + mov ESI, [_judas_clipbuffer] + mov EBX, [_judas_zladdbuffer] + mov EDI, [fptr] ;[EDI] = gus left + mov EDX, [_judas_bufferlength] + shr EDX, 1 + add EDX, EDI + add EDX, 32 ;[EDX] = gus right +qmixer_gus8s_endphase_loop: + ;left + mov EAX, [ESI] ;Get value from clipbuffer + mov ECX, [_judas_zerolevell] + add ECX, [EBX] ;Add from zladdbuffer to zerolevel + mov EBP, ECX ;zerolevel gets 1/2048 closer to real zero + sar EBP, QMIXER_ZEROLEVELDECAY + sub ECX, EBP + mov [_judas_zerolevell], ECX + add EAX, ECX + + sar EAX, SIGNIFICANT_BITS_32 - 8 ;Shrink to 8 bit + cmp EAX, 127 + jg qmixer_gus8s_overflowl + cmp EAX, -128 + jl qmixer_gus8s_underflowl + add AL, 128 + mov byte [EDI], AL +qmixer_gus8s_cutdonel: + + ;right + mov EAX, [ESI+4] ;Get value from clipbuffer + mov ECX, [_judas_zerolevelr] + add ECX, [EBX+4] ;Add from zladdbuffer to zerolevel + mov EBP, ECX ;zerolevel gets 1/2048 closer to real zero + sar EBP, QMIXER_ZEROLEVELDECAY + sub ECX, EBP + mov [_judas_zerolevelr], ECX + add EAX, ECX + + sar EAX, SIGNIFICANT_BITS_32 - 8 ;Shrink to 8 bit + cmp EAX, 127 + jg qmixer_gus8s_overflowr + cmp EAX, -128 + jl qmixer_gus8s_underflowr + add AL, 128 + mov byte [EDX], AL +qmixer_gus8s_cutdoner: + + add ESI, 8 + add EBX, 8 + inc EDI + inc EDX + dec dword [postproc] + jnz qmixer_gus8s_endphase_loop + ret +qmixer_gus8s_overflowl: + mov byte [EDI], 255 + shr EAX, 1 + cmp [_judas_clipped], AL + jae qmixer_gus8s_cutdonel + mov [_judas_clipped], AL + jmp qmixer_gus8s_cutdonel +qmixer_gus8s_underflowl: + mov byte [EDI], 0 + neg EAX + shr EAX, 1 + cmp [_judas_clipped], AL + jae qmixer_gus8s_cutdonel + mov [_judas_clipped], AL + jmp qmixer_gus8s_cutdonel +qmixer_gus8s_overflowr: + mov byte [EDX], 255 + shr EAX, 1 + cmp [_judas_clipped], AL + jae qmixer_gus8s_cutdoner + mov [_judas_clipped], AL + jmp qmixer_gus8s_cutdoner +qmixer_gus8s_underflowr: + mov byte [EDX], 0 + neg EAX + shr EAX, 1 + cmp [_judas_clipped], AL + jae qmixer_gus8s_cutdoner + mov [_judas_clipped], AL + jmp qmixer_gus8s_cutdoner +qmixer_quit: ret + +qmix_cubic_: +_qmix_cubic: + mov dword [cptr], offset _judas_channel +qmix_cubic_channelloop: + call qmixchannel_cubic + add dword [cptr], CHANNEL_size ;type CHANNEL in TASM + cmp dword [cptr], offset _judas_channel + CHANNELS * CHANNEL_size ;type CHANNEL in TASM + jne qmix_cubic_channelloop + ret + +qmix_linear_: +_qmix_linear: + mov dword [cptr], offset _judas_channel +qmix_linear_channelloop: + call qmixchannel_linear + add dword [cptr], CHANNEL_size ;type CHANNEL in TASM + cmp dword [cptr], offset _judas_channel + CHANNELS * CHANNEL_size ;type CHANNEL + jne qmix_cubic_channelloop + ret + +q_zerovolume: + ; Zerolevel stuff... + + mov EAX, [dptr] ;Get correct place at zladdbuf + sub EAX, [_judas_clipbuffer] ; + add EAX, [_judas_zladdbuffer] ; + mov EDX, [EBX + Chn_LastValL] + add [EAX], EDX + mov EDX, [EBX + Chn_LastValR] + mov dword [EBX + Chn_LastValL], 0 + add [EAX+4], EDX + mov dword [EBX + Chn_LastValR], 0 + + mov ESI, [samples] + mov EBP, [EBX + Chn_Pos] + mov ECX, EBP + shr EBP, 16 + shl ECX, 16 + mov CX, [EBX + Chn_FractPos] + mov EAX, [EBX + Chn_Freq] + mov EDX, EAX + shr EDX, 16 + shl EAX, 16 + div dword [_judas_mixrate] ;EAX = mixrate + test byte [EBX + Chn_VoiceMode], VM_16BIT ;If 16bit then double + jz q_zerovolume_not16bit ;the count + shl ESI, 1 +q_zerovolume_not16bit: + mul ESI ;EDX:EAX = pos. add + add ECX, EAX ;Add low part + adc EBP, EDX ;Add high part + mov [EBX + Chn_FractPos], CX ;Save fractpos + shl EBP, 16 ;(won't change now) + shr ECX, 16 ;Now shift back: ECX + or ECX, EBP ;is integer pos + test byte [EBX + Chn_VoiceMode], VM_16BIT ;Final adjust for 16bit + jz q_zerovolume_no16bitadjust + and ECX, 0fffffffeh +q_zerovolume_no16bitadjust: + test byte [EBX + Chn_VoiceMode], VM_LOOP ;Is it looped? + jnz q_zerovolume_looped + cmp ECX, [EBX + Chn_End] + jae q_zerovolume_oneshot_end +q_zerovolume_ready: + mov [EBX + Chn_Pos], ECX ;Store pos + ret +q_zerovolume_oneshot_end: + mov byte [EBX + Chn_VoiceMode], VM_OFF + ret +q_zerovolume_looped: + mov EAX, [EBX + Chn_End] + sub EAX, [EBX + Chn_Repeat] ;EAX = subtract value +q_zerovolume_looped_loop: + cmp ECX, [EBX + Chn_End] + jb q_zerovolume_ready + sub ECX, EAX + jmp q_zerovolume_looped_loop + + ;MACROS FOR QUALITY MIXER! + +%macro q_volpan14 0 + ;Volume! + xor EAX, EAX + mov AX, [EBX + Chn_Vol] + xor ECX, ECX + mov CL, [EBX + Chn_MasterVol] + imul EAX, ECX + shr EAX, 8 + mov EBP, EAX ;14 bit volume in AX + or AX, word [EBX + Chn_SmoothVolL+1] + or AX, word [EBX + Chn_SmoothVolR+1] + jz near q_zerovolume + ;Panning! + xor EAX, EAX + mov AL, [EBX + Chn_Panning] + mov ECX, EBP ;EBP = leftvol, ECX = rightvol + imul ECX, EAX + neg EAX ;EAX = 255-EAX + add EAX, 255 ; + imul EBP, EAX +%endmacro + +%macro q_mix8_stereo_l 0 + movsx EAX, byte [ESI] ; + movsx EBP, byte [ESI+1] ; + mov EBX, [rightvol] ;Smooth volume slide ;uv + sub EBP, EAX ;EBP = 16bit slidevalue + mov EDX, ECX ;EDX = fractpos + sub EBX, [SmoothVolR] ; + shr EDX, 17 ;32768x interpolation + add EDI, 8 ; + imul EBP, EDX ;EBP = interpolated value + sar EBX, QMIXER_VOLUMESMOOTH ;1/32 closer + mov EDX, [leftvol] ;Smooth volume sli. + add EBX, [SmoothVolR] ; + sub EDX, [SmoothVolL] ; + sal EAX, 8 ; + sar EDX, QMIXER_VOLUMESMOOTH ;1/32 closer + mov [SmoothVolR], EBX ; + shr EBX, 7 ; + add EDX, [SmoothVolL] ; + sar EBP, 7 ; + mov [SmoothVolL], EDX ; + shr EDX, 7 ; + add EBP, EAX ; + + add ECX, [fractadd] ;Sample pos fractional part + mov EAX, EBP ; + adc ESI, [integeradd] ;Sample pos integer part + + imul EBP, EBX ; + + imul EAX, EDX ; + + add [EDI-8], EAX ; + add [EDI-8+4], EBP ; +%endmacro + +%macro q_mix8_stereo_c 0 ;Here starts the pervert Hermite interpolation!!! + + movsx EBP, byte [ESI+IPMINUS1] + movsx EDX, byte [ESI+IP1] + movsx EBX, byte [ESI+IP2] + movsx EAX, byte [ESI+IP0] + sal EBX, 8 ; + sal EDX, 8 ; + mov dword [ip2], EBX ; + sal EAX, 8 ; + mov dword [ip1], EDX ; + mov EBX, EAX ; + sub EAX, EDX ; + sal EBP, 8 ; + + mov [ipminus1], EBP ; + lea EAX, [EAX*4+EDX] ; + mov EDX, ECX ; + sub EAX, EBX ; + shr EDX, 19 ;8192x interpolation + sub EAX, EBP ; + add EAX, [ip2] ; + lea EBP, [EBX*4+EBX] ; + + imul EAX, EDX ; + + sar EAX, 32-19+1 ; + add EBP, [ip2] ; + sar EBP, 1 ; + add EAX, [ip1] ; + add EAX, [ip1] ; + add EDI, 8 ; + sub EAX, EBP ; + mov EBP, [ip1] ; + add EAX, [ipminus1] ; + sub EBP, [ipminus1] ; + + imul EAX, EDX ; + + sar EBP, 1 ; + sar EAX, 32-19 ; + add ECX, [fractadd] ;Sample pos fract. + adc ESI, [integeradd] ;Sample pos integer + add EAX, EBP ; + + imul EAX, EDX ; + + sar EAX, 32-19 ; + mov EDX, [leftvol] ;Smooth vol.sl. + add EAX, EBX ; + mov EBX, [rightvol] ;Smooth vol.sl. + sub EDX, [SmoothVolL] ; + sub EBX, [SmoothVolR] ; + sar EBX, QMIXER_VOLUMESMOOTH ;1/32 closer + add EBX, [SmoothVolR] ; + sar EDX, QMIXER_VOLUMESMOOTH ;1/32 closer + mov [SmoothVolR], EBX ; + shr EBX, 7 ; + add EDX, [SmoothVolL] ; + mov [SmoothVolL], EDX ; + mov EBP, EAX ; + shr EDX, 7 ; + + imul EAX, EDX ; + + imul EBP, EBX ; + + add [EDI-8], EAX ; + add [EDI-8+4], EBP ; +%endmacro + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;pos adds are already defined. EBP contains volume for left channel +;and ECX for right. +%macro q_mixloop8 1 ; routine + mov [leftvol], EBP ;Save volumes + mov [rightvol], ECX ; + + mov ESI, [EBX + Chn_Pos] ;ESI:ECX = pos. ESI is integer + mov CX, [EBX + Chn_FractPos] ;part and ECX fraction. + shl ECX, 16 + + mov EAX, [EBX + Chn_End] ;Get sample end and repeat + mov [smpend], EAX ;positions. Calculate subtract + sub EAX, [EBX + Chn_Repeat] ;value. + mov [smpsubtract], EAX + + mov EDI, [dptr] ;Where to mix to? + mov EAX, [samples] ;How many samples to mix? + mov [loopcount], EAX + + ;Click removing for volume change and sample replace + + mov EAX, [EBX + Chn_Pos] ;Is it a new sound? + cmp EAX, [EBX + Chn_PrevPos] + je near %%soonmixloop ;jump if not +;New sound + mov EDX, [leftvol] +%IFDEF QMIXER_STARTMUTE + shr EDX, QMIXER_STARTMUTE ;Just to remove low frequency clicks +%ENDIF + mov [SmoothVolL], EDX + mov EDX, [rightvol] +%IFDEF QMIXER_STARTMUTE + shr EDX, QMIXER_STARTMUTE +%ENDIF + mov [SmoothVolR], EDX + mov [saved_reg], EBX + %1 ;routine (macro parameter) + mov EBX, [saved_reg] + mov EDX, [EBX + Chn_LastValL] + mov [saved_reg], EAX + sub EDX, EAX + mov EAX, [_judas_zladdbuffer] ;Get correct place at zladdbuf + sub EAX, [_judas_clipbuffer] ; + add [EAX+EDI-8], EDX + mov EDX, [EBX + Chn_LastValR] + sub EDX, EBP + add [EAX+EDI-8+4], EDX + mov EAX, [saved_reg] ;Maybe needed if sample ends now + mov [saved_reg], EBX + jmp near %%donezladjust + +%%soonmixloop: mov EDX, [EBX + Chn_SmoothVolL] + mov EBP, [EBX + Chn_SmoothVolR] + mov [SmoothVolL], EDX + mov [SmoothVolR], EBP + mov [saved_reg], EBX + + align 16 + +%%mixloop: %1 ; routine (macro parameter) - Mix one sample +%%donezladjust: + cmp ESI, [smpend] ;End of sample? + jae %%hitend + + dec dword [loopcount] ;Still shit to do? + jnz near %%mixloop + + mov EBX, [saved_reg] + mov [EBX + Chn_Pos], ESI ;No, all done. Save position + mov [EBX + Chn_PrevPos], ESI ;..and prevpos + shr ECX, 16 + mov [EBX + Chn_FractPos], CX + mov [EBX + Chn_LastValL], EAX ;Save Last values also + mov [EBX + Chn_LastValR], EBP ; + mov EAX, [SmoothVolL] ;Save volume + mov EBP, [SmoothVolR] ; + mov [EBX + Chn_SmoothVolL], EAX ; + mov [EBX + Chn_SmoothVolR], EBP ; + ret + +%%hitend: + mov EBX, [saved_reg] + test byte [EBX + Chn_VoiceMode], VM_LOOP ;Is it a looped sample? + jz %%oneshot +%%subloop: + sub ESI, [smpsubtract] ;Looped. Go to loop start. + cmp ESI, [smpend] + jae %%subloop ;Fucking shit, it got far beyond sample end. + + dec dword [loopcount] ;Still shit to do? + jnz near %%mixloop + + mov [EBX + Chn_Pos], ESI ;No, all done. Save position + shr ECX, 16 + mov [EBX + Chn_FractPos], CX + mov [EBX + Chn_PrevPos], ESI ;Save prevpos too + mov [EBX + Chn_LastValL], EAX ;...Last values also + mov [EBX + Chn_LastValR], EBP ; + mov EAX, [SmoothVolL] ;Save volume + mov EBP, [SmoothVolR] ; + mov [EBX + Chn_SmoothVolL], EAX ; + mov [EBX + Chn_SmoothVolR], EBP ; + ret + +%%oneshot: + mov byte [EBX + Chn_VoiceMode], VM_OFF ;Your time to die, sample! + + ;If sample doesn't end at zero, zerolevel must be adjusted. + mov dword [EBX + Chn_Pos], 0 + mov dword [EBX + Chn_LastValL], 0 + mov dword [EBX + Chn_LastValR], 0 + cmp dword [loopcount], 1 + jbe %%oneshot_lastsample + mov EDX, EDI ;Get correct place in zladdbuf + sub EDX, [_judas_clipbuffer] ; + add EDX, [_judas_zladdbuffer] ; + add [EDX], EAX + add [EDX+4], EBP + mov byte [EBX + Chn_PrevVM], VM_OFF ;No need to update zerolevel + ret ;at beginning of next chunk + +%%oneshot_lastsample: ;Last sample in mixed chunk + mov byte [EBX + Chn_PrevVM], VM_ON + ret +%endmacro + +%macro q_mix16_stereo_l 0 + movsx EAX, word [ESI*2] ; + movsx EBP, word [ESI*2+2] ; + mov EBX, [rightvol] ;Smooth volume slide ;uv + sub EBP, EAX ;EBP = 16bit slidevalue + mov EDX, ECX ;EDX = fractpos + sub EBX, [SmoothVolR] ; + shr EDX, 17 ;32768x interpolation + add EDI, 8 ; + imul EBP, EDX ;EBP = interpolated value + sar EBX, QMIXER_VOLUMESMOOTH ;1/32 closer + mov EDX, [leftvol] ;Smooth volume sli. + add EBX, [SmoothVolR] ; + sub EDX, [SmoothVolL] ; + sar EDX, QMIXER_VOLUMESMOOTH ;1/32 closer + mov [SmoothVolR], EBX ; + shr EBX, 7 ; + add EDX, [SmoothVolL] ; + sar EBP, 15 ; + mov [SmoothVolL], EDX ; + shr EDX, 7 ; + add EBP, EAX ; + + add ECX, [fractadd] ;Sample pos fractional part + mov EAX, EBP ; + adc ESI, [integeradd] ;Sample pos integer part + + imul EBP, EBX ; + + imul EAX, EDX ; + + add [EDI-8], EAX ; + add [EDI-8+4], EBP ; +%endmacro + +%macro q_mix16_stereo_c 0 ;Here starts the pervert Hermite interpolation!!! + movsx EBP, word [ESI*2+IPMINUS1*2] + movsx EAX, word [ESI*2+IP0*2] + movsx EBX, word [ESI*2+IP2*2] + movsx EDX, word [ESI*2+IP1*2] + mov [ip2], EBX ; + mov [ip1], EDX ; + mov EBX, EAX ; + + sub EAX, EDX ; + mov [ipminus1], EBP ; + lea EAX, [EAX*4+EDX] ; + mov EDX, ECX ; + sub EAX, EBX ; + shr EDX, 19 ;8192x interpolation + sub EAX, EBP ; + add EAX, [ip2] ; + lea EBP, [EBX*4+EBX] ; + + imul EAX, EDX ; + + sar EAX, 32-19+1 ; + add EBP, [ip2] ; + sar EBP, 1 ; + add EAX, [ip1] ; + add EAX, [ip1] ; + add EDI, 8 ; + sub EAX, EBP ; + mov EBP, [ip1] ; + add EAX, [ipminus1] ; + sub EBP, [ipminus1] ; + + imul EAX, EDX ; + + sar EBP, 1 ; + sar EAX, 32-19 ; + add ECX, [fractadd] ;Sample pos fract. + adc ESI, [integeradd] ;Sample pos integer + add EAX, EBP ; + + imul EAX, EDX ; + + sar EAX, 32-19 ; + mov EDX, [leftvol] ;Smooth vol.sl. + add EAX, EBX ; + mov EBX, [rightvol] ;Smooth vol.sl. + sub EDX, [SmoothVolL] ; + sub EBX, [SmoothVolR] ; + sar EBX, QMIXER_VOLUMESMOOTH ;1/32 closer + add EBX, [SmoothVolR] ; + sar EDX, QMIXER_VOLUMESMOOTH ;1/32 closer + mov [SmoothVolR], EBX ; + shr EBX, 7 ; + add EDX, [SmoothVolL] ; + mov [SmoothVolL], EDX ; + mov EBP, EAX ; + shr EDX, 7 ; + + imul EAX, EDX ; + + imul EBP, EBX ; + + add [EDI-8], EAX ; + add [EDI-8+4], EBP ; +%endmacro + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;pos adds are already defined. EBP contains volume for left channel +;and ECX for right. +%macro q_mixloop16 1 ; routine + mov [leftvol], EBP ;Save volumes + mov [rightvol], ECX ; + + mov ESI, [EBX + Chn_Pos] ;ESI:ECX = pos. ESI is integer + shr ESI, 1 + mov CX, [EBX + Chn_FractPos] ;part and ECX fraction. + shl ECX, 16 + + mov EAX, [EBX + Chn_End] ;Get sample end and repeat + shr EAX, 1 + mov [smpend], EAX ;positions. Calculate subtract + mov EAX, [EBX + Chn_End] + sub EAX, [EBX + Chn_Repeat] ;value. + shr EAX, 1 + mov [smpsubtract], EAX + + mov EDI, [dptr] ;Where to mix to? + mov EAX, [samples] ;How many samples to mix? + mov [loopcount], EAX + + ;Click removing for volume change and sample replace + + mov EAX, [EBX + Chn_Pos] ;Is it a new sound? + cmp EAX, [EBX + Chn_PrevPos] + je near %%soonmixloop ;jump if not +;New sound + mov EDX, [leftvol] +%IFDEF QMIXER_STARTMUTE + shr EDX, QMIXER_STARTMUTE ;Just to remove low frequency clicks +%ENDIF + mov [SmoothVolL], EDX + mov EDX, [rightvol] +%IFDEF QMIXER_STARTMUTE + shr EDX, QMIXER_STARTMUTE +%ENDIF + mov [SmoothVolR], EDX + mov [saved_reg], EBX + %1 ; routine macro parameter + mov EBX, [saved_reg] + mov EDX, [EBX + Chn_LastValL] + mov [saved_reg], EAX + sub EDX, EAX + mov EAX, [_judas_zladdbuffer] ;Get correct place at zladdbuf + sub EAX, [_judas_clipbuffer] ; + add [EAX+EDI-8], EDX + mov EDX, [EBX + Chn_LastValR] + sub EDX, EBP + add [EAX+EDI-8+4], EDX + mov EAX, [saved_reg] ;Maybe needed if sample ends now + mov [saved_reg], EBX + jmp near %%donezladjust + +%%soonmixloop: mov EDX, [EBX + Chn_SmoothVolL] + mov EBP, [EBX + Chn_SmoothVolR] + mov [SmoothVolL], EDX + mov [SmoothVolR], EBP + mov [saved_reg], EBX + + align 16 + +%%mixloop: %1 ; routine macro parameter - Mix one sample +%%donezladjust: + cmp ESI, [smpend] ;End of sample? + jae %%hitend + + dec dword [loopcount] ;Still shit to do? + jnz near %%mixloop + + mov EBX, [saved_reg] + shl ESI, 1 + mov [EBX + Chn_Pos], ESI ;No, all done. Save position + mov [EBX + Chn_PrevPos], ESI ;...and prevpos + shr ECX, 16 + mov [EBX + Chn_FractPos], CX + mov [EBX + Chn_LastValL], EAX ;Save Last values also + mov [EBX + Chn_LastValR], EBP ; + mov EAX, [SmoothVolL] ;Save volume + mov EBP, [SmoothVolR] ; + mov [EBX + Chn_SmoothVolL], EAX ; + mov [EBX + Chn_SmoothVolR], EBP ; + ret + +%%hitend: + mov EBX, [saved_reg] + test byte [EBX + Chn_VoiceMode], VM_LOOP ;Is it a looped sample? + jz %%oneshot +%%subloop: + sub ESI, [smpsubtract] ;Looped. Go to loop start. + cmp ESI, [smpend] + jae %%subloop ;Fucking shit, it got far beyond sample end. + + dec dword [loopcount] ;Still shit to do? + jnz near %%mixloop + + shl ESI, 1 + mov [EBX + Chn_Pos], ESI ;No, all done. Save position + mov [EBX + Chn_PrevPos], ESI ;Save prevpos too + shr ECX, 16 + mov [EBX + Chn_FractPos], CX + mov [EBX + Chn_LastValL], EAX ;...Last values also + mov [EBX + Chn_LastValR], EBP ; + mov EAX, [SmoothVolL] ;Save volume + mov EBP, [SmoothVolR] ; + mov [EBX + Chn_SmoothVolL], EAX ; + mov [EBX + Chn_SmoothVolR], EBP ; + ret + +%%oneshot: + mov byte [EBX + Chn_VoiceMode], VM_OFF ;Your time to die, sample! + + ;If sample doesn't end at zero, zerolevel must be adjusted. + mov dword [EBX + Chn_Pos], 0 + mov dword [EBX + Chn_LastValL], 0 + mov dword [EBX + Chn_LastValR], 0 + cmp dword [loopcount], 1 + jbe %%oneshot_lastsample + mov EDX, EDI ;Get correct place in zladdbuf + sub EDX, [_judas_clipbuffer] ; + add EDX, [_judas_zladdbuffer] ; + add [EDX], EAX + add [EDX+4], EBP + mov byte [EBX + Chn_PrevVM], VM_OFF ;No need to update zerolevel + ret ;at beginning of next chunk + +%%oneshot_lastsample: ;Last sample in mixed chunk + mov byte [EBX + Chn_PrevVM], VM_ON + ret +%endmacro + + ;Qualitymixes [samples] of channel [cptr] to buffer at [dptr]. Destroys + ;every register. LINEAR INTERPOLATION! + +qmixchannel_linear: + mov EBX, [cptr] + mov AL, [EBX + Chn_VoiceMode] + test AL, VM_ON + jnz qmixc_l_vm_on + cmp AL, [EBX + Chn_PrevVM] ;Sound discontinuity? + je near qmixc_l_quit ;no? + mov [EBX + Chn_PrevVM], AL ;yes... + mov EDX, [_judas_zladdbuffer] ;so must move zerolevel + add EDX, [dptr] + mov EAX, [EBX + Chn_LastValL] + sub EDX, [_judas_clipbuffer] + mov dword [EBX + Chn_LastValL], 0 + add [EDX], EAX + mov EAX, [EBX + Chn_LastValR] + mov dword [EBX + Chn_LastValR], 0 + add [EDX+4], EAX + jmp qmixc_l_quit +qmixc_l_vm_on: + mov EAX, [EBX + Chn_Freq] + cmp EAX, 535232 ;Highest linear freq + jbe qmixc_l_freqok + mov EAX, 535232 +qmixc_l_freqok: + mov EDX, EAX + shr EDX, 16 + shl EAX, 16 + div dword [_judas_mixrate] + mov word [fractadd + 2], AX + shr EAX, 16 + mov [integeradd], EAX + test byte [EBX + Chn_VoiceMode], VM_16BIT ;Is sampledata 16bit? + jnz near qmixc_l_16bit ;Jump if yes. + test byte [_judas_mixmode], STEREO ;No, 8bit. Stereo audio? + jz qmixc_l_mono_8bit ;Jump if mono. +qmixc_l_stereo_8bit: +qmixc_l_mono_8bit: ;Mix in stereo, even if mono output + q_volpan14 + q_mixloop8 q_mix8_stereo_l + ret +qmixc_l_16bit: + test byte [_judas_mixmode], STEREO ;It's 16bit. Stereo? + jz qmixc_l_mono_16bit +qmixc_l_stereo_16bit: +qmixc_l_mono_16bit: ;Mix in stereo, even if mono output + q_volpan14 + q_mixloop16 q_mix16_stereo_l +qmixc_l_quit: ret + + ;Qualitymixes [samples] of channel [cptr] to buffer at [dptr]. Destroys + ;every register. CUBIC INTERPOLATION! + +qmixchannel_cubic: + mov EBX, [cptr] + mov AL, [EBX + Chn_VoiceMode] + test AL, VM_ON + jnz qmixc_c_vm_on + cmp AL, [EBX + Chn_PrevVM] ;Sound discontinuity? + je near qmixc_c_quit ;no? + mov [EBX + Chn_PrevVM], AL ;yes... + mov EDX, [_judas_zladdbuffer] ;so must move zerolevel + add EDX, [dptr] + mov EAX, [EBX + Chn_LastValL] + sub EDX, [_judas_clipbuffer] + mov dword [EBX + Chn_LastValL], 0 + add [EDX], EAX + mov EAX, [EBX + Chn_LastValR] + mov dword [EBX + Chn_LastValR], 0 + add [EDX+4], EAX + jmp qmixc_c_quit +qmixc_c_vm_on: + mov [EBX + Chn_PrevVM], AL + mov EAX, [EBX + Chn_Freq] + cmp EAX, 535232 ;Highest linear freq + jbe qmixc_c_freqok + mov EAX, 535232 +qmixc_c_freqok: + mov EDX, EAX + shr EDX, 16 + shl EAX, 16 + div dword [_judas_mixrate] + mov word [fractadd + 2], AX + shr EAX, 16 + mov [integeradd], EAX + test byte [EBX + Chn_VoiceMode], VM_16BIT ;Is sampledata 16bit? + jnz near qmixc_c_16bit ;Jump if yes. + test byte [_judas_mixmode], STEREO ;No, 8bit. Stereo audio? + jz qmixc_c_mono_8bit ;Jump if mono. +qmixc_c_stereo_8bit: +qmixc_c_mono_8bit: ;Mix in stereo, even if mono output + q_volpan14 + q_mixloop8 q_mix8_stereo_c + ret +qmixc_c_16bit: + test byte [_judas_mixmode], STEREO ;It's 16bit. Stereo? + jz qmixc_c_mono_16bit +qmixc_c_stereo_16bit: +qmixc_c_mono_16bit: ;Mix in stereo, even if mono output + q_volpan14 + q_mixloop16 q_mix16_stereo_c +qmixc_c_quit: ret + +;Safety mixer for calls from c program + +safemixer_: +_safemixer: + cmp dword [_judas_mixersys], 0 + je safemixer_helldontcall + cmp byte [_judas_initialized], 0 + je safemixer_helldontcall + pushad + call dword [_judas_mixersys] + popad +safemixer_helldontcall: + ret + +judas_code_lock_end_: +_judas_code_lock_end: +; end diff --git a/JUDASCFG.H b/JUDASCFG.H new file mode 100644 index 0000000..36ea4e5 --- /dev/null +++ b/JUDASCFG.H @@ -0,0 +1,53 @@ +/* + * Configurable things. + * + * REMEMBER: + * If you change CHANNELS or SIGNIFICANT_BITS or quality mixer constants, write + * the changes also to JUDASCFG.INC. When finished with configuring, type + * wmake /a to completely re-compile the JUDAS library. + */ + +/* + * How many times in second the DMA buffer goes round. The smaller this is, + * the larger the buffer and the more there is delay in sound effects, but then + * judas_update() doesn't have to be called so often. DMA buffer maximum size + * (64K) sets a limit on the minimum value though. Don't fear, judas_init() + * checks the buffer size and will simply limit it to 64K. + */ +#define PER_SECOND 15 + +/* + * Number of digital channels. Increase or decrease according to your needs. + * If it's below 32, all tunes can't be loaded. If it's above 32, you can + * play 32-channel tunes and even have sound fx at the same time! + * Number of channels will affect mixing speed just a tiny bit, however an + * unused channel takes EXTREMELY little time compared to a used one. + */ +#define CHANNELS 32 + +/* SIGNIFICANT_BITS_16 + * Number of significant bits in mixing for fast mixer. Maximum is 16, because + * fast mixer uses 16 bit mixing. If it's below 16, the leftover bits are used + * for clipping. At the current setting 14, 2 bits are used for clipping, so + * you can distort sound up to 4x without getting an overflow (nasty-sounding + * distortion.) With 13 bits, you have 3 bits for clipping and can distort up + * to 8x etc. + * + * SIGNIFICANT_BITS_32 (Fixed) + * Number of significant bits in mixing for quality mixer. Maximum is 32. + * However, DON'T CHANGE IT, because the routines of quality mixer only work + * correctly if it's 30. Why is it here then? - Just to let you know how many + * significant bits quality mixer uses. + */ +#define SIGNIFICANT_BITS_16 14 +#define SIGNIFICANT_BITS_32 30 + +/* Quality mixer constants in judascfg.inc + * + * QMIXER_STARTMUTE defines how much the starts of sounds are muted. + * 0 = no muting, 14 = zero volume, 2 = good; + * QMIXER_ZEROLEVELDECAY defines how slow the shifted zero level gets back to + * it's original position. 0 = instantly, 32 = never, 7 = good; + * QMIXER_VOLUMESMOOTH defines how much volume changes are smoothed. 0 = not + * at all, 22 = prevent volume changes, 6 = good; + */ diff --git a/JUDASCFG.INC b/JUDASCFG.INC new file mode 100644 index 0000000..8beaffe --- /dev/null +++ b/JUDASCFG.INC @@ -0,0 +1,6 @@ +%define CHANNELS 32 +%define SIGNIFICANT_BITS_16 14 +%define SIGNIFICANT_BITS_32 30 +;%define QMIXER_STARTMUTE 2 If this line is disabled, there will be no start muting! +%define QMIXER_ZEROLEVELDECAY 7 +%define QMIXER_VOLUMESMOOTH 6 diff --git a/JUDASDMA.C b/JUDASDMA.C new file mode 100644 index 0000000..de549f6 --- /dev/null +++ b/JUDASDMA.C @@ -0,0 +1,73 @@ +/* + * JUDAS DMA allocation. + */ + + /* + * Borland / Watcom REGS structure compatibility + */ +#ifdef __DJGPP__ +#include +#include +#include +#include +#define _BORLAND_DOS_REGS +#endif + +#include +#include +#include "judasmem.h" + +#define DMA_MAXSIZE 65536 + +int dma_reserve(int size); + +unsigned dma_address; +static char dma_initialized = 0; + +int dma_reserve(int size) +{ + #ifdef __DJGPP__ + int selector; + #else + static union REGS glenregs; + #endif + + if (dma_initialized) return 1; + + /* Round size upward to paragraph limit */ + size += 15; + size &= 0xfffffff0; + + /* Limit size to maximum */ + if (size > DMA_MAXSIZE) size = DMA_MAXSIZE; + + /* Buffer address will be used from interrupt, lock it! */ + if (!judas_memlock(&dma_address, sizeof (dma_address))) return 0; + + /* Use DPMI functions because _dos_allocmem() doesn't work reliably */ + #ifdef __DJGPP__ + if (__dpmi_allocate_dos_memory((size * 2) >> 4, &selector) == -1) return 0; + __dpmi_get_segment_base_address(selector, (unsigned long *)&dma_address); + + #else + glenregs.w.ax = 0x100; + glenregs.w.bx = (size * 2) >> 4; + int386(0x31, &glenregs, &glenregs); + if (glenregs.w.cflag) return 0; + glenregs.w.ax = 0x6; + glenregs.w.bx = glenregs.w.dx; + int386(0x31, &glenregs, &glenregs); + if (glenregs.w.cflag) return 0; + dma_address = glenregs.w.cx << 16 | glenregs.w.dx; + #endif + + /* Check for DMA page wrap */ + if ((dma_address & 0xffff) > (0x10000 - size)) + { + dma_address += 65535; + dma_address &= 0xffff0000; + } + + dma_initialized = 1; + return 1; +} diff --git a/JUDASDMA.H b/JUDASDMA.H new file mode 100644 index 0000000..3166958 --- /dev/null +++ b/JUDASDMA.H @@ -0,0 +1,16 @@ +/* + * Internal header file: DMA functions (actually only dma_reserve() is in the + * JUDASDMA module, rest are coded in assembler!) + */ + +#define DMA_WRITE_ONESHOT 0x48 +#define DMA_READ_ONESHOT 0x44 +#define DMA_WRITE_LOOP 0x58 +#define DMA_READ_LOOP 0x54 +#define DMA_MAXSIZE 65536 + +extern unsigned dma_address; + +int dma_reserve(int size); +void dma_program(unsigned char mode, unsigned offset, unsigned length); +unsigned dma_query(); diff --git a/JUDASERR.H b/JUDASERR.H new file mode 100644 index 0000000..77636e5 --- /dev/null +++ b/JUDASERR.H @@ -0,0 +1,11 @@ +/* + * JUDAS error codes + */ +#define JUDAS_OK 0 +#define JUDAS_OPEN_ERROR 1 +#define JUDAS_READ_ERROR 2 +#define JUDAS_WRONG_FORMAT 3 +#define JUDAS_OUT_OF_MEMORY 4 +#define JUDAS_HARDWARE_ERROR 5 +#define JUDAS_ILLEGAL_CONFIG 6 +#define JUDAS_OUT_OF_CHANNELS 7 diff --git a/JUDASGUS.H b/JUDASGUS.H new file mode 100644 index 0000000..40b7177 --- /dev/null +++ b/JUDASGUS.H @@ -0,0 +1,259 @@ +/* + * Internal header file: Ultrasound shit from the SDK + */ +#define JOYSTICK_TIMER 0x201 /* 201 */ +#define JOYSTICK_DATA 0x201 /* 201 */ +#define GF1_MIDI_CTRL 0x100 /* 3X0 */ +#define GF1_MIDI_DATA 0x101 /* 3X1 */ + +#define GF1_PAGE 0x102 /* 3X2 */ +#define GF1_REG_SELECT 0x103 /* 3X3 */ +#define GF1_DATA_LOW 0x104 /* 3X4 */ +#define GF1_DATA_HI 0x105 /* 3X5 */ +#define GF1_IRQ_STAT 0x006 /* 2X6 */ +#define GF1_DRAM 0x107 /* 3X7 */ +#define GF1_MIX_CTRL 0x000 /* 2X0 */ +#define GF1_TIMER_CTRL 0x008 /* 2X8 */ +#define GF1_TIMER_DATA 0x009 /* 2X9 */ +#define GF1_IRQ_CTRL 0x00B /* 2XB */ + +/* The GF1 Hardware clock. */ +#define CLOCK_RATE 9878400L + +/* Mixer control bits. */ +#define ENABLE_LINE 0x01 +#define ENABLE_DAC 0x02 +#define ENABLE_MIC 0x04 + +/* interrupt controller 1 */ +#define CNTRL_8259 0x21 +#define OCR_8259 0x20 +#define EOI 0x20 +#define REARM3 0x2F3 +#define REARM5 0x2F5 + +/* interrupt controller 2 */ +#define CNTRL_M_8259 0x21 +#define CNTRL_M2_8259 0xA1 +#define OCR_2_8259 0xA0 +#define DMA_CONTROL 0x41 +#define SET_DMA_ADDRESS 0x42 +#define SET_DRAM_LOW 0x43 +#define SET_DRAM_HIGH 0x44 + +#define TIMER_CONTROL 0x45 +#define TIMER1 0x46 +#define TIMER2 0x47 + +#define SET_SAMPLE_RATE 0x48 +#define SAMPLE_CONTROL 0x49 + +#define SET_JOYSTICK 0x4B +#define MASTER_RESET 0x4C + +/* Voice register mapping. */ +#define SET_CONTROL 0x00 +#define SET_FREQUENCY 0x01 +#define SET_START_HIGH 0x02 +#define SET_START_LOW 0x03 +#define SET_END_HIGH 0x04 +#define SET_END_LOW 0x05 +#define SET_VOLUME_RATE 0x06 +#define SET_VOLUME_START 0x07 +#define SET_VOLUME_END 0x08 +#define SET_VOLUME 0x09 +#define SET_ACC_HIGH 0x0a +#define SET_ACC_LOW 0x0b +#define SET_BALANCE 0x0c +#define SET_VOLUME_CONTROL 0x0d +#define SET_VOICES 0x0e + +#define GET_CONTROL 0x80 +#define GET_FREQUENCY 0x81 +#define GET_START_HIGH 0x82 +#define GET_START_LOW 0x83 +#define GET_END_HIGH 0x84 +#define GET_END_LOW 0x85 +#define GET_VOLUME_RATE 0x86 +#define GET_VOLUME_START 0x87 +#define GET_VOLUME_END 0x88 +#define GET_VOLUME 0x89 +#define GET_ACC_HIGH 0x8a +#define GET_ACC_LOW 0x8b +#define GET_BALANCE 0x8c +#define GET_VOLUME_CONTROL 0x8d +#define GET_VOICES 0x8e +#define GET_IRQV 0x8f + +/******************************************************************** + * + * MIDI defines + * + *******************************************************************/ + +#define MIDI_RESET 0x03 +#define MIDI_ENABLE_XMIT 0x20 +#define MIDI_ENABLE_RCV 0x80 + +#define MIDI_RCV_FULL 0x01 +#define MIDI_XMIT_EMPTY 0x02 +#define MIDI_FRAME_ERR 0x10 +#define MIDI_OVERRUN 0x20 +#define MIDI_IRQ_PEND 0x80 + +/******************************************************************** + * + * JOYSTICK defines + * + *******************************************************************/ + +#define JOY_POSITION 0x0f +#define JOY_BUTTONS 0xf0 + +/******************************************************************** + * + * GF1 irq/dma programmable latches + * + *******************************************************************/ + +/* GF1_IRQ_STATUS (port 3X6) */ +#define MIDI_TX_IRQ 0x01 /* pending MIDI xmit IRQ */ +#define MIDI_RX_IRQ 0x02 /* pending MIDI recv IRQ */ +#define GF1_TIMER1_IRQ 0x04 /* general purpose timer */ +#define GF1_TIMER2_IRQ 0x08 /* general purpose timer */ +#define WAVETABLE_IRQ 0x20 /* pending wavetable IRQ */ +#define ENVELOPE_IRQ 0x40 /* pending volume envelope IRQ */ +#define DMA_TC_IRQ 0x80 /* pending dma tc IRQ */ + + +/* GF1_MIX_CTRL (port 2X0) */ +#define ENABLE_LINE_IN 0x01 /* 0=enable */ +#define ENABLE_OUTPUT 0x02 /* 0=enable */ +#define ENABLE_MIC_IN 0x04 /* 1=enable */ +#define ENABLE_GF1_IRQ 0x08 /* 1=enable */ +#define GF122 0x10 /* ?? */ +#define ENABLE_MIDI_LOOP 0x20 /* 1=enable loop back */ +#define SELECT_GF1_REG 0x40 /* 0=irq latches */ + /* 1=dma latches */ + +/******************************************************************** + * + * GF1 global registers ($41-$4C) + * + *******************************************************************/ + +/* DMA control register */ +#define DMA_ENABLE 0x01 +#define DMA_READ 0x02 /* 1=read,0=write */ +#define DMA_WIDTH_16 0x04 /* 1=16 bit,0=8 bit (dma chan width)*/ +#define DMA_RATE 0x18 /* 00=fast, 11=slow */ +#define DMA_IRQ_ENABLE 0x20 /* 1=enable */ +#define DMA_IRQ_PENDING 0x40 /* read */ +#define DMA_DATA_16 0x40 /* write (data width) */ +#define DMA_TWOS_COMP 0x80 /* 1=do twos comp */ + +/* These are the xfer rate bits ... */ +#define DMA_R0 0x00 /* Fastest DMA xfer (~650khz) */ +#define DMA_R1 0x08 /* fastest / 2 */ +#define DMA_R2 0x10 /* fastest / 4 */ +#define DMA_R3 0x18 /* Slowest DMA xfer (fastest / 8) */ + +/* SAMPLE control register */ +#define ENABLE_ADC 0x01 +#define ADC_MODE 0x02 /* 0=mono, 1=stereo */ +#define ADC_DMA_WIDTH 0x04 /* 0=8 bit, 1=16 bit */ +#define ADC_IRQ_ENABLE 0x20 /* 1=enable */ +#define ADC_IRQ_PENDING 0x40 /* 1=irq pending */ +#define ADC_TWOS_COMP 0x80 /* 1=do twos comp */ + +/* RESET control register */ +#define GF1_MASTER_RESET 0x01 /* 0=hold in reset */ +#define GF1_OUTPUT_ENABLE 0x02 /* enable output */ +#define GF1_MASTER_IRQ 0x04 /* master IRQ enable */ + +/******************************************************************** + * + * GF1 voice specific registers ($00 - $0E and $80-$8f) + * + *******************************************************************/ + +/* ($0,$80) Voice control register */ +#define VOICE_STOPPED 0x01 /* voice has stopped */ +#define STOP_VOICE 0x02 /* stop voice */ +#define VC_DATA_TYPE 0x04 /* 0=8 bit,1=16 bit */ +#define VC_LOOP_ENABLE 0x08 /* 1=enable */ +#define VC_BI_LOOP 0x10 /* 1=bi directional looping */ +#define VC_WAVE_IRQ 0x20 /* 1=enable voice's wave irq */ +#define VC_DIRECT 0x40 /* 0=increasing,1=decreasing */ +#define VC_IRQ_PENDING 0x80 /* 1=wavetable irq pending */ + +/* ($1,$81) Frequency control */ +/* Bit 0 - Unused */ +/* Bits 1-9 - Fractional portion */ +/* Bits 10-15 - Integer portion */ + +/* ($2,$82) Accumulator start address (high) */ +/* Bits 0-11 - HIGH 12 bits of address */ +/* Bits 12-15 - Unused */ + +/* ($3,$83) Accumulator start address (low) */ +/* Bits 0-4 - Unused */ +/* Bits 5-8 - Fractional portion */ +/* Bits 9-15 - Low 7 bits of integer portion */ + +/* ($4,$84) Accumulator end address (high) */ +/* Bits 0-11 - HIGH 12 bits of address */ +/* Bits 12-15 - Unused */ + +/* ($5,$85) Accumulator end address (low) */ +/* Bits 0-4 - Unused */ +/* Bits 5-8 - Fractional portion */ +/* Bits 9-15 - Low 7 bits of integer portion */ + + +/* ($6,$86) Volume Envelope control register */ +#define VL_RATE_MANTISSA 0x3f +#define VL_RATE_RANGE 0xC0 + +/* ($7,$87) Volume envelope start */ +#define VL_START_MANT 0x0F +#define VL_START_EXP 0xF0 + +/* ($8,$88) Volume envelope end */ +#define VL_END_MANT 0x0F +#define VL_END_EXP 0xF0 + +/* ($9,$89) Current volume register */ +/* Bits 0-3 are unused */ +/* Bits 4-11 - Mantissa of current volume */ +/* Bits 10-15 - Exponent of current volume */ + +/* ($A,$8A) Accumulator value (high) */ +/* Bits 0-12 - HIGH 12 bits of current position (a19-a7) */ + +/* ($B,$8B) Accumulator value (low) */ +/* Bits 0-8 - Fractional portion */ +/* Bits 9-15 - Integer portion of low adress (a6-a0) */ + +/* ($C,$8C) Pan (balance) position */ +/* Bits 0-3 - Balance position 0=full left, 0x0f=full right */ + +/* ($D,$8D) Volume control register */ +#define VOLUME_STOPPED 0x01 /* volume has stopped */ +#define STOP_VOLUME 0x02 /* stop volume */ +#define VC_ROLLOVER 0x04 /* Roll PAST end & gen IRQ */ +#define VL_LOOP_ENABLE 0x08 /* 1=enable */ +#define VL_BI_LOOP 0x10 /* 1=bi directional looping */ +#define VL_WAVE_IRQ 0x20 /* 1=enable voice's wave irq */ +#define VL_DIRECT 0x40 /* 0=increasing,1=decreasing */ +#define VL_IRQ_PENDING 0x80 /* 1=wavetable irq pending */ + +/* ($E,$8E) # of Active voices */ +/* Bits 0-5 - # of active voices -1 */ + +/* ($F,$8F) - Sources of IRQs */ +/* Bits 0-4 - interrupting voice number */ +/* Bit 5 - Always a 1 */ +#define VOICE_VOLUME_IRQ 0x40 /* individual voice irq bit */ +#define VOICE_WAVE_IRQ 0x80 /* individual waveform irq bit */ + diff --git a/JUDASGUS.INC b/JUDASGUS.INC new file mode 100644 index 0000000..89e0039 --- /dev/null +++ b/JUDASGUS.INC @@ -0,0 +1,218 @@ +; Ugly shit from GUS SDK + + %define GF1_MIDI_CTRL 100h ; 3X0 + %define GF1_MIDI_DATA 101h ; 3X1 + %define GF1_PAGE 102h ; 3X2 + %define GF1_REG_SELECT 103h ; 3X3 + %define GF1_DATA_LOW 104h ; 3X4 + %define GF1_DATA_HI 105h ; 3X5 + %define GF1_IRQ_STAT 006h ; 2X6 + %define GF1_DRAM 107h ; 3X7 + %define GF1_MIX_CTRL 000h ; 2X0 + %define GF1_TIMER_CTRL 008h ; 2X8 + %define GF1_TIMER_DATA 009h ; 2X9 + %define GF1_IRQ_CTRL 00Bh ; 2XB + +; The GF1 Hardware clock. + %define CLOCK_RATE 9878400 + +; Mixer control bits. + %define ENABLE_LINE 01h + %define ENABLE_DAC 02h + %define ENABLE_MIC 04h + + %define DMA_CONTROL 41h + %define SET_DMA_ADDRESS 42h + %define SET_DRAM_LOW 43h + %define SET_DRAM_HIGH 44h + %define TIMER_CONTROL 45h + %define TIMER1 46h + %define TIMER2 47h + %define SET_SAMPLE_RATE 48h + %define SAMPLE_CONTROL 49h + %define SET_JOYSTICK 4Bh + %define MASTER_RESET 4Ch + +; Voice register mapping. + %define SET_CONTROL 00h + %define SET_FREQUENCY 01h + %define SET_START_HIGH 02h + %define SET_START_LOW 03h + %define SET_END_HIGH 04h + %define SET_END_LOW 05h + %define SET_VOLUME_RATE 06h + %define SET_VOLUME_START 07h + %define SET_VOLUME_END 08h + %define SET_VOLUME 09h + %define SET_ACC_HIGH 0ah + %define SET_ACC_LOW 0bh + %define SET_BALANCE 0ch + %define SET_VOLUME_CONTROL 0dh + %define SET_VOICES 0eh + + %define GET_CONTROL 80h + %define GET_FREQUENCY 81h + %define GET_START_HIGH 82h + %define GET_START_LOW 83h + %define GET_END_HIGH 84h + %define GET_END_LOW 85h + %define GET_VOLUME_RATE 86h + %define GET_VOLUME_START 87h + %define GET_VOLUME_END 88h + %define GET_VOLUME 89h + %define GET_ACC_HIGH 8ah + %define GET_ACC_LOW 8bh + %define GET_BALANCE 8ch + %define GET_VOLUME_CONTROL 8dh + %define GET_VOICES 8eh + %define GET_IRQV 8fh + + %define MIDI_RESET 3h + +;******************************************************************* +;* +;* GF1 irq/dma programmable latches +;* +;****************************************************************** + +; GF1_IRQ_STATUS (port 3X6) + %define MIDI_TX_IRQ 01h ; pending MIDI xmit IRQ + %define MIDI_RX_IRQ 02h ; pending MIDI recv IRQ + %define GF1_TIMER1_IRQ 04h ; general purpose timer + %define GF1_TIMER2_IRQ 08h ; general purpose timer + %define WAVETABLE_IRQ 20h ; pending wavetable IRQ + %define ENVELOPE_IRQ 40h ; pending volume envelope IRQ + %define DMA_TC_IRQ 80h ; pending dma tc IRQ + + +; GF1_MIX_CTRL (port 2X0) + %define ENABLE_LINE_IN 01h ; 0=enable + %define ENABLE_OUTPUT 02h ; 0=enable + %define ENABLE_MIC_IN 04h ; 1=enable + %define ENABLE_GF1_IRQ 08h ; 1=enable + %define GF122 10 ; ?? + %define ENABLE_MIDI_LOOP 20h ; 1=enable loop back + %define SELECT_GF1_REG 40h ; 1=irq latches + ; 0=dma latches + +;******************************************************************* +;* +;* GF1 global registers ($41-$4C) +;* +;******************************************************************* + +; DMA control register + %define DMA_ENABLE 01h + %define DMA_READ 02h ; 1=read,0=write + %define DMA_WIDTH_16 04h ; 1=16 bit,0=8 bit (dma chan width) + %define DMA_RATE 18h ; 00=fast, 11=slow + %define DMA_IRQ_ENABLE 20h ; 1=enable + %define DMA_IRQ_PENDING 40h ; read + %define DMA_DATA_16 40h ; write (data width) + %define DMA_TWOS_COMP 80h ; 1=do twos comp + +; These are the xfer rate bits ... + %define DMA_R0 00h ; Fastest DMA xfer (~650khz) + %define DMA_R1 08h ; fastest / 2 + %define DMA_R2 10h ; fastest / 4 + %define DMA_R3 18h ; Slowest DMA xfer (fastest / 8) + +; SAMPLE control register + %define ENABLE_ADC 01h + %define ADC_MODE 02h ; 0=mono, 1=stereo + %define ADC_DMA_WIDTH 04h ; 0=8 bit, 1=16 bit + %define ADC_IRQ_ENABLE 20h ; 1=enable + %define ADC_IRQ_PENDING 40h ; 1=irq pending + %define ADC_TWOS_COMP 80h ; 1=do twos comp + +; RESET control register + %define GF1_MASTER_RESET 01h ; 0=hold in reset + %define GF1_OUTPUT_ENABLE 02h ; enable output + %define GF1_MASTER_IRQ 04h ; master IRQ enable + +;******************************************************************* +;* +;* GF1 voice specific registers ($00 - $0E and $80-$8f) +;* +;****************************************************************** + +; ($0,$80) Voice control register + %define VOICE_STOPPED 01h ; voice has stopped + %define STOP_VOICE 02h ; stop voice + %define VC_DATA_TYPE 04h ; 0=8 bit,1=16 bit + %define VC_LOOP_ENABLE 08h ; 1=enable + %define VC_BI_LOOP 10h ; 1=bi directional looping + %define VC_WAVE_IRQ 20h ; 1=enable voice's wave irq + %define VC_DIRECT 40h ; 0=increasing,1=decreasing + %define VC_IRQ_PENDING 80h ; 1=wavetable irq pending + +; ($1,$81) Frequency control +; Bit 0 - Unused +; Bits 1-9 - Fractional portion +; Bits 10-15 - Integer portion + +; ($2,$82) Accumulator start address (high) +; Bits 0-11 - HIGH 12 bits of address +; Bits 12-15 - Unused + +; ($3,$83) Accumulator start address (low) +; Bits 0-4 - Unused +; Bits 5-8 - Fractional portion +; Bits 9-15 - Low 7 bits of integer portion + +; ($4,$84) Accumulator end address (high) +; Bits 0-11 - HIGH 12 bits of address +; Bits 12-15 - Unused + +; ($5,$85) Accumulator end address (low) +; Bits 0-4 - Unused +; Bits 5-8 - Fractional portion +; Bits 9-15 - Low 7 bits of integer portion + + +; ($6,$86) Volume Envelope control register + %define VL_RATE_MANTISSA 3fh + %define VL_RATE_RANGE 0C0h + +; ($7,$87) Volume envelope start + %define VL_START_MANT 0Fh + %define VL_START_EXP 0F0h + +; ($8,$88) Volume envelope end + %define VL_END_MANT 0Fh + %define VL_END_EXP 0F0h + +; ($9,$89) Current volume register +; Bits 0-3 are unused +; Bits 4-11 - Mantissa of current volume +; Bits 10-15 - Exponent of current volume + +; ($A,$8A) Accumulator value (high) +; Bits 0-12 - HIGH 13 bits of current position (a19-a7) + +; ($B,$8B) Accumulator value (low) +; Bits 0-8 - Fractional portion +; Bits 9-15 - Integer portion of low adress (a6-a0) + +; ($C,$8C) Pan (balance) position +; Bits 0-3 - Balance position 0=full left, = 0f=full right + +; ($D,$8D) Volume control register + %define VOLUME_STOPPED 01h ; volume has stopped + %define STOP_VOLUME 02h ; stop volume + %define VC_ROLLOVER 04h ; Roll PAST end & gen IRQ + %define VL_LOOP_ENABLE 08h ; 1=enable + %define VL_BI_LOOP 10h ; 1=bi directional looping + %define VL_WAVE_IRQ 20h ; 1=enable voice's wave irq + %define VL_DIRECT 40h ; 0=increasing,1=decreasing + %define VL_IRQ_PENDING 80h ; 1=wavetable irq pending + +; ($E,$8E) # of Active voices +; Bits 0-5 - # of active voices -1 + +; ($F,$8F) - Sources of IRQs +; Bits 0-4 - interrupting voice number +; Bit 5 - Always a 1 + %define VOICE_VOLUME_IRQ 40h ; individual voice irq bit + %define VOICE_WAVE_IRQ 80h ; individual waveform irq bit + diff --git a/JUDASIO.C b/JUDASIO.C new file mode 100644 index 0000000..0997d5b --- /dev/null +++ b/JUDASIO.C @@ -0,0 +1,55 @@ +/* + * JUDAS file I/O functions. Used by sample/music loading. If you + * use a datafile system and/or compression in your program, just modify these + * functions appropriately. JUDAS needs to do the following: + * - open file (in readonly binary mode) + * - read from file + * - seek + * - close file + */ + +#include +#include +#include + +#ifdef __DJGPP__ +#include /* for mode definitions */ +#include /* compatibility mode */ +#endif + +int judas_open(char *name); +int judas_seek(int handle, int bytes, int whence); +int judas_read(int handle, void *buffer, int size); +void judas_close(int handle); + +/* + * Returns nonnegative file handle if successful, -1 on error + */ +int judas_open(char *name) +{ + return open(name, O_RDONLY | O_BINARY); +} + +/* + * Returns file position after seek or -1 on error + */ +int judas_seek(int handle, int bytes, int whence) +{ + return lseek(handle, bytes, whence); +} + +/* + * Returns number of bytes actually read, -1 on error + */ +int judas_read(int handle, void *buffer, int size) +{ + return read(handle, buffer, size); +} + +/* + * Returns nothing + */ +void judas_close(int handle) +{ + close(handle); +} diff --git a/JUDASLIB.A b/JUDASLIB.A new file mode 100644 index 0000000000000000000000000000000000000000..448e0ef200768b93d2cd69639753157c08b650bf GIT binary patch literal 334152 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 z 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%0A$AgdnsL^bEGLi0KrlNeKIlM`l%`#} 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%0Aqffk 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 literal 0 HcmV?d00001 diff --git a/JUDASMEM.C b/JUDASMEM.C new file mode 100644 index 0000000..5ac31f7 --- /dev/null +++ b/JUDASMEM.C @@ -0,0 +1,239 @@ +/* + * JUDAS DPMI memory locking functions. Includes also functions to + * allocate/free locked memory. Feel free to use in your own proggys. + */ + +#define FALSE 0 +#define TRUE 1 +#define NULL 0 + +/* + * Borland / Watcom REGS structure compatibility + */ +#ifdef __DJGPP__ +#define _BORLAND_DOS_REGS +#endif + +#include +#include +#include +#include // temporary +#include +#ifdef __DJGPP__ +#include +#include +#include +#include +#endif + +int judas_memlock(void *start, unsigned size); +int judas_memunlock(void *start, unsigned size); +void *locked_malloc(int size); +void locked_free(void *address); +void *dos_malloc(int size); +void dos_free(void *address); +int DPMI_MapMemory (unsigned long *physaddress, unsigned long *linaddress, unsigned long size); +int DPMI_UnmapMemory (unsigned long *linaddress); + + +/* + * We allocate 4 bytes more and store the block size before the actual block + * passed to user. Afterwards, when freeing, we know the size (needed in + * unlocking!) + */ +void *locked_malloc(int size) +{ + unsigned *block = malloc(size + sizeof(unsigned)); + if (!block) return NULL; + *block = size; + if (!judas_memlock(block, size + sizeof(unsigned))) + { + free(block); + return NULL; + } + block++; + return block; +} + +void locked_free(void *address) +{ + unsigned *block; + + if (!address) return; + block = (unsigned *)address - 1; + judas_memunlock(block, *block); + free(block); +} + +int judas_memlock(void *start, unsigned size) +{ + unsigned *address; + + #ifdef __DJGPP__ + __dpmi_meminfo dpmimeminfo; + + __dpmi_get_segment_base_address(_my_ds(), (unsigned long *)&address); + address = (unsigned *)((unsigned char*)address + (unsigned)start); + dpmimeminfo.address = (unsigned long)address; + dpmimeminfo.size = (unsigned long)size; + if (__dpmi_lock_linear_region(&dpmimeminfo) == -1) return 0; + else return 1; + + + #else + union REGS glenregs; + + address = (unsigned *)start; + memset(&glenregs, 0, sizeof(glenregs)); + glenregs.w.ax = 0x600; + glenregs.w.bx = (unsigned)address >> 16; + glenregs.w.cx = (unsigned)address & 0xffff; + glenregs.w.si = size >> 16; + glenregs.w.di = size & 0xffff; + int386(0x31, &glenregs, &glenregs); + if (glenregs.w.cflag) return 0; + return 1; + #endif +} + +int judas_memunlock(void *start, unsigned size) +{ + unsigned *address; + + #ifdef __DJGPP__ + __dpmi_meminfo dpmimeminfo; + + __dpmi_get_segment_base_address(_my_ds(), (unsigned long *)&address); + address = (unsigned *)((unsigned char*)address + (unsigned)start); + dpmimeminfo.address = (unsigned long)address; + dpmimeminfo.size = (unsigned long)size; + if (__dpmi_unlock_linear_region(&dpmimeminfo) == -1) return 0; + else return 1; + + #else + union REGS glenregs; + + address = (unsigned *)start; + memset(&glenregs, 0, sizeof(glenregs)); + glenregs.w.ax = 0x601; + glenregs.w.bx = (unsigned)address >> 16; + glenregs.w.cx = (unsigned)address & 0xffff; + glenregs.w.si = size >> 16; + glenregs.w.di = size & 0xffff; + int386(0x31, &glenregs, &glenregs); + if (glenregs.w.cflag) return 0; + return 1; + #endif +} + +void *dos_malloc(int size) +{ + unsigned *address; + + #ifdef __DJGPP__ + int selector; + + /* allocate 16 bytes more to store selector of allocated block and align black on para */ + if (__dpmi_allocate_dos_memory(((size + 16) >> 4) + 1, &selector) == -1) return 0; + __dpmi_get_segment_base_address(selector, (unsigned long *)&address); + _farpokel (_dos_ds, (unsigned) address, selector); + address += 4; + return address; + + #else + union REGS glenregs; + + /* allocate 16 bytes more to store selector of allocated block and align block on parag */ + memset(&glenregs, 0, sizeof(glenregs)); + glenregs.w.ax = 0x100; + glenregs.w.bx = (unsigned) (((size + 16) >> 4) + 1); + int386(0x31, &glenregs, &glenregs); + if (glenregs.w.cflag) return 0; + address = (unsigned *) (glenregs.w.ax << 4); + *address = (unsigned) glenregs.w.dx; + address += 4; + return address; + #endif +} + +void dos_free(void *address) +{ + #ifdef __DJGPP__ + __dpmi_free_dos_memory (_farpeekl (_dos_ds, (unsigned) ((unsigned *)address - 4) ) ); + + #else + union REGS glenregs; + + memset(&glenregs, 0, sizeof(glenregs)); + glenregs.w.ax = 0x101; + glenregs.w.dx = (unsigned) *((unsigned *)address - 4); + int386(0x31, &glenregs, &glenregs); + #endif +} + +int DPMI_MapMemory (unsigned long *physaddress, unsigned long *linaddress, unsigned long size) +{ + #ifdef __DJGPP__ + __dpmi_meminfo dpmimeminfo; + unsigned *address; + + __dpmi_get_segment_base_address(_my_ds(), (unsigned long *)&address); +// printf ("DS base Address = %#06x\n", (unsigned int)address); + + dpmimeminfo.address = (unsigned long)*physaddress; + dpmimeminfo.size = (unsigned long)size; +// printf ("Physical Address = %#06x\n", (unsigned int)dpmimeminfo.address); + if (__dpmi_physical_address_mapping (&dpmimeminfo) == -1) { + *linaddress = 0; + return FALSE; + } +// printf ("Linear Address = %#06x\n", (unsigned int)dpmimeminfo.address); + dpmimeminfo.address -= (unsigned long)address; +// printf ("Final Address = %#06x\n", (unsigned int)dpmimeminfo.address); + *linaddress = (unsigned long)dpmimeminfo.address; + return TRUE; + + #else + union REGS r; + + memset(&r, 0, sizeof(r)); + r.x.ebx = (*physaddress) >> 16; + r.x.ecx = (*physaddress); + r.x.esi = size >> 16; + r.x.edi = size; + r.x.eax = 0x800; + int386 (0x31, &r, &r); + if (r.x.cflag) { + *linaddress = 0; + return FALSE; + } + *linaddress = (r.w.bx << 16) + r.w.cx; + return TRUE; + #endif +} + +int DPMI_UnmapMemory (unsigned long *linaddress) +{ + #ifdef __DJGPP__ + __dpmi_meminfo dpmimeminfo; + unsigned *address; + + __dpmi_get_segment_base_address(_my_ds(), (unsigned long *)&address); + memset(&dpmimeminfo, 0, sizeof(dpmimeminfo)); + dpmimeminfo.address = (unsigned long)*linaddress; + dpmimeminfo.address += (unsigned long)address; + if (__dpmi_free_physical_address_mapping (&dpmimeminfo) == -1) return FALSE; + return TRUE; + + #else + union REGS r; + + memset(&r, 0, sizeof(r)); + r.x.ebx = (*linaddress) >> 16; + r.x.ecx = (*linaddress); + r.x.eax = 0x801; + int386 (0x31, &r, &r); + if (r.x.cflag) return FALSE; + return TRUE; + #endif +} diff --git a/JUDASMEM.H b/JUDASMEM.H new file mode 100644 index 0000000..068c3a8 --- /dev/null +++ b/JUDASMEM.H @@ -0,0 +1,11 @@ +/* + * Internal header file: memory locking + */ +int judas_memlock(void *start, unsigned size); +int judas_memunlock(void *start, unsigned size); +void *locked_malloc(int size); +void locked_free(void *address); +void *dos_malloc(int size); +void dos_free(void *address); +int DPMI_MapMemory (unsigned long *physaddress, unsigned long *linaddress, unsigned long size); +int DPMI_UnmapMemory (unsigned long *linaddress); diff --git a/JUDASMOD.C b/JUDASMOD.C new file mode 100644 index 0000000..6fa9b46 --- /dev/null +++ b/JUDASMOD.C @@ -0,0 +1,1394 @@ +/* + * JUDAS 31-instrument mod loading/playing + * + * Changes: + * V2.02 Removed checking of read error in instrument loading + * Added end > length check in instrument loading. DALLAS.MOD works now! + * V2.03 Removed some weird printf instruction + * Moved tables only used by MODs here + * If there's a volslide with both up & down parameters nonzero must use + * the up parameter only! + * V2.04 Scaled channel default pannings to 1/2 original, no more hurting ears! + * Added panning command "E8?" (Undocumented, but CP supports it) + * Corrected behaviour of instrumentnumber without a note when followed + * by a note without instrumentnumber! + * V2.05 When playing a MOD and there's retrig without note then must ALWAYS + * retrig on tick 0! And E90 mustn't do anything, as well as a notedelay + * without a note! + * Changed also mt_patterns to unsigned because there can actually be + * 256 patterns if the song is saved with FT2 in MOD-format! + * V2.06 Phase of the sine vibrato rotated 180 degrees! + * Corrected behaviour of two pattern breaks on the same row + * Added song looping control. Moving to an already played song position + * is treated as looping. This may cause false alarms in some weird songs. + * Added support for vumeters (smp structure address provided). + * Added support for the more precise volume + * Added song rewinding/forwarding + * + */ + +#include +#include +#include +#include +#include "judas.h" +#include "judastbl.h" + +#ifdef __DJGPP__ +#define HANDLE_PRAGMA_PACK_PUSH_POP 1 +#endif + +/* + * A useful function by HEADLESS! + */ +#define HEU16_TO_UNSIGNED_SHORT(heu16) (((unsigned short) (heu16).high << 8) | (heu16).low) + +#define AMIGA_CLOCK 3579364 +#define MOD_INSTRUMENTS 31 +#define MOD_MAXLENGTH 128 +#define MOD_MAXCHANNELS 32 +#define MOD_INFOBLOCK 1084 +#define MOD_NAMEOFFSET 0 +#define MOD_INSTROFFSET 20 +#define MOD_LENGTHOFFSET 950 +#define MOD_ORDEROFFSET 952 +#define MOD_IDENTOFFSET 1080 + +/* + * Motorola format 16-bit integer + */ +#pragma pack(push,1) +typedef struct +{ + unsigned char high; + unsigned char low; +} HEU16; + +/* + * mod identification entry + */ +typedef struct +{ + char *string; + int channels; +} MOD_IDENT; + +/* + * Converted note structure + */ +typedef struct +{ + unsigned char note; + unsigned char instrument; + unsigned char command; + unsigned char data; +} NOTE; + +/* + * Mod instrument structure (in Motorola format...) + */ +typedef struct +{ + char name[22]; + HEU16 length; + unsigned char finetune; + char vol; + HEU16 repeat; + HEU16 replen; +} MOD_INSTRUMENT; +#pragma pack(pop) + +/* + * Our instrument structure + */ +typedef struct +{ + SAMPLE *smp; + char vol; + unsigned char finetune; +} INSTRUMENT; + +/* + * Track structure for mod playing + */ +typedef struct +{ + INSTRUMENT *ip; + unsigned char newnote; + unsigned char note; + unsigned char instrument; + unsigned char newinstrument; + unsigned char effect; + unsigned char effectdata; + unsigned char nybble1; + unsigned char nybble2; + unsigned char smp; + signed char finetune; + signed char vol; + short baseperiod; + short period; + short targetperiod; + unsigned char tp; + unsigned char tpspeed; + unsigned char volspeedup; + unsigned char volspeeddown; + unsigned char portaspeedup; + unsigned char portaspeeddown; + unsigned char vibratotype; + unsigned char vibratospeed; + unsigned char vibratodepth; + unsigned char vibratophase; + unsigned char sampleoffset; + unsigned char usesampleoffset; + unsigned char glissando; + unsigned char tremolotype; + unsigned char tremolospeed; + unsigned char tremolodepth; + unsigned char tremolophase; + unsigned char patternloopline; + unsigned char patternloopcount; + unsigned char retrigcount; +} TRACK; + +/* + * Prototypes + */ +int judas_loadmod(char *name); +void judas_freemod(void); +void judas_playmod(int rounds); +void judas_stopmod(void); +unsigned char judas_getmodpos(void); +unsigned char judas_getmodline(void); +unsigned char judas_getmodtick(void); +unsigned char judas_getmodchannels(void); +char *judas_getmodname(void); +void judas_forwardmod(void); +void judas_rewindmod(void); +static void judas_mutechannelsmod(); +void modplayer(void); +static int init_modplayer(void); +static void modplayer_code_lock_start(void); +static void modplayer_code_lock_end(void); +static void startnewnote(TRACK *tptr, CHANNEL *chptr); +static void extendedcommand(TRACK *tptr, CHANNEL *chptr); + +/* + * Mod indentification entries, don't know if anyone writes mods with + * for example 2 channels, but they can be created with FT2! + */ +static MOD_IDENT ident[] = +{ + {"2CHN", 2}, + {"M.K.", 4}, + {"M!K!", 4}, + {"4CHN", 4}, + {"6CHN", 6}, + {"8CHN", 8}, + {"10CH", 10}, + {"12CH", 12}, + {"14CH", 14}, + {"16CH", 16}, + {"18CH", 18}, + {"20CH", 20}, + {"22CH", 22}, + {"24CH", 24}, + {"26CH", 26}, + {"28CH", 28}, + {"30CH", 30}, + {"32CH", 32} +}; + +/* + * Panning table (AMIGA hardware emulation!) + */ +static int panningtable[] = +{ + 64, 191, 191, 64 +}; + +/* Period table for MODs, lowest octave (0) with all finetunes */ +unsigned short periodtable[16][12] = +{ + {6848, 6464, 6096, 5760, 5424, 5120, 4832, 4560, 4304, 4064, 3840, 3624}, + {6800, 6416, 6056, 5720, 5392, 5096, 4808, 4536, 4280, 4040, 3816, 3600}, + {6752, 6368, 6016, 5672, 5360, 5056, 4776, 4504, 4256, 4016, 3792, 3576}, + {6704, 6328, 5968, 5632, 5320, 5024, 4736, 4472, 4224, 3984, 3760, 3552}, + {6656, 6280, 5928, 5592, 5280, 4984, 4704, 4440, 4192, 3960, 3736, 3528}, + {6608, 6232, 5888, 5552, 5240, 4952, 4672, 4408, 4160, 3928, 3704, 3496}, + {6560, 6192, 5840, 5512, 5208, 4912, 4640, 4376, 4128, 3896, 3680, 3472}, + {6512, 6144, 5800, 5472, 5168, 4880, 4600, 4344, 4104, 3872, 3656, 3448}, + {7256, 6848, 6464, 6096, 5760, 5424, 5120, 4832, 4560, 4304, 4032, 3840}, + {7200, 6800, 6416, 6056, 5720, 5400, 5088, 4808, 4536, 4280, 4040, 3816}, + {7152, 6752, 6368, 6016, 5672, 5360, 5056, 4776, 4504, 4256, 4016, 3792}, + {7096, 6704, 6328, 5968, 5632, 5320, 5024, 4736, 4472, 4224, 3984, 3760}, + {7048, 6656, 6280, 5928, 5592, 5280, 4984, 4704, 4440, 4192, 3952, 3736}, + {7000, 6608, 6232, 5888, 5552, 5240, 4952, 4672, 4408, 4160, 3928, 3704}, + {6944, 6560, 6192, 5840, 5512, 5208, 4912, 4640, 4376, 4128, 3896, 3680}, + {6896, 6512, 6144, 5800, 5472, 5168, 4880, 4600, 4344, 4104, 3872, 3656} +}; + +/* Module infoblock pointer */ +static unsigned char *mod_info = NULL; + +/* Module patterns pointer */ +static unsigned char *mod_patterns = NULL; + +/* Module status stuff */ +static unsigned char mt_oldpos; +static unsigned char mt_pos; +static unsigned char mt_line; +static unsigned char mt_length; +static unsigned char mt_channels; +static unsigned char mt_ticktempo; +static unsigned char mt_tickcount; +static unsigned char mt_patternbreak; +static unsigned char mt_patterndelay; +static unsigned mt_patterns; +static int mt_patternlength; +static unsigned char *mt_order; +static char *mt_poshasbeenplayed; +static INSTRUMENT instrument[MOD_INSTRUMENTS]; +static TRACK track[MOD_MAXCHANNELS]; +static char modplayer_firsttime = 1; +static int mt_roundcounter; +static int mt_wind = 0; +static int mt_windingpause = 0; + +/* External variables */ +extern unsigned char judas_bpmtempo; +extern unsigned judas_bpmcount; +extern void (*judas_player)(void); + +void judas_forwardmod(void) +{ + mt_wind = 1; +} + +void judas_rewindmod(void) +{ + mt_wind = -1; +} + +int judas_loadmod(char *name) +{ + int count; + int handle; + NOTE *destptr; + unsigned char *srcptr; + + /* Don't waste memory if Nosound */ + judas_error = JUDAS_OK; + if (judas_device == DEV_NOSOUND) return 1; + + judas_error = JUDAS_OUT_OF_MEMORY; + if (modplayer_firsttime) + { + if (!init_modplayer()) return 0; + } + /* Unload previous mod */ + judas_freemod(); + + judas_error = JUDAS_OPEN_ERROR; + handle = judas_open(name); + if (handle == -1) return 0; + + judas_error = JUDAS_OUT_OF_MEMORY; + /* Get memory for the mod infoblock */ + mod_info = locked_malloc(MOD_INFOBLOCK); + if (!mod_info) + { + judas_close(handle); + return 0; + } + + judas_error = JUDAS_WRONG_FORMAT; + /* Read the infoblock */ + if (judas_read(handle, mod_info, MOD_INFOBLOCK) != MOD_INFOBLOCK) + { + judas_freemod(); + judas_close(handle); + return 0; + } + /* + * Clear byte 20 of header to prevent weird shit when printing + * modulename + */ + mod_info[20] = 0; + + /* Determine number of channels */ + mt_channels = 0; + for (count = 0; count < 18; count++) + { + if (!memcmp(ident[count].string, &mod_info[MOD_IDENTOFFSET], 4)) + mt_channels = ident[count].channels; + } + /* If none of these then it's a format we can't handle!!! */ + if (!mt_channels) + { + judas_freemod(); + judas_close(handle); + return 0; + } + /* + * Check that there aren't too many channels + */ + if (mt_channels > CHANNELS) + { + judas_error = JUDAS_OUT_OF_CHANNELS; + mt_channels = CHANNELS; + judas_freemod(); + judas_close(handle); + return 0; + } + /* Calculate patternlength */ + mt_patternlength = mt_channels * 256; + + /* Now search thru orderlist to find out amount of patterns */ + mt_length = mod_info[MOD_LENGTHOFFSET]; + mt_order = &mod_info[MOD_ORDEROFFSET]; + mt_poshasbeenplayed = locked_malloc(MOD_MAXLENGTH); + if (!mt_poshasbeenplayed) { + judas_freemod(); + judas_close(handle); + return 0; + } + mt_patterns = 0; + for (count = 0; count < MOD_MAXLENGTH; count++) + { + if (mt_order[count] > mt_patterns) mt_patterns = mt_order[count]; + } + mt_patterns++; + + judas_error = JUDAS_OUT_OF_MEMORY; + /* Reserve memory for patterns and load them */ + mod_patterns = locked_malloc(mt_patternlength * mt_patterns); + if (!mod_patterns) + { + judas_freemod(); + judas_close(handle); + return 0; + } + judas_error = JUDAS_READ_ERROR; + if (judas_read(handle, mod_patterns, mt_patternlength * mt_patterns) != mt_patternlength * mt_patterns) + { + judas_freemod(); + judas_close(handle); + return 0; + } + + /* Convert patterns into easier-to-read format */ + destptr = (NOTE *)mod_patterns; + srcptr = mod_patterns; + for (count = 0; count < mt_patternlength * mt_patterns / 4; count++) + { + /* + * Note: FT2 saves the 13th bit of period into 5th bit of + * samplenumber, and when loading it ofcourse cannot read + * the period back correctly! We don't use the 13th bit! + */ + unsigned short period = ((srcptr[0] & 0x0f) << 8) | srcptr[1]; + unsigned char note = 0, instrument, command; + if (period) + { + int findnote; + int offset = 0x7fffffff; + + for (findnote = 0; findnote < 96; findnote++) + { + if (abs(period - (periodtable[0][findnote % 12] >> (findnote / 12))) < offset) + { + note = findnote + 1; + offset = abs(period - (periodtable[0][findnote % 12] >> (findnote / 12))); + } + } + } + instrument = (srcptr[0] & 0xf0) | ((srcptr[2] & 0xf0) >> 4); + command = srcptr[2] & 0x0f; + destptr->note = note; + destptr->instrument = instrument; + destptr->command = command; + srcptr += 4; + destptr++; + } + + /* Now load instruments */ + { + INSTRUMENT *i_ptr = &instrument[0]; + MOD_INSTRUMENT *mod_instr_ptr = (MOD_INSTRUMENT *)(&mod_info[MOD_INSTROFFSET]); + + for (count = 0; count < MOD_INSTRUMENTS; count++) + { + int length, repeat, end; + unsigned char voicemode; + + length = HEU16_TO_UNSIGNED_SHORT(mod_instr_ptr->length) << 1; + repeat = HEU16_TO_UNSIGNED_SHORT(mod_instr_ptr->repeat) << 1; + end = HEU16_TO_UNSIGNED_SHORT(mod_instr_ptr->replen) << 1; + i_ptr->finetune = mod_instr_ptr->finetune; + i_ptr->vol = mod_instr_ptr->vol; + if (length) + { + if (end > 2) + { + voicemode = VM_LOOP | VM_ON; + end += repeat; + if (end > length) end = length; + } + else + { + voicemode = VM_ONESHOT | VM_ON; + end = length; + } + judas_error = JUDAS_OUT_OF_MEMORY; + i_ptr->smp = judas_allocsample(length); + if (!i_ptr->smp) + { + judas_freemod(); + judas_close(handle); + return 0; + } + judas_read(handle, i_ptr->smp->start, length); + i_ptr->smp->repeat = i_ptr->smp->start + repeat; + i_ptr->smp->end = i_ptr->smp->start + end; + i_ptr->smp->voicemode = voicemode; + judas_ipcorrect(i_ptr->smp); + } + else + { + i_ptr->smp = NULL; + } + i_ptr++; + mod_instr_ptr++; + } + } + /* Loading done successfully! */ + judas_error = JUDAS_OK; + judas_close(handle); + return 1; +} + +void judas_freemod(void) +{ + int count; + + if (modplayer_firsttime) + { + if (!init_modplayer()) return; + } + judas_stopmod(); + + /* Free infoblock & patterns */ + if (mod_info) + { + locked_free(mod_info); + mod_info = NULL; + } + if (mod_patterns) + { + locked_free(mod_patterns); + mod_patterns = NULL; + } + + /* Remove all samples used by song */ + for (count = 0; count < MOD_INSTRUMENTS; count++) + { + judas_freesample(instrument[count].smp); + instrument[count].smp = NULL; + } +} + +unsigned char judas_getmodpos(void) +{ + return mt_pos; +} + +unsigned char judas_getmodline(void) +{ + return mt_line; +} + +unsigned char judas_getmodtick(void) +{ + return mt_tickcount; +} + +unsigned char judas_getmodchannels(void) +{ + return mt_channels; +} + +char *judas_getmodname(void) +{ + if (mod_info) return &mod_info[MOD_NAMEOFFSET]; + else return NULL; +} + +/* + * This is called the first time when loading or unloading a mod. It clears + * all sample pointers to prevent weird shit and locks all code & data touched + * by the player routine. + */ +static int init_modplayer(void) +{ + int count; + + mt_channels = 0; + for (count = 0; count < MOD_INSTRUMENTS; count++) + { + instrument[count].smp = NULL; + } + if (!judas_locktables()) return 0; + if (!judas_memlock(&modplayer_code_lock_start, (int)&modplayer_code_lock_end - (int)&modplayer_code_lock_start)) return 0; + if (!judas_memlock(&periodtable[0][0], sizeof periodtable)) return 0; + if (!judas_memlock(&instrument[0], sizeof instrument)) return 0; + if (!judas_memlock(&track[0], sizeof track)) return 0; + if (!judas_memlock(&mod_info, sizeof mod_info)) return 0; + if (!judas_memlock(&mod_patterns, sizeof mod_patterns)) return 0; + if (!judas_memlock(&mt_oldpos, sizeof mt_oldpos)) return 0; + if (!judas_memlock(&mt_pos, sizeof mt_pos)) return 0; + if (!judas_memlock(&mt_line, sizeof mt_line)) return 0; + if (!judas_memlock(&mt_length, sizeof mt_length)) return 0; + if (!judas_memlock(&mt_channels, sizeof mt_channels)) return 0; + if (!judas_memlock(&mt_patterns, sizeof mt_patterns)) return 0; + if (!judas_memlock(&mt_ticktempo, sizeof mt_ticktempo)) return 0; + if (!judas_memlock(&mt_tickcount, sizeof mt_tickcount)) return 0; + if (!judas_memlock(&mt_patternlength, sizeof mt_patternlength)) return 0; + if (!judas_memlock(&mt_order, sizeof mt_order)) return 0; + if (!judas_memlock(&mt_poshasbeenplayed, sizeof mt_poshasbeenplayed)) return 0; + if (!judas_memlock(&mt_patternbreak, sizeof mt_patternbreak)) return 0; + if (!judas_memlock(&mt_patterndelay, sizeof mt_patterndelay)) return 0; + if (!judas_memlock(&mt_roundcounter, sizeof mt_roundcounter)) return 0; + if (!judas_memlock(&mt_wind, sizeof mt_wind)) return 0; + if (!judas_memlock(&mt_windingpause, sizeof mt_windingpause)) return 0; + modplayer_firsttime = 0; + return 1; +} + +static void modplayer_code_lock_start(void) +{ +} + +void judas_mutechannelsmod(void) +{ + int count; + TRACK *tptr = &track[0]; + CHANNEL *chptr = &judas_channel[0]; + + for (count = 0; count < mt_channels; count++) + { + chptr->smp = NULL; + chptr->voicemode = VM_OFF; + chptr->vol = 0; + chptr->panning = panningtable[count % 4]; + tptr->ip = &instrument[0]; + tptr->instrument = 0; + tptr->effect = 0; + tptr->effectdata = 0; + tptr->nybble1 = 0; + tptr->nybble2 = 0; + tptr->tp = 0; + tptr->tpspeed = 0; + tptr->volspeedup = 0; + tptr->volspeeddown = 0; + tptr->glissando = 0; + tptr->patternloopline = 0; + tptr->patternloopcount = 0; + tptr->retrigcount = 0; + tptr->sampleoffset = 0; + tptr->usesampleoffset = 0; + tptr->tremolotype = 0; + tptr->vibratotype = 0; + chptr++; + tptr++; + } +} + +void judas_playmod(int rounds) +{ + int count; + + if (!mod_info) return; + judas_player = NULL; + mt_roundcounter = rounds; + mt_wind = 0; + mt_pos = 0; + mt_line = 0; + mt_tickcount = 0; + mt_ticktempo = 6; + mt_patterndelay = 0; + judas_bpmcount = 0; + judas_bpmtempo = 125; + for (count = 1; count < MOD_MAXLENGTH; count++) + { + mt_poshasbeenplayed[count] = 0; + } + judas_mutechannelsmod(); + mt_poshasbeenplayed[0] = 1; + judas_player = &modplayer; +} + +void judas_stopmod(void) +{ + CHANNEL *chptr = &judas_channel[0]; + int count; + + if (mt_channels > CHANNELS) mt_channels = CHANNELS; + if (!mod_info) return; + judas_player = NULL; + for (count = 0; count < mt_channels; count++) + { + chptr->voicemode = VM_OFF; + chptr->smp = NULL; + chptr++; + } +} + +void modplayer(void) +{ + TRACK *tptr = &track[0]; + CHANNEL *chptr = &judas_channel[0]; + int count; + + /* + * Song winding + */ + if (mt_wind > 0) { + mt_wind = 0; + if (mt_pos < mt_length-1) { + int count; + mt_pos++; + mt_oldpos = mt_pos; + mt_line = 0; + mt_tickcount = 0; + mt_patterndelay = 0; + for (count = 0; count < MOD_MAXLENGTH; count++) + { + mt_poshasbeenplayed[count] = 0; + } + mt_poshasbeenplayed[mt_pos] = 1; + judas_mutechannelsmod(); + mt_windingpause = 1; + return; + } + } else + if (mt_wind < 0) { + mt_wind = 0; + if (mt_pos > 0) { + int count; + mt_pos--; + mt_oldpos = mt_pos; + mt_line = 0; + mt_tickcount = 0; + mt_patterndelay = 0; + for (count = 0; count < MOD_MAXLENGTH; count++) + { + mt_poshasbeenplayed[count] = 0; + } + mt_poshasbeenplayed[mt_pos] = 1; + judas_mutechannelsmod(); + mt_windingpause = 1; + return; + } + } + if (mt_windingpause) { + mt_windingpause = 0; + return; + } + + /* + * Set new notes or do something else? + */ + if ((!mt_tickcount) && (!mt_patterndelay)) + { + NOTE *noteptr; + + /* + * Beware of illegal patternnumbers + */ + if (mt_order[mt_pos] >= mt_patterns) return; + noteptr = (NOTE *)(mod_patterns + mt_order[mt_pos] * mt_patternlength + mt_line * mt_channels * sizeof(NOTE)); + mt_patternbreak = 0; + mt_oldpos = mt_pos; + for (count = mt_channels; count > 0; count--) + { + tptr->newnote = 0; + tptr->retrigcount = 0; + + /* + * Get note (if any) + */ + if (noteptr->note) + { + tptr->note = noteptr->note - 1; + tptr->newnote = 1; + } + /* + * Get effect, effect data etc. + */ + tptr->effect = noteptr->command; + tptr->effectdata = noteptr->data; + tptr->nybble1 = noteptr->data >> 4; + tptr->nybble2 = noteptr->data & 0xf; + tptr->newinstrument = noteptr->instrument; + + /* + * Set sampleoffset here + */ + if (tptr->newinstrument) tptr->usesampleoffset = 0; + if (tptr->effect == 0x9) + { + if (tptr->effectdata) tptr->sampleoffset = tptr->effectdata; + tptr->usesampleoffset = 1; + } + + /* + * Start new note if there is one; but check there + * isn't notedelay (toneportamento is handled by + * startnewnote()!) + */ + if ((tptr->effect != 0xe) || (tptr->nybble1 != 0xd) || (tptr->nybble2 == 0)) + { + if (tptr->newnote) startnewnote(tptr, chptr); + if (tptr->newinstrument) + { + tptr->instrument = tptr->newinstrument - 1; + tptr->ip = &instrument[tptr->instrument]; + tptr->vol = tptr->ip->vol; + chptr->vol = tptr->vol * 256; + } + } + + /* + * Reset period if not vibrato or toneportamento + */ + if ((tptr->effect < 0x3) || (tptr->effect > 0x6)) + { + tptr->period = tptr->baseperiod; + } + /* + * Reset volume if not tremolo + */ + if (tptr->effect != 0x7) + { + chptr->vol = tptr->vol * 256; + } + switch (tptr->effect) + { + case 0x0: + break; + + /* Set portamento speed up */ + case 0x1: + if (tptr->effectdata) tptr->portaspeedup = tptr->effectdata; + break; + + /* Set portamento speed down */ + case 0x2: + if (tptr->effectdata) tptr->portaspeeddown = tptr->effectdata; + break; + + /* Set TP. speed */ + case 0x3: + if (tptr->effectdata) tptr->tpspeed = tptr->effectdata; + break; + + /* Set vibrato */ + case 0x4: + if (tptr->nybble1) tptr->vibratospeed = tptr->nybble1; + if (tptr->nybble2) tptr->vibratodepth = tptr->nybble2; + break; + + /* Set tremolo */ + case 0x7: + if (tptr->nybble1) tptr->tremolospeed = tptr->nybble1; + if (tptr->nybble2) tptr->tremolodepth = tptr->nybble2; + break; + + /* Set Panning */ + case 0x8: + chptr->panning = tptr->effectdata; + break; + + /* Volume slide speed set */ + case 0x5: + case 0x6: + case 0xa: + if (tptr->effectdata) + { + tptr->volspeedup = tptr->nybble1; + tptr->volspeeddown = tptr->nybble2; + } + break; + + /* Pos. jump */ + case 0xb: + mt_line = 63; + mt_pos = tptr->effectdata - 1; + break; + + /* Set volume */ + case 0xc: + chptr->vol = tptr->effectdata * 256; + if (chptr->vol < 0) chptr->vol = 0; + if (chptr->vol > 64*256) chptr->vol = 64*256; + tptr->vol = chptr->vol / 256; + break; + + /* Pattern break */ + case 0xd: + mt_line = tptr->nybble1 * 10 + tptr->nybble2 - 1; + if (!mt_patternbreak) + { + mt_patternbreak = 1; + mt_pos++; + } + break; + + /* Extended command */ + case 0xe: + extendedcommand(tptr, chptr); + break; + + /* Set tempo */ + case 0xf: + if (!tptr->effectdata) + { + if (mt_roundcounter != 1) + { + judas_stopmod(); + judas_playmod(mt_roundcounter); + if (mt_roundcounter) mt_roundcounter--; + return; + } else { + judas_stopmod(); + return; + } + } + if (tptr->effectdata < 32) mt_ticktempo = tptr->effectdata; + else judas_bpmtempo = tptr->effectdata; + break; + } + if (tptr->period) chptr->freq = AMIGA_CLOCK / tptr->period; + noteptr++; + chptr++; + tptr++; + } + } + if (mt_tickcount) + { + /* + * If tick isn't 0, update effects like portamento & arpeggio + */ + for (count = mt_channels; count > 0; count--) + { + switch (tptr->effect) + { + /* Arpeggio */ + case 0x0: + { + if (tptr->effectdata) + { + char phase = mt_tickcount % 3; + switch (phase) + { + unsigned char arpnote; + + case 0: + tptr->period = tptr->baseperiod; + break; + + case 1: + arpnote = tptr->note + tptr->nybble1; + if (arpnote > 95) arpnote = 95; + tptr->period = periodtable[tptr->finetune][arpnote % 12] >> (arpnote / 12); + break; + + case 2: + arpnote = tptr->note + tptr->nybble2; + if (arpnote > 95) arpnote = 95; + tptr->period = periodtable[tptr->finetune][arpnote % 12] >> (arpnote / 12); + break; + } + } + } + break; + + /* Portamento up */ + case 0x1: + tptr->baseperiod -= tptr->portaspeedup; + if (tptr->baseperiod < 27) tptr->baseperiod = 27; + tptr->period = tptr->baseperiod; + break; + + /* Portamento down */ + case 0x2: + tptr->baseperiod += tptr->portaspeeddown; + if (tptr->baseperiod > 7256) tptr->baseperiod = 7256; + tptr->period = tptr->baseperiod; + break; + + /* Toneportamento */ + case 0x3: + if (tptr->tp) + { + if (tptr->baseperiod < tptr->targetperiod) + { + tptr->baseperiod += tptr->tpspeed; + if (tptr->baseperiod >= tptr->targetperiod) + { + tptr->baseperiod = tptr->targetperiod; + tptr->tp = 0; + } + } + if (tptr->baseperiod > tptr->targetperiod) + { + tptr->baseperiod -= tptr->tpspeed; + if (tptr->baseperiod <= tptr->targetperiod) + { + tptr->baseperiod = tptr->targetperiod; + tptr->tp = 0; + } + } + tptr->period = tptr->baseperiod; + if (tptr->glissando) + { + int offset = 0x7fffffff; + int sc; + short bestperiod = 0; + + for (sc = 0; sc < 96; sc++) + { + int newoffset = abs(tptr->period - (periodtable[tptr->finetune][sc % 12] >> (sc / 12))); + + if (newoffset < offset) + { + bestperiod = periodtable[tptr->finetune][sc % 12] >> (sc / 12); + offset = newoffset; + } + } + tptr->period = bestperiod; + } + } + break; + + /* Vibrato */ + case 0x4: + tptr->vibratophase += tptr->vibratospeed * 4; + tptr->period = tptr->baseperiod + ((vibratotable[vibratotypetable[tptr->vibratotype & 3]][tptr->vibratophase] * tptr->vibratodepth) >> 5); + if (tptr->period < 27) tptr->period = 27; + if (tptr->period > 7256) tptr->period = 7256; + break; + + /* Toneportamento + volslide */ + case 0x5: + if (tptr->tp) + { + if (tptr->baseperiod < tptr->targetperiod) + { + tptr->baseperiod += tptr->tpspeed; + if (tptr->baseperiod >= tptr->targetperiod) + { + tptr->baseperiod = tptr->targetperiod; + tptr->tp = 0; + } + } + if (tptr->baseperiod > tptr->targetperiod) + { + tptr->baseperiod -= tptr->tpspeed; + if (tptr->baseperiod <= tptr->targetperiod) + { + tptr->baseperiod = tptr->targetperiod; + tptr->tp = 0; + } + } + tptr->period = tptr->baseperiod; + if (tptr->glissando) + { + int offset = 0x7fffffff; + int sc; + short bestperiod = 0; + + for (sc = 0; sc < 96; sc++) + { + int newoffset = abs(tptr->period - (periodtable[tptr->finetune][sc % 12] >> (sc / 12))); + + if (newoffset < offset) + { + bestperiod = periodtable[tptr->finetune][sc % 12] >> (sc / 12); + offset = newoffset; + } + } + tptr->period = bestperiod; + } + } + if (tptr->volspeedup) + { + chptr->vol += tptr->volspeedup * 256; + if (chptr->vol > 64*256) chptr->vol = 64*256; + } + else + { + chptr->vol -= tptr->volspeeddown * 256; + if (chptr->vol < 0) chptr->vol = 0; + } + tptr->vol = chptr->vol / 256; + break; + + /* Vibrato + volslide */ + case 0x6: + tptr->vibratophase += tptr->vibratospeed * 4; + tptr->period = tptr->baseperiod + ((vibratotable[vibratotypetable[tptr->vibratotype & 3]][tptr->vibratophase] * tptr->vibratodepth) >> 5); + if (tptr->period < 27) tptr->period = 27; + if (tptr->period > 7256) tptr->period = 7256; + if (tptr->volspeedup) + { + chptr->vol += tptr->volspeedup * 256; + if (chptr->vol > 64*256) chptr->vol = 64*256; + } + else + { + chptr->vol -= tptr->volspeeddown * 256; + if (chptr->vol < 0) chptr->vol = 0; + } + tptr->vol = chptr->vol / 256; + break; + + /* Tremolo */ + case 0x7: + tptr->tremolophase += tptr->tremolospeed * 4; + chptr->vol = (tptr->vol + ((vibratotable[tptr->tremolotype & 3][tptr->tremolophase] * tptr->tremolodepth) >> 4)) * 256; + if (chptr->vol < 0) chptr->vol = 0; + if (chptr->vol > 64*256) chptr->vol = 64*256; + break; + + /* Volume Slide */ + case 0xa: + if (tptr->volspeedup) + { + chptr->vol += tptr->volspeedup * 256; + if (chptr->vol > 64*256) chptr->vol = 64*256; + } + else + { + chptr->vol -= tptr->volspeeddown * 256; + if (chptr->vol < 0) chptr->vol = 0; + } + tptr->vol = chptr->vol / 256; + break; + + /* Extended command */ + case 0xe: + extendedcommand(tptr, chptr); + break; + } + if (tptr->period) chptr->freq = AMIGA_CLOCK / tptr->period; + chptr++; + tptr++; + } + } + + /* + * Advance song + */ + mt_tickcount++; + if (mt_tickcount >= mt_ticktempo) + { + mt_tickcount = 0; + if (mt_patterndelay) + { + mt_patterndelay--; + } + if (!mt_patterndelay) + { + mt_line++; + if (mt_line >= 64) + { + mt_line = 0; + mt_pos++; + } + if (mt_pos >= mt_length) + { + mt_pos = 0; + mt_oldpos = mt_pos; + if (mt_roundcounter != 1) + { + int count; + if (mt_roundcounter) mt_roundcounter--; + for (count = 0; count < MOD_MAXLENGTH; count++) + { + mt_poshasbeenplayed[count] = 0; + } + mt_poshasbeenplayed[mt_pos] = 1; + } + else { + judas_stopmod(); + return; + } + } + /* + * Song looping detection & control! + */ + if (mt_pos != mt_oldpos) + { + if (mt_poshasbeenplayed[mt_pos]) + { + if (mt_roundcounter != 1) + { + int count; + if (mt_roundcounter) mt_roundcounter--; + for (count = 0; count < MOD_MAXLENGTH; count++) + { + mt_poshasbeenplayed[count] = 0; + } + mt_poshasbeenplayed[mt_pos] = 1; + } + else { + judas_stopmod(); + return; + } + } + else mt_poshasbeenplayed[mt_pos] = 1; + } + } + } + /* + * We're Done! + */ +} + +static void startnewnote(TRACK *tptr, CHANNEL *chptr) +{ + /* + * Change instrument if necessary + */ + if (tptr->newinstrument) + { + tptr->instrument = tptr->newinstrument - 1; + tptr->ip = &instrument[tptr->instrument]; + } + + /* + * Now we set the note on + */ + tptr->finetune = tptr->ip->finetune; + if (!(tptr->vibratotype & 4)) tptr->vibratophase = 0; + if (!(tptr->tremolotype & 4)) tptr->tremolophase = 0; + if ((tptr->effect == 0x3) || (tptr->effect == 0x5)) + { + /* + * Toneportamento + */ + tptr->targetperiod = periodtable[tptr->finetune][tptr->note % 12] >> (tptr->note / 12); + tptr->tp = 1; + } + else + { + /* + * Normal note start + */ + tptr->baseperiod = periodtable[tptr->finetune][tptr->note % 12] >> (tptr->note / 12); + tptr->period = tptr->baseperiod; + tptr->tp = 0; + if (tptr->ip->smp) + { + chptr->fractpos = 0; + chptr->repeat = tptr->ip->smp->repeat; + chptr->end = tptr->ip->smp->end; + chptr->voicemode = tptr->ip->smp->voicemode; + chptr->smp = tptr->ip->smp; + if (tptr->usesampleoffset) + { + chptr->pos = tptr->ip->smp->start + (tptr->sampleoffset << 8); + if (chptr->pos >= tptr->ip->smp->end) + { + if (chptr->voicemode & VM_LOOP) + { + chptr->pos = tptr->ip->smp->repeat; + } + else + { + chptr->voicemode = VM_OFF; + chptr->smp = NULL; + } + } + } + else + { + chptr->pos = tptr->ip->smp->start; + } + } + + } +} + +/* + * Extended commands can occur both at tick 0 and on other ticks; make it a + * function to prevent having to write it twice in the code + */ +static void extendedcommand(TRACK *tptr, CHANNEL *chptr) +{ + switch(tptr->nybble1) + { + /* Fine porta up */ + case 0x1: + if (!mt_tickcount) + { + if (tptr->nybble2) tptr->portaspeedup = tptr->nybble2; + tptr->baseperiod -= tptr->portaspeedup; + if (tptr->baseperiod < 27) tptr->baseperiod = 27; + } + break; + + /* Fine porta down */ + case 0x2: + if (!mt_tickcount) + { + if (tptr->nybble2) tptr->portaspeeddown = tptr->nybble2; + tptr->baseperiod += tptr->portaspeeddown; + if (tptr->baseperiod > 7256) tptr->baseperiod = 7256; + } + break; + + /* Set glissando */ + case 0x3: + if (!mt_tickcount) tptr->glissando = tptr->nybble2; + break; + + /* Set vibrato waveform */ + case 0x4: + if (!mt_tickcount) + { + tptr->vibratotype = tptr->nybble2 & 3; + tptr->vibratotype |= tptr->nybble2 & 4; + } + break; + + /* Set finetune */ + case 0x5: + if ((!mt_tickcount) && (tptr->newnote)) + { + tptr->finetune = (tptr->nybble2 - 8) & 15; + tptr->baseperiod = periodtable[tptr->finetune][tptr->note % 12] >> (tptr->note / 12); + tptr->period = tptr->baseperiod; + } + break; + + /* Patternloop */ + case 0x6: + if (!mt_tickcount) + { + if (!tptr->nybble2) tptr->patternloopline = mt_line; + else + { + if (!tptr->patternloopcount) + { + tptr->patternloopcount = tptr->nybble2; + mt_line = tptr->patternloopline - 1; + } + else + { + tptr->patternloopcount--; + if (tptr->patternloopcount) mt_line = tptr->patternloopline - 1; + } + } + } + break; + + /* Set tremolo waveform */ + case 0x7: + if (!mt_tickcount) + { + tptr->tremolotype = vibratotypetable[tptr->nybble2 & 3]; + tptr->tremolotype |= tptr->nybble2 & 4; + } + break; + + /* Set panning (Undocumented) */ + case 0x8: + { + chptr->panning = (tptr->nybble2 << 4) | tptr->nybble2; + } + break; + + /* Retrig */ + case 0x9: + if (tptr->nybble2) + { + /* + * This looks fuckin' weird, but achieves the right + * effect... + */ + if ((!tptr->newnote) && (!mt_tickcount)) + { + tptr->retrigcount = mt_ticktempo; + } + if (tptr->retrigcount >= tptr->nybble2) + { + tptr->retrigcount = 0; + startnewnote(tptr, chptr); + } + } + tptr->retrigcount++; + break; + + /* Notedelay */ + case 0xd: + /* Don't start on tick 0 or if there's no note */ + if ((!mt_tickcount) || (!tptr->newnote)) break; + if (mt_tickcount == tptr->nybble2) + { + startnewnote(tptr, chptr); + if (tptr->newinstrument) + { + tptr->vol = tptr->ip->vol; + chptr->vol = tptr->vol * 256; + } + } + break; + + /* Cut note */ + case 0xc: + if (mt_tickcount == tptr->nybble2) + { + chptr->vol = 0; + tptr->vol = 0; + } + break; + + /* Fine volslide up */ + case 0xa: + if (!mt_tickcount) + { + if (tptr->nybble2) tptr->volspeedup = tptr->nybble2; + chptr->vol += tptr->volspeedup * 256; + if (chptr->vol > 64*256) chptr->vol = 64*256; + tptr->vol = chptr->vol / 256; + } + break; + + /* Fine volslide down */ + case 0xb: + if (!mt_tickcount) + { + if (tptr->nybble2) tptr->volspeeddown = tptr->nybble2; + chptr->vol -= tptr->volspeeddown * 256; + if (chptr->vol < 0) chptr->vol = 0; + tptr->vol = chptr->vol / 256; + } + break; + + /* Patterndelay */ + case 0xe: + if (!mt_tickcount) + { + mt_patterndelay = tptr->nybble2 + 1; + } + break; + } +} + + +static void modplayer_code_lock_end(void) +{ +} + diff --git a/JUDASRAW.C b/JUDASRAW.C new file mode 100644 index 0000000..135d3b8 --- /dev/null +++ b/JUDASRAW.C @@ -0,0 +1,58 @@ +/* + * JUDAS raw sample routines + */ + +#include +#include +#include +#include "judas.h" + +SAMPLE *judas_loadrawsample(char *name, int repeat, int end, unsigned char voicemode); + +SAMPLE *judas_loadrawsample(char *name, int repeat, int end, unsigned char voicemode) +{ + int length; + int handle; + SAMPLE *smp; + + /* Don't waste memory if Nosound */ + judas_error = JUDAS_OK; + if (judas_device == DEV_NOSOUND) + { + return &fakesample; + } + + judas_error = JUDAS_OPEN_ERROR; + handle = judas_open(name); + if (handle == -1) return NULL; + length = judas_seek(handle, 0, SEEK_END); + if (length == -1) + { + judas_close(handle); + return NULL; + } + judas_seek(handle, 0, SEEK_SET); + smp = judas_allocsample(length); + if (!smp) + { + judas_close(handle); + return NULL; + } + if (end == 0) end = length; + if (end > length) end = length; + if (repeat > length - 1) repeat = length - 1; + judas_error = JUDAS_READ_ERROR; + if (judas_read(handle, smp->start, length) != length) + { + judas_freesample(smp); + judas_close(handle); + return NULL; + } + judas_close(handle); + smp->repeat = smp->start + repeat; + smp->end = smp->start + end; + smp->voicemode = voicemode | VM_ON; + judas_ipcorrect(smp); + judas_error = JUDAS_OK; + return smp; +} diff --git a/JUDASS3M.C b/JUDASS3M.C new file mode 100644 index 0000000..0a7e80c --- /dev/null +++ b/JUDASS3M.C @@ -0,0 +1,1735 @@ +/* + * JUDAS Screamtracker 3 Module loading/playing + * + * Not supported: + * - stereo samples (not supported by ST3 either) + * + * Changes: + * V2.01 Arpeggio command must use the previous value if infobyte = 0 + * V2.02 Removed checking of read error in instrument loading + * V2.03 Don't use toneportamento if no note previously on that channel + * Moved tables only used by S3Ms here + * V2.04 Added support for stereo enable/disable & "panning magic" to loading + * Added surround panning command "X??" (Undocumented, CP supports it) + * Vibrato command must use old speed even if depth changes if nybble1=0 + * Allow 0 at header.byte1a, previously only 1Ah was accepted + * Kill sound if it is portamented higher than HIGHPERIOD, which is now 1 + * Portamento up and down use the same previous value + * Scaled default left-right panning to 1/2 original to prevent earache + * Sample offset command uses the previous value if parameter is 00 + * V2.06 Phase of the sine vibrato rotated 180 degrees + * Note cut didn't work at all, now it does! + * Added song looping control (might have removed an old, fatal bug + * too!) Moving to an already played song position is treated as + * looping. This may cause false alarms in some weird songs. + * Added support for vumeters (smp structure address provided). + * Added support for the more precise volume + * Added song rewinding/forwarding + * + */ + +#include +#include +#include +#include +#include "judas.h" +#include "judastbl.h" + +#ifdef __DJGPP__ +#define HANDLE_PRAGMA_PACK_PUSH_POP 1 +#endif + +#define S3M_MAXCHANNELS 32 +#define S3M_MAXPATTERNS 100 +#define S3M_MAXINSTRUMENTS 100 +#define S3M_MAXORDER 256 +#define HIGHPERIOD 1 +#define LOWPERIOD 29015 +#define S3M_LEFT 64 +#define S3M_RIGHT 191 + +#pragma pack(push,1) +typedef struct +{ + unsigned char songname[28]; + unsigned char byte1a; + unsigned char filetype; + unsigned short unused1; + unsigned short ordernum; + unsigned short instnum; + unsigned short pattnum; + unsigned short flags; + unsigned short createdwith; + unsigned short fileversion; + unsigned char id[4]; + unsigned char globalvol; + unsigned char initialspeed; + unsigned char initialtempo; + unsigned char mastermult; + unsigned char ultraclicks; + unsigned char panningmagic; + unsigned char unused[8]; + unsigned short special; + unsigned char chsettings[S3M_MAXCHANNELS]; +} S3M_HEADER; + +typedef struct +{ + unsigned char type; + unsigned char dosname[12]; + unsigned char paraptrhigh; + unsigned short paraptr; + unsigned length; + unsigned loopstart; + unsigned loopend; + signed char vol; + unsigned char disknum; + unsigned char pack; + unsigned char flags; + unsigned c2rate; + unsigned unused; + unsigned short guspos; + unsigned short int512; + unsigned lastused; + unsigned char name[28]; + unsigned char id[4]; +} S3M_INSTRUMENT; + +typedef struct +{ + unsigned char note; + unsigned char instrument; + unsigned char voleffect; + unsigned char effect; + unsigned char effectdata; +} NOTE; +#pragma pack(pop) + +typedef struct +{ + SAMPLE *sp; + signed char vol; + unsigned c2rate; +} INSTRUMENT; + +typedef struct +{ + INSTRUMENT *ip; + SAMPLE *sp; + unsigned char newnote; + unsigned char note; + unsigned char instrument; + unsigned char newinstrument; + unsigned char voleffect; + unsigned char effect; + unsigned char effectdata; + unsigned char nybble1; + unsigned char nybble2; + signed char notevol; + signed char vol; + unsigned short c2rate; + short baseperiod; + short period; + short targetperiod; + unsigned char tp; + unsigned char tpspeed; + unsigned char volspeedup; + unsigned char volspeeddown; + unsigned char portaspeed; + unsigned char vibratotype; + unsigned char vibratospeed; + unsigned char vibratodepth; + unsigned char vibratophase; + unsigned char sampleoffset; + unsigned char usesampleoffset; + unsigned char tremolotype; + unsigned char tremolospeed; + unsigned char tremolodepth; + unsigned char tremolophase; + unsigned char retrigcount; + unsigned char retriginterval; + unsigned char retrigvolchange; + unsigned char tremorcount; + unsigned char tremorontime; + unsigned char tremorofftime; + unsigned char tremorstatus; + unsigned char arpeggiointerval; +} TRACK; + +/* Prototypes */ +int judas_loads3m(char *name); +void judas_frees3m(void); +void judas_plays3m(int rounds); +void judas_stops3m(void); +unsigned char judas_gets3mpos(void); +unsigned char judas_gets3mline(void); +unsigned char judas_gets3mtick(void); +unsigned char judas_gets3mchannels(void); +char *judas_gets3mname(void); +void judas_forwards3m(void); +void judas_rewinds3m(void); +static void judas_mutechannelss3m(); +static int init_s3mplayer(void); +static void s3mplayer_code_lock_start(void); +static void s3mplayer(void); +static void startnewnote(TRACK *tptr, CHANNEL *chptr); +static void extendedcommand(TRACK *tptr, CHANNEL *chptr); +static void volslide(TRACK *tptr); +static void s3mplayer_code_lock_end(void); + +/* Period table for S3Ms */ +unsigned short s3mperiodtable[] = +{ + 1712, 1616, 1524, 1440, 1356, 1280, 1208, 1140, 1076, 1016, 960, 907 +}; + +/* S3M Set Finetune values */ +unsigned short s3mfinetunetable[] = +{ + 7895, 7941, 7985, 8046, 8107, 8169, 8232, 8280, 8363, 8413, 8463, 8529, + 8581, 8651, 8723, 8757 +}; + +static S3M_HEADER header; +static char s3m_loaded = 0; +static char s3mplayer_firsttime = 1; +static char s3m_channels; +static unsigned char s3m_chpanning[S3M_MAXCHANNELS]; +static unsigned char s3m_order[S3M_MAXORDER]; +static char s3m_poshasbeenplayed[S3M_MAXORDER]; +static unsigned short instparapointers[S3M_MAXINSTRUMENTS]; +static unsigned short pattparapointers[S3M_MAXINSTRUMENTS]; +static unsigned char defaultchannelpanpos[S3M_MAXCHANNELS]; +static INSTRUMENT s3m_instrument[S3M_MAXINSTRUMENTS]; +static TRACK s3m_track[S3M_MAXCHANNELS]; +static unsigned s3m_pattlength; +static unsigned char *s3m_pattptr = NULL; +static unsigned short loadpattlen; +static unsigned short s3m_oldpos; +static unsigned short s3m_pos; +static unsigned short s3m_line; +static unsigned short s3m_ticktempo; +static unsigned short s3m_tickcount; +static unsigned char s3m_globalvol; +static unsigned char s3m_patternbreak; +static unsigned char s3m_patterndelay; +static unsigned char s3m_patternloopline; +static unsigned char s3m_patternloopcount; +static int s3m_roundcounter; +static int s3m_wind = 0; +static int s3m_windingpause = 0; + +/* External variables */ +extern unsigned char judas_bpmtempo; +extern unsigned judas_bpmcount; +extern void (*judas_player)(void); + +/* External variables */ +extern unsigned char judas_bpmtempo; +extern unsigned judas_bpmcount; +extern void (*judas_player)(void); + +void judas_forwards3m(void) +{ + s3m_wind = 1; +} + +void judas_rewinds3m(void) +{ + s3m_wind = -1; +} + +int judas_loads3m(char *name) +{ + int handle; + int count; + unsigned char chconverttable[S3M_MAXCHANNELS]; + INSTRUMENT *ip; + S3M_INSTRUMENT *instbuffer; + + /* Don't waste memory if Nosound */ + judas_error = JUDAS_OK; + if (judas_device == DEV_NOSOUND) return 1; + + judas_error = JUDAS_OUT_OF_MEMORY; + if (s3mplayer_firsttime) + { + if (!init_s3mplayer()) return 0; + } + + judas_frees3m(); + /* + * Try to open the file + */ + judas_error = JUDAS_OPEN_ERROR; + handle = judas_open(name); + if (handle == -1) + { + return 0; + } + /* + * Read in the header + */ + judas_error = JUDAS_READ_ERROR; + if (judas_read(handle, &header, sizeof header) != sizeof header) + { + judas_close(handle); + return 0; + } + /* + * Check it is a S3M + */ + judas_error = JUDAS_WRONG_FORMAT; + if ((header.byte1a != 0x1a && header.byte1a != 0) || (header.filetype != 0x10) || (memcmp("SCRM", header.id, 4))) + { + judas_close(handle); + return 0; + } + header.byte1a = 0x0; /* Serves as the songname endzero from now on */ + s3m_channels = 0; + for (count = 0; count < S3M_MAXCHANNELS; count++) + { + chconverttable[count] = 0xff; + if (header.chsettings[count] < 16) + { + chconverttable[count] = s3m_channels; + /* Stereo may be disabled */ + if (header.mastermult & 128) { + if (header.chsettings[count] & 8) s3m_chpanning[(unsigned char)s3m_channels] = S3M_RIGHT; + else s3m_chpanning[(unsigned char)s3m_channels] = S3M_LEFT; + } else s3m_chpanning[(unsigned char)s3m_channels] = MIDDLE; + s3m_channels++; + } + } + /* + * Quit, if not enough channels + */ + if (s3m_channels > CHANNELS) + { + judas_error = JUDAS_OUT_OF_CHANNELS; + s3m_channels = CHANNELS; + judas_close(handle); + return 0; + } + + /* + * Read in and check the orderlist + */ + memset(&s3m_order, 255, sizeof s3m_order); + judas_error = JUDAS_READ_ERROR; + if (judas_read(handle, &s3m_order, header.ordernum) != header.ordernum) + { + judas_close(handle); + return 0; + } + judas_error = JUDAS_WRONG_FORMAT; + { + int firstpattern = S3M_MAXORDER+666; + int firstloop = S3M_MAXORDER+666; + for (count = header.ordernum-1; count>=0; count--) + { + if (s3m_order[count] != 254) + { + if (s3m_order[count] == 255) firstloop = count; + else firstpattern = count; + } + } + if (firstpattern >= firstloop) + { + /* + * There was no patterns in the order list!! + */ + judas_close(handle); + return 0; + } + } + + /* + * Read in the instrument & pattern parapointers + */ + judas_error = JUDAS_READ_ERROR; + if (judas_read(handle, &instparapointers, header.instnum * sizeof(short)) != header.instnum * sizeof(short)) + { + judas_close(handle); + return 0; + } + if (judas_read(handle, &pattparapointers, header.pattnum * sizeof(short)) != header.pattnum * sizeof(short)) + { + judas_close(handle); + return 0; + } + /* + * Reserve intermediate buffer for S3M format instruments (to prevent + * backwards seeks patterns & samples are loaded later) + */ + judas_error = JUDAS_OUT_OF_MEMORY; + instbuffer = malloc(header.instnum * sizeof(S3M_INSTRUMENT)); + if (!instbuffer) + { + judas_close(handle); + return 0; + } + + /* + * Read in and use default channel pan positions (panning magic) if enabled + */ + if (header.panningmagic == 252) + { + if (judas_read(handle, &defaultchannelpanpos, s3m_channels * sizeof(char)) != s3m_channels * sizeof(char)) + { + judas_close(handle); + return 0; + } + for (count = 0; count < s3m_channels; count++) + { + if (defaultchannelpanpos[count] & 32) { + /* Channel panning pos defined */ + s3m_chpanning[count] = ((defaultchannelpanpos[count] & 15) << 4) | + (defaultchannelpanpos[count] & 15); + } else { + /* Panning pos for this channel not defined. Use default */ + switch (s3m_chpanning[count]) + { + case MIDDLE: + s3m_chpanning[count] = 0x77; /* 128 would be faster though */ + break; + case S3M_LEFT: + s3m_chpanning[count] = 0x33; + break; + case S3M_RIGHT: + s3m_chpanning[count] = 0xBB; + break; + } + } + } + } + + /* + * Read instruments + */ + for (count = 0; count < header.instnum; count++) + { + S3M_INSTRUMENT *s3mip = instbuffer + count; + /* + * Seek to correct pos to find instrument + */ + judas_error = JUDAS_READ_ERROR; + if (judas_seek(handle, instparapointers[count] << 4, SEEK_SET) == -1) + { + free(instbuffer); + judas_close(handle); + return 0; + } + /* + * Read the instrument + */ + if (judas_read(handle, s3mip, sizeof (S3M_INSTRUMENT)) != sizeof (S3M_INSTRUMENT)) + { + free(instbuffer); + judas_close(handle); + return 0; + } + if (s3mip->flags & 4) + { + s3mip->length <<= 1; + s3mip->loopstart <<= 1; + s3mip->loopend <<= 1; + } + } + /* + * Reserve memory for patterns + */ + judas_error = JUDAS_OUT_OF_MEMORY; + s3m_pattlength = 64 * sizeof(NOTE) * s3m_channels; + s3m_pattptr = locked_malloc(s3m_pattlength * header.pattnum); + if (!s3m_pattptr) + { + free(instbuffer); + judas_close(handle); + return 0; + } + /* + * Empty all the patterns + */ + { + NOTE *noteptr = (NOTE *)s3m_pattptr; + for (count = header.pattnum * s3m_channels * 64; count; count--) + { + noteptr->note = 0xff; + noteptr->voleffect = 0xff; + noteptr->instrument = 0x0; + noteptr->effect = 0xff; + noteptr++; + } + } + /* + * Read in & unpack patterns + */ + for (count = 0; count < header.pattnum; count++) + { + int rowcount; + unsigned char *packbuffer; + NOTE *destptr; + unsigned char *srcptr; + + /* + * HUUDAN.S3M has weird patternparapointers that + * point to offset is 0! Since offset 0 contains + * the S3M header there cannot be any correct pattern. + * Seeking to offset 0 would cause a backwards seek, which is + * bad if you're using a compressed datafile! + */ + if (pattparapointers[count]) + { + /* + * Seek to correct pos to find pattern + */ + judas_error = JUDAS_READ_ERROR; + if (judas_seek(handle, pattparapointers[count] << 4, SEEK_SET) == -1) + { + free(instbuffer); + judas_frees3m(); + judas_close(handle); + return 0; + } + if (judas_read(handle, &loadpattlen, sizeof loadpattlen) != sizeof loadpattlen) + { + free(instbuffer); + judas_frees3m(); + judas_close(handle); + return 0; + } + + if (loadpattlen > 2) + { + /* + * Weird, packed data length has the length indicator + * word included (I really checked this out!) + */ + loadpattlen -= 2; + judas_error = JUDAS_OUT_OF_MEMORY; + packbuffer = malloc(loadpattlen); + memset(packbuffer, 0, loadpattlen); + if (!packbuffer) + { + free(instbuffer); + judas_frees3m(); + judas_close(handle); + return 0; + } + + judas_error = JUDAS_READ_ERROR; + if (judas_read(handle, packbuffer, loadpattlen) != loadpattlen) + { + free(instbuffer); + free(packbuffer); + judas_frees3m(); + judas_close(handle); + return 0; + } + + srcptr = packbuffer; + destptr = (NOTE *)(&s3m_pattptr[count * s3m_pattlength]); + for (rowcount = 0; rowcount < 64; rowcount++) + { + unsigned char control; + + for(;;) + { + control = *srcptr++; + if (control) + { + unsigned char channel = chconverttable[control & 31]; + if (control & 32) + { + if (channel < s3m_channels) + { + if (*srcptr < 254) + { + destptr[channel].note = (*srcptr & 15) + (*srcptr >> 4) * 12; + } + else destptr[channel].note = *srcptr; + srcptr++; + destptr[channel].instrument = *srcptr++; + } + else srcptr += 2; + } + if (control & 64) + { + if (channel < s3m_channels) + { + destptr[channel].voleffect = *srcptr++; + } + else srcptr++; + } + if (control & 128) + { + if (channel < s3m_channels) + { + destptr[channel].effect = *srcptr++; + destptr[channel].effectdata = *srcptr++; + } + else srcptr += 2; + } + } + else break; + } + destptr += s3m_channels; + } + free(packbuffer); + } + } + } + /* + * Now read samples + */ + ip = &s3m_instrument[0]; + for (count = 0; count < header.instnum; count++) + { + S3M_INSTRUMENT *s3mip = instbuffer + count; + ip->vol = s3mip->vol; + ip->c2rate = s3mip->c2rate; + /* + * Alloc & read sample (if there is one) + */ + if ((s3mip->type == 1) && (s3mip->length)) + { + judas_error = JUDAS_OUT_OF_MEMORY; + ip->sp = judas_allocsample(s3mip->length); + if (!ip->sp) + { + free(instbuffer); + judas_frees3m(); + judas_close(handle); + return 0; + } + /* + * Seek & read sample + */ + judas_seek(handle, (s3mip->paraptr | (s3mip->paraptrhigh << 16)) << 4, SEEK_SET); + judas_read(handle, ip->sp->start, s3mip->length); + /* + * Looped or non-looped? + */ + if ((s3mip->flags & 1) && (s3mip->loopstart != s3mip->loopend)) + { + ip->sp->voicemode = VM_LOOP | VM_ON; + ip->sp->repeat = ip->sp->start + s3mip->loopstart; + ip->sp->end = ip->sp->start + s3mip->loopend; + } + else + { + ip->sp->voicemode = VM_ONESHOT | VM_ON; + ip->sp->repeat = ip->sp->start; + ip->sp->end = ip->sp->start + s3mip->length; + } + /* + * Sixteenbit? + */ + if (s3mip->flags & 4) + { + ip->sp->voicemode |= VM_16BIT; + } + /* + * Convert to signed if necessary + */ + if (header.fileversion == 2) + { + if (s3mip->flags & 4) + { + unsigned short *cptr = (unsigned short *)ip->sp->start; + unsigned ccount = s3mip->length >> 1; + while (ccount) + { + *cptr += 0x8000; + cptr++; + ccount--; + } + } + else + { + unsigned char *cptr = ip->sp->start; + unsigned ccount = s3mip->length; + while (ccount) + { + *cptr += 0x80; + cptr++; + ccount--; + } + } + } + /* + * All done, just correct interpolation + */ + judas_ipcorrect(ip->sp); + } + ip++; + } + free(instbuffer); + judas_error = JUDAS_OK; + judas_close(handle); + s3m_loaded = 1; + return 1; +} + +void judas_frees3m(void) +{ + int count; + INSTRUMENT *ip = &s3m_instrument[0]; + + if (s3mplayer_firsttime) + { + if (!init_s3mplayer()) return; + } + judas_stops3m(); + s3m_loaded = 0; + if (s3m_pattptr) + { + locked_free(s3m_pattptr); + s3m_pattptr = NULL; + } + for (count = 0; count < S3M_MAXINSTRUMENTS; count++) + { + if (ip->sp) + { + judas_freesample(ip->sp); + ip->sp = NULL; + } + ip++; + } +} + +unsigned char judas_gets3mpos(void) +{ + return s3m_pos; +} + +unsigned char judas_gets3mline(void) +{ + return s3m_line; +} + +unsigned char judas_gets3mtick(void) +{ + return s3m_tickcount; +} + +unsigned char judas_gets3mchannels(void) +{ + return s3m_channels; +} + +char *judas_gets3mname(void) +{ + if (s3m_loaded) return header.songname; + else return NULL; +} + +static int init_s3mplayer(void) +{ + int count; + INSTRUMENT *ip = &s3m_instrument[0]; + + s3m_channels = 0; + for (count = 0; count < S3M_MAXINSTRUMENTS; count++) + { + ip->sp = NULL; + ip++; + } + if (!judas_locktables()) return 0; + if (!judas_memlock(&s3mplayer_code_lock_start, (int)&s3mplayer_code_lock_end - (int)&s3mplayer_code_lock_start)) return 0; + if (!judas_memlock(&s3mperiodtable, sizeof s3mperiodtable)) return 0; + if (!judas_memlock(&s3mfinetunetable, sizeof s3mfinetunetable)) return 0; + if (!judas_memlock(&header, sizeof header)) return 0; + if (!judas_memlock(&s3m_loaded, sizeof s3m_loaded)) return 0; + if (!judas_memlock(&s3m_channels, sizeof s3m_channels)) return 0; + if (!judas_memlock(&s3m_chpanning, sizeof s3m_chpanning)) return 0; + if (!judas_memlock(&s3m_poshasbeenplayed, sizeof s3m_poshasbeenplayed)) return 0; + if (!judas_memlock(&s3m_order, sizeof s3m_order)) return 0; + if (!judas_memlock(&s3m_instrument, sizeof s3m_instrument)) return 0; + if (!judas_memlock(&s3m_track, sizeof s3m_track)) return 0; + if (!judas_memlock(&s3m_pattlength, sizeof s3m_pattlength)) return 0; + if (!judas_memlock(&s3m_pattptr, sizeof s3m_pattptr)) return 0; + if (!judas_memlock(&s3m_oldpos, sizeof s3m_oldpos)) return 0; + if (!judas_memlock(&s3m_pos, sizeof s3m_pos)) return 0; + if (!judas_memlock(&s3m_line, sizeof s3m_line)) return 0; + if (!judas_memlock(&s3m_ticktempo, sizeof s3m_ticktempo)) return 0; + if (!judas_memlock(&s3m_tickcount, sizeof s3m_tickcount)) return 0; + if (!judas_memlock(&s3m_globalvol, sizeof s3m_globalvol)) return 0; + if (!judas_memlock(&s3m_patternbreak, sizeof s3m_patternbreak)) return 0; + if (!judas_memlock(&s3m_patterndelay, sizeof s3m_patterndelay)) return 0; + if (!judas_memlock(&s3m_patternloopline, sizeof s3m_patternloopline)) return 0; + if (!judas_memlock(&s3m_patternloopcount, sizeof s3m_patternloopcount)) return 0; + if (!judas_memlock(&s3m_roundcounter, sizeof s3m_roundcounter)) return 0; + if (!judas_memlock(&s3m_wind, sizeof s3m_wind)) return 0; + if (!judas_memlock(&s3m_windingpause, sizeof s3m_windingpause)) return 0; + s3mplayer_firsttime = 0; + return 1; +} + +static void s3mplayer_code_lock_start(void) +{ +} + +void judas_mutechannelss3m(void) +{ + int count; + TRACK *tptr = &s3m_track[0]; + CHANNEL *chptr = &judas_channel[0]; + for (count = 0; count < s3m_channels; count++) + { + chptr->smp = NULL; + chptr->voicemode = VM_OFF; + chptr->panning = s3m_chpanning[count]; + tptr->ip = &s3m_instrument[0]; + tptr->sp = NULL; + tptr->c2rate = 8363; + tptr->newnote = 0; + tptr->instrument = 0; + tptr->note = 0; + tptr->notevol = 0; + tptr->vol = 0; + tptr->effect = 0; + tptr->effectdata = 0; + tptr->nybble1 = 0; + tptr->nybble2 = 0; + tptr->voleffect = 0; + tptr->tp = 0; + tptr->tpspeed = 0; + tptr->volspeedup = 0; + tptr->volspeeddown = 0; + tptr->retriginterval = 0; + tptr->arpeggiointerval = 0; + tptr->retrigvolchange = 0; + tptr->sampleoffset = 0; + tptr->usesampleoffset = 0; + tptr->vibratotype = 0; + tptr->tremolotype = 0; + chptr++; + tptr++; + } +} + +void judas_plays3m(int rounds) +{ + int count; + + if (!s3m_loaded) return; + judas_player = NULL; + s3m_roundcounter = rounds; + s3m_wind = 0; + s3m_pos = 0; + s3m_oldpos = s3m_pos; + s3m_line = 0; + s3m_tickcount = 0; + s3m_ticktempo = header.initialspeed; + s3m_globalvol = header.globalvol; + s3m_patterndelay = 0; + judas_bpmcount = 0; + judas_bpmtempo = header.initialtempo; + for (count = 1; count < S3M_MAXORDER; count++) + { + s3m_poshasbeenplayed[count] = 0; + } + s3m_poshasbeenplayed[0] = 1; + judas_mutechannelss3m(); + judas_player = &s3mplayer; +} + +void judas_stops3m(void) +{ + int count; + CHANNEL *chptr = &judas_channel[0]; + + if (s3m_channels > CHANNELS) s3m_channels = CHANNELS; + if (!s3m_loaded) return; + judas_player = NULL; + for (count = s3m_channels; count > 0; count--) + { + chptr->smp = NULL; + chptr->voicemode = VM_OFF; + chptr++; + } +} + +void s3mplayer(void) +{ + TRACK *tptr = &s3m_track[0]; + CHANNEL *chptr = &judas_channel[0]; + int count; + + /* + * Song winding + */ + if (s3m_wind > 0) { + s3m_wind = 0; + if (s3m_pos < header.ordernum-1) { + int count; + for (count = s3m_pos+1; count < header.ordernum; count++) + { + if (s3m_order[count] != 254 && s3m_order[count] != 255) break; + } + if (count != header.ordernum) { + s3m_pos++; + s3m_oldpos = s3m_pos; + s3m_line = 0; + s3m_tickcount = 0; + s3m_patterndelay = 0; + for (count = 0; count < header.ordernum; count++) + { + s3m_poshasbeenplayed[count] = 0; + } + s3m_poshasbeenplayed[s3m_pos] = 1; + judas_mutechannelss3m(); + s3m_windingpause = 1; + return; + } + } + } else + if (s3m_wind < 0) { + s3m_wind = 0; + if (s3m_pos > 0) { + int count; + s3m_pos--; + s3m_oldpos = s3m_pos; + s3m_line = 0; + s3m_tickcount = 0; + s3m_patterndelay = 0; + for (count = 0; count < header.ordernum; count++) + { + s3m_poshasbeenplayed[count] = 0; + } + s3m_poshasbeenplayed[s3m_pos] = 1; + judas_mutechannelss3m(); + s3m_windingpause = 1; + return; + } + } + if (s3m_windingpause) { + s3m_windingpause = 0; + return; + } + + /* + * Set new notes or do something else? + */ + if ((!s3m_tickcount) && (!s3m_patterndelay)) + { + NOTE *noteptr; + + if (s3m_order[s3m_pos] == 254) + { + while (s3m_order[s3m_pos] == 254) + { + s3m_pos++; + if (s3m_pos >= header.ordernum) { + s3m_pos = 0; + s3m_oldpos = s3m_pos; + if (s3m_roundcounter != 1) + { + int count; + if (s3m_roundcounter) s3m_roundcounter--; + for (count = 0; count < header.ordernum; count++) + { + s3m_poshasbeenplayed[count] = 0; + } + s3m_poshasbeenplayed[s3m_pos] = 1; + } + else { + judas_stops3m(); + return; + } + } else s3m_oldpos = S3M_MAXORDER+666; + } + } + if (s3m_order[s3m_pos] == 255) + { + s3m_pos = 0; + s3m_oldpos = s3m_pos; + if (s3m_roundcounter != 1) + { + int count; + if (s3m_roundcounter) s3m_roundcounter--; + for (count = 0; count < header.ordernum; count++) + { + s3m_poshasbeenplayed[count] = 0; + } + s3m_poshasbeenplayed[s3m_pos] = 1; + } + else { + judas_stops3m(); + return; + } + + while (s3m_order[s3m_pos] == 254) + { + s3m_pos++; + if (s3m_pos >= header.ordernum) + { + judas_stops3m(); + return; + } + } + } + /* + * Song looping detection & control! + */ + if (s3m_pos != s3m_oldpos) + { + if (s3m_poshasbeenplayed[s3m_pos]) + { + if (s3m_roundcounter != 1) + { + int count; + if (s3m_roundcounter) s3m_roundcounter--; + for (count = 0; count < header.ordernum; count++) + { + s3m_poshasbeenplayed[count] = 0; + } + s3m_poshasbeenplayed[s3m_pos] = 1; + } + else { + judas_stops3m(); + return; + } + } + else s3m_poshasbeenplayed[s3m_pos] = 1; + } + + noteptr = (NOTE *)&s3m_pattptr[s3m_pattlength * s3m_order[s3m_pos] + sizeof(NOTE) * s3m_line * s3m_channels]; + + s3m_oldpos = s3m_pos; + s3m_patternbreak = 0; + for (count = s3m_channels; count > 0; count--) + { + tptr->newnote = 0; + + /* + * Get note (if any) + */ + if (noteptr->note != 255) + { + tptr->note = noteptr->note; + tptr->newnote = 1; + } + /* + * Get effect, effect data etc. + */ + tptr->voleffect = noteptr->voleffect; + tptr->effect = noteptr->effect; + tptr->effectdata = noteptr->effectdata; + tptr->nybble1 = noteptr->effectdata >> 4; + tptr->nybble2 = noteptr->effectdata & 0xf; + tptr->newinstrument = noteptr->instrument; + + /* + * Set sampleoffset here (unlike claimed in the + * previous version, offset 00 uses the old value! :)) + */ + if (tptr->newinstrument) tptr->usesampleoffset = 0; + if (tptr->effect == 15) + { + if (tptr->effectdata) tptr->sampleoffset = tptr->effectdata; + tptr->usesampleoffset = 1; + } + + /* + * Start new note if there is one; but check there + * isn't notedelay + */ + if ((tptr->effect != 19) || (tptr->nybble1 != 0xd) || (tptr->nybble2 == 0)) + { + if (tptr->newnote) startnewnote(tptr, chptr); + /* + * If an instrument number, reload + * volume. Instrument number IS taken into + * account! + */ + if (tptr->newinstrument) + { + tptr->instrument = tptr->newinstrument - 1; + tptr->notevol = s3m_instrument[tptr->instrument].vol; + tptr->vol = tptr->notevol; + } + } + + /* + * Reset period if not vibrato + */ + if ((tptr->effect != 8) && (tptr->effect != 11)) + { + tptr->period = tptr->baseperiod; + } + /* + * Reset volume if not tremolo / tremor + */ + if ((tptr->effect != 9) && (tptr->effect != 18)) + { + tptr->vol = tptr->notevol; + } + + /* + * Now check volume column (it's much simpler than in + * FT2!) + */ + if (tptr->voleffect != 255) + { + /* Applies only if there isn't notedelay */ + if ((tptr->effect != 19) || (tptr->nybble1 != 0xd) || (tptr->nybble2 == 0)) + { + tptr->notevol = tptr->voleffect; + if (tptr->notevol > 64) tptr->notevol = 64; + if (tptr->notevol < 0) tptr->notevol = 64; + tptr->vol = tptr->notevol; + } + } + /* + * Then the regular effects + */ + switch (tptr->effect) + { + case 0xff: + break; + + /* Portamento up */ + case 6: + if (tptr->effectdata) tptr->portaspeed = tptr->effectdata; + /* Fine & extrafine done here */ + switch(tptr->portaspeed >> 4) + { + case 0xe: + tptr->baseperiod -= (tptr->portaspeed & 15); + break; + + case 0xf: + tptr->baseperiod -= (tptr->portaspeed & 15) * 4; + break; + } + if (tptr->baseperiod < HIGHPERIOD) { + chptr->voicemode = VM_OFF; + chptr->smp = NULL; + } + tptr->period = tptr->baseperiod; + break; + + /* Portamento down */ + case 5: + if (tptr->effectdata) tptr->portaspeed = tptr->effectdata; + /* Fine & extrafine done here */ + switch(tptr->portaspeed >> 4) + { + case 0xe: + tptr->baseperiod += (tptr->portaspeed & 15); + break; + + case 0xf: + tptr->baseperiod += (tptr->portaspeed & 15) * 4; + break; + } + if (tptr->baseperiod > LOWPERIOD) tptr->baseperiod = LOWPERIOD; + tptr->period = tptr->baseperiod; + break; + + /* Set TP. speed */ + case 7: + if (tptr->effectdata) tptr->tpspeed = tptr->effectdata; + break; + + /* Set vibrato */ + case 8: + if (tptr->effectdata) + { + if (tptr->nybble1) tptr->vibratospeed = tptr->nybble1; + tptr->vibratodepth = tptr->nybble2; + } + break; + + /* Arpeggio */ + case 10: + if (tptr->effectdata) + { + tptr->arpeggiointerval = tptr->effectdata; + } + break; + + /* Set tremolo */ + case 18: + if (tptr->effectdata) + { + tptr->tremolospeed = tptr->nybble1; + tptr->tremolodepth = tptr->nybble2; + } + break; + + /* Volume slide speed set */ + case 4: + case 11: + case 12: + if (tptr->effectdata) + { + tptr->volspeedup = tptr->nybble1; + tptr->volspeeddown = tptr->nybble2; + } + volslide(tptr); + break; + + /* Pos. jump */ + case 2: + s3m_line = 63; + s3m_pos = tptr->effectdata - 1; + break; + + /* Pattern break */ + case 3: + if (!s3m_patternbreak) + { + s3m_patternbreak = 1; + s3m_line = tptr->nybble1 * 10 + tptr->nybble2 - 1; + s3m_pos++; + } + break; + + /* Extended command */ + case 19: + extendedcommand(tptr, chptr); + break; + + /* Set BPM tempo */ + case 20: + if (tptr->effectdata > 0x20) + { + judas_bpmtempo = tptr->effectdata; + } + break; + + /* Set ticktempo */ + case 1: + if (tptr->effectdata) + { + s3m_ticktempo = tptr->effectdata; + } + break; + + /* Global volume */ + case 22: + s3m_globalvol = tptr->effectdata; + if (s3m_globalvol > 64) s3m_globalvol = 64; + break; + + /* Surround panning (Undocumented) */ + case 24: + { + short int temp = tptr->effectdata; + temp <<= 1; + if (temp == 256) temp = 255; + if (temp > 255) { + /* From behind */ + temp = 255-(temp-255); + } + chptr->panning = temp; + } + break; + + /* Multi retrig */ + case 17: + if (tptr->effectdata) + { + tptr->retrigvolchange = tptr->nybble1; + tptr->retriginterval = tptr->nybble2; + } + if (tptr->retrigcount >= tptr->retriginterval) + { + startnewnote(tptr, chptr); + /* Now modify volume */ + if (!retrigmultable[tptr->retrigvolchange]) + { + tptr->notevol += retrigaddtable[tptr->retrigvolchange]; + if (tptr->notevol < 0) tptr->notevol = 0; + if (tptr->notevol > 64) tptr->notevol = 64; + tptr->vol = tptr->notevol; + } + else + { + tptr->notevol = (tptr->notevol * retrigmultable[tptr->retrigvolchange]) >> 4; + if (tptr->notevol > 64) tptr->notevol = 64; + tptr->vol = tptr->notevol; + } + } + tptr->retrigcount++; + break; + + /* Tremor */ + case 9: + if (tptr->effectdata) + { + tptr->tremorontime = tptr->nybble1; + tptr->tremorofftime = tptr->nybble2; + } + break; + } + if (tptr->period) + { + chptr->freq = 14317056 / tptr->period; + } + chptr->vol = (tptr->vol * s3m_globalvol) << 2; + noteptr++; + chptr++; + tptr++; + } + } + if (s3m_tickcount) + { + /* + * If tick isn't 0, update "continuous" effects + */ + for (count = s3m_channels; count > 0; count--) + { + switch (tptr->effect) + { + case 0xff: + break; + + /* Arpeggio */ + case 10: + { + char phase = s3m_tickcount % 3; + switch (phase) + { + unsigned char arpnote; + + case 0: + tptr->period = tptr->baseperiod; + break; + + case 1: + arpnote = tptr->note + (tptr->arpeggiointerval >> 4); + if (arpnote > 95) arpnote = 95; + tptr->period = ((8363 * 16 * s3mperiodtable[arpnote % 12]) >> (arpnote / 12)) / tptr->c2rate; + break; + + case 2: + arpnote = tptr->note + (tptr->arpeggiointerval & 15); + if (arpnote > 95) arpnote = 95; + tptr->period = ((8363 * 16 * s3mperiodtable[arpnote % 12]) >> (arpnote / 12)) / tptr->c2rate; + break; + } + } + break; + + /* Portamento up */ + case 6: + if (tptr->portaspeed < 0xe0) + { + tptr->baseperiod -= tptr->portaspeed * 4; + } + if (tptr->baseperiod < HIGHPERIOD) { + chptr->voicemode = VM_OFF; + chptr->smp = NULL; + } + tptr->period = tptr->baseperiod; + break; + + /* Portamento down */ + case 5: + if (tptr->portaspeed < 0xe0) + { + tptr->baseperiod += tptr->portaspeed * 4; + } + if (tptr->baseperiod > LOWPERIOD) tptr->baseperiod = LOWPERIOD; + tptr->period = tptr->baseperiod; + break; + + /* Toneportamento */ + case 7: + if (tptr->tp) + { + if (tptr->baseperiod < tptr->targetperiod) + { + tptr->baseperiod += tptr->tpspeed * 4; + if (tptr->baseperiod >= tptr->targetperiod) + { + tptr->baseperiod = tptr->targetperiod; + tptr->tp = 0; + } + } + if (tptr->baseperiod > tptr->targetperiod) + { + tptr->baseperiod -= tptr->tpspeed * 4; + if (tptr->baseperiod <= tptr->targetperiod) + { + tptr->baseperiod = tptr->targetperiod; + tptr->tp = 0; + } + } + tptr->period = tptr->baseperiod; + } + break; + + /* Vibrato */ + case 8: + tptr->vibratophase += tptr->vibratospeed * 4; + tptr->period = tptr->baseperiod + ((vibratotable[vibratotypetable[tptr->vibratotype & 3]][tptr->vibratophase] * tptr->vibratodepth) >> 3); + if (tptr->period < HIGHPERIOD) tptr->period = HIGHPERIOD; + if (tptr->period > LOWPERIOD) tptr->period = LOWPERIOD; + break; + + /* Toneportamento + volslide */ + case 12: + if (tptr->tp) + { + if (tptr->baseperiod < tptr->targetperiod) + { + tptr->baseperiod += tptr->tpspeed * 4; + if (tptr->baseperiod >= tptr->targetperiod) + { + tptr->baseperiod = tptr->targetperiod; + tptr->tp = 0; + } + } + if (tptr->baseperiod > tptr->targetperiod) + { + tptr->baseperiod -= tptr->tpspeed * 4; + if (tptr->baseperiod <= tptr->targetperiod) + { + tptr->baseperiod = tptr->targetperiod; + tptr->tp = 0; + } + } + tptr->period = tptr->baseperiod; + } + volslide(tptr); + break; + + /* Vibrato + volslide */ + case 11: + tptr->vibratophase += tptr->vibratospeed * 4; + tptr->period = tptr->baseperiod + ((vibratotable[vibratotypetable[tptr->vibratotype & 3]][tptr->vibratophase] * tptr->vibratodepth) >> 3); + if (tptr->period < HIGHPERIOD) tptr->period = HIGHPERIOD; + if (tptr->period > LOWPERIOD) tptr->period = LOWPERIOD; + volslide(tptr); + break; + + /* Tremolo */ + case 18: + tptr->tremolophase += tptr->tremolospeed * 4; + tptr->vol = tptr->notevol + ((vibratotable[tptr->tremolotype & 3][tptr->tremolophase] * tptr->tremolodepth) >> 4); + if (tptr->vol < 0) tptr->vol = 0; + if (tptr->vol > 64) tptr->vol = 64; + break; + + /* Volume Slide */ + case 4: + volslide(tptr); + break; + + /* Extended command */ + case 19: + extendedcommand(tptr, chptr); + break; + + /* Multi retrig */ + case 17: + if (tptr->retrigcount >= tptr->retriginterval) + { + startnewnote(tptr, chptr); + /* Now modify volume */ + if (!retrigmultable[tptr->retrigvolchange]) + { + tptr->notevol += retrigaddtable[tptr->retrigvolchange]; + if (tptr->notevol < 0) tptr->notevol = 0; + if (tptr->notevol > 64) tptr->notevol = 64; + tptr->vol = tptr->notevol; + } + else + { + tptr->notevol = (tptr->notevol * retrigmultable[tptr->retrigvolchange]) >> 4; + if (tptr->notevol > 64) tptr->notevol = 64; + tptr->vol = tptr->notevol; + } + } + tptr->retrigcount++; + break; + + /* Tremor */ + case 9: + if (!tptr->tremorcount) + { + tptr->tremorstatus ^= 1; + if (tptr->tremorstatus) tptr->tremorcount = tptr->tremorontime + 1; + else tptr->tremorcount = tptr->tremorofftime + 1; + } + if (tptr->tremorstatus) tptr->vol = tptr->notevol; + else tptr->vol = 0; + tptr->tremorcount--; + break; + } + if (tptr->period) + { + chptr->freq = 14317056 / tptr->period; + } + chptr->vol = (tptr->vol * s3m_globalvol) << 2; + chptr++; + tptr++; + } + } + /* + * Advance song + */ + s3m_tickcount++; + if (s3m_tickcount >= s3m_ticktempo) + { + s3m_tickcount = 0; + if (s3m_patterndelay) + { + s3m_patterndelay--; + } + if (!s3m_patterndelay) + { + s3m_line++; + if (s3m_line >= 64) + { + s3m_line = 0; + s3m_pos++; + } + } + } +} + +static void startnewnote(TRACK *tptr, CHANNEL *chptr) +{ + /* + * Change instrument if necessary + */ + if (tptr->newinstrument) + { + tptr->instrument = tptr->newinstrument - 1; + tptr->ip = &s3m_instrument[tptr->instrument]; + } + + /* + * Handle keyoff + */ + if (tptr->note == 254) + { + chptr->voicemode = VM_OFF; + chptr->smp = NULL; + return; + } + + /* + * Now get samplepointer + */ + tptr->sp = tptr->ip->sp; + + /* + * Don't go further if sample doesn't exist + */ + if (tptr->sp) + { + tptr->c2rate = tptr->ip->c2rate; + tptr->retrigcount = 0; + if (!(tptr->vibratotype & 4)) tptr->vibratophase = 0; + if (!(tptr->tremolotype & 4)) tptr->tremolophase = 0; + tptr->tremorcount = 0; + tptr->tremorstatus = 0; + /* + * Toneportamento mustn't be used if there's no note on that + * channel (to make the bassline in HERRA.S3M audible) + */ + if (((tptr->effect == 7) || (tptr->effect == 12)) && (chptr->voicemode != VM_OFF)) + { + /* + * Toneportamento + */ + tptr->targetperiod = ((8363 * 16 * s3mperiodtable[tptr->note % 12]) >> (tptr->note / 12)) / tptr->c2rate; + tptr->tp = 1; + } + else + { + /* + * Normal note start + */ + tptr->tp = 0; + tptr->baseperiod = ((8363 * 16 * s3mperiodtable[tptr->note % 12]) >> (tptr->note / 12)) / tptr->c2rate; + tptr->period = tptr->baseperiod; + chptr->fractpos = 0; + chptr->repeat = tptr->sp->repeat; + chptr->end = tptr->sp->end; + chptr->voicemode = tptr->sp->voicemode; + chptr->smp = tptr->sp; + if (tptr->usesampleoffset) + { + if (tptr->sp->voicemode & VM_16BIT) chptr->pos = tptr->sp->start + (tptr->sampleoffset << 9); + else chptr->pos = tptr->sp->start + (tptr->sampleoffset << 8); + if (chptr->pos >= tptr->sp->end) + { + if (chptr->voicemode & VM_LOOP) + { + chptr->pos = tptr->sp->repeat; + } + else + { + chptr->voicemode = VM_OFF; + chptr->smp = NULL; + } + } + } + else + { + chptr->pos = tptr->sp->start; + } + } + } +} + +/* + * Extended commands can occur both at tick 0 and on other ticks; make it a + * function to prevent having to write it twice in the code + */ +static void extendedcommand(TRACK *tptr, CHANNEL *chptr) +{ + switch(tptr->nybble1) + { + /* Set finetune */ + case 0x2: + if ((!s3m_tickcount) && (tptr->newnote)) + { + tptr->c2rate = s3mfinetunetable[tptr->nybble2]; + tptr->baseperiod = ((8363 * 16 * s3mperiodtable[tptr->note % 12]) >> (tptr->note / 12)) / tptr->c2rate; + tptr->period = tptr->baseperiod; + } + break; + + /* Set vibrato waveform */ + case 0x3: + if (!s3m_tickcount) + { + tptr->vibratotype = tptr->nybble2 & 3; + tptr->vibratotype |= tptr->nybble2 & 4; + } + break; + + /* Set tremolo waveform */ + case 0x4: + if (!s3m_tickcount) + { + tptr->tremolotype = vibratotypetable[tptr->nybble2 & 3]; + tptr->tremolotype |= tptr->nybble2 & 4; + } + break; + + /* Set panning */ + case 0x8: + if (!s3m_tickcount) + { + chptr->panning = (tptr->nybble2 << 4) | tptr->nybble2; + } + break; + + /* Patternloop */ + case 0xb: + if (!s3m_tickcount) + { + if (!tptr->nybble2) s3m_patternloopline = s3m_line; + else + { + if (!s3m_patternloopcount) + { + s3m_patternloopcount = tptr->nybble2; + s3m_line = s3m_patternloopline - 1; + } + else + { + s3m_patternloopcount--; + if (s3m_patternloopcount) s3m_line = s3m_patternloopline - 1; + } + } + } + break; + + /* Notedelay */ + case 0xd: + /* Don't start on tick 0 */ + if (!s3m_tickcount) break; + if (s3m_tickcount == tptr->nybble2) + { + /* If no new note, just reload volume */ + if (tptr->newnote) startnewnote(tptr, chptr); + if (tptr->newinstrument) + { + /* Store new instrument number */ + tptr->instrument = tptr->newinstrument - 1; + tptr->notevol = s3m_instrument[tptr->instrument].vol; + tptr->vol = tptr->notevol; + } + if (tptr->voleffect != 255) + { + tptr->notevol = tptr->voleffect; + if (tptr->notevol < 0) tptr->notevol = 64; + if (tptr->notevol > 64) tptr->notevol = 64; + tptr->vol = tptr->notevol; + } + } + break; + + /* Cut note */ + case 0xc: + if ((s3m_tickcount == tptr->nybble2) && (tptr->nybble2)) + { + tptr->notevol = 0; + tptr->vol = 0; + } + break; + + /* Patterndelay */ + case 0xe: + if (!s3m_tickcount) + { + s3m_patterndelay = tptr->nybble2 + 1; + } + break; + } +} + +static void volslide(TRACK *tptr) +{ + if (s3m_tickcount) + { + if (tptr->volspeeddown == 0) + { + tptr->notevol += tptr->volspeedup; + if (tptr->notevol > 64) tptr->notevol = 64; + tptr->vol = tptr->notevol; + return; + } + if (tptr->volspeedup == 0) + { + tptr->notevol -= tptr->volspeeddown; + if (tptr->notevol < 0) tptr->notevol = 0; + tptr->vol = tptr->notevol; + return; + } + } + else + { + if (tptr->volspeeddown == 15) + { + tptr->notevol += tptr->volspeedup; + if (tptr->notevol > 64) tptr->notevol = 64; + tptr->vol = tptr->notevol; + return; + } + if (tptr->volspeedup == 15) + { + tptr->notevol -= tptr->volspeeddown; + if (tptr->notevol < 0) tptr->notevol = 0; + tptr->vol = tptr->notevol; + return; + } + } +} + +static void s3mplayer_code_lock_end(void) +{ +} + diff --git a/JUDASSMP.C b/JUDASSMP.C new file mode 100644 index 0000000..0aa2905 --- /dev/null +++ b/JUDASSMP.C @@ -0,0 +1,378 @@ +/* + * JUDAS sample handling + */ + +#include +#include +#include +#include +#include "judas.h" +#include "judascfg.h" + +/* A "volume profile" for each sample will be precalculated. + * + * VUPROFILESAMPLES defines how many samplepoints are used in the estimation of + * one volume value. VUPROFILERATIO defines how many samplepoints there are + * between the volume estimations. The first sample in the sampledata will be + * in the middle of the first chunk to investigate. + */ +#define VUPROFILERATIO 512 + +/* + * This is something interesting...the sample pointer might go this many bytes + * over the sample end while in the mixing inner loop. Here's the formula: + * + * maximum_playspeed / minimum_mixspeed (round upwards for safety) + * bytes = --------------------------------------------------------------- + * repeats_in_mixing_inner_loop + * + * Maximum playspeed is 535232 (highest speed in linear freq. table), minimum + * mixspeed is 5000 and there are 16 repeats. + * And, of course, because there can be 16bit samples the number of bytes must + * be multiplied with 2. + */ +#define ENDSAFETYAREA ((3456 > 4*VUPROFILERATIO)? 3456:4*VUPROFILERATIO) +#define STARTSAFETYAREA 4 + +SAMPLE *judas_allocsample(int length); +void judas_freesample(SAMPLE *smp); +void judas_playsample(SAMPLE *smp, unsigned chnum, unsigned frequency, unsigned short volume, unsigned char panning); +void judas_ipcorrect(SAMPLE *smp); +void judas_stopsample(unsigned chnum); +void judas_preventdistortion(unsigned channels); +void judas_setmastervolume(unsigned chnum, unsigned char mastervol); +void judas_setmusicmastervolume(unsigned musicchannels, unsigned char mastervol); +void judas_setsfxmastervolume(unsigned musicchannels, unsigned char mastervol); +void judas_calcvuprofile(SAMPLE *smp); +float judas_getvumeter(unsigned chnum); + +SAMPLE *judas_allocsample(int length) +{ + SAMPLE *smp; + int vuprofilelen = 1+length/VUPROFILERATIO; + + judas_error = JUDAS_OUT_OF_MEMORY; + + length += STARTSAFETYAREA + ENDSAFETYAREA; // *** + smp = (SAMPLE *)locked_malloc(sizeof (SAMPLE)); + if (!smp) return NULL; + smp->start = locked_malloc(length); + if (!smp->start) + { + judas_freesample(smp); + return NULL; + } + // vuprofile allocation: + smp->vuprofile = locked_malloc(vuprofilelen+1); // Some extra, because of interpolation + if (!smp->vuprofile) + { + judas_freesample(smp); + return NULL; + } + + smp->start += STARTSAFETYAREA; // *** + smp->repeat = NULL; + smp->end = NULL; + judas_error = JUDAS_OK; + return smp; +} + +void judas_freesample(SAMPLE *smp) +{ + int c; + CHANNEL *chptr; + + if (!smp) return; + if (smp == &fakesample) return; + /* + * Go thru all channels; if that sample is playing then stop the + * channel; we don't want a crash or audible shit! + */ + chptr = &judas_channel[0]; + for (c = CHANNELS; c > 0; c--) + { + if (chptr->smp == smp) + { + chptr->smp = NULL; + chptr->voicemode = VM_OFF; + } + chptr++; + } + /* + * Free the sample data and then the sample structure itself + */ + if (smp->vuprofile) locked_free(smp->vuprofile); + if (smp->start) locked_free(smp->start-STARTSAFETYAREA); // *** + locked_free(smp); +} + +void judas_ipcorrect(SAMPLE *smp) +{ + if (!smp) return; + if (smp == &fakesample) return; + + { + int count; + for (count = -STARTSAFETYAREA ; count < 0 ; count++) + *(smp->start + count) = 0; + } + + if (smp->voicemode & VM_LOOP) + { + int count, lcount = smp->end - smp->repeat; + unsigned char *src = &smp->repeat[0], *dest = &smp->end[0]; + + if (!src) return; + if (!dest) return; + + if (smp->voicemode & VM_16BIT) count = ENDSAFETYAREA; + else count = ENDSAFETYAREA / 2; + while (count) + { + if (lcount <= 0) + { + lcount = smp->end - smp->repeat; + src = &smp->repeat[0]; + } + *dest++ = *src++; + lcount--; + count--; + } + } + else + { + /* Since V2.04 the data beyond sample end is presumed to be + * what it was at sample end, not zero! (Because of q-mixer) + */ + if (!smp->end) return; + if (smp->voicemode & VM_16BIT) { + int count = ENDSAFETYAREA/2; + char *dest = &smp->end[0]; + while (count) + { + *dest = smp->end[-2]; + *(dest+1) = smp->end[-1]; + dest += 2; + count--; + } + } else { + int count = ENDSAFETYAREA; + char *dest = &smp->end[0]; + while (count) + { + *dest++ = smp->end[-1]; /* Toivottavasti samplen pituus > 0 */ + count--; + } + } + } + judas_calcvuprofile(smp); +} + +void judas_calcvuprofile(SAMPLE *smp) +{ + if (smp->voicemode & VM_16BIT) + { + if (smp->voicemode & VM_LOOP) + { + unsigned char *vup = smp->vuprofile; + int samples = (smp->end - smp->start)/2; + int chunks = 1+samples/VUPROFILERATIO+1; // +1 for interpolation + int chunk; + for (chunk = 0; chunk < chunks; chunk++) + { + float thisvol = 0; + int chunksample; + signed short *firstsmpinchunk = (short *) smp->start - VUPROFILERATIO/2 + chunk*VUPROFILERATIO; + if (firstsmpinchunk < (short *) smp->start) firstsmpinchunk = (short *) smp->start; + for (chunksample = 0; (chunksample < VUPROFILERATIO); chunksample++) + { + thisvol += ((float) firstsmpinchunk[chunksample]*firstsmpinchunk[chunksample])/16384; + } + thisvol = sqrt(255*sqrt((float)thisvol/VUPROFILERATIO)); + if (thisvol > 255) thisvol = 255; + *vup = thisvol; + vup++; + } + } else + { + unsigned char *vup = smp->vuprofile; + int samples = (smp->end - smp->start)/2; + int chunks = 1+samples/VUPROFILERATIO+1; // +1 for interpolation + int chunk; + for (chunk = 0; chunk < chunks; chunk++) + { + float thisvol = 0; + int chunksample; + signed short *firstsmpinchunk = (short *) smp->start - VUPROFILERATIO/2 + chunk*VUPROFILERATIO; + if (firstsmpinchunk < (short *) smp->start) firstsmpinchunk = (short *) smp->start; + for (chunksample = 0; (chunksample < VUPROFILERATIO); chunksample++) + { + if (&firstsmpinchunk[chunksample] >= (short *) smp->end) break; + thisvol += ((float) firstsmpinchunk[chunksample]*firstsmpinchunk[chunksample])/16384; + } + thisvol = sqrt(255*sqrt((float)thisvol/VUPROFILERATIO)); + if (thisvol > 255) thisvol = 255; + *vup = thisvol; + vup++; + } + } + } else + { + if (smp->voicemode & VM_LOOP) + { + unsigned char *vup = smp->vuprofile; + int samples = (smp->end - smp->start); + int chunks = 1+samples/VUPROFILERATIO+1; // +1 for interpolation + int chunk; + for (chunk = 0; chunk < chunks; chunk++) + { + float thisvol = 0; + int chunksample; + signed char *firstsmpinchunk = (signed char *) smp->start - VUPROFILERATIO/2 + chunk*VUPROFILERATIO; + if (firstsmpinchunk < (signed char *) smp->start) firstsmpinchunk = (signed char *) smp->start; + for (chunksample = 0; (chunksample < VUPROFILERATIO); chunksample++) + { + thisvol += ((float) firstsmpinchunk[chunksample]*firstsmpinchunk[chunksample]); + } + thisvol = sqrt(255*sqrt(4*(float)thisvol/VUPROFILERATIO)); + if (thisvol > 255) thisvol = 255; + *vup = thisvol; + vup++; + } + } else + { + unsigned char *vup = smp->vuprofile; + int samples = (smp->end - smp->start); + int chunks = 1+samples/VUPROFILERATIO+1; // +1 for interpolation + int chunk; + for (chunk = 0; chunk < chunks; chunk++) + { + float thisvol = 0; + int chunksample; + signed char *firstsmpinchunk = (signed char *) smp->start - VUPROFILERATIO/2 + chunk*VUPROFILERATIO; + if (firstsmpinchunk < (signed char *) smp->start) firstsmpinchunk = (signed char *) smp->start; + for (chunksample = 0; (chunksample < VUPROFILERATIO); chunksample++) + { + if (&firstsmpinchunk[chunksample] >= (signed char *) smp->end) break; + thisvol += ((float) firstsmpinchunk[chunksample]*firstsmpinchunk[chunksample]); + } + thisvol = sqrt(255*sqrt(4*(float)thisvol/VUPROFILERATIO)); + if (thisvol > 255) thisvol = 255; + *vup = thisvol; + vup++; + } + } + } +} + +float judas_getvumeter(unsigned chnum) { + if ( + (chnum >= CHANNELS) || + ((judas_channel[chnum].voicemode & VM_ON) == VM_OFF) || + (!judas_channel[chnum].smp) + ) return 0; + { + SAMPLE *smp = judas_channel[chnum].smp; + float vu = judas_channel[chnum].vol; + char *pos = judas_channel[chnum].pos; + int chunk; + float fractpos, vuprofile0, vuprofile1; + if (pos >= smp->end || pos < smp->start) return 0; // Might have changed!!! + if (smp->voicemode & VM_16BIT) + { + chunk = (pos - smp->start)/(2*VUPROFILERATIO); + fractpos = (float)(pos - smp->start)/(2*VUPROFILERATIO)-chunk; + } else { + chunk = (pos - smp->start)/(VUPROFILERATIO); + fractpos = (float)(pos - smp->start)/(VUPROFILERATIO)-chunk; + } + // Interpolation: + vuprofile0 = smp->vuprofile[chunk]; + vuprofile1 = smp->vuprofile[chunk+1]; + vuprofile0 *= vuprofile0/255; + vuprofile1 *= vuprofile1/255; + vu *= fractpos*(vuprofile1-vuprofile0)+vuprofile0; + // max now 255*64*256 + vu /= (float)64*255*256; // Shrink to 0..1 but not 1 + return (vu<1)? vu:0.99999999; + } +} + +void judas_playsample(SAMPLE *smp, unsigned chnum, unsigned frequency, unsigned short volume, unsigned char panning) +{ + CHANNEL *chptr; + + if (!smp) return; + if (smp == &fakesample) return; + if (chnum >= CHANNELS) return; + chptr = &judas_channel[chnum]; + chptr->voicemode = VM_OFF; + chptr->pos = smp->start; + chptr->repeat = smp->repeat; + chptr->end = smp->end; + chptr->smp = smp; + chptr->fractpos = 0; + chptr->freq = frequency; + chptr->vol = volume; + chptr->panning = panning; + chptr->voicemode = smp->voicemode; +} + +void judas_stopsample(unsigned chnum) +{ + CHANNEL *chptr; + + if (chnum >= CHANNELS) return; + chptr = &judas_channel[chnum]; + chptr->voicemode = VM_OFF; + chptr->smp = NULL; +} + +void judas_preventdistortion(unsigned active_channels) +{ + int count; + unsigned char mastervol; + + if (active_channels < 2) mastervol = 255; + else mastervol = 256 / active_channels; + for (count = 0; count < CHANNELS; count++) + { + judas_setmastervolume(count, mastervol); + } +} + +void judas_setmastervolume(unsigned chnum, unsigned char mastervol) +{ + CHANNEL *chptr; + + if (chnum >= CHANNELS) return; + chptr = &judas_channel[chnum]; + chptr->mastervol = mastervol; +} + +void judas_setmusicmastervolume(unsigned musicchannels, unsigned char mastervol) +{ + CHANNEL *chptr = &judas_channel[0]; + int count; + + if (musicchannels > CHANNELS) musicchannels = CHANNELS; + for (count = 0; count < musicchannels; count++) + { + chptr->mastervol = mastervol; + chptr++; + } +} + +void judas_setsfxmastervolume(unsigned musicchannels, unsigned char mastervol) +{ + CHANNEL *chptr; + int count; + + if (musicchannels >= CHANNELS) return; + chptr = &judas_channel[musicchannels]; + for (count = musicchannels; count < CHANNELS; count++) + { + chptr->mastervol = mastervol; + chptr++; + } +} diff --git a/JUDASTBL.C b/JUDASTBL.C new file mode 100644 index 0000000..c10f6f9 --- /dev/null +++ b/JUDASTBL.C @@ -0,0 +1,135 @@ +/* + * JUDAS tables (those common to all players) + */ + +#include "judas.h" + +/* + * Multi retrig tables + * (well, actually not required for MODs, but for XMs & S3Ms) + */ +signed char retrigaddtable[] = +{ + 0, -1, -2, -4, -8, -16, 0, 0, 0, +1, +2, +4, +8, +16, 0, 0 +}; +signed char retrigmultable[] = +{ + 0, 0, 0, 0, 0, 0, 11, 8, 0, 0, 0, 0, 0, 0, 24, 32 +}; + +/* + * Conversion table from standard vibrato types to XM track & instr. vibrato. + * NOTE: Random isn't implemented, who needs it?! + */ +unsigned char vibratotypetable[] = +{ + 4, 2, 1, 0 +}; + +/* Vibrato tables (used both for normal & instrument vibrato) */ +signed char vibratotable[5][256] = +{ + { + 0, -2, -3, -5, -6, -8, -9, -11, -12, -14, -16, -17, -19, -20, -22, -23, + -24, -26, -27, -29, -30, -32, -33, -34, -36, -37, -38, -39, -41, -42, -43, -44, + -45, -46, -47, -48, -49, -50, -51, -52, -53, -54, -55, -56, -56, -57, -58, -59, + -59, -60, -60, -61, -61, -62, -62, -62, -63, -63, -63, -64, -64, -64, -64, -64, + -64, -64, -64, -64, -64, -64, -63, -63, -63, -62, -62, -62, -61, -61, -60, -60, + -59, -59, -58, -57, -56, -56, -55, -54, -53, -52, -51, -50, -49, -48, -47, -46, + -45, -44, -43, -42, -41, -39, -38, -37, -36, -34, -33, -32, -30, -29, -27, -26, + -24, -23, -22, -20, -19, -17, -16, -14, -12, -11, -9, -8, -6, -5, -3, -2, + 0, 2, 3, 5, 6, 8, 9, 11, 12, 14, 16, 17, 19, 20, 22, 23, + 24, 26, 27, 29, 30, 32, 33, 34, 36, 37, 38, 39, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 56, 57, 58, 59, + 59, 60, 60, 61, 61, 62, 62, 62, 63, 63, 63, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 63, 63, 63, 62, 62, 62, 61, 61, 60, 60, + 59, 59, 58, 57, 56, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, + 45, 44, 43, 42, 41, 39, 38, 37, 36, 34, 33, 32, 30, 29, 27, 26, + 24, 23, 22, 20, 19, 17, 16, 14, 12, 11, 9, 8, 6, 5, 3, 2 + }, + { + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, + -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, + -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, + -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, + -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, + -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, + -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, + -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64 + }, + { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, + 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, + 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, + 24, 24, 25, 25, 26, 26, 27, 27, 28, 28, 29, 29, 30, 30, 31, 31, + 32, 32, 33, 33, 34, 34, 35, 35, 36, 36, 37, 37, 38, 38, 39, 39, + 40, 40, 41, 41, 42, 42, 43, 43, 44, 44, 45, 45, 46, 46, 47, 47, + 48, 48, 49, 49, 50, 50, 51, 51, 52, 52, 53, 53, 54, 54, 55, 55, + 56, 56, 57, 57, 58, 58, 59, 59, 60, 60, 61, 61, 62, 62, 63, 63, + -64, -64, -63, -63, -62, -62, -61, -61, -60, -60, -59, -59, -58, -58, -57, -57, + -56, -56, -55, -55, -54, -54, -53, -53, -52, -52, -51, -51, -50, -50, -49, -49, + -48, -48, -47, -47, -46, -46, -45, -45, -44, -44, -43, -43, -42, -42, -41, -41, + -40, -40, -39, -39, -38, -38, -37, -37, -36, -36, -35, -35, -34, -34, -33, -33, + -32, -32, -31, -31, -30, -30, -29, -29, -28, -28, -27, -27, -26, -26, -25, -25, + -24, -24, -23, -23, -22, -22, -21, -21, -20, -20, -19, -19, -18, -18, -17, -17, + -16, -16, -15, -15, -14, -14, -13, -13, -12, -12, -11, -11, -10, -10, -9, -9, + -8, -8, -7, -7, -6, -6, -5, -5, -4, -4, -3, -3, -2, -2, -1, -1 + }, + { + 0, 0, -1, -1, -2, -2, -3, -3, -4, -4, -5, -5, -6, -6, -7, -7, + -8, -8, -9, -9, -10, -10, -11, -11, -12, -12, -13, -13, -14, -14, -15, -15, + -16, -16, -17, -17, -18, -18, -19, -19, -20, -20, -21, -21, -22, -22, -23, -23, + -24, -24, -25, -25, -26, -26, -27, -27, -28, -28, -29, -29, -30, -30, -31, -31, + -32, -32, -33, -33, -34, -34, -35, -35, -36, -36, -37, -37, -38, -38, -39, -39, + -40, -40, -41, -41, -42, -42, -43, -43, -44, -44, -45, -45, -46, -46, -47, -47, + -48, -48, -49, -49, -50, -50, -51, -51, -52, -52, -53, -53, -54, -54, -55, -55, + -56, -56, -57, -57, -58, -58, -59, -59, -60, -60, -61, -61, -62, -62, -63, -63, + 64, 64, 63, 63, 62, 62, 61, 61, 60, 60, 59, 59, 58, 58, 57, 57, + 56, 56, 55, 55, 54, 54, 53, 53, 52, 52, 51, 51, 50, 50, 49, 49, + 48, 48, 47, 47, 46, 46, 45, 45, 44, 44, 43, 43, 42, 42, 41, 41, + 40, 40, 39, 39, 38, 38, 37, 37, 36, 36, 35, 35, 34, 34, 33, 33, + 32, 32, 31, 31, 30, 30, 29, 29, 28, 28, 27, 27, 26, 26, 25, 25, + 24, 24, 23, 23, 22, 22, 21, 21, 20, 20, 19, 19, 18, 18, 17, 17, + 16, 16, 15, 15, 14, 14, 13, 13, 12, 12, 11, 11, 10, 10, 9, 9, + 8, 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1 + }, + { + 0, 2, 3, 5, 6, 8, 9, 11, 12, 14, 16, 17, 19, 20, 22, 23, + 24, 26, 27, 29, 30, 32, 33, 34, 36, 37, 38, 39, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 56, 57, 58, 59, + 59, 60, 60, 61, 61, 62, 62, 62, 63, 63, 63, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 63, 63, 63, 62, 62, 62, 61, 61, 60, 60, + 59, 59, 58, 57, 56, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, + 45, 44, 43, 42, 41, 39, 38, 37, 36, 34, 33, 32, 30, 29, 27, 26, + 24, 23, 22, 20, 19, 17, 16, 14, 12, 11, 9, 8, 6, 5, 3, 2, + 0, -2, -3, -5, -6, -8, -9, -11, -12, -14, -16, -17, -19, -20, -22, -23, + -24, -26, -27, -29, -30, -32, -33, -34, -36, -37, -38, -39, -41, -42, -43, -44, + -45, -46, -47, -48, -49, -50, -51, -52, -53, -54, -55, -56, -56, -57, -58, -59, + -59, -60, -60, -61, -61, -62, -62, -62, -63, -63, -63, -64, -64, -64, -64, -64, + -64, -64, -64, -64, -64, -64, -63, -63, -63, -62, -62, -62, -61, -61, -60, -60, + -59, -59, -58, -57, -56, -56, -55, -54, -53, -52, -51, -50, -49, -48, -47, -46, + -45, -44, -43, -42, -41, -39, -38, -37, -36, -34, -33, -32, -30, -29, -27, -26, + -24, -23, -22, -20, -19, -17, -16, -14, -12, -11, -9, -8, -6, -5, -3, -2 + } +}; + +static char tables_locked = 0; + +int judas_locktables(void) +{ + if (tables_locked) return 1; + if (!judas_memlock(&retrigaddtable[0], sizeof retrigaddtable)) return 0; + if (!judas_memlock(&retrigmultable[0], sizeof retrigmultable)) return 0; + if (!judas_memlock(&vibratotypetable[0], sizeof vibratotypetable)) return 0; + if (!judas_memlock(&vibratotable[0][0], sizeof vibratotable)) return 0; + tables_locked = 1; + return 1; +} diff --git a/JUDASTBL.H b/JUDASTBL.H new file mode 100644 index 0000000..4470893 --- /dev/null +++ b/JUDASTBL.H @@ -0,0 +1,10 @@ +/* + * Internal header file: tables needed in module playing + */ + +extern signed char retrigaddtable[]; +extern signed char retrigmultable[]; +extern unsigned char vibratotypetable[]; +extern signed char vibratotable[5][256]; + +int judas_locktables(void); diff --git a/JUDASW.GIF b/JUDASW.GIF new file mode 100644 index 0000000000000000000000000000000000000000..d04b5979b6b04035f5d3186a167c22a21ba19abf GIT binary patch literal 25165 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 MZ@%-98vy_SJ6`IpJ^%m! literal 0 HcmV?d00001 diff --git a/JUDASWAV.C b/JUDASWAV.C new file mode 100644 index 0000000..4a7d819 --- /dev/null +++ b/JUDASWAV.C @@ -0,0 +1,462 @@ +/* + * JUDAS WAV handling + */ + +#include +#include +#include +#include +#include +#include +#include "judas.h" + +#ifdef __DJGPP__ +#include /* for mode definitions */ +#include /* compatibility mode */ +#define HANDLE_PRAGMA_PACK_PUSH_POP 1 +#endif + +#define MAXFILES 100 /* 100 max wav files per 1 library */ + +extern char *filewriterbuffer; +extern int filewriterbuffersize; +extern unsigned char judas_initialized; +extern void safemixer(void *address, int length); + +SAMPLE *judas_loadwav(char *name); +void judas_setwavlib(char *name); + + +#pragma pack(push,1) +typedef struct +{ + char rifftext[4]; + unsigned totallength; + char wavetext[4]; + char formattext[4]; + unsigned formatlength; + unsigned short format; + unsigned short channels; + unsigned freq; + unsigned avgbytes; + unsigned short blockalign; + unsigned short bits; + char datatext[4]; + unsigned datalength; +} WAV_HEADER; + +typedef struct +{ + unsigned offset; // offset in library for this wav entry + unsigned size; // size of this wav entry + char filename[64]; // wav filename +} WAV_ENTRY; + +typedef struct +{ + char logo[4]; // JDSL format sign (header has max 100 wav entries) + unsigned filescount; // number of wav files in library + unsigned encoding; // encoding method + WAV_ENTRY files[MAXFILES]; // single wav entry +} LIB_HEADER; +#pragma pack(pop) + +static LIB_HEADER libheader; +static char libname[260] = {0}; + + +/* + * Opens a file for wav writing. + * Returns nonnegative file handle if successful, -1 on error + */ +int judas_wavwriter_open(char *name) +{ + WAV_HEADER header; + int handle; + + if (!judas_initialized) return -1; + header.totallength = 0; // Will be determined after writing the pcm data + header.datalength = 0; // Will be determined after writing the pcm data + memcpy(header.rifftext, "RIFF", 4); + memcpy(header.wavetext, "WAVE", 4); + memcpy(header.formattext, "fmt ", 4); + memcpy(header.datatext, "data", 4); + header.formatlength = 16; + header.format = 1; + header.freq = judas_mixrate; + switch (judas_mixmode) { + case (MONO | EIGHTBIT): + header.channels = 1; + header.avgbytes = header.freq; + header.blockalign = 1; + header.bits = 8; + break; + case (MONO | SIXTEENBIT): + header.channels = 1; + header.avgbytes = header.freq * 2; + header.blockalign = 2; + header.bits = 16; + break; + case (STEREO | EIGHTBIT): + header.channels = 2; + header.avgbytes = header.freq * 2; + header.blockalign = 2; + header.bits = 8; + break; + case (STEREO | SIXTEENBIT): + header.channels = 2; + header.avgbytes = header.freq * 4; + header.blockalign = 4; + header.bits = 16; + break; + } + + handle = open(name, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, S_IREAD | S_IWRITE); + if (handle == -1) { + return handle; + } + if (write(handle, &header, sizeof(WAV_HEADER)) < sizeof(WAV_HEADER)) { + close(handle); + return -1; + } + + if (filewriterbuffer == NULL) { + if (!(filewriterbuffer = locked_malloc(filewriterbuffersize))) return -1; + } + + return handle; +} + +/* + * Wav writer. Writes one bufferlength of mixed data into the wav file. + * Returns nonnegative file handle if successful, -1 on error + */ +int judas_wavwriter_writesome(int handle) +{ + if (!judas_initialized) return -1; + if (handle == -1) return -1; + + safemixer(filewriterbuffer, filewriterbuffersize); + if (write(handle, filewriterbuffer, filewriterbuffersize) < filewriterbuffersize) { + close(handle); + return -1; + } + return handle; +} + +/* + * Finishes wav writing and closes the wav writer file. + * Returns 0 on success, -1 on error + */ +int judas_wavwriter_close(int handle) +{ + int totallength; + int datalength; + + if (filewriterbuffer) locked_free(filewriterbuffer); + + totallength = filelength(handle); + datalength = totallength - sizeof(WAV_HEADER); + totallength -= 8; + if (datalength < 0) { + close(handle); + return -1; + } + if (lseek(handle, 4, SEEK_SET) == -1) { + close(handle); + return -1; + }; + if (write(handle, &totallength, 4) == -1) { + close(handle); + return -1; + } + if (lseek(handle, sizeof(WAV_HEADER)-4, SEEK_SET) == -1) { + close(handle); + return -1; + }; + if (write(handle, &datalength, 4) == -1) { + close(handle); + return -1; + } + return close(handle); +} + +SAMPLE *judas_loadwav(char *name) +{ + int length; + int reallength; + int handle; + SAMPLE *smp; + WAV_HEADER header; + int fcount; + + /* Don't waste memory if Nosound */ + judas_error = JUDAS_OK; + if (judas_device == DEV_NOSOUND) + { + return &fakesample; + } + + /* + * Try to open + */ + judas_error = JUDAS_OPEN_ERROR; + + if (!libname[0]) strcpy (libname, "jdswav.lib"); + handle = judas_open(libname); + if (handle != -1) { + /* library open success */ + judas_error = JUDAS_READ_ERROR; + if (judas_read (handle, &libheader, sizeof(LIB_HEADER)) != sizeof(LIB_HEADER)) { + judas_close (handle); + return NULL; + } + + /* check JDSL library logo */ + if ((libheader.logo[0] != 'J') || (libheader.logo[1] != 'D') || (libheader.logo[2] != 'S')) { + judas_close (handle); + return NULL; + } + + /* ni compare files and set library file pointer on success */ + for (fcount = 0; fcount < libheader.filescount; fcount++) { + if (strnicmp(name, (char *)&libheader.files[fcount].filename, sizeof(name)) == 0) { + if (judas_seek(handle, libheader.files[fcount].offset, SEEK_SET) == -1) { + judas_close (handle); + return NULL; + } + break; // success - wav entry was found + } + } + + /* close library handle if wav file not found in library */ + if (fcount == libheader.filescount) { + judas_close (handle); + } + } + + /* try to load as single wav file if wav entry not found in library */ + if (handle == -1) { + handle = judas_open(name); + if (handle == -1) return NULL; + } + + /* + * Read identification + */ + judas_error = JUDAS_READ_ERROR; + if (judas_read(handle, &header, 12) != 12) + { + judas_close(handle); + return NULL; + } + judas_error = JUDAS_WRONG_FORMAT; + if (memcmp("RIFF", header.rifftext, 4)) + { + judas_close(handle); + return NULL; + } + if (memcmp("WAVE", header.wavetext, 4)) + { + judas_close(handle); + return NULL; + } + /* + * Search for the FORMAT chunk + */ + for (;;) + { + judas_error = JUDAS_READ_ERROR; + if (judas_read(handle, &header.formattext, 8) != 8) + { + judas_close(handle); + return NULL; + } + if (!memcmp("fmt ", &header.formattext, 4)) break; + if (judas_seek(handle, header.formatlength, SEEK_CUR) == -1) + { + judas_close(handle); + return NULL; + } + } + /* + * Read in the FORMAT chunk + */ + if (judas_read(handle, &header.format, 16) != 16) + { + judas_close(handle); + return NULL; + } + /* + * Skip data if the format chunk was bigger than what we use + */ + if (judas_seek(handle, header.formatlength - 16, SEEK_CUR) == -1) + { + judas_close(handle); + return NULL; + } + /* + * Check for correct format + */ + judas_error = JUDAS_WRONG_FORMAT; + if (header.format != 1) + { + judas_close(handle); + return NULL; + } + /* + * Search for the DATA chunk + */ + for (;;) + { + judas_error = JUDAS_READ_ERROR; + if (judas_read(handle, &header.datatext, 8) != 8) + { + judas_close(handle); + return NULL; + } + if (!memcmp("data", &header.datatext, 4)) break; + if (judas_seek(handle, header.datalength, SEEK_CUR) == -1) + { + judas_close(handle); + return NULL; + } + } + /* + * Allocate sample, load audio data, do processing (unsigned->signed, + * stereo->mono) + */ + length = header.datalength; + reallength = length; + if (header.channels == 2) reallength >>= 1; + smp = judas_allocsample(reallength); + if (!smp) + { + judas_close(handle); + return NULL; + } + if (header.channels == 2) + { + if (header.bits == 16) + { + unsigned count = length >> 2; + short *buffer; + short *src; + short *dest; + + judas_error = JUDAS_OUT_OF_MEMORY; + buffer = malloc(length); + if (!buffer) + { + judas_freesample(smp); + judas_close(handle); + return NULL; + } + judas_error = JUDAS_READ_ERROR; + if (judas_read(handle, buffer, length) != length) + { + free(buffer); + judas_freesample(smp); + judas_close(handle); + return NULL; + } + src = buffer; + dest = (short *)smp->start; + while (count--) + { + int average = (src[0] + src[1]) / 2; + *dest = average; + src += 2; + dest++; + } + free(buffer); + smp->repeat = smp->start; + smp->end = smp->start + reallength; + smp->voicemode = VM_ON | VM_16BIT; + } + else + { + unsigned count = length >> 1; + unsigned char *buffer; + unsigned char *src; + signed char *dest; + + judas_error = JUDAS_OUT_OF_MEMORY; + buffer = malloc(length); + if (!buffer) + { + judas_freesample(smp); + judas_close(handle); + return NULL; + } + judas_error = JUDAS_READ_ERROR; + if (judas_read(handle, buffer, length) != length) + { + free(buffer); + judas_freesample(smp); + judas_close(handle); + return NULL; + } + src = buffer; + dest = (signed char *)smp->start; + while (count--) + { + int average = (src[0] + src[1] - 0x100) / 2; + *dest = average; + src += 2; + dest++; + } + free(buffer); + smp->repeat = smp->start; + smp->end = smp->start + reallength; + smp->voicemode = VM_ON; + } + } + else + { + if (header.bits == 16) + { + judas_error = JUDAS_READ_ERROR; + if (judas_read(handle, smp->start, length) != length) + { + judas_freesample(smp); + judas_close(handle); + return NULL; + } + smp->repeat = smp->start; + smp->end = smp->start + length; + smp->voicemode = VM_ON | VM_16BIT; + } + else + { + unsigned count = length; + char *src = smp->start; + + judas_error = JUDAS_READ_ERROR; + if (judas_read(handle, smp->start, length) != length) + { + judas_freesample(smp); + judas_close(handle); + return NULL; + } + while (count--) + { + *src += 0x80; + src++; + } + smp->repeat = smp->start; + smp->end = smp->start + length; + smp->voicemode = VM_ON; + } + } + judas_ipcorrect(smp); + judas_error = JUDAS_OK; + judas_close(handle); + return smp; +} + +void judas_setwavlib(char *name) +{ + strcpy (libname, name); +} + diff --git a/JUDASXM.C b/JUDASXM.C new file mode 100644 index 0000000..f2962c3 --- /dev/null +++ b/JUDASXM.C @@ -0,0 +1,2361 @@ +/* + * JUDAS Extended Module loading/playing + * + * Changes: + * V2.03 Added support for empty patterns + * Moved tables only used by XMs here + * If both volslide params. nonzero must only slide up + * V2.04 Corrected global volume limits. ("Gf0" fucks up no more) + * MUSTN'T change instrptr/sampleptr when starting a note with tone- + * portamento! + * V2.05 Retrig counter must be always zeroed at the start of a tick! + * Plus: retrigcount & multiretrigcount aren't the same thing!!! + * V2.06 Phase of the track (not instrument) sine vibrato rotated 180 degrees + * Corrected behaviour of two pattern breaks on the same row + * Note cut didn't work at all, now it does! + * Added song looping control. Moving to an already played song position + * is treated as looping. This may cause false alarms in some weird songs. + * Added support for vumeters (smp structure address provided). + * Added support for the more precise volume + * Added song rewinding/forwarding + * + */ + +#include +#include +#include +#include +#include "judas.h" +#include "judastbl.h" + +#ifdef __DJGPP__ +#define HANDLE_PRAGMA_PACK_PUSH_POP 1 +#endif + +#define ENV_ON 1 +#define ENV_SUSTAIN 2 +#define ENV_LOOP 4 +#define SMP_LOOP 1 +#define SMP_PINGPONGLOOP 2 +#define SMP_16BIT 16 +#define MAX_CHANNELS 32 +#define MAX_PATTERNS 256 +#define MAX_INSTRUMENTS 128 +#define MAX_SAMPLES 16 +#define KEYOFF 0x61 + +#pragma pack(push,1) +typedef struct +{ + char id[17]; + char modname[20]; + unsigned char idbyte; + char trackername[20]; + unsigned short version; +} XM_ID; + +typedef struct +{ + unsigned char note; + unsigned char instrument; + unsigned char voleffect; + unsigned char effect; + unsigned char effectdata; +} XM_NOTE; + +typedef struct +{ + unsigned headersize; + unsigned short songlength; + unsigned short restartpos; + unsigned short channels; + unsigned short patterns; + unsigned short instruments; + unsigned short uselinear; + unsigned short defaulttempo; + unsigned short defaultbpmtempo; + unsigned char order[256]; +} XM_HEADER; + +typedef struct +{ + unsigned headersize; + unsigned char packingtype; + unsigned short rows; + unsigned short packsize; +} XM_PATTERN; + +typedef struct +{ + unsigned short x; + unsigned short y; +} ENVELOPE; + +typedef struct +{ + unsigned headersize; + char name[22]; + unsigned char type; + unsigned short numbersamples; + unsigned sampleheadersize; + unsigned char sampletable[96]; + ENVELOPE volenv[12]; + ENVELOPE panenv[12]; + unsigned char volenvpoints; + unsigned char panenvpoints; + unsigned char volsustpoint; + unsigned char volloopstart; + unsigned char volloopend; + unsigned char pansustpoint; + unsigned char panloopstart; + unsigned char panloopend; + unsigned char volenvflags; + unsigned char panenvflags; + unsigned char vibratotype; + unsigned char vibratosweep; + unsigned char vibratodepth; + unsigned char vibratorate; + short fadeout; + short reserved; + unsigned char c_volenv[325]; + unsigned char c_panenv[325]; +} XM_INSTRUMENT; + +typedef struct +{ + unsigned length; + unsigned loopstart; + unsigned looplength; + unsigned char vol; + signed char finetune; + unsigned char sampleflags; + unsigned char panning; + signed char relativenote; + unsigned char reserved; + char name[22]; +} XM_SAMPLE; +#pragma pack(pop) + +typedef struct +{ + unsigned short rows; + unsigned char *data; +} OUR_PATTERN; + +typedef struct +{ + XM_INSTRUMENT *ip; + XM_SAMPLE *xsp; + SAMPLE *sp; + unsigned char newnote; + short realnote; + unsigned char note; + unsigned char instrument; + unsigned char newinstrument; + unsigned char voleffect; + unsigned char effect; + unsigned char effectdata; + unsigned char nybble1; + unsigned char nybble2; + unsigned char smp; + signed char notevol; + signed char vol; + signed char finetune; + short baseperiod; + short period; + short targetperiod; + unsigned char notepanning; + unsigned char keyon; + int volenvpos; + int panenvpos; + int fadeoutvalue; + unsigned char tp; + unsigned char tpspeed; + unsigned char volspeedup; + unsigned char volspeeddown; + unsigned char portaspeedup; + unsigned char portaspeeddown; + unsigned char panspeedright; + unsigned char panspeedleft; + unsigned char vibratotype; + unsigned char vibratospeed; + unsigned char vibratodepth; + unsigned char vibratophase; + unsigned char sampleoffset; + unsigned char glissando; + unsigned char tremolotype; + unsigned char tremolospeed; + unsigned char tremolodepth; + unsigned char tremolophase; + unsigned char instrvibratophase; + unsigned short instrvibratodepth; + unsigned char multiretrigcount; + unsigned char retrigcount; + unsigned char retriginterval; + unsigned char retrigvolchange; + unsigned char patternloopline; + unsigned char patternloopcount; + unsigned char tremorcount; + unsigned char tremorontime; + unsigned char tremorofftime; + unsigned char tremorstatus; +} TRACK; + +/* Prototypes */ +int judas_loadxm(char *name); +void judas_freexm(void); +void judas_playxm(int rounds); +void judas_stopxm(void); +unsigned char judas_getxmpos(void); +unsigned char judas_getxmline(void); +unsigned char judas_getxmtick(void); +unsigned char judas_getxmchannels(void); +char *judas_getxmname(void); +void judas_forwardxm(void); +void judas_rewindxm(void); +static void judas_mutechannelsxm(); +static int init_xmplayer(void); +static void xmplayer_code_lock_start(void); +static void xmplayer(void); +static void startnewnote(TRACK *tptr, CHANNEL *chptr); +static void changeinstrument(TRACK *tptr); +static void extendedcommand(TRACK *tptr, CHANNEL *chptr); +static int getamigaperiod(int note, int finetune); +static void xmplayer_code_lock_end(void); + +/* XM linear frequency table */ +static unsigned linearfreqtable[] = +{ + 535232, 534749, 534266, 533784, 533303, 532822, 532341, 531861, 531381, 530902, + 530423, 529944, 529466, 528988, 528511, 528034, 527558, 527082, 526607, 526131, + 525657, 525183, 524709, 524236, 523763, 523290, 522818, 522346, 521875, 521404, + 520934, 520464, 519994, 519525, 519057, 518588, 518121, 517653, 517186, 516720, + 516253, 515788, 515322, 514858, 514393, 513929, 513465, 513002, 512539, 512077, + 511615, 511154, 510692, 510232, 509771, 509312, 508852, 508393, 507934, 507476, + 507018, 506561, 506104, 505647, 505191, 504735, 504280, 503825, 503371, 502917, + 502463, 502010, 501557, 501104, 500652, 500201, 499749, 499298, 498848, 498398, + 497948, 497499, 497050, 496602, 496154, 495706, 495259, 494812, 494366, 493920, + 493474, 493029, 492585, 492140, 491696, 491253, 490809, 490367, 489924, 489482, + 489041, 488600, 488159, 487718, 487278, 486839, 486400, 485961, 485522, 485084, + 484647, 484210, 483773, 483336, 482900, 482465, 482029, 481595, 481160, 480726, + 480292, 479859, 479426, 478994, 478562, 478130, 477699, 477268, 476837, 476407, + 475977, 475548, 475119, 474690, 474262, 473834, 473407, 472979, 472553, 472126, + 471701, 471275, 470850, 470425, 470001, 469577, 469153, 468730, 468307, 467884, + 467462, 467041, 466619, 466198, 465778, 465358, 464938, 464518, 464099, 463681, + 463262, 462844, 462427, 462010, 461593, 461177, 460760, 460345, 459930, 459515, + 459100, 458686, 458272, 457859, 457446, 457033, 456621, 456209, 455797, 455386, + 454975, 454565, 454155, 453745, 453336, 452927, 452518, 452110, 451702, 451294, + 450887, 450481, 450074, 449668, 449262, 448857, 448452, 448048, 447644, 447240, + 446836, 446433, 446030, 445628, 445226, 444824, 444423, 444022, 443622, 443221, + 442821, 442422, 442023, 441624, 441226, 440828, 440430, 440033, 439636, 439239, + 438843, 438447, 438051, 437656, 437261, 436867, 436473, 436079, 435686, 435293, + 434900, 434508, 434116, 433724, 433333, 432942, 432551, 432161, 431771, 431382, + 430992, 430604, 430215, 429827, 429439, 429052, 428665, 428278, 427892, 427506, + 427120, 426735, 426350, 425965, 425581, 425197, 424813, 424430, 424047, 423665, + 423283, 422901, 422519, 422138, 421757, 421377, 420997, 420617, 420237, 419858, + 419479, 419101, 418723, 418345, 417968, 417591, 417214, 416838, 416462, 416086, + 415711, 415336, 414961, 414586, 414212, 413839, 413465, 413092, 412720, 412347, + 411975, 411604, 411232, 410862, 410491, 410121, 409751, 409381, 409012, 408643, + 408274, 407906, 407538, 407170, 406803, 406436, 406069, 405703, 405337, 404971, + 404606, 404241, 403876, 403512, 403148, 402784, 402421, 402058, 401695, 401333, + 400970, 400609, 400247, 399886, 399525, 399165, 398805, 398445, 398086, 397727, + 397368, 397009, 396651, 396293, 395936, 395579, 395222, 394865, 394509, 394153, + 393798, 393442, 393087, 392733, 392378, 392024, 391671, 391317, 390964, 390612, + 390259, 389907, 389556, 389204, 388853, 388502, 388152, 387802, 387452, 387102, + 386753, 386404, 386056, 385707, 385359, 385012, 384664, 384317, 383971, 383624, + 383278, 382932, 382587, 382242, 381897, 381552, 381208, 380864, 380521, 380177, + 379834, 379492, 379149, 378807, 378466, 378124, 377783, 377442, 377102, 376762, + 376422, 376082, 375743, 375404, 375065, 374727, 374389, 374051, 373714, 373377, + 373040, 372703, 372367, 372031, 371695, 371360, 371025, 370690, 370356, 370022, + 369688, 369355, 369021, 368688, 368356, 368023, 367691, 367360, 367028, 366697, + 366366, 366036, 365706, 365376, 365046, 364717, 364388, 364059, 363731, 363403, + 363075, 362747, 362420, 362093, 361766, 361440, 361114, 360788, 360463, 360137, + 359813, 359488, 359164, 358840, 358516, 358193, 357869, 357547, 357224, 356902, + 356580, 356258, 355937, 355616, 355295, 354974, 354654, 354334, 354014, 353695, + 353376, 353057, 352739, 352420, 352103, 351785, 351468, 351150, 350834, 350517, + 350201, 349885, 349569, 349254, 348939, 348624, 348310, 347995, 347682, 347368, + 347055, 346741, 346429, 346116, 345804, 345492, 345180, 344869, 344558, 344247, + 343936, 343626, 343316, 343006, 342697, 342388, 342079, 341770, 341462, 341154, + 340846, 340539, 340231, 339924, 339618, 339311, 339005, 338700, 338394, 338089, + 337784, 337479, 337175, 336870, 336566, 336263, 335959, 335656, 335354, 335051, + 334749, 334447, 334145, 333844, 333542, 333242, 332941, 332641, 332341, 332041, + 331741, 331442, 331143, 330844, 330546, 330247, 329950, 329652, 329355, 329057, + 328761, 328464, 328168, 327872, 327576, 327280, 326985, 326690, 326395, 326101, + 325807, 325513, 325219, 324926, 324633, 324340, 324047, 323755, 323463, 323171, + 322879, 322588, 322297, 322006, 321716, 321426, 321136, 320846, 320557, 320267, + 319978, 319690, 319401, 319113, 318825, 318538, 318250, 317963, 317676, 317390, + 317103, 316817, 316532, 316246, 315961, 315676, 315391, 315106, 314822, 314538, + 314254, 313971, 313688, 313405, 313122, 312839, 312557, 312275, 311994, 311712, + 311431, 311150, 310869, 310589, 310309, 310029, 309749, 309470, 309190, 308911, + 308633, 308354, 308076, 307798, 307521, 307243, 306966, 306689, 306412, 306136, + 305860, 305584, 305308, 305033, 304758, 304483, 304208, 303934, 303659, 303385, + 303112, 302838, 302565, 302292, 302019, 301747, 301475, 301203, 300931, 300660, + 300388, 300117, 299847, 299576, 299306, 299036, 298766, 298497, 298227, 297958, + 297689, 297421, 297153, 296884, 296617, 296349, 296082, 295815, 295548, 295281, + 295015, 294749, 294483, 294217, 293952, 293686, 293421, 293157, 292892, 292628, + 292364, 292100, 291837, 291574, 291311, 291048, 290785, 290523, 290261, 289999, + 289737, 289476, 289215, 288954, 288693, 288433, 288173, 287913, 287653, 287393, + 287134, 286875, 286616, 286358, 286099, 285841, 285583, 285326, 285068, 284811, + 284554, 284298, 284041, 283785, 283529, 283273, 283017, 282762, 282507, 282252, + 281998, 281743, 281489, 281235, 280981, 280728, 280475, 280222, 279969, 279716, + 279464, 279212, 278960, 278708, 278457, 278206, 277955, 277704, 277453, 277203, + 276953, 276703, 276453, 276204, 275955, 275706, 275457, 275209, 274960, 274712, + 274465, 274217, 273970, 273722, 273476, 273229, 272982, 272736, 272490, 272244, + 271999, 271753, 271508, 271263, 271018, 270774, 270530, 270286, 270042, 269798, + 269555, 269312, 269069, 268826, 268583, 268341, 268099, 267857 +}; + +/* XM Amiga frequency table. */ +unsigned amigafreqtable[] = +{ + 907, 900, 894, 887, 881, 875, 868, 862, 856, 850, 844, 838, 832, 826, 820, 814, + 808, 802, 796, 791, 785, 779, 774, 768, 762, 757, 752, 746, 741, 736, 730, 725, + 720, 715, 709, 704, 699, 694, 689, 684, 678, 675, 670, 665, 660, 655, 651, 646, + 640, 636, 632, 628, 623, 619, 614, 610, 604, 601, 597, 592, 588, 584, 580, 575, + 570, 567, 563, 559, 555, 551, 547, 543, 538, 535, 532, 528, 524, 520, 516, 513, + 508, 505, 502, 498, 494, 491, 487, 484, 480, 477, 474, 470, 467, 463, 460, 457, + 453, 450, 447, 443, 440, 437, 434, 431, 428 +}; + +/* Variables and structures */ +static XM_ID xm_id; +static XM_HEADER xm_header; +static XM_PATTERN load_pattern; +static OUR_PATTERN pattern[MAX_PATTERNS]; +static XM_INSTRUMENT *first_instr_ptr; +static XM_SAMPLE *xm_smp_ptr[MAX_INSTRUMENTS * MAX_SAMPLES]; +static SAMPLE *smp_ptr[MAX_INSTRUMENTS * MAX_SAMPLES]; +static TRACK track[MAX_CHANNELS]; +static XM_NOTE emptynote = {0, 0, 0, 0, 0}; +static char xm_loaded = 0; +static char xmplayer_firsttime = 1; +static unsigned short xm_oldpos; +static unsigned short xm_pos; +static unsigned short xm_line; +static unsigned short xm_ticktempo; +static unsigned short xm_tickcount; +static unsigned char xm_globalvol; +static unsigned char xm_globalvolspeedup; +static unsigned char xm_globalvolspeeddown; +static unsigned char xm_patternbreak; +static unsigned char xm_patterndelay; +static int lowperiod; +static int highperiod; +static int xm_roundcounter; +static char *xm_poshasbeenplayed; +static int xm_wind = 0; +static int xm_windingpause = 0; + +/* External variables */ +extern unsigned char judas_bpmtempo; +extern unsigned judas_bpmcount; +extern void (*judas_player)(void); + +void judas_forwardxm(void) +{ + xm_wind = 1; +} + +void judas_rewindxm(void) +{ + xm_wind = -1; +} + +int judas_loadxm(char *name) +{ + int handle; + int count; + + /* Don't waste memory if Nosound */ + judas_error = JUDAS_OK; + if (judas_device == DEV_NOSOUND) return 1; + judas_error = JUDAS_OUT_OF_MEMORY; + if (xmplayer_firsttime) + { + if (!init_xmplayer()) return 0; + } + /* + * Free previous XM + */ + judas_freexm(); + /* + * Try to open the file + */ + judas_error = JUDAS_OPEN_ERROR; + handle = judas_open(name); + if (handle == -1) + { + return 0; + } + /* + * Read in the identification + */ + judas_error = JUDAS_WRONG_FORMAT; + if (judas_read(handle, &xm_id, sizeof xm_id) != sizeof xm_id) + { + judas_close(handle); + return 0; + } + /* + * Check that it's an XM and the version is correct + */ + if ((memcmp("Extended Module:", xm_id.id, 16)) || (xm_id.idbyte != 0x1a)) + { + judas_close(handle); + return 0; + } + xm_id.idbyte = 0x0; /* Serves as the songname endzero from now on */ + if (xm_id.version < 0x103) + { + judas_close(handle); + return 0; + } + /* + * Read in the header + */ + judas_error = JUDAS_READ_ERROR; + if (judas_read(handle, &xm_header, sizeof xm_header) != sizeof xm_header) + { + judas_close(handle); + return 0; + } + /* + * Check that there aren't too many channels + */ + if (xm_header.channels > CHANNELS) + { + xm_header.channels = CHANNELS; + judas_error = JUDAS_OUT_OF_CHANNELS; + judas_close(handle); + return 0; + } + + xm_poshasbeenplayed = locked_malloc(256); + if (!xm_poshasbeenplayed) { + judas_close(handle); + return 0; + } + /* + * If header is longer that what we use of it, skip the extra bytes + */ + if (judas_seek(handle, xm_header.headersize - sizeof xm_header, SEEK_CUR) == -1) + { + judas_close(handle); + return 0; + } + /* + * Load the patterns + */ + for (count = 0; count < xm_header.patterns; count++) + { + unsigned char *packptr; + unsigned char *unpackptr; + unsigned packleft; + /* + * Load pattern header + */ + judas_error = JUDAS_READ_ERROR; + if (judas_read(handle, &load_pattern, sizeof load_pattern) != sizeof load_pattern) + { + judas_freexm(); + judas_close(handle); + return 0; + } + /* + * Skip extra data in pattern header + */ + if (judas_seek(handle, load_pattern.headersize - sizeof load_pattern, SEEK_CUR) == -1) + { + judas_freexm(); + judas_close(handle); + return 0; + } + /* + * Allocate memory for unpacked pattern & clear it + */ + pattern[count].rows = load_pattern.rows; + pattern[count].data = locked_malloc(5 * load_pattern.rows * xm_header.channels); + judas_error = JUDAS_OUT_OF_MEMORY; + if (!pattern[count].data) + { + judas_freexm(); + judas_close(handle); + return 0; + } + memset(pattern[count].data, 0, 5 * load_pattern.rows * xm_header.channels); + if (load_pattern.packsize) + { + unsigned char *pack_buffer; + /* + * Allocate packeddata-buffer + */ + pack_buffer = malloc(load_pattern.packsize); + if (!pack_buffer) + { + judas_freexm(); + judas_close(handle); + return 0; + } + /* + * Read the packed data in + */ + judas_error = JUDAS_READ_ERROR; + if (judas_read(handle, pack_buffer, load_pattern.packsize) != load_pattern.packsize) + { + free(pack_buffer); + judas_freexm(); + judas_close(handle); + return 0; + } + packptr = pack_buffer; + unpackptr = pattern[count].data; + packleft = load_pattern.packsize; + while (packleft) + { + unsigned char control = *packptr++; + packleft--; + /* Packed? */ + if (control & 0x80) + { + /* Note? */ + if (control & 0x01) + { + *unpackptr++ = *packptr++; + packleft--; + } + else unpackptr++; + /* Instrument? */ + if (control & 0x02) + { + *unpackptr++ = *packptr++; + packleft--; + } + else unpackptr++; + /* Volume column? */ + if (control & 0x04) + { + *unpackptr++ = *packptr++; + packleft--; + } + else unpackptr++; + /* Effect? */ + if (control & 0x08) + { + *unpackptr++ = *packptr++; + packleft--; + } + else unpackptr++; + /* Effect parameter? */ + if (control & 0x10) + { + *unpackptr++ = *packptr++; + packleft--; + } + else unpackptr++; + } + else + { + *unpackptr++ = control; /* Note */ + *unpackptr++ = *packptr++; /* Instrument */ + *unpackptr++ = *packptr++; /* Volume c. */ + *unpackptr++ = *packptr++; /* Effect */ + *unpackptr++ = *packptr++; /* Effect p. */ + packleft -= 4; + } + } + free(pack_buffer); + } + } + /* + * Load instruments. This is heavy shit. + */ + first_instr_ptr = locked_malloc(sizeof (XM_INSTRUMENT) * xm_header.instruments); + judas_error = JUDAS_OUT_OF_MEMORY; + if (!first_instr_ptr) + { + judas_freexm(); + judas_close(handle); + return 0; + } + for (count = 0; count < xm_header.instruments; count++) + { + XM_INSTRUMENT *instr_ptr = first_instr_ptr + count; + + /* + * Read the part common to all instruments + */ + judas_error = JUDAS_READ_ERROR; + if (judas_read(handle, instr_ptr, 29) != 29) + { + judas_freexm(); + judas_close(handle); + return 0; + } + if (instr_ptr->numbersamples) + { + int s; + + /* + * There are samples; read the rest in + */ + if (judas_read(handle, ((char *)instr_ptr) + 29, sizeof(XM_INSTRUMENT) - 29 - 650) != sizeof(XM_INSTRUMENT) - 29 - 650) + { + judas_freexm(); + judas_close(handle); + return 0; + } + /* + * Skip over extra shit in instr. header + */ + if (judas_seek(handle, instr_ptr->headersize - (sizeof(XM_INSTRUMENT) - 650), SEEK_CUR) == -1) + { + judas_freexm(); + judas_close(handle); + return 0; + } + /* + * Precalculate envelopes + */ + if (instr_ptr->volenvflags & ENV_ON) + { + for (s = 0; s < instr_ptr->volenvpoints; s++) + { + /* Paranoid check */ + if (instr_ptr->volenv[s].x > 324) instr_ptr->volenv[s].x = 324; + } + for (s = 0; s < instr_ptr->volenvpoints - 1; s++) + { + int x, y, dx, dy; + + x = instr_ptr->volenv[s].x; + y = instr_ptr->volenv[s].y; + dx = instr_ptr->volenv[s+1].x - instr_ptr->volenv[s].x; + dy = instr_ptr->volenv[s+1].y - instr_ptr->volenv[s].y; + if (dx) + { + for (x = 0; x < dx; x++) + { + instr_ptr->c_volenv[x + instr_ptr->volenv[s].x] = + y + dy * x / dx; + } + } + if (s == instr_ptr->volenvpoints - 2) + { + for (x = instr_ptr->volenv[s+1].x; x < 325; x++) + { + instr_ptr->c_volenv[x] = instr_ptr->volenv[s+1].y; + } + } + } + instr_ptr->volsustpoint = instr_ptr->volenv[instr_ptr->volsustpoint].x; + instr_ptr->volloopstart = instr_ptr->volenv[instr_ptr->volloopstart].x; + instr_ptr->volloopend = instr_ptr->volenv[instr_ptr->volloopend].x; + /* If zero length loop, must deactivate loop */ + if (instr_ptr->volloopstart == instr_ptr->volloopend) + { + instr_ptr->volenvflags &= ~ENV_LOOP; + } + } + if (instr_ptr->panenvflags & ENV_ON) + { + for (s = 0; s < instr_ptr->panenvpoints; s++) + { + /* Paranoid check */ + if (instr_ptr->panenv[s].x > 324) instr_ptr->panenv[s].x = 324; + } + for (s = 0; s < instr_ptr->panenvpoints - 1; s++) + { + int x, y, dx, dy; + + x = instr_ptr->panenv[s].x; + y = instr_ptr->panenv[s].y; + dx = instr_ptr->panenv[s+1].x - instr_ptr->panenv[s].x; + dy = instr_ptr->panenv[s+1].y - instr_ptr->panenv[s].y; + if (dx) + { + for (x = 0; x < dx; x++) + { + instr_ptr->c_panenv[x + instr_ptr->panenv[s].x] = + y + dy * x / dx; + } + } + if (s == instr_ptr->panenvpoints - 2) + { + for (x = instr_ptr->panenv[s+1].x; x < 325; x++) + { + instr_ptr->c_panenv[x] = instr_ptr->panenv[s+1].y; + } + } + } + instr_ptr->pansustpoint = instr_ptr->panenv[instr_ptr->pansustpoint].x; + instr_ptr->panloopstart = instr_ptr->panenv[instr_ptr->panloopstart].x; + instr_ptr->panloopend = instr_ptr->panenv[instr_ptr->panloopend].x; + /* If zero length loop, must deactivate loop */ + if (instr_ptr->panloopstart == instr_ptr->panloopend) + { + instr_ptr->panenvflags &= ~ENV_LOOP; + } + } + /* + * First round, allocate xmsample & sample structures, + * but do not read any audio data yet + */ + for (s = 0; s < instr_ptr->numbersamples; s++) + { + XM_SAMPLE *xsp; + SAMPLE *sp; + unsigned reserve; + + /* + * Allocate memory for sample header + */ + xsp = locked_malloc(sizeof(XM_SAMPLE)); + judas_error = JUDAS_OUT_OF_MEMORY; + if (!xsp) + { + judas_freexm(); + judas_close(handle); + return 0; + } + xm_smp_ptr[count * MAX_SAMPLES + s] = xsp; + + /* + * Read in sample header + */ + judas_error = JUDAS_READ_ERROR; + if (judas_read(handle, xsp, sizeof(XM_SAMPLE)) != sizeof(XM_SAMPLE)) + { + judas_freexm(); + judas_close(handle); + return 0; + } + /* + * Hop over extra shit in sample header + */ + if (judas_seek(handle, instr_ptr->sampleheadersize - sizeof(XM_SAMPLE), SEEK_CUR) == -1) + { + judas_freexm(); + judas_close(handle); + return 0; + } + /* + * If looplength is zero, loop must be de- + * activated + */ + if (!xsp->looplength) + { + xsp->sampleflags &= ~(SMP_LOOP | SMP_PINGPONGLOOP); + } + reserve = xsp->length; + if (xsp->sampleflags & SMP_PINGPONGLOOP) reserve += xsp->looplength; + /* + * There might be zero length samples, don't + * go to hell because of them + */ + if (reserve) + { + /* + * Reserve sample, go to hell if fail + */ + sp = judas_allocsample(reserve); + if (!sp) + { + judas_freexm(); + judas_close(handle); + return 0; + } + smp_ptr[count * MAX_SAMPLES + s] = sp; + } + } + /* + * Second round: time for the big shit! + */ + for (s = 0; s < instr_ptr->numbersamples; s++) + { + XM_SAMPLE *xsp = xm_smp_ptr[count * MAX_SAMPLES + s]; + SAMPLE *sp = smp_ptr[count * MAX_SAMPLES + s]; + /* + * Check if this sample exists + */ + if (sp) + { + /* + * Read sample data (delta values) + */ + + judas_error = JUDAS_READ_ERROR; + if (judas_read(handle, sp->start, xsp->length) != xsp->length) + { + judas_freexm(); + judas_close(handle); + return 0; + } + + sp->voicemode = VM_ON; + if (xsp->sampleflags & (SMP_LOOP | SMP_PINGPONGLOOP)) sp->voicemode |= VM_LOOP; + /* + * Convert to normal signed data + */ + if (xsp->sampleflags & SMP_16BIT) + { + int cc = xsp->length >> 1; + unsigned short old = 0; + unsigned short *cptr = (unsigned short *)sp->start; + + sp->voicemode |= VM_16BIT; + while (cc--) + { + *cptr += old; + old = *cptr++; + } + } + else + { + int cc = xsp->length; + unsigned char old = 0; + unsigned char *cptr = sp->start; + + while (cc--) + { + *cptr += old; + old = *cptr++; + } + } + /* + * Ugly shit: "unroll" pingpong-loop + */ + if (xsp->sampleflags & SMP_PINGPONGLOOP) + { + sp->voicemode |= VM_LOOP; + if (xsp->sampleflags & SMP_16BIT) + { + int uc = xsp->looplength >> 1; + short *source = (short *)(sp->start + xsp->loopstart + xsp->looplength - 2); + short *dest = (short *)(sp->start + xsp->loopstart + xsp->looplength); + + while (uc--) *dest++ = *source--; + } + else + { + int uc = xsp->looplength; + char *source = sp->start + xsp->loopstart + xsp->looplength - 1; + char *dest = sp->start + xsp->loopstart + xsp->looplength; + + while (uc--) *dest++ = *source--; + } + xsp->looplength <<= 1; + } + /* + * Fix repeat & end in "our" sample structure + */ + if (sp->voicemode & VM_LOOP) + { + sp->repeat = sp->start + xsp->loopstart; + sp->end = sp->start + xsp->loopstart + xsp->looplength; + } + else sp->end = sp->start + xsp->length; + /* + * Interpolation-correction + */ + judas_ipcorrect(sp); + /* + * We're done with this sample! + */ + } + } + } + else + { + /* + * Header without samples; skip over extra if any + */ + judas_error = JUDAS_READ_ERROR; + if (judas_seek(handle, instr_ptr->headersize - 29, SEEK_CUR) == -1) + { + judas_freexm(); + judas_close(handle); + return 0; + } + } + } + judas_close(handle); + judas_error = JUDAS_OK; + xm_loaded = 1; + return 1; +} + +void judas_freexm(void) +{ + int count; + + if (xmplayer_firsttime) + { + if (!init_xmplayer()) return; + } + judas_stopxm(); + xm_loaded = 0; + for (count = 0; count < MAX_PATTERNS; count++) + { + if (pattern[count].data) + { + locked_free(pattern[count].data); + pattern[count].data = NULL; + } + pattern[count].rows = 64; + } + if (first_instr_ptr) + { + locked_free(first_instr_ptr); + first_instr_ptr = NULL; + } + for (count = 0; count < MAX_SAMPLES * MAX_INSTRUMENTS; count++) + { + if (xm_smp_ptr[count]) + { + locked_free(xm_smp_ptr[count]); + xm_smp_ptr[count] = NULL; + } + if (smp_ptr[count]) + { + judas_freesample(smp_ptr[count]); + smp_ptr[count] = NULL; + } + } +} + +unsigned char judas_getxmpos(void) +{ + return xm_pos; +} + +unsigned char judas_getxmline(void) +{ + return xm_line; +} + +unsigned char judas_getxmtick(void) +{ + return xm_tickcount; +} + +unsigned char judas_getxmchannels(void) +{ + return xm_header.channels; +} + +char *judas_getxmname(void) +{ + if (xm_loaded) return xm_id.modname; + else return NULL; +} + +static int init_xmplayer(void) +{ + int count; + + xm_header.channels = 0; + for (count = 0; count < MAX_PATTERNS; count++) + { + pattern[count].data = NULL; + } + first_instr_ptr = NULL; + for (count = 0; count < MAX_SAMPLES * MAX_INSTRUMENTS; count++) + { + xm_smp_ptr[count] = NULL; + smp_ptr[count] = NULL; + } + if (!judas_locktables()) return 0; + if (!judas_memlock(&xmplayer_code_lock_start, (int)&xmplayer_code_lock_end - (int)&xmplayer_code_lock_start)) return 0; + if (!judas_memlock(&linearfreqtable, sizeof linearfreqtable)) return 0; + if (!judas_memlock(&amigafreqtable, sizeof amigafreqtable)) return 0; + if (!judas_memlock(&xm_header, sizeof xm_header)) return 0; + if (!judas_memlock(&pattern[0], sizeof pattern)) return 0; + if (!judas_memlock(&first_instr_ptr, sizeof first_instr_ptr)) return 0; + if (!judas_memlock(&xm_smp_ptr[0], sizeof xm_smp_ptr)) return 0; + if (!judas_memlock(&smp_ptr[0], sizeof smp_ptr)) return 0; + if (!judas_memlock(&track[0], sizeof track)) return 0; + if (!judas_memlock(&xm_loaded, sizeof xm_loaded)) return 0; + if (!judas_memlock(&xm_poshasbeenplayed, sizeof xm_poshasbeenplayed)) return 0; // ** + if (!judas_memlock(&xm_oldpos, sizeof xm_oldpos)) return 0; // ** + if (!judas_memlock(&xm_pos, sizeof xm_pos)) return 0; + if (!judas_memlock(&xm_line, sizeof xm_line)) return 0; + if (!judas_memlock(&xm_ticktempo, sizeof xm_ticktempo)) return 0; + if (!judas_memlock(&xm_tickcount, sizeof xm_tickcount)) return 0; + if (!judas_memlock(&xm_patternbreak, sizeof xm_patternbreak)) return 0; + if (!judas_memlock(&xm_patterndelay, sizeof xm_patterndelay)) return 0; + if (!judas_memlock(&xm_globalvol, sizeof xm_globalvol)) return 0; + if (!judas_memlock(&xm_globalvolspeedup, sizeof xm_globalvolspeedup)) return 0; + if (!judas_memlock(&xm_globalvolspeeddown, sizeof xm_globalvolspeeddown)) return 0; + if (!judas_memlock(&lowperiod, sizeof lowperiod)) return 0; + if (!judas_memlock(&highperiod, sizeof highperiod)) return 0; + if (!judas_memlock(&emptynote, sizeof emptynote)) return 0; + if (!judas_memlock(&xm_roundcounter, sizeof xm_roundcounter)) return 0; + if (!judas_memlock(&xm_wind, sizeof xm_wind)) return 0; + if (!judas_memlock(&xm_windingpause, sizeof xm_windingpause)) return 0; + xmplayer_firsttime = 0; + return 1; +} + +static void xmplayer_code_lock_start(void) +{ +} + +void judas_mutechannelsxm() +{ + int count; + TRACK *tptr = &track[0]; + CHANNEL *chptr = &judas_channel[0]; + for (count = xm_header.channels; count > 0; count--) + { + chptr->smp = NULL; + chptr->voicemode = VM_OFF; + tptr->ip = first_instr_ptr; + tptr->xsp = NULL; + tptr->sp = NULL; + tptr->instrument = 0; + tptr->smp = 0; + tptr->notevol = 0; + tptr->vol = 0; + tptr->effect = 0; + tptr->effectdata = 0; + tptr->nybble1 = 0; + tptr->nybble2 = 0; + tptr->voleffect = 0; + tptr->volenvpos = 0; + tptr->panenvpos = 0; + tptr->keyon = 0; + tptr->tp = 0; + tptr->tpspeed = 0; + tptr->volspeedup = 0; + tptr->volspeeddown = 0; + tptr->glissando = 0; + tptr->retriginterval = 0; + tptr->retrigvolchange = 0; + tptr->patternloopline = 0; + tptr->patternloopcount = 0; + tptr->sampleoffset = 0; + tptr->tremolotype = 0; + tptr->vibratotype = 0; + chptr++; + tptr++; + } +} + +void judas_playxm(int rounds) +{ + int count; + + if (!xm_loaded) return; + judas_player = NULL; + xm_roundcounter = rounds; + xm_wind = 0; + xm_pos = 0; + xm_line = 0; + xm_tickcount = 0; + xm_ticktempo = xm_header.defaulttempo; + xm_globalvol = 64; + xm_patterndelay = 0; + judas_bpmcount = 0; + judas_bpmtempo = xm_header.defaultbpmtempo; + for (count = 1; count < 256; count++) + { + xm_poshasbeenplayed[count] = 0; + } + xm_poshasbeenplayed[0] = 1; + judas_mutechannelsxm(); + if (xm_header.uselinear) + { + lowperiod = 7743; + highperiod = 64; + } + else + { + lowperiod = 29024; + highperiod = 28; + } + judas_player = &xmplayer; +} + +void judas_stopxm(void) +{ + int count; + CHANNEL *chptr = &judas_channel[0]; + + if (xm_header.channels > CHANNELS) xm_header.channels = CHANNELS; + if (!xm_loaded) return; + judas_player = NULL; + for (count = xm_header.channels; count > 0; count--) + { + chptr->smp = NULL; + chptr->voicemode = VM_OFF; + chptr++; + } +} + +void xmplayer(void) +{ + TRACK *tptr = &track[0]; + CHANNEL *chptr = &judas_channel[0]; + OUR_PATTERN *pattptr = &pattern[xm_header.order[xm_pos]]; + int count; + + /* + * Song winding + */ + if (xm_wind > 0) { + xm_wind = 0; + if (xm_pos < xm_header.songlength-1) { + int count; + xm_pos++; + xm_oldpos = xm_pos; + xm_line = 0; + xm_tickcount = 0; + xm_patterndelay = 0; + for (count = 0; count < 256; count++) + { + xm_poshasbeenplayed[count] = 0; + } + xm_poshasbeenplayed[xm_pos] = 1; + judas_mutechannelsxm(); + xm_windingpause = 1; + return; + } + } else + if (xm_wind < 0) { + xm_wind = 0; + if (xm_pos > 0) { + int count; + xm_pos--; + xm_oldpos = xm_pos; + xm_line = 0; + xm_tickcount = 0; + xm_patterndelay = 0; + for (count = 0; count < 256; count++) + { + xm_poshasbeenplayed[count] = 0; + } + xm_poshasbeenplayed[xm_pos] = 1; + judas_mutechannelsxm(); + xm_windingpause = 1; + return; + } + } + if (xm_windingpause) { + xm_windingpause = 0; + return; + } + + /* + * Set new notes or do something else? + */ + if ((!xm_tickcount) && (!xm_patterndelay)) + { + XM_NOTE *noteptr = (XM_NOTE *)pattptr->data; + + if (!noteptr) noteptr = &emptynote; + else noteptr += xm_header.channels * xm_line; + + xm_patternbreak = 0; + xm_oldpos = xm_pos; + for (count = xm_header.channels; count > 0; count--) + { + tptr->newnote = 0; + tptr->retrigcount = 0; + + /* + * Get note (if any) + */ + if (noteptr->note) + { + tptr->note = noteptr->note - 1; + tptr->newnote = 1; + } + /* + * Get effect, effect data etc. + */ + tptr->voleffect = noteptr->voleffect; + tptr->effect = noteptr->effect; + tptr->effectdata = noteptr->effectdata; + tptr->nybble1 = noteptr->effectdata >> 4; + tptr->nybble2 = noteptr->effectdata & 0xf; + tptr->newinstrument = noteptr->instrument; + + /* + * Set sampleoffset here + */ + if (tptr->effect == 0x9) + { + if (tptr->effectdata) tptr->sampleoffset = tptr->effectdata; + } + + /* + * Start new note if there is one; but check there + * isn't notedelay (toneportamento is handled by + * startnewnote()!) + * Also restart shit if an instrument number! + */ + if ((tptr->effect != 0xe) || (tptr->nybble1 != 0xd) || (tptr->nybble2 == 0)) + { + if (tptr->newnote) startnewnote(tptr, chptr); + if (tptr->newinstrument) changeinstrument(tptr); + } + + /* + * Reset period if not vibrato or toneportamento + */ + if (((tptr->effect < 0x3) || (tptr->effect > 0x6)) && (tptr->voleffect != 0xb) && (tptr->voleffect != 0xf)) + { + tptr->period = tptr->baseperiod; + } + /* + * Reset volume if not tremolo / tremor + */ + if ((tptr->effect != 0x7) && (tptr->effect != 0x1d)) + { + tptr->vol = tptr->notevol; + } + /* + * Now check some effects: volume column done first. + */ + switch (tptr->voleffect >> 4) + { + case 0x0: + break; + + /* Set volume */ + case 0x1: + case 0x2: + case 0x3: + case 0x4: + case 0x5: + /* Applies only if there isn't notedelay */ + if ((tptr->effect != 0xe) || (tptr->nybble1 != 0xd) || (tptr->nybble2 == 0)) + { + tptr->notevol = tptr->voleffect - 0x10; + if (tptr->notevol > 64) tptr->notevol = 64; + tptr->vol = tptr->notevol; + } + break; + + /* Fine volslide down */ + case 0x8: + tptr->notevol -= tptr->voleffect & 0xf; + if (tptr->notevol < 0) tptr->notevol = 0; + tptr->vol = tptr->notevol; + break; + + /* Fine volslide up */ + case 0x9: + tptr->notevol += tptr->voleffect & 0xf; + if (tptr->notevol > 64) tptr->notevol = 64; + tptr->vol = tptr->notevol; + break; + + /* Set vibrato speed */ + case 0xa: + if (tptr->voleffect & 0xf) + { + tptr->vibratospeed = tptr->voleffect & 0xf; + } + break; + + /* Vibrato */ + case 0xb: + if (tptr->voleffect & 0xf) + { + tptr->vibratodepth = tptr->voleffect & 0xf; + } + break; + + /* Set panning */ + case 0xc: + /* Applies only if there isn't notedelay */ + if ((tptr->effect != 0xe) || (tptr->nybble1 != 0xd) || (tptr->nybble2 == 0)) + { + tptr->notepanning = (tptr->voleffect & 0xf) << 4 | (tptr->voleffect & 0xf); + } + break; + + /* Toneportamento */ + case 0xf: + if (tptr->voleffect & 0xf) + { + tptr->tpspeed = (tptr->voleffect & 0xf) << 4; + } + break; + } + + /* + * Then the regular effects + */ + switch (tptr->effect) + { + case 0x0: + break; + + /* Set portamento speed up */ + case 0x1: + if (tptr->effectdata) tptr->portaspeedup = tptr->effectdata; + break; + + /* Set portamento speed down */ + case 0x2: + if (tptr->effectdata) tptr->portaspeeddown = tptr->effectdata; + break; + + /* Set TP. speed */ + case 0x3: + if (tptr->effectdata) tptr->tpspeed = tptr->effectdata; + break; + + /* Set vibrato */ + case 0x4: + if (tptr->nybble1) tptr->vibratospeed = tptr->nybble1; + if (tptr->nybble2) tptr->vibratodepth = tptr->nybble2; + break; + + /* Set tremolo */ + case 0x7: + if (tptr->nybble1) tptr->tremolospeed = tptr->nybble1; + if (tptr->nybble2) tptr->tremolodepth = tptr->nybble2; + break; + + /* Set Panning */ + case 0x8: + tptr->notepanning = tptr->effectdata; + break; + + /* Volume slide speed set */ + case 0x5: + case 0x6: + case 0xa: + if (tptr->effectdata) + { + tptr->volspeedup = tptr->nybble1; + tptr->volspeeddown = tptr->nybble2; + } + break; + + /* Pos. jump */ + case 0xb: + xm_line = pattptr->rows - 1; + xm_pos = tptr->effectdata - 1; + break; + + /* Set volume */ + case 0xc: + tptr->notevol = tptr->effectdata; + if (tptr->notevol < 0) tptr->notevol = 0; + if (tptr->notevol > 64) tptr->notevol = 64; + tptr->vol = tptr->notevol; + break; + + /* Pattern break */ + case 0xd: + xm_line = tptr->nybble1 * 10 + tptr->nybble2 - 1; + if (!xm_patternbreak) + { + xm_patternbreak = 1; + xm_pos++; + } + break; + + /* Extended command */ + case 0xe: + extendedcommand(tptr, chptr); + break; + + /* Set tempo */ + case 0xf: + if (!tptr->effectdata) { + if (xm_roundcounter != 1) + { + judas_stopxm(); + judas_playxm(xm_roundcounter); + if (xm_roundcounter) xm_roundcounter--; + return; + } else { + judas_stopxm(); + return; + } + } + if (tptr->effectdata < 32) xm_ticktempo = tptr->effectdata; + else judas_bpmtempo = tptr->effectdata; + break; + + /* Global volume */ + case 0x10: + xm_globalvol = tptr->effectdata; + if (xm_globalvol > 64) xm_globalvol = 64; + break; + + /* Global volume slide */ + case 0x11: + if (tptr->effectdata) + { + xm_globalvolspeedup = tptr->nybble1; + xm_globalvolspeeddown = tptr->nybble2; + } + break; + + /* Keyoff */ + case 0x14: + tptr->keyon = 0; + if (!(tptr->ip->volenvflags & ENV_ON)) + { + tptr->notevol = 0; + tptr->vol = 0; + } + break; + + /* Set envpos */ + case 0x15: + tptr->volenvpos = tptr->effectdata; + tptr->panenvpos = tptr->effectdata; + break; + + /* Panning slide */ + case 0x19: + if (tptr->effectdata) + { + tptr->panspeedright = tptr->nybble1; + tptr->panspeedleft = tptr->nybble2; + } + break; + + /* Multi retrig */ + case 0x1b: + if (tptr->nybble1) tptr->retrigvolchange = tptr->nybble1; + if (tptr->nybble2) tptr->retriginterval = tptr->nybble2; + if (tptr->multiretrigcount >= tptr->retriginterval) + { + startnewnote(tptr, chptr); + tptr->multiretrigcount = 0; + /* Now modify volume */ + if (!retrigmultable[tptr->retrigvolchange]) + { + tptr->notevol += retrigaddtable[tptr->retrigvolchange]; + if (tptr->notevol < 0) tptr->notevol = 0; + if (tptr->notevol > 64) tptr->notevol = 64; + tptr->vol = tptr->notevol; + } + else + { + tptr->notevol = (tptr->notevol * retrigmultable[tptr->retrigvolchange]) >> 4; + if (tptr->notevol > 64) tptr->notevol = 64; + tptr->vol = tptr->notevol; + } + } + tptr->multiretrigcount++; + break; + + /* Tremor */ + case 0x1d: + if (tptr->effectdata) + { + tptr->tremorontime = tptr->nybble1; + tptr->tremorofftime = tptr->nybble2; + } + break; + + /* Extra fine portamento */ + case 0x21: + switch (tptr->nybble1) + { + case 1: + if (tptr->nybble2) tptr->portaspeedup = tptr->nybble2; + tptr->baseperiod -= tptr->portaspeedup; + if (tptr->baseperiod < highperiod) tptr->baseperiod = highperiod; + tptr->period = tptr->baseperiod; + break; + + case 2: + if (tptr->nybble2) tptr->portaspeeddown = tptr->nybble2; + tptr->baseperiod += tptr->portaspeeddown; + if (tptr->baseperiod > lowperiod) tptr->baseperiod = lowperiod; + tptr->period = tptr->baseperiod; + break; + } + break; + } + if (noteptr != &emptynote) noteptr++; + chptr++; + tptr++; + } + } + if (xm_tickcount) + { + /* + * If tick isn't 0, update "continuous" effects + */ + for (count = xm_header.channels; count > 0; count--) + { + /* + * Volume column + */ + switch (tptr->voleffect >> 4) + { + case 0x0: + break; + + /* Volslide down */ + case 0x6: + tptr->notevol -= tptr->voleffect & 0xf; + if (tptr->notevol < 0) tptr->notevol = 0; + tptr->vol = tptr->notevol; + break; + + /* Volslide up */ + case 0x7: + tptr->notevol += tptr->voleffect & 0xf; + if (tptr->notevol > 64) tptr->notevol = 64; + tptr->vol = tptr->notevol; + break; + + /* Vibrato */ + case 0xb: + tptr->vibratophase += tptr->vibratospeed * 4; + tptr->period = tptr->baseperiod + ((vibratotable[vibratotypetable[tptr->vibratotype & 3]][tptr->vibratophase] * tptr->vibratodepth) >> 3); + if (tptr->period < highperiod) tptr->period = highperiod; + if (tptr->period > lowperiod) tptr->period = lowperiod; + break; + + /* Panslide left */ + case 0xd: + { + int new = tptr->notepanning; + new -= tptr->voleffect & 0xf; + if (new < 0) new = 0; + tptr->notepanning = new; + } + break; + + /* Panslide right */ + case 0xe: + { + int new = tptr->notepanning; + new += tptr->voleffect & 0xf; + if (new > 255) new = 255; + tptr->notepanning = new; + } + break; + + /* Toneportamento */ + case 0xf: + if (tptr->tp) + { + if (tptr->baseperiod < tptr->targetperiod) + { + tptr->baseperiod += tptr->tpspeed * 4; + if (tptr->baseperiod >= tptr->targetperiod) + { + tptr->baseperiod = tptr->targetperiod; + tptr->tp = 0; + } + } + if (tptr->baseperiod > tptr->targetperiod) + { + tptr->baseperiod -= tptr->tpspeed * 4; + if (tptr->baseperiod <= tptr->targetperiod) + { + tptr->baseperiod = tptr->targetperiod; + tptr->tp = 0; + } + } + tptr->period = tptr->baseperiod; + if (tptr->glissando) + { + if (xm_header.uselinear) + { + tptr->period += tptr->finetune / 2; + tptr->period += 32; + tptr->period &= 0xffc0; + tptr->period -= tptr->finetune / 2; + } + else + { + int offset = 0x7fffffff; + int sc; + short bestperiod = 0; + + for (sc = 0; sc <= 118; sc++) + { + int newoffset = abs(tptr->period - getamigaperiod(sc, tptr->finetune)); + + if (newoffset < offset) + { + bestperiod = getamigaperiod(sc, tptr->finetune); + offset = newoffset; + } + } + tptr->period = bestperiod; + } + } + } + break; + } + + + /* + * Regular effects + */ + switch (tptr->effect) + { + /* Arpeggio */ + case 0x0: + if (tptr->effectdata) + { + char phase = xm_tickcount % 3; + switch (phase) + { + case 0: + tptr->period = tptr->baseperiod; + break; + + case 1: + if (xm_header.uselinear) tptr->period = tptr->baseperiod - tptr->nybble1 * 64; + else tptr->period = getamigaperiod(tptr->realnote + tptr->nybble1, tptr->finetune); + if (tptr->period < highperiod) tptr->period = highperiod; + break; + + case 2: + if (xm_header.uselinear) tptr->period = tptr->baseperiod - tptr->nybble2 * 64; + else tptr->period = getamigaperiod(tptr->realnote + tptr->nybble2, tptr->finetune); + if (tptr->period < highperiod) tptr->period = highperiod; + break; + } + } + break; + + /* Portamento up */ + case 0x1: + tptr->baseperiod -= tptr->portaspeedup * 4; + if (tptr->baseperiod < highperiod) tptr->baseperiod = highperiod; + tptr->period = tptr->baseperiod; + break; + + /* Portamento down */ + case 0x2: + tptr->baseperiod += tptr->portaspeeddown * 4; + if (tptr->baseperiod > lowperiod) tptr->baseperiod = lowperiod; + tptr->period = tptr->baseperiod; + break; + + /* Toneportamento */ + case 0x3: + if (tptr->tp) + { + if (tptr->baseperiod < tptr->targetperiod) + { + tptr->baseperiod += tptr->tpspeed * 4; + if (tptr->baseperiod >= tptr->targetperiod) + { + tptr->baseperiod = tptr->targetperiod; + tptr->tp = 0; + } + } + if (tptr->baseperiod > tptr->targetperiod) + { + tptr->baseperiod -= tptr->tpspeed * 4; + if (tptr->baseperiod <= tptr->targetperiod) + { + tptr->baseperiod = tptr->targetperiod; + tptr->tp = 0; + } + } + tptr->period = tptr->baseperiod; + if (tptr->glissando) + { + if (xm_header.uselinear) + { + tptr->period += tptr->finetune / 2; + tptr->period += 32; + tptr->period &= 0xffc0; + tptr->period -= tptr->finetune / 2; + } + else + { + int offset = 0x7fffffff; + int sc; + short bestperiod = 0; + + for (sc = 0; sc <= 118; sc++) + { + int newoffset = abs(tptr->period - getamigaperiod(sc, tptr->finetune)); + + if (newoffset < offset) + { + bestperiod = getamigaperiod(sc, tptr->finetune); + offset = newoffset; + } + } + tptr->period = bestperiod; + } + } + } + break; + + /* Vibrato */ + case 0x4: + tptr->vibratophase += tptr->vibratospeed * 4; + tptr->period = tptr->baseperiod + ((vibratotable[vibratotypetable[tptr->vibratotype & 3]][tptr->vibratophase] * tptr->vibratodepth) >> 3); + if (tptr->period < highperiod) tptr->period = highperiod; + if (tptr->period > lowperiod) tptr->period = lowperiod; + break; + + /* Toneportamento + volslide */ + case 0x5: + if (tptr->tp) + { + if (tptr->baseperiod < tptr->targetperiod) + { + tptr->baseperiod += tptr->tpspeed * 4; + if (tptr->baseperiod >= tptr->targetperiod) + { + tptr->baseperiod = tptr->targetperiod; + tptr->tp = 0; + } + } + if (tptr->baseperiod > tptr->targetperiod) + { + tptr->baseperiod -= tptr->tpspeed * 4; + if (tptr->baseperiod <= tptr->targetperiod) + { + tptr->baseperiod = tptr->targetperiod; + tptr->tp = 0; + } + } + tptr->period = tptr->baseperiod; + if (tptr->glissando) + { + if (xm_header.uselinear) + { + tptr->period += tptr->finetune / 2; + tptr->period += 32; + tptr->period &= 0xffc0; + tptr->period -= tptr->finetune / 2; + } + else + { + int offset = 0x7fffffff; + int sc; + short bestperiod = 0; + + for (sc = 0; sc <= 118; sc++) + { + int newoffset = abs(tptr->period - getamigaperiod(sc, tptr->finetune)); + + if (newoffset < offset) + { + bestperiod = getamigaperiod(sc, tptr->finetune); + offset = newoffset; + } + } + tptr->period = bestperiod; + } + } + } + if (tptr->volspeedup) + { + tptr->notevol += tptr->volspeedup; + if (tptr->notevol > 64) tptr->notevol = 64; + } + else + { + tptr->notevol -= tptr->volspeeddown; + if (tptr->notevol < 0) tptr->notevol = 0; + } + tptr->vol = tptr->notevol; + break; + + /* Vibrato + volslide */ + case 0x6: + tptr->vibratophase += tptr->vibratospeed * 4; + tptr->period = tptr->baseperiod + ((vibratotable[vibratotypetable[tptr->vibratotype & 3]][tptr->vibratophase] * tptr->vibratodepth) >> 3); + if (tptr->period < highperiod) tptr->period = highperiod; + if (tptr->period > lowperiod) tptr->period = lowperiod; + if (tptr->volspeedup) + { + tptr->notevol += tptr->volspeedup; + if (tptr->notevol > 64) tptr->notevol = 64; + } + else + { + tptr->notevol -= tptr->volspeeddown; + if (tptr->notevol < 0) tptr->notevol = 0; + } + tptr->vol = tptr->notevol; + break; + + /* Tremolo */ + case 0x7: + tptr->tremolophase += tptr->tremolospeed * 4; + tptr->vol = tptr->notevol + ((vibratotable[tptr->tremolotype & 3][tptr->tremolophase] * tptr->tremolodepth) >> 4); + if (tptr->vol < 0) tptr->vol = 0; + if (tptr->vol > 64) tptr->vol = 64; + break; + + /* Volume Slide */ + case 0xa: + if (tptr->volspeedup) + { + tptr->notevol += tptr->volspeedup; + if (tptr->notevol > 64) tptr->notevol = 64; + } + else + { + tptr->notevol -= tptr->volspeeddown; + if (tptr->notevol < 0) tptr->notevol = 0; + } + tptr->vol = tptr->notevol; + break; + + /* Extended command */ + case 0xe: + extendedcommand(tptr, chptr); + break; + + /* Global volume slide */ + case 0x11: + if (xm_globalvolspeedup) + { + xm_globalvol += xm_globalvolspeedup; + if (xm_globalvol > 64) xm_globalvol = 64; + } + else + { + if (xm_globalvol > xm_globalvolspeeddown) + xm_globalvol -= xm_globalvolspeeddown; + else xm_globalvol = 0; + } + break; + + /* Panning slide */ + case 0x19: + { + int new = tptr->notepanning; + + new += tptr->panspeedright; + if (new < 0) new = 0; + new -= tptr->panspeedleft; + if (new > 255) new = 255; + tptr->notepanning = new; + } + break; + + /* Multi retrig */ + case 0x1b: + if (tptr->multiretrigcount >= tptr->retriginterval) + { + startnewnote(tptr, chptr); + tptr->multiretrigcount = 0; + /* Now modify volume */ + if (!retrigmultable[tptr->retrigvolchange]) + { + tptr->notevol += retrigaddtable[tptr->retrigvolchange]; + if (tptr->notevol < 0) tptr->notevol = 0; + if (tptr->notevol > 64) tptr->notevol = 64; + tptr->vol = tptr->notevol; + } + else + { + tptr->notevol = (tptr->notevol * retrigmultable[tptr->retrigvolchange]) >> 4; + if (tptr->notevol > 64) tptr->notevol = 64; + tptr->vol = tptr->notevol; + } + } + tptr->multiretrigcount++; + break; + + /* Tremor */ + case 0x1d: + if (!tptr->tremorcount) + { + tptr->tremorstatus ^= 1; + if (tptr->tremorstatus) tptr->tremorcount = tptr->tremorontime + 1; + else tptr->tremorcount = tptr->tremorofftime + 1; + } + if (tptr->tremorstatus) tptr->vol = tptr->notevol; + else tptr->vol = 0; + tptr->tremorcount--; + break; + } + chptr++; + tptr++; + } + } + + /* + * Update envelopes and set the frequency, volume & panning of + * the channel (unlike in mods, these depend on many things) + */ + tptr = &track[0]; + chptr = &judas_channel[0]; + for (count = xm_header.channels; count > 0; count--) + { + XM_INSTRUMENT *ip = tptr->ip; + short finalperiod = tptr->period; + + /* + * Update fadeout (must be done BEFORE before calculating + * the final volume -- checked visually by recording FT2's + * sound output to tape and then sampling it!) + */ + if (!tptr->keyon) + { + tptr->fadeoutvalue -= ip->fadeout; + if (tptr->fadeoutvalue < 0) tptr->fadeoutvalue = 0; + } + + /* + * Calculate final channel values + */ + { + int finalpan; + int finalvol; + if (ip->volenvflags & ENV_ON) finalvol = (tptr->vol * xm_globalvol * ip->c_volenv[tptr->volenvpos] * (tptr->fadeoutvalue >> 9)) >> 10; + else finalvol = (tptr->vol * xm_globalvol * (tptr->fadeoutvalue >> 9)) >> 4; + if (ip->panenvflags & ENV_ON) finalpan = tptr->notepanning + (ip->c_panenv[tptr->panenvpos] - 32) * (128 - abs(tptr->notepanning - 128)) / 32; + else finalpan = tptr->notepanning; + if (finalpan < 0) finalpan = 0; + if (finalpan > 255) finalpan = 255; + if (finalvol > 64*256) finalvol = 64*256; + chptr->vol = finalvol; + chptr->panning = finalpan; + } + /* + * Update instrument vibrato + */ + if ((ip->vibratodepth) && (ip->vibratorate)) + { + if (ip->vibratosweep) + { + if (tptr->keyon) + { + tptr->instrvibratodepth += (ip->vibratodepth << 8) / ip->vibratosweep; + if (tptr->instrvibratodepth > (ip->vibratodepth << 8)) tptr->instrvibratodepth = ip->vibratodepth << 8; + } + } + else tptr->instrvibratodepth = ip->vibratodepth << 8; + tptr->instrvibratophase += ip->vibratorate; + finalperiod += (vibratotable[ip->vibratotype & 3][tptr->instrvibratophase] * tptr->instrvibratodepth) >> 14; + } + if (finalperiod < (highperiod - 15)) finalperiod = highperiod - 15; + if (finalperiod > (lowperiod + 15)) finalperiod = lowperiod + 15; + if (xm_header.uselinear) chptr->freq = linearfreqtable[finalperiod % 768] >> (finalperiod / 768); + else chptr->freq = 14317456 / finalperiod; + /* + * Update envelopes (must be done AFTER setting the finalvol + * or else EASTWING.XM will sound incorrect!) + */ + if ((ip->volenvflags & ENV_ON) && ((!(ip->volenvflags & ENV_SUSTAIN)) || (!tptr->keyon) || (tptr->volenvpos != ip->volsustpoint))) + { + tptr->volenvpos++; + if (tptr->volenvpos > 324) tptr->volenvpos = 324; + if (ip->volenvflags & ENV_LOOP) + { + if (tptr->volenvpos >= ip->volloopend) + tptr->volenvpos = ip->volloopstart; + } + } + if ((ip->panenvflags & ENV_ON) && ((!(ip->panenvflags & ENV_SUSTAIN)) || (!tptr->keyon) || (tptr->panenvpos != ip->pansustpoint))) + { + tptr->panenvpos++; + if (tptr->panenvpos > 324) tptr->panenvpos = 324; + if (ip->panenvflags & ENV_LOOP) + { + if (tptr->panenvpos >= ip->panloopend) + tptr->panenvpos = ip->panloopstart; + } + } + + tptr++; + chptr++; + } + /* + * Advance song + */ + if (xm_ticktempo) + { + xm_tickcount++; + if (xm_tickcount >= xm_ticktempo) + { + xm_tickcount = 0; + if (xm_patterndelay) + { + xm_patterndelay--; + } + if (!xm_patterndelay) + { + xm_line++; + if (xm_line >= pattptr->rows) + { + xm_line = 0; + xm_pos++; + } + if (xm_pos >= xm_header.songlength) { + xm_pos = xm_header.restartpos; + xm_oldpos = xm_pos; + if (xm_roundcounter != 1) + { + int count; + if (xm_roundcounter) xm_roundcounter--; + for (count = 0; count < 256; count++) + { + xm_poshasbeenplayed[count] = 0; + } + xm_poshasbeenplayed[xm_pos] = 1; + } + else { + judas_stopxm(); + return; + } + } + /* + * Song looping detection & control! + */ + if (xm_pos != xm_oldpos) + { + if (xm_poshasbeenplayed[xm_pos]) + { + if (xm_roundcounter != 1) + { + int count; + if (xm_roundcounter) xm_roundcounter--; + for (count = 0; count < 256; count++) + { + xm_poshasbeenplayed[count] = 0; + } + xm_poshasbeenplayed[xm_pos] = 1; + } + else { + judas_stopxm(); + return; + } + } + else xm_poshasbeenplayed[xm_pos] = 1; + } + } + } + } +} + +static void startnewnote(TRACK *tptr, CHANNEL *chptr) +{ + if (tptr->newinstrument) tptr->instrument = tptr->newinstrument - 1; + /* + * Handle keyoff + */ + if (tptr->note == KEYOFF - 1) + { + tptr->keyon = 0; + if (!(tptr->ip->volenvflags & ENV_ON)) + { + tptr->notevol = 0; + tptr->vol = 0; + } + return; + } + + /* + * Now get instrptr & sampleptr, but DON'T change them if using + * toneportamento + */ + if ((tptr->effect == 0x3) || (tptr->effect == 0x5) || ((tptr->voleffect >> 4) == 0xf)) + { + tptr->tp = 1; + } + else + { + tptr->tp = 0; + if (tptr->instrument < xm_header.instruments) tptr->ip = first_instr_ptr + tptr->instrument; + tptr->smp = tptr->ip->sampletable[tptr->note]; + tptr->xsp = xm_smp_ptr[tptr->instrument * MAX_SAMPLES + tptr->smp]; + tptr->sp = smp_ptr[tptr->instrument * MAX_SAMPLES + tptr->smp]; + } + /* + * Don't go further if sample doesn't exist + */ + if ((tptr->xsp) && (tptr->sp)) + { + tptr->finetune = tptr->xsp->finetune; + tptr->multiretrigcount = 1; + if (!(tptr->vibratotype & 4)) tptr->vibratophase = 0; + if (!(tptr->tremolotype & 4)) tptr->tremolophase = 0; + tptr->tremorcount = 0; + tptr->tremorstatus = 0; + tptr->realnote = tptr->note + tptr->xsp->relativenote; + if (tptr->realnote < 0) tptr->realnote = 0; + if (tptr->realnote > 118) tptr->realnote = 118; + if (tptr->tp) + { + /* + * Toneportamento + */ + if (xm_header.uselinear) tptr->targetperiod = 7680 - tptr->realnote * 64 - tptr->finetune / 2; + else tptr->targetperiod = getamigaperiod(tptr->realnote, tptr->finetune); + } + else + { + /* + * Normal note start + */ + if (xm_header.uselinear) tptr->baseperiod = 7680 - tptr->realnote * 64 - tptr->finetune / 2; + else tptr->baseperiod = getamigaperiod(tptr->realnote, tptr->finetune); + tptr->period = tptr->baseperiod; + chptr->fractpos = 0; + chptr->pos = tptr->sp->start; + chptr->repeat = tptr->sp->repeat; + chptr->end = tptr->sp->end; + chptr->voicemode = tptr->sp->voicemode; + chptr->smp = tptr->sp; + if (tptr->effect == 0x9) + { + char *newpos; + + if (tptr->sp->voicemode & VM_16BIT) newpos = tptr->sp->start + (tptr->sampleoffset << 9); + else newpos = tptr->sp->start + (tptr->sampleoffset << 8); + if (newpos >= tptr->sp->end) + { + chptr->voicemode = VM_OFF; + chptr->smp = NULL; + } + else chptr->pos = newpos; + } + } + } +} + +/* + * Extended commands can occur both at tick 0 and on other ticks; make it a + * function to prevent having to write it twice in the code + */ +static void extendedcommand(TRACK *tptr, CHANNEL *chptr) +{ + switch(tptr->nybble1) + { + /* Fine porta up */ + case 0x1: + if (!xm_tickcount) + { + if (tptr->nybble2) tptr->portaspeedup = tptr->nybble2; + tptr->baseperiod -= tptr->portaspeedup * 4; + if (tptr->baseperiod < highperiod) tptr->baseperiod = highperiod; + } + break; + + /* Fine porta down */ + case 0x2: + if (!xm_tickcount) + { + if (tptr->nybble2) tptr->portaspeeddown = tptr->nybble2; + tptr->baseperiod += tptr->portaspeeddown * 4; + if (tptr->baseperiod > lowperiod) tptr->baseperiod = lowperiod; + } + break; + + /* Set glissando */ + case 0x3: + if (!xm_tickcount) tptr->glissando = tptr->nybble2; + break; + + /* Set vibrato waveform */ + case 0x4: + if (!xm_tickcount) + { + tptr->vibratotype = tptr->nybble2 & 3; + tptr->vibratotype |= tptr->nybble2 & 4; + } + break; + + /* Set finetune */ + case 0x5: + if ((!xm_tickcount) && (tptr->newnote)) + { + tptr->finetune = (tptr->nybble2 << 4) - 128; + if (xm_header.uselinear) tptr->baseperiod = 7680 - tptr->realnote * 64 - tptr->finetune / 2; + else tptr->baseperiod = getamigaperiod(tptr->realnote, tptr->finetune); + tptr->period = tptr->baseperiod; + } + break; + + /* Patternloop */ + case 0x6: + if (!xm_tickcount) + { + if (!tptr->nybble2) tptr->patternloopline = xm_line; + else + { + if (!tptr->patternloopcount) + { + tptr->patternloopcount = tptr->nybble2; + xm_line = tptr->patternloopline - 1; + } + else + { + tptr->patternloopcount--; + if (tptr->patternloopcount) xm_line = tptr->patternloopline - 1; + } + } + } + break; + + /* Set tremolo waveform */ + case 0x7: + if (!xm_tickcount) + { + tptr->tremolotype = vibratotypetable[tptr->nybble2 & 3]; + tptr->tremolotype |= tptr->nybble2 & 4; + } + break; + + /* Retrig */ + case 0x9: + if (tptr->nybble2) + { + if (tptr->retrigcount >= tptr->nybble2) + { + tptr->retrigcount = 0; + /* Don't retrig on tick 0 */ + if (xm_tickcount) + { + startnewnote(tptr, chptr); + tptr->keyon = 1; + tptr->fadeoutvalue = 32768; + tptr->volenvpos = 0; + tptr->panenvpos = 0; + tptr->instrvibratophase = 0; + tptr->instrvibratodepth = 0; + } + } + } + else + { + /* + * Special case e90: starts the note at tick 0 but + * doesn't restart envelope! + */ + if (!xm_tickcount) + { + startnewnote(tptr, chptr); + } + } + tptr->retrigcount++; + break; + + /* Notedelay */ + case 0xd: + /* Don't start on tick 0 */ + if (!xm_tickcount) break; + if (xm_tickcount == tptr->nybble2) + { + startnewnote(tptr, chptr); + if (tptr->newinstrument) changeinstrument(tptr); + else + { + tptr->keyon = 1; + tptr->fadeoutvalue = 32768; + tptr->volenvpos = 0; + tptr->panenvpos = 0; + tptr->instrvibratophase = 0; + tptr->instrvibratodepth = 0; + } + if ((tptr->voleffect >= 0x10) && (tptr->voleffect <= 0x50)) + { + tptr->notevol = tptr->voleffect - 0x10; + tptr->vol = tptr->notevol; + } + if ((tptr->voleffect >> 4) == 0xc) + { + tptr->notepanning = (tptr->voleffect & 0xf) << 4 | (tptr->voleffect & 0xf); + } + } + break; + + /* Cut note */ + case 0xc: + if (xm_tickcount == tptr->nybble2) + { + tptr->notevol = 0; + tptr->vol = 0; + } + break; + + /* Fine volslide up */ + case 0xa: + if (!xm_tickcount) + { + if (tptr->nybble2) tptr->volspeedup = tptr->nybble2; + tptr->notevol += tptr->volspeedup; + if (tptr->notevol > 64) tptr->notevol = 64; + } + break; + + /* Fine volslide down */ + case 0xb: + if (!xm_tickcount) + { + if (tptr->nybble2) tptr->volspeeddown = tptr->nybble2; + tptr->notevol -= tptr->volspeeddown; + if (tptr->notevol < 0) tptr->notevol = 0; + } + break; + + /* Patterndelay */ + case 0xe: + if (!xm_tickcount) + { + xm_patterndelay = tptr->nybble2 + 1; + } + break; + } +} + +static void changeinstrument(TRACK *tptr) +{ + tptr->instrument = tptr->newinstrument - 1; + if (tptr->xsp) + { + tptr->notepanning = tptr->xsp->panning; + tptr->notevol = tptr->xsp->vol; + tptr->vol = tptr->notevol; + } + /* + * Must NOT restart envelopes if there's a keyoff just in that place + */ + if ((!tptr->newnote) || (tptr->note != KEYOFF - 1)) + { + tptr->keyon = 1; + tptr->fadeoutvalue = 32768; + tptr->volenvpos = 0; + tptr->panenvpos = 0; + tptr->instrvibratophase = 0; + tptr->instrvibratodepth = 0; + } +} + +static int getamigaperiod(int note, int finetune) +{ + int noteoffset = (note % 12) << 3; + if (finetune >= 0) + { + int roughfinetune = finetune / 16; + int finefinetune = finetune & 15; + return ((amigafreqtable[8 + noteoffset + roughfinetune] * (16 - finefinetune) + + amigafreqtable[9 + noteoffset + roughfinetune] * finefinetune) + * 2) >> (note / 12); + } + else + { + int roughfinetune = (finetune - 15) / 16; + int finefinetune = finetune & 15; + return ((amigafreqtable[8 + noteoffset + roughfinetune] * (16 - finefinetune) + + amigafreqtable[9 + noteoffset + roughfinetune] * finefinetune) + * 2) >> (note / 12); + } +} + +static void xmplayer_code_lock_end(void) +{ +} + diff --git a/JUVSCPW.GIF b/JUVSCPW.GIF new file mode 100644 index 0000000000000000000000000000000000000000..7f7ee0cb44b52ffbe43e859e9a76b39e06838388 GIT binary patch literal 21603 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 +#include +#include "judasmem.h" +#ifdef __DJGPP__ +#include +#include +#endif + +typedef struct +{ + unsigned char rawcode; + unsigned char ascii; + char *name; +} KEY; + +int kbd_init(void); +void kbd_uninit(void); +unsigned char waitkey(void); +unsigned char kbd_getkey(void); +int checkkey(unsigned char rawcode); +unsigned char getascii(unsigned char rawcode); +char *getname(unsigned char rawcode); +static int kbd_lock(void); +static void kbd_unlock(void); + +unsigned short kbd_get_ds(void); +#ifdef __DJGPP__ +/* DJGPP version */ +inline unsigned short kbd_get_ds(void) { + return (unsigned short)_my_ds(); +} + +#else +/* WATCOM C++ version */ +#pragma aux kbd_get_ds = \ +"mov ax, ds" \ +modify [ax] \ +value [ax]; +#endif + + +extern int kbd_code_lock_start; +extern int kbd_code_lock_end; +extern unsigned char kt[]; +extern unsigned short kbd_ds; +#ifdef __WATCOMC__ +extern void (__interrupt __far kbd_handler)(); +#else +void kbd_handler(void); +#endif + +static unsigned char kbd_initialized = 0; +#ifdef __DJGPP__ +/* DJGPP version + __dpmi_paddr structure contains (unsigned long offset32, unsigned short selector) */ +__dpmi_paddr kbd_oldvect; +__dpmi_paddr kbd_newvect; + +#else +/* WATCOM C++ version */ +static void (__interrupt __far *kbd_oldvect)(); +static void (__interrupt __far *kbd_newvect)(); +#endif + +static KEY keyname[] = {{0x0e, 8, "BACKSPACE"}, + {0x3a, 0, "CAPSLOCK"}, + {0x1c, 13, "ENTER"}, + {0x01, 27, "ESC"}, + {0x45, 0, "NUMLOCK"}, + {0x36, 0, "SHIFT"}, + {0x46, 0, "SCR. LOCK"}, + {0x39, ' ', "SPACE"}, + {0x0f, 0, "TAB"}, + {0x3b, 0, "F1"}, + {0x3c, 0, "F2"}, + {0x3d, 0, "F3"}, + {0x3e, 0, "F4"}, + {0x3f, 0, "F5"}, + {0x40, 0, "F6"}, + {0x41, 0, "F7"}, + {0x42, 0, "F8"}, + {0x43, 0, "F9"}, + {0x44, 0, "F10"}, + {0x57, 0, "F11"}, + {0x58, 0, "F12"}, + {0x54, 0, "SYSTEM REQ."}, + {0x4c, '5', "GREY 5"}, + {0x37, '*', "GREY *"}, + {0x4a, '-', "GREY -"}, + {0x4e, '+', "GREY +"}, + {0x1e, 'A', "A"}, + {0x31, 'N', "N"}, + {0x30, 'B', "B"}, + {0x18, 'O', "O"}, + {0x2e, 'C', "C"}, + {0x19, 'P', "P"}, + {0x20, 'D', "D"}, + {0x10, 'Q', "Q"}, + {0x12, 'E', "E"}, + {0x13, 'R', "R"}, + {0x21, 'F', "F"}, + {0x1f, 'S', "S"}, + {0x22, 'G', "G"}, + {0x14, 'T', "T"}, + {0x23, 'H', "H"}, + {0x16, 'U', "U"}, + {0x17, 'I', "I"}, + {0x2f, 'V', "V"}, + {0x24, 'J', "J"}, + {0x11, 'W', "W"}, + {0x25, 'K', "K"}, + {0x2D, 'X', "X"}, + {0x26, 'L', "L"}, + {0x15, 'Y', "Y"}, + {0x32, 'M', "M"}, + {0x2c, 'Z', "Z"}, + {0x02, '1', "1"}, + {0x03, '2', "2"}, + {0x04, '3', "3"}, + {0x05, '4', "4"}, + {0x06, '5', "5"}, + {0x07, '6', "6"}, + {0x08, '7', "7"}, + {0x09, '8', "8"}, + {0x0a, '9', "9"}, + {0x0b, '0', "0"}, + {0x0c, '-', "-"}, + {0x0d, '=', "="}, + {0x1a, '[', "["}, + {0x1b, ']', "]"}, + {0x1c, ';', ";"}, + {0x27, ';', ";"}, + {0x28, 0x27, "'"}, + {0x29, '`', "`"}, + {0x33, ',', ","}, + {0x34, '.', "."}, + {0x35, '/', "/"}, + {0x2b, '\\', "\\"}, + {0x56, 0, "<>"}, + {0x37 + 0x80, 0, "PRT. SCR."}, + {0x38 + 0x80, 0, "ALT"}, + {0x1d + 0x80, 0, "CTRL"}, + {0x35 + 0x80, '/', "GREY /"}, + {0x53 + 0x80, 8, "DELETE"}, + {0x50 + 0x80, 0, "CRS. DOWN"}, + {0x4f + 0x80, 0, "END"}, + {0x47 + 0x80, 0, "HOME"}, + {0x52 + 0x80, 0, "INSERT"}, + {0x4b + 0x80, 0, "CRS. LEFT"}, + {0x51 + 0x80, 0, "PAGE DOWN"}, + {0x49 + 0x80, 0, "PAGE UP"}, + {0x4d + 0x80, 0, "CRS. RIGHT"}, + {0x48 + 0x80, 0, "CRS. UP"}, + {0x5b + 0x80, 0, "WINDOWS KEY"}, + {0x5d + 0x80, 0, "MENU KEY"}, + {0xff, 0, "?"}}; + +int kbd_init(void) +{ + if (kbd_initialized) return 1; + if (!kbd_lock()) return 0; + kbd_ds = kbd_get_ds(); + + #ifdef __DJGPP__ + /* DJGPP version */ + kbd_newvect.offset32 = (unsigned long) &kbd_handler; + kbd_newvect.selector = (unsigned short) _my_cs(); + __dpmi_get_protected_mode_interrupt_vector(9, &kbd_oldvect); + __dpmi_set_protected_mode_interrupt_vector(9, &kbd_newvect); + + #else + /* WATCOM C++ version */ + kbd_newvect = &kbd_handler; + kbd_oldvect = _dos_getvect(9); + _dos_setvect(9, kbd_newvect); + #endif + + kbd_initialized = 1; + return 1; +} + +void kbd_uninit(void) +{ + if (!kbd_initialized) return; + #ifdef __DJGPP__ + /* DJGPP version */ + __dpmi_set_protected_mode_interrupt_vector(9, &kbd_oldvect); + + #else + /* WATCOM C++ version */ + _dos_setvect(9, kbd_oldvect); + #endif + + kbd_unlock(); + kbd_initialized = 0; +} + + +static int kbd_lock(void) +{ + if (!judas_memlock(&kbd_code_lock_start, (int)&kbd_code_lock_end - (int)&kbd_code_lock_start)) return 0; + return 1; +} + +static void kbd_unlock(void) +{ + judas_memunlock(&kbd_code_lock_start, (int)&kbd_code_lock_end - (int)&kbd_code_lock_start); +} + +unsigned char waitkey(void) +{ + int index; + + for (;;) + { + for (index = 0; index < 255; index++) + { + if (kt[index]) + { + kt[index] = 0; + return index; + } + } + } +} + +unsigned char kbd_getkey(void) +{ + int index; + + for (index = 0; index < 255; index++) + { + if (kt[index]) + { + kt[index] = 0; + return index; + } + } + return 0; +} + +int checkkey(unsigned char rawcode) +{ + if (kt[rawcode]) + { + kt[rawcode] = 0; + return 1; + } + return 0; +} + +unsigned char getascii(unsigned char rawcode) +{ + KEY *ptr = &keyname[0]; + + while (ptr->rawcode != 255) + { + if (ptr->rawcode == rawcode) + { + return ptr->ascii; + } + ptr++; + } + return 0x00; +} + +char *getname(unsigned char rawcode) +{ + KEY *ptr = &keyname[0]; + + while (ptr->rawcode != 0xff) + { + if (ptr->rawcode == rawcode) + { + return ptr->name; + } + ptr++; + } + return ptr->name; +} + + diff --git a/KBD.H b/KBD.H new file mode 100644 index 0000000..cb88a78 --- /dev/null +++ b/KBD.H @@ -0,0 +1,111 @@ +#define NOTPRESSED 0 +#define PRESSED 128 +#define KEY_BACKSPACE 0x0E +#define KEY_CAPSLOCK 0x3A +#define KEY_ENTER 0x1C +#define KEY_ESC 0x01 +#define KEY_ALT 0xb8 +#define KEY_CTRL 0x9D +#define KEY_SHIFT 0x36 +#define KEY_NUMLOCK 0x45 +#define KEY_SCROLLLOCK 0x46 +#define KEY_SPACE 0x39 +#define KEY_TAB 0x0F +#define KEY_F1 0x3B +#define KEY_F2 0x3C +#define KEY_F3 0x3D +#define KEY_F4 0x3E +#define KEY_F5 0x3F +#define KEY_F6 0x40 +#define KEY_F7 0x41 +#define KEY_F8 0x42 +#define KEY_F9 0x43 +#define KEY_F10 0x44 +#define KEY_F11 0x57 +#define KEY_F12 0x58 +#define KEY_GREY0 0x52 +#define KEY_GREY1 0x4F +#define KEY_GREY2 0x50 +#define KEY_GREY3 0x51 +#define KEY_GREY4 0x4B +#define KEY_GREY5 0x4C +#define KEY_GREY6 0x4D +#define KEY_GREY7 0x47 +#define KEY_GREY8 0x48 +#define KEY_GREY9 0x49 +#define KEY_GREYCOLON 0x53 +#define KEY_GREYASTERSK 0x37 +#define KEY_GREYMINUS 0x4A +#define KEY_GREYPLUS 0x4E +#define KEY_A 0x1E +#define KEY_N 0x31 +#define KEY_B 0x30 +#define KEY_O 0x18 +#define KEY_C 0x2E +#define KEY_P 0x19 +#define KEY_D 0x20 +#define KEY_Q 0x10 +#define KEY_E 0x12 +#define KEY_R 0x13 +#define KEY_F 0x21 +#define KEY_S 0x1F +#define KEY_G 0x22 +#define KEY_T 0x14 +#define KEY_H 0x23 +#define KEY_U 0x16 +#define KEY_I 0x17 +#define KEY_V 0x2F +#define KEY_J 0x24 +#define KEY_W 0x11 +#define KEY_K 0x25 +#define KEY_X 0x2D +#define KEY_L 0x26 +#define KEY_Y 0x15 +#define KEY_M 0x32 +#define KEY_Z 0x2C +#define KEY_1 0x02 +#define KEY_2 0x03 +#define KEY_3 0x04 +#define KEY_4 0x05 +#define KEY_5 0x06 +#define KEY_6 0x07 +#define KEY_7 0x08 +#define KEY_8 0x09 +#define KEY_9 0x0A +#define KEY_0 0x0B +#define KEY_MINUS 0x0C +#define KEY_EQUAL 0x0D +#define KEY_BRACKETL 0x1A +#define KEY_BRACKETR 0x1B +#define KEY_SEMICOLON 0x1C +#define KEY_APOST1 0x28 +#define KEY_APOST2 0x29 +#define KEY_COMMA 0x33 +#define KEY_COLON 0x34 +#define KEY_SLASH 0x35 +#define KEY_BACKSLASH 0x2B +#define KEY_PRTSC 0x37 + 0x80 +#define KEY_SLASH2 0x35 + 0x80 +#define KEY_DEL 0x53 + 0x80 +#define KEY_DOWN 0x50 + 0x80 +#define KEY_END 0x4F + 0x80 +#define KEY_HOME 0x47 + 0x80 +#define KEY_INS 0x52 + 0x80 +#define KEY_LEFT 0x4B + 0x80 +#define KEY_PGDN 0x51 + 0x80 +#define KEY_PGUP 0x49 + 0x80 +#define KEY_RIGHT 0x4D + 0x80 +#define KEY_UP 0x48 + 0x80 +#define KEY_WINDOWS 0x5B + 0x80 +#define KEY_MENU 0x5D + 0x80 +#define KEY_PAUSE 0xFF + +int kbd_init(void); +void kbd_uninit(void); +int waitkey(void); +int kbd_getkey(void); +int checkkey(int rawcode); +unsigned char getascii(int rawcode); +char *getname(int rawcode); + +extern unsigned char kt[]; diff --git a/KBDASM.ASM b/KBDASM.ASM new file mode 100644 index 0000000..769261a --- /dev/null +++ b/KBDASM.ASM @@ -0,0 +1,116 @@ +; Keyboard interrupt handler +; Modified by BSpider for NASM on 5th Sep 2006 + + %idefine offset + %include "segments.inc" + %ifdef djgpp + section .text + %else + segment _TEXT + %endif + +%define KUP 0 +%define KDOWN 128 +%define FALSE 0 +%define TRUE 1 + + global _kt + global _kbd_code_lock_start + global _kbd_code_lock_end + global _kbd_ds + ; expose both callinf conventions + global kbd_handler_ + global _kbd_handler + + align 4 + +_kbd_code_lock_start: + +_kbd_ds dw 0 +_kt times 256 db (KUP) +extendedkey db 0 +pausekey db 0 +xlattable db 00h, 01h, 02h, 03h, 04h, 05h, 06h, 07h + db 08h, 09h, 0ah, 0bh, 0ch, 0dh, 0eh, 0fh + db 10h, 11h, 12h, 13h, 14h, 15h, 16h, 17h + db 18h, 19h, 1ah, 1bh, 1ch, 9dh, 1eh, 1fh + db 20h, 21h, 22h, 23h, 24h, 25h, 26h, 27h + db 28h, 29h, 36h, 2bh, 2ch, 2dh, 2eh, 2fh + db 30h, 31h, 32h, 33h, 34h, 35h, 36h, 37h + db 0b8h, 39h, 3ah, 3bh, 3ch, 3dh, 3eh, 3fh + db 40h, 41h, 42h, 43h, 44h, 45h, 46h, 0c7h + db 0c8h, 0c9h, 4ah, 0cbh, 4ch, 0cdh, 4eh, 0cfh + db 0d0h, 0d1h, 0d2h, 0d3h, 54h, 55h, 56h, 57h + db 58h, 59h, 5ah, 5bh, 5ch, 5dh, 5eh, 5fh + db 60h, 61h, 62h, 63h, 64h, 65h, 66h, 67h + db 68h, 69h, 6ah, 6bh, 6ch, 6dh, 6eh, 6fh + db 70h, 71h, 72h, 73h, 74h, 75h, 76h, 77h + db 78h, 79h, 7ah, 7bh, 7ch, 7dh, 7eh, 7fh + db 80h, 81h, 82h, 83h, 84h, 85h, 86h, 87h + db 88h, 89h, 8ah, 8bh, 8ch, 8dh, 8eh, 8fh + db 90h, 91h, 92h, 93h, 94h, 95h, 96h, 97h + db 98h, 99h, 9ah, 9bh, 1ch, 9dh, 9eh, 9fh + db 0a0h, 0a1h, 0a2h, 0a3h, 0a4h, 0a5h, 0a6h, 0a7h + db 0a8h, 0a9h, 0aah, 0abh, 0ach, 0adh, 0aeh, 0afh + db 0b0h, 0b1h, 0b2h, 0b3h, 0b4h, 0b5h, 0b6h, 0b7h + db 0b8h, 0b9h, 0bah, 0bbh, 0bch, 0bdh, 0beh, 0bfh + db 0c0h, 0c1h, 0c2h, 0c3h, 0c4h, 0c5h, 0c6h, 0c7h + db 0c8h, 0c9h, 0cah, 0cbh, 0cch, 0cdh, 0ceh, 0cfh + db 0d0h, 0d1h, 0d2h, 0d3h, 0d4h, 0d5h, 0d6h, 0d7h + db 0d8h, 0d9h, 0dah, 0dbh, 0dbh, 0ddh, 0deh, 0dfh + db 0e0h, 0e1h, 0e2h, 0e3h, 0e4h, 0e5h, 0e6h, 0e7h + db 0e8h, 0e9h, 0eah, 0ebh, 0ech, 0edh, 0eeh, 0efh + db 0f0h, 0f1h, 0f2h, 0f3h, 0f4h, 0f5h, 0f6h, 0f7h + db 0f8h, 0f9h, 0fah, 0fbh, 0fch, 0fdh, 0feh, 0ffh + + align 4 + + +kbd_handler_: +_kbd_handler: + push DS ;Save registers + push EAX + push EBX + mov AX, [CS:_kbd_ds] + mov DS, AX + sti ;As early as possible + in AL, 60h ;Read keycode + cmp byte [pausekey], FALSE ;Pause codes left? + je kbd_nopause + dec byte [pausekey] ;Maybe next time... + jmp kbd_done +kbd_nopause: cmp AL, 0e0h ;Will the next be + jne kbd_noextended ;extended? + mov byte [extendedkey], TRUE + jmp kbd_done +kbd_noextended: cmp AL, 0e1h ;Is it the pause stream? + jne kbd_nopause2 + mov byte [_kt+0ffh], KDOWN ;Store Pause Key-Down + mov byte [pausekey], 5 ;There'll be five codes + jmp kbd_done ;coming to discard +kbd_nopause2: mov AH, AL + and AH, 80h ;AH holds the up/down + xor AH, 80h ;status + and AL, 7fh + cmp byte [extendedkey], TRUE ;Was this supposed to be + jne kbd_dontadd80 ;an extended code? + mov byte [extendedkey], FALSE + cmp AL, 2ah ;Skip "system request" + je kbd_done + add AL, 80h +kbd_dontadd80: mov EBX, offset xlattable + xlat + xor EBX, EBX ;EBX is the key table + mov BL, AL ;index + add EBX, offset _kt + mov [EBX], AH +kbd_done: mov AL, 20h ;Non-specific end of + out 20h, AL ;interrupt + pop EBX ;Restore used registers + pop EAX + pop DS + iretd ;Return from interrupt + +_kbd_code_lock_end: + +; end diff --git a/MAKEEXDJ.BAT b/MAKEEXDJ.BAT new file mode 100644 index 0000000..fdd7f2f --- /dev/null +++ b/MAKEEXDJ.BAT @@ -0,0 +1,6 @@ +set DJGPP=C:\DJGPP\DJGPP.ENV +set PATH=C:\DJGPP\BIN;%PATH% +del *.exe +make.exe -f jp.dj +rem make.exe -f anal.dj + diff --git a/MAKEEXWT.BAT b/MAKEEXWT.BAT new file mode 100644 index 0000000..617f1ad --- /dev/null +++ b/MAKEEXWT.BAT @@ -0,0 +1,5 @@ +rem rebuild example files +del *.exe +wmake /f jp.wat +wmake /f anal.wat +del *.obj diff --git a/MAKEFILE.DJ b/MAKEFILE.DJ new file mode 100644 index 0000000..e5942ba --- /dev/null +++ b/MAKEFILE.DJ @@ -0,0 +1,73 @@ +# +# JUDAS library makefile for DJGPP v2.0 +# +# note : nasm compiles for coff output : +# nasm jasmdj.asm -ddjgpp -fcoff -E!nasm.err +# rem nasm jasmdj.asm -dwatcom -fobj -E!nasm.err +# + +CC = gcc +AS = nasm +AR = ar +RANLIB = ranlib +STRIP = strip +COFF2EXE = stubify +AFLAGS = -ddjgpp -fcoff +#Warning: -O1/O2/O3 optimize will generate invalid code with GCC > 4.0.1 +CFLAGS = -g -c -Wall -Wno-unused -Wno-pointer-sign -march=pentium -Os +LFLAGS = -g + +OBJS = judas.o judassmp.o judasraw.o judaswav.o judasio.o \ + judasmem.o judastbl.o judasxm.o judasmod.o judass3m.o \ + judasdma.o jasmdj.o + +LIB = judaslib.a + +all : $(LIB) clean + @echo done. + + +#make the judas sound library +$(LIB) : $(OBJS) + @$(AR) rc $(LIB) $(OBJS) + $(RANLIB) $(LIB) + +judas.o : judas.c + $(CC) $(CFLAGS) $< + +judassmp.o : judassmp.c + $(CC) $(CFLAGS) $< + +judasraw.o : judasraw.c + $(CC) $(CFLAGS) $< + +judaswav.o : judaswav.c + $(CC) $(CFLAGS) $< + +judasio.o : judasio.c + $(CC) $(CFLAGS) $< + +judasmem.o : judasmem.c + $(CC) $(CFLAGS) $< + +judastbl.o : judastbl.c + $(CC) $(CFLAGS) $< + +judasxm.o : judasxm.c + $(CC) $(CFLAGS) $< + +judasmod.o : judasmod.c + $(CC) $(CFLAGS) $< + +judass3m.o : judass3m.c + $(CC) $(CFLAGS) $< + +judasdma.o : judasdma.c + $(CC) $(CFLAGS) $< + +jasmdj.o : jasmdj.asm + $(AS) $(AFLAGS) $< + +clean : + @if exist *.o del *.o > nul + diff --git a/MAKEFILE.WAT b/MAKEFILE.WAT new file mode 100644 index 0000000..0dbafeb --- /dev/null +++ b/MAKEFILE.WAT @@ -0,0 +1,38 @@ +# JUDAS library makefile +# +# NOTE: modules which are or might be called from within an interrupt have the +# -zu option, to tell that SS != DS. It'll crash otherwise. +# +# note : nasm compiles for obj output : +# nasm judasasm.asm -dwatcom -fobj -E!nasm.err +# rem nasm judasasm.asm -ddjgpp -fcoff -E!nasm.err +# + +judas.lib: judas.obj judassmp.obj judasraw.obj judaswav.obj judastbl.obj judasxm.obj judasmod.obj judass3m.obj judasio.obj judasasm.obj judasdma.obj judasmem.obj + wlib -n -c judas.lib @judas.cmd + +judas.obj: judas.c + wcl386 -c -d2 -w3 -zp4 judas.c +judassmp.obj: judassmp.c + wcl386 -c -d2 -w3 -zp4 judassmp.c +judasraw.obj: judasraw.c + wcl386 -c -d2 -w3 -zp4 judasraw.c +judaswav.obj: judaswav.c + wcl386 -c -d2 -w3 -zp4 judaswav.c +judasio.obj: judasio.c + wcl386 -c -d2 -w3 -zp4 judasio.c +judasmem.obj: judasmem.c + wcl386 -c -d2 -w3 -zp4 judasmem.c +judastbl.obj: judastbl.c + wcl386 -c -d2 -w3 -zp4 judastbl.c +judasxm.obj: judasxm.c + wcl386 -c -d2 -zu -w3 -zp4 judasxm.c +judasmod.obj: judasmod.c + wcl386 -c -d2 -zu -w3 -zp4 judasmod.c +judass3m.obj: judass3m.c + wcl386 -c -d2 -zu -w3 -zp4 judass3m.c +judasdma.obj: judasdma.c + wcl386 -c -d2 -zu -w3 -zp4 judasdma.c +judasasm.obj: judasasm.asm + nasm -dwatcom -fobj judasasm.asm + diff --git a/MENUMOVE.WAV b/MENUMOVE.WAV new file mode 100644 index 0000000000000000000000000000000000000000..03bd13b100cd0a0f515d669c89b6926767483791 GIT binary patch literal 5792 zcmdT`TZmm}6`sB+78ERnB7#_@_#(Ex*cVH&4QNZLO-X8++ce``&&;{*`~AMp+522) z&P>islBP*o)3h|bQ57l`^{M)zC`eIXq=+E+q_6sY|K8`!j1AI?KKO^6{a@C<*0;X( zt-a@oZyq^v{?56%;}0HtcyYb;xlhf_&E1B7pFcJy{@*tDvAKo$)_e;#z8?fWN5H2i zRjp|~qpsR648z3Jt#I@CtInhM+sRl+dv)b2x8MGyN9X6CeB_DM_~J{?h38Hg&;H<@ z*T?Gdd++$fr$2Moop(P}-MTW#+;H;Bo7ckC)8GF3yfFWSNHn*x$m4_*A1zqdvVqq4N_zM+|#GltbF(R8`m$7;vgMP zE?>E{v%Ry`4GmdK_HMrY&i8NZ<(_Fe>E_KL!%wr= zlw?&^T9t)!YfZ(l{V4Qp)6kpCPaiwE?smu9Tccj&xQ?mHaH}T6VIF#6HW+j}?Klc7 zxlxyb?JHN$=V92{+}j;^rq}NF2cykC6lDid9K~rq=%}jXZ_LUFfm$@f08 zsiFEYdeQM*%W&eno4A%655}Y3X!qjYW;;szlV>kY##`Gv+o-`{IOz8}QP>`x-|j|1 zoOScqk{TP8YRj-qxzUtWMQ+q8ON$#u=xa?R;TZLmRmsl>dFR01@{%Hwo#LGO=_88ngo_wUtM0?Xj*ZWx@rS}J9*w|ce2E{EhmWE z@RtUTrn>#TXZHrNts6cl&pHY6vP>sz_j>Js>_(k#-idw3u{E@M*zQE0ZAi76VtcmQ zk_|WN$damSYc54^=3+f)_k2i)}X z3|QhI2%|7+k0z60;@EM2d$K#(>ZWm$Ws&QK?QX|6bce;&>vh>c@iawMH4~n6O_h;u zr7oK;zyov~`?jGfs&3ghHXXDEDv368g2+>AbCCj>PY1DVjGDs~H&Y%Q<191#AU#q;9(&z6gGd^CSvUKn-<^Lqu^r z&vh(fb^y(XnXOCpx@Zqwx7;AWZQJv4$Fx0=sF8DC0navCMl-!6178MO3=UlE7>t(oF5bTch~_hy1P2rGJq*y}1voWi z#q^^H??PF%f+#>bnr>*QH=d>&cJeHCbVZR{%|;9DVwk3Z|6~*_+k_0JC=rc{fbtjt zP{07cxlpHN`!Tqri2%YcG0~9QAkBMSyopghDl27miQUIn8A?F~K!Z&kf)J#Occ!JQ z^aFuV`v?=mw}m$L*bPNm!~y@lh!4NQDdM4c&^X_7(5wMIbyNarFlj+KD+sl#lme;? zsN7&K2p;n_i8pD0MT}ay0s|}Vf=b^(Jyq08a1NT_T=+(3VHl)Lms^qo6G*_+A!`@$ zT=0m32<13lgec_Aft>i)3KD|a$R~7zC$!8krU{Y zHRm5X=&GWh5Q76(v5P8!NZ}oB8H^2MgHR7_13&ta;$=z}Okn_X&>Kb(wm}aZ!+FHo z!6n%u2vCKn!VIdzJ03RpBMbof#ut&890(TCMU_3rL`X!$ML-diK}clI=+uE|7;;CX zkOos`rHYIYfY605F3wIRYY!U=9$NF9;?)?lK+0o{*aGUs#Z2kp~bLNfH#C@`rb+D%=+I z&fT#KKZ}QoOXoCd)zISq5#|!yUbAxoKL4kGFS;k6!u~&x{X#` zF=Ke;oX*PY+!wYY0;AIYfb$XtcEqgs1wphba~2l_c-FZ@&e{vH3uY(;k*@Glc*2b- zlOkdnS!B(58ClE(7s8W>!@vwx4Elss;e;_cW*LeD7#7|ZIj#X&LWT>+mgOs6;!0cYlAf(Kz2x28qG$$s&Q zYcwur2mKtFb2^PP%VC<*%&tI)<T zX!Apb|G;9}g8wy2Z;7j*7`ad7aQF#<|8F_CC8+tWvZSJU%bJpTzhenN); literal 0 HcmV?d00001 diff --git a/MENUSEL.WAV b/MENUSEL.WAV new file mode 100644 index 0000000000000000000000000000000000000000..c2a046774b8dfec3282e017124a2caa3cdb0bb6d GIT binary patch literal 10510 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 literal 0 HcmV?d00001 diff --git a/README.TXT b/README.TXT new file mode 100644 index 0000000..d4f9db7 --- /dev/null +++ b/README.TXT @@ -0,0 +1,11 @@ +Build notes by RayeR +******************** + +This source package was accomodated to use with latest GCC 4.6.x, DJGPP 2.04 +Due to GCC optimalization issues since ver. 4.0.1 I modified global +optimization flag from -O3 to -Os (-O1 and higher produced crashing code). +It would be better to isolate problem function or file and modify the +optimization locally. +To build the package just run !DJGPP.BAT under plain DOS or Windows DOS box. +When using intel HDA be sure to not have set BLASTER environment variable +otherwise it will try initialize SB first. \ No newline at end of file diff --git a/SEGMENTS.INC b/SEGMENTS.INC new file mode 100644 index 0000000..9eaa360 --- /dev/null +++ b/SEGMENTS.INC @@ -0,0 +1,22 @@ + +; predefined segments for NASM - 32 bit obj format + + +%ifdef watcom +segment _TEXT public align=256 class=CODE use32 +segment _DATA public align=256 class=DATA use32 +segment _BSS public align=256 class=BSS use32 +segment STACK stack align=256 class=STACK use32 +segment _BSS + +%elifdef djgpp +bits 32 + +%else +segment _TEXT public align=256 class=CODE use32 +segment _DATA public align=256 class=DATA use32 +segment _BSS public align=256 class=BSS use32 +segment STACK stack align=256 class=STACK use32 +segment _BSS + +%endif \ No newline at end of file diff --git a/SPEED.DOC b/SPEED.DOC new file mode 100644 index 0000000..16a98a6 --- /dev/null +++ b/SPEED.DOC @@ -0,0 +1,40 @@ +JUDAS FAST MIXER, CUBIC PLAYER 2.00à++ & MIDAS 0.6.0 SPEED TEST +--------------------------------------------------------------- + + CPU: AMD K5 PR166 + Soundcard: VIBRA16 at A220 I5 D1 H5 + Audio Mode: 16bit stereo at 44.1 kHz, with & without interpolation +Test Method: Shell to DOS from player and measure amount of CPU time left + (by using a program which counts how many times it can increase + a counter before the BIOS 18.2 Hz clock increases) + Tune: T2-REMIX.XM (26 channels), CPU usage measured 10 seconds from + where the rock drums (?) enter + Remarks: CP2 used the Standard Mixer (equal to Fast Mixer in JUDAS). + Its AOI mode was used in the interpolation test. MIDAS couldn't + interpolate so it participated only in the first test. + + +RESULTS +------- + +TEST 1: Without interpolation + + Player CP2 JUDAS MIDAS +Time Left 88.7% 92.4% 92.6% + +TEST 2: With interpolation + + Player CP2 JUDAS +Time Left 78.8% 85.1% + + +CONCLUSIONS +----------- + +In the non-interpolation test, JUDAS is only a little bit slower than MIDAS. +One must note that JUDAS performs clipping of audio output, while MIDAS doesn't +(it clips only 8bit output.) CP2 is clearly slower than the two. + +The interpolation test doesn't leave much to guess...however the interpolation +of 8-bit samples in CP2 is more sophisticated (16-bit.) Cadaver can't hear the +difference though! diff --git a/TIMER.C b/TIMER.C new file mode 100644 index 0000000..e3c2ed3 --- /dev/null +++ b/TIMER.C @@ -0,0 +1,129 @@ +/* + * Timer handler, calls a user function at the desired PIT clock frequency. + * + * Doesn't "officially" belong to JUDAS but feel free to use! This is + * blasphemy-ware too! + */ + +#include +#include +#include "judasmem.h" +#ifdef __DJGPP__ +#include +#include +#endif + +int timer_init(unsigned short frequency, void (*function)()); +void timer_uninit(void); +static int timer_lock(void); +static void timer_unlock(void); +unsigned short timer_get_ds(void); +#ifdef __WATCOMC__ +void __interrupt __far timer_handler(void); +#else +void timer_handler(void); +#endif + +#ifdef __DJGPP__ +/* DJGPP version */ +inline unsigned short timer_get_ds(void) { + return (unsigned short)_my_ds(); +} + +#else +/* WATCOM C++ version */ +#pragma aux timer_get_ds = \ +"mov ax, ds" \ +modify [ax] \ +value [ax]; +#endif + + +static unsigned char timer_initialized = 0; +#ifdef __DJGPP__ +/* DJGPP version + __dpmi_paddr structure contains (unsigned long offset32, unsigned short selector) */ +extern __dpmi_paddr timer_oldvect; +static __dpmi_paddr timer_newvect; + +#else +/* WATCOM C++ version */ +extern void (__interrupt __far *timer_oldvect)(); +static void (__interrupt __far *timer_newvect)(); +#endif + +extern void (*timer_function)(); +extern unsigned timer_count; +extern unsigned short timer_frequency; +extern unsigned short timer_systemcount; +extern unsigned short timer_ds; +extern int timer_code_lock_start; +extern int timer_code_lock_end; + + +int timer_init(unsigned short frequency, void (*function)()) +{ + if (timer_initialized) return 1; + if (!timer_lock()) return 0; + timer_function = function; + timer_count = 0; + timer_systemcount = 0; + timer_frequency = frequency; + timer_ds = timer_get_ds(); + + #ifdef __DJGPP__ + /* DJGPP version */ + timer_newvect.offset32 = (unsigned long) &timer_handler; + timer_newvect.selector = (unsigned short) _my_cs(); + __dpmi_get_protected_mode_interrupt_vector(8, &timer_oldvect); + _disable(); + __dpmi_set_protected_mode_interrupt_vector(8, &timer_newvect); + + #else + /* WATCOM C++ version */ + timer_newvect = &timer_handler; + timer_oldvect = _dos_getvect(8); + _disable(); + _dos_setvect(8, timer_newvect); + #endif + + outp(0x43, 0x34); + outp(0x40, frequency); + outp(0x40, frequency >> 8); + _enable(); + timer_initialized = 1; + return 1; +} + +void timer_uninit(void) +{ + if (!timer_initialized) return; + _disable(); + + #ifdef __DJGPP__ + /* DJGPP version */ + __dpmi_set_protected_mode_interrupt_vector(8, &timer_oldvect); + + #else + /* WATCOM C++ version */ + _dos_setvect(8, timer_oldvect); + #endif + + outp(0x43, 0x34); + outp(0x40, 0x00); + outp(0x40, 0x00); + _enable(); + timer_unlock(); + timer_initialized = 0; +} + +static int timer_lock(void) +{ + if (!judas_memlock(&timer_code_lock_start, (int)&timer_code_lock_end - (int)&timer_code_lock_start)) return 0; + return 1; +} + +static void timer_unlock(void) +{ + judas_memunlock(&timer_code_lock_start, (int)&timer_code_lock_end - (int)&timer_code_lock_start); +} diff --git a/TIMER.H b/TIMER.H new file mode 100644 index 0000000..ee6ddf7 --- /dev/null +++ b/TIMER.H @@ -0,0 +1,5 @@ +int timer_init(unsigned short frequency, void (*function)()); +void timer_uninit(void); + +extern unsigned short timer_frequency; +extern volatile unsigned timer_count; diff --git a/TIMERASM.ASM b/TIMERASM.ASM new file mode 100644 index 0000000..6f4f392 --- /dev/null +++ b/TIMERASM.ASM @@ -0,0 +1,63 @@ +; Timer interrupt handler +; Modified by BSpider for NASM on 5th Sep 2006 + + %include "segments.inc" + %ifdef djgpp + section .text + %else + segment _TEXT + %endif + + global _timer_code_lock_start + global _timer_code_lock_end + global _timer_oldvect + global _timer_count + global _timer_systemcount + global _timer_frequency + global _timer_ds + global _timer_function + ; expose both calling conventions + global timer_handler_ + global _timer_handler + +_timer_code_lock_start: + + align 4 + +_timer_ds dw 0 +_timer_oldvect dd 0, 0 +_timer_count dd 0 +_timer_systemcount dw 0 +_timer_frequency dw 0 +_timer_function dd 0 + + align 4 + + +timer_handler_: +_timer_handler: + pushad + push DS + push ES + mov DS, [CS:_timer_ds] + mov ES, [CS:_timer_ds] + sti + mov AL, 20h + out 20h, AL + inc dword [_timer_count] + call dword [_timer_function] + mov AX, [_timer_frequency] + add [_timer_systemcount], AX + jc timer_callold + pop ES + pop DS + popad + iretd +timer_callold: pop ES + pop DS + popad + jmp dword far [CS:_timer_oldvect] + +_timer_code_lock_end: + +; end diff --git a/TIMERASMT.ASM b/TIMERASMT.ASM new file mode 100644 index 0000000..cb0dc85 --- /dev/null +++ b/TIMERASMT.ASM @@ -0,0 +1,56 @@ +; Timer interrupt handler + + .386 + .model flat + + .code + + public _timer_code_lock_start + public _timer_code_lock_end + public _timer_oldvect + public _timer_count + public _timer_systemcount + public _timer_frequency + public _timer_ds + public _timer_function + public timer_handler_ + +_timer_code_lock_start: + + align 4 + +_timer_ds dw 0 +_timer_oldvect df 0 +_timer_count dd 0 +_timer_systemcount dw 0 +_timer_frequency dw 0 +_timer_function dd 0 + + align 4 + + +timer_handler_: pushad + push DS + push ES + mov DS, CS:_timer_ds + mov ES, CS:_timer_ds + sti + mov AL, 20h + out 20h, AL + inc _timer_count + call _timer_function + mov AX, _timer_frequency + add _timer_systemcount, AX + jc timer_callold + pop ES + pop DS + popad + iretd +timer_callold: pop ES + pop DS + popad + jmp CS:_timer_oldvect + +_timer_code_lock_end: + +; end diff --git a/TUNE1.XM b/TUNE1.XM new file mode 100644 index 0000000000000000000000000000000000000000..e2fedf5a9b1b3c0f9a5e8619fd5bae3f13be6d4e GIT binary patch literal 10520 zcmeHNYiwLc6`s3$?>cEn3T+zNDlMC;U>q4YuH(iy>&8w~J0x)vHBr-42x-Z#9n;h) zezf67klTymN(f}hzi1Fh2>J&#Qd_9RA3$nAP+CO8BS3*h;!%l$P#^h`*xt)`9&_*9 z^=@j5HdTpd@64R@&6zXjoH_St-+6SbJX|W5^8H6jN-$?P`{pFGTL$|hU*pScXeQ&jAOoK7TH-WjzcqV6#8RKQLp6>&_ zJj-%&yW1S|!d4vF2`Q7U*$qO4t(j^Vk{EO4L0@zRsdD8fE-EehF#*;HT~|M>W7X0w z&H-6KO}nT;i#4CDvF0cC37GJ%?Yax6br-@W|4Sbvc@7jfre!O_cH!b73Q^eXQ>b*O zlp~*rq@r^>+_|hM1SB)V7I#n?o!414#A~(!j5jF+2b9*bbV1qO>L_hVoNC!>L%<5$0$c6=GYF5+Mc}=UbpsU zoJPESlOOg56p`(Lb@`E^@M=XpuSEfeXJLM~`;)!F0G8_TC$|R!YCwx0?o1f~KrGr8 zs0E!C=~Ek71oPp$A8rdc9+;FMpmgiQ9#$UfD)^H{Wj{Sx)Q>JRj32pWFQt{Xw7$1rCOqht)S@t|VJMNpbMgwq;*G|Umk!_p&Bh9Ld9}cPW!+ ze=y+TE;Vr=7|d$$8~x+KAwb82A@!jR2+l$vgAx-K!5}3E#5jQ=5+j35b&4X9VWgQ9 z#gs^OkW`EfX#x!;2{hf4+l0lr<1jb*3QcXoJc?X<8WG*=Xw>QKu%t7NZFbc&Of5933rF&kEfA$iQ8a&Ji;zB&1MzaYt0&yM_<5dy{LOo z??Wx24x<{3UIEK)gygND3#h%QdrwD$q_- z*zK6E)$kmZlx4v1fgn_s3t{ga8_3;a|Bmxt<1?!a-CD28(zR|906TEM%0wJs?z6M3gq1qj(ZDwYC4r$(wU zreF<0f~ZzflTwVkn;5YBN4*{5ehweqKZ$U={l1F#<$Gw72CnQ7`8j zsOa)y#Hu&tw()xwPd80xOyeKYkh$&=b1bwTnCaov{hgVNIrt57ckC;%)8OQH4=g$< z4&+lb`yPqf!4{fl-XBsYH6p|CM8wnc0I7IQ+Z`6c>WUb3iOZ;%IZsjR*7 zb;^pjGMOw6!bvmR6U-13MQ_H^I*M-98)hZpVulDTJ7bV6TrhvbuUHEx_gHt9#v3{A zyofA?b}ewkgT$}@hgy)Dim*2S7L6BCjFThdU4dD%0M4#MV_qjET4V{!aSv$uIZ)?T zq~L^!*s+9H%JtM8WZYRt^~O;oS$D2W*BwX+RVk=eSc@mjmTFxVUwEIXDXKn``o#SO zP%KibiS>vQx<73nI#iTvm}|mib$_aUhpl&i`smB%>m`ZO;{#^>@5~eQel6wOpEf5y z7e#a(DV#DV?>3w1Dc>C34PevHRp^<1AvV6C06Mv^?cjcgb0r~(AM}||{5lrJf?R&M zHHOS{T{q!E#^w3sYIeh4%?le7A>(pu$V79x#T^Fyn`Rd{)8ujv{imDtGU>4zXXNUC zgE^ab5OWqx-dv3<$!ji*3o}NtbfbC43NDr?s~mj{IQ$a16oI7(EJa``0`((6PX$z| z zc}wEeklvZlU>mWSOwN;{$3lALqi%L?26;dm)-&>8d2n#(NLu_7o?$*#u0Fene;=M< zO^7|6O?cU+f^Qm4_(nfFL(S}k=jYDP%$<+U^QZbJuo3hfe-_{G<(QqBouT8|6<4mm zYT3%%s?5xVnG3Uk

dzb?IGlxc!Fz^tT{^)IadWIvfrAhQWBOn;Sxl0g1-gtO9G2V%S$U`EC!Vi;`-M|Ie!`nNeKQwzT zQ4*se`a@3=O-%e{Bmp0PHHyZ>Xw<~uBR+npBxqt(_yxD)S5@6J-Mh1J7kL=4ce<;( zs=6Ls)$@S6Du+JsZM}1I5DP@R}95Qvb%j z+}0h1zHs;A`3o0@VVLnIdP+4br7~)ax=ML~k16H(K_;7N0{N0`V=3w`b@fvvUxYHfGv$b6VN5`!!@+oJPWVNQ~EY8j(5*pJf zf6lA4f$5dGQJTwTJrdn-Ku_d(?Wk5XvfyeG!{m#h3vA@oOe+c`4^hZSbQ47_-9)iS z+elj>R)vG4NW(@89UBfy(=8h#;mF#Ct8{AK*Qt4Ges;oB?b!*)W#67Y+G+>#h7cv; zJ1I0lCL2jEH`+#`Co4!&O2U+ zvBF8+*-B|hl}?)p_1LS*T#6tDGpKlmmIh>;$a93N0dalW;2Itr<%M9NVUYxdRhnat zugifex3DW#Ugr{xx0D@2T|ugoDLSu^=!8BvUg;nhl9jUHhK9Awsk9r*;1EvXmdpBh zUggW3v$87t!rE+jKREmDr_(Z+6G&6eXywlAzQ$~?vf5%J_A{)9Jy$<3h^^PLeaC#A z{&EBJl!WvEbeIJArmHuggIwsKgNy(e(v;6JdC-{68xXU_!PBgV0L2(f;HEs7qYT_c z4%{SXlOzab3ZHHA!I8MVRsxGH4KMmFMwy_jh0$`kjtNUl)3tH=MOqt`ZmB(|IH(?z z@}pwrXGUBe>-l`d2~H`Gb*C?nwH7b1CH`ooeejWkDKY>iv*!e z;d>gLf`$_O`c$SGTS-b*R@(U8iSA0+j6B+zHeYBeTyZn z%~RJWvvY_oR2h5%HmkX6JjN+%vI+s0!Qu+^_2^sB^XNr%1#SyGn*qtiz?Y$~Kwpo( z1wD^mM1KVR3G}8WKgiyTv65f#bO$%4ol;z zu2zSMTs6HqEI+ArE_oi zau?v+YRRWv??r3hR0llo*%F^qIk1!szFxw)e2tcHzTXAXr6&)($|PM58(#-mqze0^ z4R4rfh+Wvik4`~TWgd$ikhG(C;krO zhv{Wk+DDxlISjZ=UHQkP-_(aUt6!8_z(GJ-doRcjm@Org+cAujH2z7uAW|D2cWAOw zpDu}}{T$B(nfw{xf-ekJ-)~R{ajQYWMEqI2?`%X&VVnj!_JhQ)`Bi)1EXB6Y{R)E@(H~-{-8RtEpX;-w z&)Wm6OHF`XI_I_hY<%SU_wQ>}8z{-UPP`n5rNJgA z_TOidtrCao^b2-o?ZR;yOeoOm^%l0^Sz}~E(wt^ G9p*n&`TgJk literal 0 HcmV?d00001