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 0000000..1b9f8f1 Binary files /dev/null and b/EXPLSION.WAV differ diff --git a/FART.WAV b/FART.WAV new file mode 100644 index 0000000..52f18f6 Binary files /dev/null and b/FART.WAV differ diff --git a/FILE_ID.DIZ b/FILE_ID.DIZ new file mode 100644 index 0000000..09a4911 --- /dev/null +++ b/FILE_ID.DIZ @@ -0,0 +1,17 @@ + --+++***>>> 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 0000000..1011dec Binary files /dev/null and b/JDSWAV.ZIP differ diff --git a/JP.C b/JP.C new file mode 100644 index 0000000..7425052 --- /dev/null +++ b/JP.C @@ -0,0 +1,733 @@ +/* + * Judas Player. This program plays MODs, XMs and S3Ms and shows how to set up + * the signal handlers to ensure proper JUDAS shutdown even in the case of user + * break. + * + * It uses the timer routines in TIMER.C & TIMERASM.ASM. These don't + * "officially" belong to JUDAS but use them if you need them! + * + * Note: New features of Judas are automagically included in the new versions + * of JP, so they are not documented here. + * V2.04 Corrected special keys leaving shit to keyboard buffer -bug. + * V2.06 Rewrote a lot of things... + * V2.07 Support for 48000 Hz sample rate (AC97 only). + * V2.08 Now should really work with all ICH4 AC97 integrated codecs. + * V2.09 SIS7012 should now work too + * V2.09b ICH4+ support corrected + * V2.09c dev version + * V2.09d corrected dev version with full WATCOM C++ & DJGPP support + * V2.09e all ICH4+ AC97 devices now use memory mapped IO if available + * V2.09f WAV library builder added to JUDAS + * V2.10a HDA codecs support added + * V2.10b judas_setwavlib function added + * V2.10d HDA codecs support update for intel 5, 6, 7 series test by RayeR + */ + +#include +#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 0000000..448e0ef Binary files /dev/null and b/JUDASLIB.A differ 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 0000000..d04b597 Binary files /dev/null and b/JUDASW.GIF differ 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 0000000..7f7ee0c Binary files /dev/null and b/JUVSCPW.GIF differ diff --git a/KBD.C b/KBD.C new file mode 100644 index 0000000..1c78d7c --- /dev/null +++ b/KBD.C @@ -0,0 +1,291 @@ +/* + * Keyboard handler + * + * Doesn't "officially" belong to JUDAS but feel free to use! This is + * blasphemy-ware too! + * + * Supports the Win95 keyboard extra keys! + */ + +#include +#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 0000000..03bd13b Binary files /dev/null and b/MENUMOVE.WAV differ diff --git a/MENUSEL.WAV b/MENUSEL.WAV new file mode 100644 index 0000000..c2a0467 Binary files /dev/null and b/MENUSEL.WAV differ diff --git a/PLRSHOOT.WAV b/PLRSHOOT.WAV new file mode 100644 index 0000000..6244581 Binary files /dev/null and b/PLRSHOOT.WAV differ 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 0000000..e2fedf5 Binary files /dev/null and b/TUNE1.XM differ diff --git a/TUNE2.XM b/TUNE2.XM new file mode 100644 index 0000000..9b5feef Binary files /dev/null and b/TUNE2.XM differ