├── BOOTSTRP.BAT ├── BOOTSTRP.SH ├── DOSBUILD.BAT ├── DRVSRC ├── CAPLISTS.INC ├── COMDATA.INC ├── COMDECS.INC ├── COMFUNCS.INC ├── HDA.INC ├── HDA16S.ASM ├── HDA16SD.ASM └── PORTLIST.INC ├── HMIAPPND.EXE ├── RAYTOOLS ├── EDUGB3.BAT ├── EXAMPLE.BAT ├── EXAMPLE2.BAT └── SOUNDBIN.ASM ├── README.md ├── TOOLSRC ├── HMIAPPND.C └── HMIREDIR.ASM └── unixbld.sh /BOOTSTRP.BAT: -------------------------------------------------------------------------------- 1 | wcl -bcl=dos -ecf -mt -we -fe=hmiappnd.exe toolsrc\hmiappnd.c 2 | -------------------------------------------------------------------------------- /BOOTSTRP.SH: -------------------------------------------------------------------------------- 1 | gcc -Werror -x c -std=c99 TOOLSRC/HMIAPPND.C -o hmiappnd 2 | -------------------------------------------------------------------------------- /DOSBUILD.BAT: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | jwasmd -bin drvsrc\hda16s.asm 4 | if errorlevel 1 goto end 5 | rem Convention is to change last letter to 'w' for "RATIONAL" version 6 | move hda16s.bin hdw16s.bin 7 | 8 | jwasmd -bin -D?FLASHTEK=1 drvsrc\hda16s.asm 9 | if errorlevel 1 goto end 10 | 11 | hmiappnd.exe oldfile=C:\RAYMAN\HMIDRV.OLD newfile=C:\RAYMAN\HMIDRV.386 drvf:E040=hda16s.bin drvr:E040=hdw16s.bin 12 | hmiappnd.exe oldfile=C:\RAYKIT\HMIDRV.OLD newfile=C:\RAYKIT\HMIDRV.386 drvf:E040=hda16s.bin drvr:E040=hdw16s.bin 13 | hmiappnd.exe oldfile=C:\EDUGB1\HMIDRV.OLD newfile=C:\EDUGB1\HMIDRV.386 drvf:E040=hda16s.bin drvr:E040=hdw16s.bin 14 | hmiappnd.exe oldfile=C:\EDUGB3\HMIDRV.OLD newfile=C:\EDUGB3\HMIDRV.386 drvf:E040=hda16s.bin drvr:E040=hdw16s.bin 15 | 16 | :detector 17 | jwasmd -bin drvsrc\hda16sd.asm 18 | if errorlevel 1 goto end 19 | rem Convention is to change last letter to 'w' for "RATIONAL" version 20 | move hda16sd.bin hdw16sd.bin 21 | 22 | jwasmd -bin -D?FLASHTEK=1 drvsrc\hda16sd.asm 23 | if errorlevel 1 goto end 24 | 25 | hmiappnd.exe oldfile=C:\RAYMAN\HMIDET.OLD newfile=C:\RAYMAN\HMIDET.386 drvf:E040=hda16sd.bin drvr:E040=hdw16sd.bin 26 | hmiappnd.exe oldfile=C:\RAYKIT\HMIDET.OLD newfile=C:\RAYKIT\HMIDET.386 drvf:E040=hda16sd.bin drvr:E040=hdw16sd.bin 27 | hmiappnd.exe oldfile=C:\EDUGB1\HMIDET.OLD newfile=C:\EDUGB1\HMIDET.386 drvf:E040=hda16sd.bin drvr:E040=hdw16sd.bin 28 | hmiappnd.exe oldfile=C:\EDUGB3\HMIDET.OLD newfile=C:\EDUGB3\HMIDET.386 drvf:E040=hda16sd.bin drvr:E040=hdw16sd.bin 29 | 30 | :redirector 31 | jwasmd -bin toolsrc\hmiredir.asm 32 | if errorlevel 1 goto end 33 | move hmiredir.bin hmiredir.com 34 | 35 | jwasmd -Idrvsrc -bin raytools\soundbin.asm 36 | if errorlevel 1 goto end 37 | move soundbin.bin soundbin.com 38 | 39 | :end 40 | -------------------------------------------------------------------------------- /DRVSRC/CAPLISTS.INC: -------------------------------------------------------------------------------- 1 | ; Lists pointed to by capabilities structure 2 | PortList dw (DEVSTOENUMERATE+1) dup (-1) 3 | DMAList dw 1,-1 ; unused 4 | NUM_DMAs equ (($-DMAList) SHR 1) - 1 5 | IRQList dw 2,3,4,5,6,7,8,9,0Ah,0Bh,0Ch,0Dh,0Eh,0Fh,-1 ; can use any 6 | NUM_IRQs equ (($-IRQList) SHR 1) - 1 7 | -------------------------------------------------------------------------------- /DRVSRC/COMDATA.INC: -------------------------------------------------------------------------------- 1 | ; Common data to detector and driver 2 | 3 | ; Capabilities structure to be returned by function call 4 | sCaps: 5 | szDeviceName db "HD Audio 16 Stereo" 6 | NAMELEN equ $-szDeviceName 7 | db (20h - NAMELEN) dup (0) 8 | wDeviceVersion dd 1 ; Working fairly satisfactorily at this point... 9 | wBitsPerSample dd 16 ; 16-bit driver 10 | wChannels dd (FORMATLOBYTE AND 0Fh) ; CHAN 11 | wMinRate dd 8000 12 | wMaxRate dd 48000 13 | wMixerOnBoard dd 0 ; Perhaps, but can't guarantee this 14 | wMixerFlags dd 0 15 | wFlags dd 300h ; Pseudo-DMA, detector needs env strings 16 | lpPortList label fword 17 | dd offset PortList 18 | lpPortList_seg label word 19 | dd ? ; Fill in segment @ runtime 20 | lpDMAList label fword 21 | dd offset DMAList 22 | lpDMAList_seg label word 23 | dd ? ; Fill in segment @ runtime 24 | lpIRQList label fword 25 | dd offset IRQList 26 | lpIRQList_seg label word 27 | dd ? ; Fill in segment @ runtime 28 | lpRateList label fword 29 | dd offset RateList 30 | lpRateList_seg label word 31 | dd ? ; Fill in segment @ runtime 32 | fBackground dd 1 33 | wID dd 0E040h ; an unused ID... 34 | wTimerID dd 100Dh ; 16-bit stereo pseudo-DMA timer 35 | 36 | include CAPLISTS.INC 37 | ; List from HDA spec, in order from R1-R7 (i.e. reduced to only those that'll fit inside a word...) 38 | RateList dw 8000,11025,16000,22050,32000,44100,48000,-1 39 | NUM_RATES equ ($ - RateList) SHR 1 40 | FormatHiBytes db 00000101b ; TYPE=0=PCM, BASE=0=48kHz, MULT=000=1, DIV=101=6 41 | db 01000011b ; TYPE=0=PCM, BASE=1=44.1kHz, MULT=000=1, DIV=011=4 42 | db 00000010b ; TYPE=0=PCM, BASE=0=48kHz, MULT=000=1, DIV=010=3 43 | db 01000001b ; TYPE=0=PCM, BASE=1=44.1kHz, MULT=000=1, DIV=001=2 44 | db 00001010b ; TYPE=0=PCM, BASE=0=48kHz, MULT=001=2, DIV=010=3 45 | db 01000000b ; TYPE=0=PCM, BASE=1=44.1kHz, MULT=000=1, DIV=000=1 46 | db 00000000b ; TYPE=0=PCM, BASE=0=48kHz, MULT=000=1, DIV=000=1 47 | FORMATLOBYTE equ 0010001b ; BITS=001=16, CHAN=0001=2 48 | 49 | ; Basic parameters set by calling application 50 | wPort label word ; for the "port": 51 | pci_dev_func db -1 ; PCI device/function in low byte (7-3 device, 2-0 func) 52 | pci_bus db -1 ; PCI bus in high byte 53 | wIrqDma label word ; set these together... 54 | irq db 0 55 | dma db 0 56 | wParam label word 57 | node db 0 58 | codec db 0 ; technically only a nibble! 59 | 60 | ; Other internal data 61 | hdareg_ptr label fword 62 | dd 0 63 | hdareg_seg label word 64 | dd 0 65 | hdareg_linaddr dd 0 66 | 67 | dwTSRbuf dd 0 68 | MAXTSRBUFOFFSET equ 100000h ; 1 MiB 69 | 70 | dwCorbDpmiHdl label dword ; only one of these three dwords will be needed 71 | dwTSRbufoffset label dword 72 | xmsentry label dword 73 | xmsentry_ip dw 0 74 | xmsentry_cs dw 0 75 | 76 | lpRirb label fword 77 | dwRirbOff dd 400h ; RIRB is always at offset 256*4 in our CORB/RIRB/BDL buffer 78 | dwCorbSelHdl label dword 79 | wCorbSel dw 0 80 | wCorbHdl dw 0 81 | dwBdlOff dd 0C00h ; BDL is always 256*8 beyond the RIRB 82 | 83 | dwCorbPhys dd 0 ; Physical address of CORB/RIRB/BDL buffer 84 | 85 | corbwpmask db 0FFh 86 | rirbwpmask db 0FFh 87 | 88 | CStr macro text:vararg ;define a string in .code 89 | local sym 90 | .const 91 | sym db text,0 92 | .code 93 | exitm 94 | endm 95 | 96 | if ?DEBUGLOG 97 | debuglog_hdl dd -1 98 | endif 99 | -------------------------------------------------------------------------------- /DRVSRC/COMDECS.INC: -------------------------------------------------------------------------------- 1 | ; Common declarations for driver and detector 2 | 3 | include HDA.INC ; from Japheth's MIT-licensed HDAutils 4 | 5 | RMCS struct ;real mode call structure 6 | union 7 | rEDI dd ? 8 | rDI dw ? 9 | ends 10 | union 11 | rESI dd ? 12 | rSI dw ? 13 | ends 14 | union 15 | rEBP dd ? 16 | rBP dw ? 17 | ends 18 | resvrd dd ? 19 | union 20 | rEBX dd ? 21 | rBX dw ? 22 | ends 23 | union 24 | rEDX dd ? 25 | rDX dw ? 26 | ends 27 | union 28 | rECX dd ? 29 | rCX dw ? 30 | ends 31 | union 32 | rEAX dd ? 33 | rAX dw ? 34 | ends 35 | rFlags dw ? 36 | rES dw ? 37 | rDS dw ? 38 | rFS dw ? 39 | rGS dw ? 40 | union 41 | rCSIP dd ? 42 | struct 43 | rIP dw ? 44 | rCS dw ? 45 | ends 46 | ends 47 | union 48 | rSSSP dd ? 49 | struct 50 | rSP dw ? 51 | rSS dw ? 52 | ends 53 | ends 54 | RMCS ends 55 | 56 | ifdef ?FLASHTEK 57 | ; Phar Lap / FlashTek Real-Mode interrupt structure 58 | RMIS struc 59 | intn dw ? 60 | rDS dw ? 61 | rES dw ? 62 | rFS dw ? 63 | rGS dw ? 64 | union 65 | rEAX dd ? 66 | rAX dw ? 67 | ends 68 | union 69 | rEDX dd ? 70 | rDX dw ? 71 | ends 72 | RMIS ends 73 | endif 74 | 75 | DEVSTOENUMERATE equ 10h 76 | -------------------------------------------------------------------------------- /DRVSRC/COMFUNCS.INC: -------------------------------------------------------------------------------- 1 | ; Common code for detector and driver 2 | 3 | entry proc far 4 | push ds 5 | push fs 6 | 7 | pop ds 8 | assume ds:_TEXT 9 | 10 | ; Fill in segments of all far pointers in caps structure 11 | mov [lpPortList_seg],ds 12 | mov [lpDMAList_seg], ds 13 | mov [lpIRQList_seg], ds 14 | mov [lpRateList_seg],ds 15 | 16 | ; Function number is in AX, and less than 15 for sure 17 | and eax,0Fh 18 | call [ftable+eax*4] 19 | 20 | pop ds 21 | assume ds:nothing 22 | 23 | retf 24 | entry endp 25 | 26 | printstderr proc near stdcall uses ebx edx pszOutStr:dword 27 | mov ebx,2 28 | mov edx,[pszOutStr] 29 | call printtofile 30 | ret 31 | printstderr endp 32 | 33 | if ?DEBUGLOG 34 | ; ---------------------------------- ; 35 | ; INTERNAL DEBUG functions from here ; 36 | ; ---------------------------------- ; 37 | assume ds:_TEXT ; always called from within DS-set portion of entry point 38 | 39 | oldvidmode db ? 40 | logtostderr proc near uses eax ebx 41 | mov ah,0Fh ; get current video mode 42 | int 10h 43 | mov [oldvidmode],al 44 | mov ax,3 ; switch to VGA text mode 45 | int 10h 46 | mov [debuglog_hdl],2 47 | ret 48 | logtostderr endp 49 | 50 | printtolog proc near stdcall uses ebx edx pszOutStr:dword 51 | mov ebx,cs:[debuglog_hdl] 52 | cmp ebx,-1 53 | je @F 54 | 55 | mov edx,[pszOutStr] 56 | call printtofile 57 | @@: 58 | ret 59 | printtolog endp 60 | 61 | openlog proc near stdcall uses eax ecx edx pszFilename:dword,append:byte 62 | .if [append] 63 | mov ax,3D02h ; OPEN R/W 64 | .else 65 | mov ah,3Ch ; CREAT 66 | .endif 67 | xor ecx,ecx ; no special attributes 68 | mov edx,[pszFilename] 69 | int 21h 70 | jc @F 71 | mov [debuglog_hdl],eax 72 | ret 73 | 74 | @@: 75 | invoke logtostderr 76 | invoke printtolog, CStr("Couldn't open ") 77 | invoke printtolog, [pszFilename] 78 | invoke printtolog, CStr(" for writing, logging to stderr...",0Dh,0Ah) 79 | ret 80 | openlog endp 81 | 82 | ; Print 16 bits to the debug log 83 | curbinword db 16 dup (?) 84 | db 0 85 | printbinword proc near stdcall uses es edi ecx ebx eax wOut:word 86 | mov ebx,ds 87 | mov es,ebx 88 | pushf 89 | cld 90 | mov edi,offset curbinword 91 | 92 | mov bx,[wOut] 93 | mov ecx,size curbinword 94 | @@: 95 | shl bx,1 96 | setc al 97 | add al,'0' ; AL = '0' if not carry, '1' if carry 98 | stosb 99 | loop @B 100 | 101 | invoke printtolog, offset curbinword 102 | popf 103 | ret 104 | printbinword endp 105 | 106 | printnodetype proc near stdcall bNodeType:byte 107 | .if [bNodeType] == WTYPE_AUDIOOUT 108 | invoke printtolog, CStr("DAC") 109 | .elseif [bNodeType] == WTYPE_AUDIOIN 110 | invoke printtolog, CStr("ADC") 111 | .elseif [bNodeType] == WTYPE_MIXER 112 | invoke printtolog, CStr("mixer") 113 | .elseif [bNodeType] == WTYPE_SELECTOR 114 | invoke printtolog, CStr("selector") 115 | .elseif [bNodeType] == WTYPE_PIN 116 | invoke printtolog, CStr("pin") 117 | .elseif [bNodeType] == WTYPE_POWER 118 | invoke printtolog, CStr("power widget") 119 | .elseif [bNodeType] == WTYPE_VOLKNOB 120 | invoke printtolog, CStr("volume widget") 121 | .elseif [bNodeType] == WTYPE_BEEPGEN 122 | invoke printtolog, CStr("beep generator") 123 | .endif 124 | ret 125 | printnodetype endp 126 | 127 | closelog proc near uses eax ebx 128 | mov ah,3Eh ; CLOSE 129 | mov ebx,[debuglog_hdl] 130 | cmp ebx,-1 131 | je @F 132 | .if ebx == 2 133 | movzx ax,[oldvidmode] ; Reset video mode 134 | int 10h 135 | .else 136 | int 21h 137 | jc @F 138 | .endif 139 | mov [debuglog_hdl],-1 140 | @@: 141 | ret 142 | closelog endp 143 | 144 | endif 145 | 146 | ; ------------------------------------------------------- ; 147 | ; ENUM functions from here (called from host application) ; 148 | ; ------------------------------------------------------- ; 149 | assume ds:_TEXT ; always called from within DS-set portion of entry point 150 | 151 | ; Get device capabilities 152 | ; Takes no parameters 153 | ; Returns pointer to capabilities structure in EDX 154 | drv_capabilities proc near 155 | if ?DEBUGLOG 156 | invoke openlog, CStr("HDA_CAPS.LOG"),0 157 | invoke printtolog, CStr("capabilities requested, filling 'port' list...",0Dh,0Ah) 158 | endif 159 | call fill_portlist 160 | if ?DEBUGLOG 161 | jc @F 162 | invoke printtolog, CStr("'port' list filled",0Dh,0Ah) 163 | @@: 164 | endif 165 | mov edx,offset sCaps 166 | if ?DEBUGLOG 167 | invoke closelog 168 | endif 169 | ret 170 | drv_capabilities endp 171 | 172 | ; ------------------------------------------------------------------------------ ; 173 | ; INTERNAL functions from here (called from within ENUM and interrupt functions) ; 174 | ; ------------------------------------------------------------------------------ ; 175 | 176 | ; Takes file handle in EBX, zero-terminated string in EDX 177 | printtofile proc near uses eax ecx es ds edi 178 | xor ecx,ecx 179 | dec ecx 180 | push cs 181 | pop es 182 | mov edi,edx 183 | xor eax,eax 184 | repne scasb 185 | not ecx ; ECX now has the string length plus one 186 | dec ecx 187 | 188 | push cs 189 | pop ds 190 | mov eax,4000h ; WRITE 191 | int 21h 192 | ret 193 | printtofile endp 194 | 195 | ; check if HDATSR is installed 196 | ; returns pointer to buffer in EBX, CF set if needed but not present 197 | check_TSR proc near 198 | if ?DEBUGLOG 199 | invoke printtolog, CStr("Checking if HDATSR is installed...",0Dh,0Ah) 200 | endif 201 | 202 | ifdef ?FLASHTEK 203 | mov cl,9Ch 204 | mov ax,2503h ; Phar Lap / FlashTek: get RM interrupt vector 205 | int 21h 206 | mov cx,bx 207 | test ebx,ebx 208 | else 209 | mov ax,200h ; get real mode interrupt vector 210 | mov bl,9Ch 211 | int 31h 212 | or cx,dx 213 | endif 214 | jz @F 215 | int 9Ch 216 | @@: 217 | .if cx == 0C0DEh ; if there's no int 9Ch, it'll be 0, not C0DEh 218 | if ?DEBUGLOG 219 | invoke printtolog, CStr("HDATSR installed, using its arena @ ") 220 | invoke printbinword,bx 221 | invoke printbinword,ax 222 | invoke printtolog, CStr("b",0Dh,0Ah) 223 | endif 224 | shl ebx,10h 225 | mov bx,ax 226 | add ebx,7Fh ; ensure 128-byte alignment 227 | and bl,80h 228 | clc 229 | .else 230 | xor ebx,ebx ; clears carry - OK since in RATIONAL, we don't need TSR 231 | ifdef ?FLASHTEK 232 | if ?DEBUGLOG 233 | invoke printtolog, CStr("HDATSR unavailable, need to allocate our own buffers - checking for DPMI...",0Dh,0Ah) 234 | endif 235 | mov ax,1686h ; DPMI - detect mode 236 | int 2Fh 237 | test ax,ax 238 | clc 239 | jz @F 240 | stc 241 | endif 242 | .endif 243 | @@: 244 | ret 245 | check_TSR endp 246 | 247 | ; check if paging is enabled - CF set if yes, clear if no 248 | check_paging proc near 249 | if ?DEBUGLOG 250 | invoke printtolog, CStr("Checking if paging is enabled...",0Dh,0Ah) 251 | endif 252 | mov eax,cs 253 | test eax,3 254 | jnz @F 255 | 256 | if ?DEBUGLOG 257 | invoke printtolog, CStr("Operating in Ring 0, checking CR0...",0Dh,0Ah) 258 | endif 259 | mov eax,cr0 260 | bt eax,1Fh ; CR0.PG 261 | jc @F 262 | 263 | if ?DEBUGLOG 264 | invoke printtolog, CStr("Paging is off!",0Dh,0Ah) 265 | clc 266 | endif 267 | ret 268 | 269 | @@: 270 | stc 271 | ret 272 | check_paging endp 273 | 274 | alloc_CORB_RIRB proc near 275 | call check_TSR 276 | jc @F 277 | mov [dwTSRbuf],ebx 278 | 279 | if ?DEBUGLOG 280 | invoke printtolog, CStr("Allocating CORB/RIRB buffer...",0Dh,0Ah) 281 | endif 282 | mov eax,0C20h ; 1 kiB for CORB + 2 kiB for RIRB + 32 bytes for BDL 283 | call alloc_dma_buf 284 | jc @F 285 | if ?DEBUGLOG 286 | invoke printtolog, CStr("CORB/RIRB buffer allocated successfully",0Dh,0Ah) 287 | endif 288 | mov [dwCorbSelHdl],eax 289 | mov [dwCorbPhys],edx 290 | .if !((eax & 0FFFF0000h) || [dwTSRbuf]) 291 | mov [dwCorbDpmiHdl],ebx 292 | if ?DEBUGLOG 293 | invoke printtolog, CStr("CORB/RIRB physical address == ") 294 | ror edx,10h 295 | invoke printbinword,dx 296 | ror edx,10h 297 | invoke printbinword,dx 298 | invoke printtolog, CStr("b",0Dh,0Ah,"DPMI handle == ") 299 | ror ebx,10h 300 | invoke printbinword,bx 301 | ror ebx,10h 302 | invoke printbinword,bx 303 | invoke printtolog, CStr("b",0Dh,0Ah) 304 | endif 305 | .endif 306 | 307 | clc 308 | @@: 309 | ret 310 | alloc_CORB_RIRB endp 311 | 312 | init_cntrlr proc near uses es edi 313 | call get_hdareg_ptr 314 | jc @@failed 315 | 316 | bts es:[edi].HDAREGS.gctl,0 317 | jc @@hda_running 318 | if ?DEBUGLOG 319 | invoke printtolog, CStr("Initializing HDA controller...",0Dh,0Ah) 320 | endif 321 | mov ecx,10000h 322 | @@: 323 | call wait_timerch2 324 | test es:[edi].HDAREGS.gctl,1 325 | loopz @B 326 | jnz @@hda_running 327 | 328 | if ?DEBUGLOG 329 | invoke printtolog, CStr("Timed out initializing HDA controller!",0Dh,0Ah) 330 | endif 331 | @@failed: 332 | stc 333 | ret 334 | 335 | @@hda_running: 336 | if ?DEBUGLOG 337 | invoke printtolog, CStr("GCAP == ") 338 | invoke printbinword,es:[edi].HDAREGS.gcap 339 | invoke printtolog, CStr("b",0Dh,0Ah,"Version == ") 340 | invoke printbinword,word ptr es:[edi].HDAREGS.vminor 341 | invoke printtolog, CStr("b",0Dh,0Ah) 342 | endif 343 | clc 344 | ret 345 | init_cntrlr endp 346 | 347 | set_busmaster proc near 348 | if ?DEBUGLOG 349 | invoke printtolog, CStr("Device initialized, ensuring it can act as busmaster...",0Dh,0Ah) 350 | endif 351 | mov ax,0B109h ; read configuration word 352 | mov bx,[wPort] 353 | mov edi,4 ; command register 354 | int 1Ah 355 | jc @F 356 | bts cx,2 ; bit 2 = bus master enabled 357 | btr cx,0Ah ; bit 10 = interrupt disable 358 | 359 | if ?DEBUGLOG 360 | invoke printtolog, CStr("Setting busmaster and clearing interrupt disable flag...",0Dh,0Ah) 361 | endif 362 | mov ax,0B10Ch ; write configuration word 363 | int 1Ah 364 | jc @F 365 | 366 | if ?DEBUGLOG 367 | invoke printtolog, CStr("Busmaster flag set",0Dh,0Ah) 368 | endif 369 | clc 370 | 371 | @@: 372 | ret 373 | set_busmaster endp 374 | 375 | start_CORB_RIRB proc near uses es edi 376 | call get_hdareg_ptr 377 | jc @@failed 378 | 379 | if ?DEBUGLOG 380 | invoke printtolog, CStr("Resetting CORB/RIRB...",0Dh,0Ah) 381 | endif 382 | and es:[edi].HDAREGS.corbctl,not 2 383 | and es:[edi].HDAREGS.rirbctl,not 2 384 | mov ecx,1000h 385 | @@: 386 | call wait_timerch2 387 | test es:[edi].HDAREGS.corbctl,2 388 | loopnz @B 389 | jz @F 390 | 391 | if ?DEBUGLOG 392 | invoke printtolog, CStr("Timed out stopping CORB DMA Engine!",0Dh,0Ah) 393 | endif 394 | jmp @@failed 395 | 396 | @@: 397 | mov edx,[dwCorbPhys] 398 | mov dword ptr es:[edi].HDAREGS.corbbase,edx 399 | mov dword ptr es:[edi].HDAREGS.corbbase+4,0 400 | add edx,[dwRirbOff] 401 | mov dword ptr es:[edi].HDAREGS.rirbbase,edx 402 | mov dword ptr es:[edi].HDAREGS.rirbbase+4,0 403 | 404 | if ?DEBUGLOG 405 | invoke printtolog, CStr("CORB write pointer == ") 406 | invoke printbinword, es:[edi].HDAREGS.corbwp 407 | invoke printtolog, CStr("b",0Dh,0Ah) 408 | endif 409 | mov es:[edi].HDAREGS.corbwp,0 ; reset CORB write pointer 410 | mov es:[edi].HDAREGS.rirbwp,8000h ; reset RIRB write pointer 411 | mov es:[edi].HDAREGS.rirbric,2 ; putting 1 here freezes QEMU (and presumably some actual hardware) 412 | 413 | if ?DEBUGLOG 414 | invoke printtolog, CStr("Resetting CORB read pointer...",0Dh,0Ah) 415 | endif 416 | bts es:[edi].HDAREGS.corbrp,15 417 | jc @@corb_in_reset 418 | mov ecx,1000h 419 | @@: 420 | call wait_timerch2 421 | cmp es:[edi].HDAREGS.corbrp,0 422 | jz @F 423 | test byte ptr es:[edi].HDAREGS.corbrp+1,80h 424 | loopz @B 425 | jnz @@corb_in_reset 426 | 427 | if ?DEBUGLOG 428 | invoke printtolog, CStr("Timed out resetting CORB read pointer, continuing anyway...",0Dh,0Ah) 429 | endif 430 | jmp @F 431 | 432 | @@corb_in_reset: 433 | if ?DEBUGLOG 434 | invoke printtolog, CStr("Reset bit on, turning it back off...",0Dh,0Ah) 435 | endif 436 | @@: 437 | btr es:[edi].HDAREGS.corbrp,15 438 | mov ecx,1000h 439 | @@: 440 | call wait_timerch2 441 | test byte ptr es:[edi].HDAREGS.corbrp+1,80h 442 | loopnz @B 443 | jz @F 444 | 445 | if ?DEBUGLOG 446 | invoke printtolog, CStr("Timed out taking CORB read pointer out of reset, continuing anyway...",0Dh,0Ah) 447 | endif 448 | 449 | @@: 450 | mov ax,es:[edi].HDAREGS.corbrp 451 | mov es:[edi].HDAREGS.corbwp,ax 452 | 453 | mov al,es:[edi].HDAREGS.corbsize 454 | mov ah,al 455 | and al,3 ; two bits setting the size 456 | and ah,0F0h ; four bits indicating size capability 457 | 458 | bt ax,0Eh ; 256 entries possible? 459 | jnc @F 460 | if ?DEBUGLOG 461 | invoke printtolog, CStr("CORB supports 256 entries",0Dh,0Ah) 462 | endif 463 | cmp al,2 ; 256 entries set? 464 | je @@corbsizeok 465 | mov al,2 466 | jmp @@setcorbsize 467 | 468 | @@: 469 | bt ax,0Dh ; 16 entries possible? 470 | jnc @F 471 | if ?DEBUGLOG 472 | invoke printtolog, CStr("CORB supports 16 entries",0Dh,0Ah) 473 | endif 474 | cmp al,1 ; 16 entries set? 475 | mov [corbwpmask],0Fh ; CORB WP wraps every 16 entries! 476 | je @@corbsizeok 477 | mov al,1 478 | jmp @@setcorbsize 479 | 480 | @@: 481 | ; if we're here, then only 2 entries are possible, and must already be set 482 | if ?DEBUGLOG 483 | invoke printtolog, CStr("CORB only supports 2 entries",0Dh,0Ah) 484 | endif 485 | mov [corbwpmask],1 ; wraps every second entry! 486 | jmp @@corbsizeok 487 | 488 | @@setcorbsize: 489 | if ?DEBUGLOG 490 | invoke printtolog, CStr("setting CORB size...",0Dh,0Ah) 491 | endif 492 | or al,ah 493 | mov es:[edi].HDAREGS.corbsize,al 494 | mov ecx,1000h 495 | @@: 496 | call wait_timerch2 497 | cmp es:[edi].HDAREGS.corbsize,al 498 | loopne @B 499 | 500 | @@corbsizeok: 501 | if ?DEBUGLOG 502 | invoke printtolog, CStr("CORB size set",0Dh,0Ah) 503 | endif 504 | mov al,es:[edi].HDAREGS.rirbsize 505 | mov ah,al 506 | and al,3 ; two bits setting the size 507 | and ah,0F0h ; four bits indicating size capability 508 | 509 | bt ax,0Eh ; 256 entries possible? 510 | jnc @F 511 | if ?DEBUGLOG 512 | invoke printtolog, CStr("RIRB supports 256 entries",0Dh,0Ah) 513 | endif 514 | cmp al,2 ; 256 entries set? 515 | je @@rirbsizeok 516 | mov al,2 517 | jmp @@setrirbsize 518 | 519 | @@: 520 | bt ax,0Dh ; 16 entries possible? 521 | jnc @F 522 | if ?DEBUGLOG 523 | invoke printtolog, CStr("RIRB supports 16 entries",0Dh,0Ah) 524 | endif 525 | cmp al,1 ; 16 entries set? 526 | mov [rirbwpmask],0Fh ; RIRB WP wraps every 16 entries! 527 | je @@rirbsizeok 528 | mov al,1 529 | jmp @@setrirbsize 530 | 531 | @@: 532 | ; if we're here, then only 2 entries are possible, and must already be set 533 | if ?DEBUGLOG 534 | invoke printtolog, CStr("RIRB only supports 2 entries",0Dh,0Ah) 535 | endif 536 | mov [rirbwpmask],1 ; wraps every second entry! 537 | jmp @@rirbsizeok 538 | 539 | @@setrirbsize: 540 | if ?DEBUGLOG 541 | invoke printtolog, CStr("setting RIRB size...",0Dh,0Ah) 542 | endif 543 | or al,ah 544 | mov es:[edi].HDAREGS.rirbsize,al 545 | mov ecx,1000h 546 | @@: 547 | call wait_timerch2 548 | cmp es:[edi].HDAREGS.rirbsize,al 549 | loopne @B 550 | 551 | @@rirbsizeok: 552 | if ?DEBUGLOG 553 | invoke printtolog, CStr("RIRB size set",0Dh,0Ah) 554 | endif 555 | 556 | if ?DEBUGLOG 557 | invoke printtolog, CStr("Starting CORB/RIRB DMA engines...",0Dh,0Ah) 558 | endif 559 | or es:[edi].HDAREGS.corbctl,3 ; turn on and enable interrupts 560 | or es:[edi].HDAREGS.rirbctl,6 ; turn on and enable overrun interrupt 561 | mov ecx,1000h 562 | @@: 563 | call wait_timerch2 564 | test es:[edi].HDAREGS.corbctl,2 565 | loopz @B 566 | jnz @F 567 | 568 | if ?DEBUGLOG 569 | invoke printtolog, CStr("Timed out initializing CORB/RIRB!",0Dh,0Ah) 570 | endif 571 | @@failed: 572 | stc 573 | ret 574 | 575 | @@: 576 | clc 577 | ret 578 | start_CORB_RIRB endp 579 | 580 | ; Get the subordinate nodes of the currently-selected [node] 581 | ; Returns the start node in EAX, and the count in EDX 582 | get_subnodes proc near 583 | mov ax,0F00h ; get parameter 584 | mov edx,4 ; subordinate node count 585 | call send_cmd_wait 586 | 587 | movzx edx,al 588 | shr eax,10h 589 | xor ah,ah 590 | 591 | ret 592 | get_subnodes endp 593 | 594 | ; Allocate a 128-byte-aligned DMA buffer in Extended Memory 595 | ; Takes size in EAX, returns XMS handle and selector in upper and lower halves of EAX, and physical address in EDX. 596 | ; If the handle in the upper half of EAX is returned as zero, then check EBX instead for a DPMI memory block handle! 597 | alloc_dma_buf proc near 598 | mov edx,[dwTSRbuf] 599 | test edx,edx 600 | jz @@try_xms_dpmi 601 | 602 | push ecx 603 | push edi 604 | push esi 605 | push ebx 606 | 607 | mov edi,eax 608 | add eax,7Fh 609 | and al,80h 610 | add edx,[dwTSRbufoffset] 611 | add eax,[dwTSRbufoffset] ; bump allocation with 128-byte align 612 | .if eax > MAXTSRBUFOFFSET 613 | if ?DEBUGLOG 614 | invoke printtolog, CStr("out of TSR arena memory!",0Dh,0Ah) 615 | ror eax,10h 616 | invoke printbinword,ax 617 | ror eax,10h 618 | invoke printbinword,ax 619 | invoke printtolog, CStr("b > ") 620 | mov ebx,MAXTSRBUFOFFSET 621 | ror ebx,10h 622 | invoke printbinword,bx 623 | ror ebx,10h 624 | invoke printbinword,bx 625 | invoke printtolog, CStr("b",0Dh,0Ah) 626 | endif 627 | xor eax,eax 628 | stc 629 | .else 630 | mov [dwTSRbufoffset],eax 631 | 632 | mov ecx,edx 633 | mov ebx,edx 634 | shr ebx,10h ; get physical base into BX:CX 635 | mov esi,edi 636 | shr esi,10h ; get desired buffer size into SI:DI 637 | call alloc_phys_sel 638 | jc @@dpmiselfail 639 | .endif 640 | 641 | pop ebx 642 | pop esi 643 | pop edi 644 | pop ecx 645 | ret 646 | 647 | @@try_xms_dpmi: 648 | CHECK_XMS_NEEDED 649 | jnc @@use_dpmi 650 | 651 | push ebp 652 | sub esp,size RMCS 653 | mov ebp,esp 654 | mov [ebp].RMCS.resvrd,0 655 | mov [ebp].RMCS.rSSSP,0 656 | 657 | mov [ebp].RMCS.rEBP,eax ; stash the size on the stack for now... 658 | mov eax,[xmsentry] 659 | test eax,eax 660 | jnz @F 661 | 662 | ; check if XMS is available 663 | mov ax,4300h 664 | int 2Fh 665 | cmp al,80h 666 | jne @@noxms 667 | 668 | mov [ebp].RMCS.rAX,4310h ; get XMS driver address 669 | mov [ebp].RMCS.rEDX,ebx ; stash EBX 670 | mov [ebp].RMCS.rECX,ecx ; stash ECX 671 | mov [ebp].RMCS.rEDI,edi ; stash EDI 672 | push es 673 | 674 | push ss 675 | pop es 676 | mov edi,ebp 677 | mov bx,2Fh 678 | xor cx,cx 679 | mov ax,0300h ; simulate real-mode interrupt 680 | int 31h 681 | 682 | pop es 683 | mov ebx,[ebp].RMCS.rEDX ; restore EBX 684 | mov ecx,[ebp].RMCS.rECX ; restore ECX 685 | mov edi,[ebp].RMCS.rEDI ; restore EDI 686 | jc @@simfail 687 | 688 | mov ax,[ebp].RMCS.rES 689 | shl eax,10h 690 | mov ax,[ebp].RMCS.rBX ; entry point in real-mode ES:BX 691 | mov [xmsentry],eax 692 | 693 | @@: 694 | mov [ebp].RMCS.rCSIP,eax 695 | mov [ebp].RMCS.rAX,900h ; allocate EMB 696 | 697 | mov eax,[ebp].RMCS.rEBP 698 | add eax,7Fh ; ensure enough room for 128-byte alignment 699 | add eax,3FFh ; round up to nearest kiB 700 | shr eax,10 ; convert to kiB 701 | test eax,0FFFF0000h ; 32-bit number of kiB needed? 702 | jz @F 703 | mov [ebp].RMCS.rAX,8900h ; allocate any extended memory (XMS 3.0) 704 | @@: 705 | mov [ebp].RMCS.rEDX,eax 706 | 707 | mov [ebp].RMCS.rESI,ebx ; stash EBX 708 | mov [ebp].RMCS.rECX,ecx ; stash ECX 709 | mov [ebp].RMCS.rEDI,edi ; stash EDI 710 | push es 711 | 712 | push ss 713 | pop es 714 | mov edi,ebp 715 | xor bx,bx 716 | xor cx,cx 717 | mov ax,0301h ; call real-mode far function 718 | int 31h 719 | 720 | pop es 721 | mov ebx,[ebp].RMCS.rESI ; restore EBX 722 | mov ecx,[ebp].RMCS.rECX ; restore ECX 723 | mov edi,[ebp].RMCS.rEDI ; restore EDI 724 | jc @@simfail 725 | 726 | cmp [ebp].RMCS.rAX,1 727 | jne @@xmsfail 728 | mov ax,[ebp].RMCS.rDX ; get the handle 729 | push ax 730 | 731 | mov [ebp].RMCS.rAX,0C00h ; lock EMB 732 | push es 733 | push ss 734 | pop es 735 | mov edi,ebp 736 | xor bx,bx 737 | xor cx,cx 738 | mov ax,0301h ; call real-mode far function 739 | int 31h 740 | 741 | pop es 742 | mov ebx,[ebp].RMCS.rESI ; restore EBX 743 | mov ecx,[ebp].RMCS.rECX ; restore ECX 744 | mov edi,[ebp].RMCS.rEDI ; restore EDI 745 | jc @@simfail 746 | 747 | mov dx,[ebp].RMCS.rDX ; upper half of physical address 748 | shl edx,10h 749 | mov dx,[ebp].RMCS.rBX ; lower half of physical address 750 | add edx,7Fh 751 | and dl,80h ; ensure 128-byte alignment 752 | 753 | mov [ebp].RMCS.rEBX,ebx ; stash EBX 754 | mov [ebp].RMCS.rESI,esi ; stash ESI 755 | 756 | mov ecx,edx 757 | mov ebx,edx 758 | shr ebx,10h ; get physical base into BX:CX 759 | mov edi,[ebp].RMCS.rEBP 760 | mov esi,edi 761 | shr esi,10h ; get desired buffer size into SI:DI 762 | call alloc_phys_sel 763 | 764 | mov ebx,[ebp].RMCS.rEBX ; restore EBX 765 | mov ecx,[ebp].RMCS.rECX ; restore ECX 766 | mov esi,[ebp].RMCS.rESI ; restore ESI 767 | mov edi,[ebp].RMCS.rEDI ; restore EDI 768 | jc @@physmapfail 769 | 770 | push ax 771 | pop eax ; now EAX contains the XMS handle and the selector 772 | clc 773 | 774 | @@retpoint: 775 | lea esp,[ebp+size RMCS] 776 | pop ebp 777 | ret 778 | 779 | @@physmapfail: 780 | pushw 0 781 | pop eax 782 | if ?DEBUGLOG 783 | invoke printtolog, CStr("Physical address mapping failed",0Dh,0Ah) 784 | jmp @@retpoint_fail 785 | 786 | @@noxms: 787 | invoke printtolog, CStr("No XMS available, can't allocate DMA buffer",0Dh,0Ah) 788 | jmp @@retpoint_fail 789 | 790 | @@xmsfail: 791 | invoke printtolog, CStr("XMS allocation failed",0Dh,0Ah) 792 | jmp @@retpoint_fail 793 | 794 | @@simfail: 795 | invoke printtolog, CStr("Failed to call real-mode procedure, can't allocate DMA buffer",0Dh,0Ah) 796 | else 797 | @@noxms: 798 | @@xmsfail: 799 | @@simfail: 800 | endif 801 | @@retpoint_fail: 802 | stc 803 | jmp @@retpoint 804 | 805 | 806 | ; If we're in Ring 0 with no paging, we don't need to go through XMS to get 807 | ; a physical address. Just use what the DOS extender provides. 808 | @@use_dpmi: 809 | push ecx 810 | push edi 811 | push esi 812 | 813 | push eax ; save size for later 814 | add eax,7Fh ; ensure enough room for 128-byte alignment 815 | mov cx,ax 816 | mov ebx,eax 817 | shr ebx,10h 818 | mov ax,0501h ; allocate memory block 819 | int 31h 820 | jc @@dpmiallocfail 821 | 822 | xchg di,[esp] ; get the size back and store the DPMI handle 823 | xchg si,[esp+2] ; get the size back and store the DPMI handle 824 | xor eax,eax 825 | add cx,7Fh 826 | adc bx,0 827 | and cl,80h ; ensure 128-byte alignment 828 | push bx 829 | push cx 830 | call alloc_phys_sel 831 | pop edx ; get the address 832 | jc @@dpmiselfail 833 | 834 | pop ebx ; get the DPMI handle 835 | @@dpmi_retpoint: 836 | pop esi 837 | pop edi 838 | pop ecx 839 | ret 840 | 841 | @@dpmiallocfail: 842 | if ?DEBUGLOG 843 | invoke printtolog, CStr("DPMI memory allocation failed",0Dh,0Ah) 844 | endif 845 | pop eax ; get the size back 846 | jmp @@dpmifail 847 | @@dpmiselfail: 848 | if ?DEBUGLOG 849 | invoke printtolog, CStr("physical-memory selector allocation failed",0Dh,0Ah) 850 | endif 851 | pop ebx ; get the DPMI handle 852 | @@dpmifail: 853 | stc 854 | jmp @@dpmi_retpoint 855 | alloc_dma_buf endp 856 | 857 | ; Free a 128-byte-aligned DMA buffer in Extended Memory 858 | ; Takes XMS handle and selector in upper and lower halves of EAX, respectively. 859 | ; If needed, takes DPMI handle in EBX. 860 | free_dma_buf proc near 861 | cmp [dwTSRbuf],0 862 | jz @@try_xms_dpmi 863 | 864 | mov bx,ax 865 | jmp free_phys_sel 866 | 867 | @@try_xms_dpmi: 868 | CHECK_XMS_NEEDED 869 | jnc @@use_dpmi 870 | 871 | push ebp 872 | sub esp,size RMCS 873 | mov ebp,esp 874 | mov [ebp].RMCS.resvrd,0 875 | mov [ebp].RMCS.rSSSP,0 876 | 877 | mov bx,ax ; get selector 878 | shr eax,10h 879 | mov [ebp].RMCS.rEDX,eax ; save XMS handle 880 | call free_phys_sel 881 | 882 | mov eax,[xmsentry] 883 | test eax,eax 884 | jz @F ; if we don't know the XMS entry point, we can't have a valid handle! 885 | push es 886 | 887 | mov [ebp].RMCS.rCSIP,eax 888 | mov [ebp].RMCS.rAX,0D00h ; unlock EMB 889 | 890 | push ss 891 | pop es 892 | mov edi,ebp 893 | xor bx,bx 894 | xor cx,cx 895 | mov ax,0301h ; call real-mode far function 896 | int 31h 897 | 898 | mov [ebp].RMCS.rAX,0A00h ; free EMB 899 | mov ax,0301h ; call real-mode far function 900 | int 31h 901 | 902 | pop es 903 | @@: 904 | lea esp,[ebp+size RMCS] 905 | pop ebp 906 | ret 907 | 908 | 909 | @@use_dpmi: 910 | push esi 911 | push edi 912 | 913 | mov bx,ax ; get selector 914 | call free_selector 915 | 916 | push ebx 917 | pop di 918 | pop si 919 | mov ax,0502h ; free memory block 920 | int 31h 921 | 922 | pop edi 923 | pop esi 924 | ret 925 | free_dma_buf endp 926 | 927 | ; Take verb in AX and payload in EDX, and return a fully-formed HDA command in EAX 928 | formulate_cmd proc near 929 | cwde 930 | .if ah ; 12-bit command? 931 | shl eax,8 932 | .else 933 | shl eax,16 934 | .endif 935 | or eax,edx ; combined verb and payload 936 | 937 | movzx edx,[wParam] ; codec address and node ID 938 | shl edx,20 939 | or eax,edx ; combined codec address, node ID, verb and payload 940 | 941 | ret 942 | formulate_cmd endp 943 | 944 | ; Take verb in AX and payload in EDX, and send command out on CORB 945 | ; Returns pre-command RIRB write pointer in EAX 946 | send_cmd proc near uses es gs edi esi 947 | call formulate_cmd 948 | push eax 949 | 950 | lgs esi,[lpRirb] 951 | call get_hdareg_ptr 952 | 953 | mov ax,es:[edi].HDAREGS.rirbwp 954 | shl eax,10h 955 | mov ax,es:[edi].HDAREGS.corbwp 956 | inc al 957 | and al,[corbwpmask] 958 | movzx esi,ax 959 | pop dword ptr gs:[esi*4] ; pop command into CORB:[(CORBWP+1)*4] 960 | mov es:[edi].HDAREGS.corbwp,si 961 | 962 | shr eax,10h 963 | ret 964 | send_cmd endp 965 | 966 | ; Take verb in AX and payload in EDX, send command out on CORB, 967 | ; and return the response in EAX when it comes in on RIRB 968 | send_cmd_wait proc near uses es gs edi 969 | call send_cmd 970 | ; now we play the waiting game... 971 | call get_hdareg_ptr 972 | if ?DEBUGLOG 973 | invoke printtolog, CStr("Command sent...",0Dh,0Ah) 974 | endif 975 | .while ax == es:[edi].HDAREGS.rirbwp 976 | call wait_timerch2 977 | .endw 978 | ; reset the controller interrupt status 979 | or es:[edi].HDAREGS.rirbsts,1 980 | 981 | movzx eax,es:[edi].HDAREGS.rirbwp 982 | lgs edi,[lpRirb] 983 | mov eax,gs:[edi+eax*8] 984 | if ?DEBUGLOG 985 | invoke printtolog, CStr("Response received!",0Dh,0Ah) 986 | endif 987 | 988 | ret 989 | send_cmd_wait endp 990 | 991 | ; wait a bit (copied from "dowait" in Japheth's MIT-licensed hdaplay) 992 | wait_timerch2 proc near uses eax ecx 993 | ; don't let it beep while this is going on! 994 | in al,61h 995 | and al,0FCh 996 | out 61h,al 997 | 998 | mov ecx,100h 999 | @@: 1000 | db 0F3h,90h ; pause (mnemonic not accepted by uasm in "386" mode...) 1001 | in al,61h 1002 | and al,10h 1003 | cmp al,ah 1004 | mov ah,al 1005 | jz @B 1006 | loop @B 1007 | ret 1008 | wait_timerch2 endp 1009 | 1010 | ; get far pointer to HD Audio device's registers in ES:EDI 1011 | ; spoils EAX/EBX/ECX/EDX/ESI on first call, but not on subsequent calls 1012 | get_hdareg_ptr proc near 1013 | .if [hdareg_seg] == 0 1014 | if ?DEBUGLOG 1015 | invoke printtolog, CStr("Creating far pointer to HDA device registers...",0Dh,0Ah) 1016 | endif 1017 | .if [hdareg_linaddr] == 0 1018 | call check_pci_bios 1019 | jc @F 1020 | 1021 | if ?DEBUGLOG 1022 | invoke printtolog, CStr("Creating linear map to HDA device registers...",0Dh,0Ah) 1023 | endif 1024 | mov ax,0B10Ah ; read configuration dword 1025 | mov di,14h ; BAR1 (upper dword) 1026 | mov bx,[wPort] 1027 | int 1Ah 1028 | jc @F 1029 | test ecx,ecx ; if upper dword not zero, we're toast! (since we're 32-bit) 1030 | stc 1031 | jnz @F 1032 | 1033 | if ?DEBUGLOG 1034 | ;invoke printtolog, CStr("Reading BAR0...",0Dh,0Ah) 1035 | endif 1036 | mov ax,0B10Ah ; read configuration dword 1037 | mov di,10h ; BAR0 (lower dword) 1038 | mov bx,[wPort] 1039 | int 1Ah 1040 | jc @F 1041 | 1042 | if ?DEBUGLOG 1043 | invoke printtolog, CStr("Mapping page(s) containing BAR0...",0Dh,0Ah) 1044 | endif 1045 | and cl,0F0h ; prevent off-by-four errors and the like 1046 | mov ebx,ecx 1047 | shr ebx,10h ; get the full address into BX:CX 1048 | ;if ?DEBUGLOG 1049 | ; invoke printtolog, CStr("BAR0 == ") 1050 | ; invoke printbinword,bx 1051 | ; invoke printbinword,cx 1052 | ; invoke printtolog, CStr("b",0Dh,0Ah) 1053 | ;endif 1054 | xor esi,esi 1055 | mov edi,size HDAREGS 1056 | call map_physmem 1057 | jc @F 1058 | 1059 | if ?DEBUGLOG 1060 | invoke printtolog, CStr("Page map successful",0Dh,0Ah) 1061 | endif 1062 | mov [hdareg_linaddr],ecx 1063 | mov word ptr [hdareg_linaddr+2],bx 1064 | .else 1065 | mov ecx,[hdareg_linaddr] 1066 | mov ebx,ecx 1067 | shr ebx,10h 1068 | .endif 1069 | ;if ?DEBUGLOG 1070 | ; invoke printtolog, CStr("hdareg_linaddr == ") 1071 | ; invoke printbinword,bx 1072 | ; invoke printbinword,cx 1073 | ; invoke printtolog, CStr("b",0Dh,0Ah) 1074 | ;endif 1075 | 1076 | if ?DEBUGLOG 1077 | invoke printtolog, CStr("Allocating selector...",0Dh,0Ah) 1078 | endif 1079 | mov edi,size HDAREGS 1080 | xor esi,esi 1081 | call alloc_selector 1082 | jc @F 1083 | mov [hdareg_seg],ax 1084 | if ?DEBUGLOG 1085 | invoke printtolog, CStr("Far pointer created successfully",0Dh,0Ah) 1086 | endif 1087 | .endif 1088 | 1089 | les edi,[hdareg_ptr] 1090 | clc 1091 | @@: 1092 | ret 1093 | get_hdareg_ptr endp 1094 | 1095 | alloc_phys_sel proc near 1096 | ; BX:CX = base physical address 1097 | ; SI:DI = size 1098 | ; returns selector in AX pointing to the physical address in BX:CX 1099 | if ?DEBUGLOG 1100 | invoke printtolog, CStr("alloc_phys_sel: base address == ") 1101 | invoke printbinword,bx 1102 | invoke printbinword,cx 1103 | invoke printtolog, CStr("b",0Dh,0Ah,"alloc_phys_sel: size == ") 1104 | invoke printbinword,si 1105 | invoke printbinword,di 1106 | invoke printtolog, CStr("b",0Dh,0Ah) 1107 | endif 1108 | call map_physmem 1109 | jc @F 1110 | if ?DEBUGLOG 1111 | invoke printtolog, CStr("Physical mapping successful",0Dh,0Ah) 1112 | endif 1113 | call alloc_selector 1114 | if ?DEBUGLOG 1115 | jc @F 1116 | invoke printtolog, CStr("Selector creation successful",0Dh,0Ah) 1117 | endif 1118 | @@: 1119 | ret 1120 | alloc_phys_sel endp 1121 | 1122 | free_phys_sel proc near 1123 | ; BX = selector 1124 | ifdef ?FLASHTEK 1125 | mov ax,3504h ; FlashTek - get base address 1126 | int 21h 1127 | mov dx,cx 1128 | shr ecx,10h 1129 | else 1130 | mov ax,6 ; get segment base address 1131 | int 31h 1132 | endif 1133 | jc @F 1134 | 1135 | if ?DEBUGLOG 1136 | invoke printtolog, CStr("free_phys_sel: base address == ") 1137 | invoke printbinword,cx 1138 | invoke printbinword,dx 1139 | invoke printtolog, CStr("b",0Dh,0Ah) 1140 | endif 1141 | xchg bx,cx 1142 | xchg cx,dx ; save the selector in DX 1143 | call unmap_physmem 1144 | 1145 | mov bx,dx 1146 | call free_selector 1147 | 1148 | @@: 1149 | ret 1150 | free_phys_sel endp 1151 | 1152 | map_physmem proc near uses eax edx esi edi 1153 | ; BX:CX = base 1154 | ; SI:DI = size (preserved after call) 1155 | ; returns linear address in BX:CX 1156 | 1157 | CHECK_XMS_NEEDED 1158 | jnc @F ; return the phys address as linear 1159 | 1160 | ; figure out which pages need to be mapped, and how many 1161 | mov ax,bx 1162 | shl eax,10h 1163 | mov ax,cx 1164 | mov edx,eax ; EDX points to the beginning of the map 1165 | 1166 | mov ax,si 1167 | shl eax,10h 1168 | mov ax,di 1169 | add eax,edx ; EAX points to the end 1170 | 1171 | and dx,0F000h ; EDX now points to the first page 1172 | and ax,0F000h ; EAX now points to the last page 1173 | sub eax,edx 1174 | add eax,1000h ; EAX now has the number of pages we need to map (SHL 12) 1175 | 1176 | xchg cx,dx 1177 | mov ebx,edx 1178 | ifdef ?FLASHTEK 1179 | mov bx,cx 1180 | mov ecx,eax 1181 | mov ax,350Ah ; FlashTek - physical address mapping 1182 | int 21h 1183 | push ebx 1184 | pop cx 1185 | pop bx 1186 | else 1187 | shr ebx,10h 1188 | mov di,ax 1189 | mov esi,eax 1190 | shr esi,10h 1191 | mov ax,0800h ; physical address mapping 1192 | int 31h 1193 | endif 1194 | jc @F 1195 | 1196 | and edx,0FFFh ; get back the offset into the page 1197 | or ecx,edx 1198 | clc 1199 | @@: 1200 | ret 1201 | map_physmem endp 1202 | 1203 | unmap_physmem proc near 1204 | ifndef ?FLASHTEK ; FlashTek has no "unmap" function... 1205 | CHECK_XMS_NEEDED 1206 | jnc @F ; this is a no-op 1207 | 1208 | ; BX:CX = linear address 1209 | and cx,0F000h ; address passed here may not be page-aligned... 1210 | mov ax,801h 1211 | int 31h 1212 | @@: 1213 | endif 1214 | ret 1215 | unmap_physmem endp 1216 | 1217 | alloc_selector proc near 1218 | ; BX:CX = base 1219 | ; SI:DI = size 1220 | ; returns selector in AX 1221 | push edx 1222 | ifdef ?FLASHTEK 1223 | push bx 1224 | push cx 1225 | pop ecx 1226 | mov ax,3501h ; FlashTek - allocate selector 1227 | int 21h 1228 | else 1229 | mov dx,cx 1230 | xor ax,ax ; allocate selector 1231 | mov cx,1 ; one selector 1232 | int 31h 1233 | endif 1234 | jc @F 1235 | 1236 | ifdef ?FLASHTEK 1237 | mov ax,3503h ; FlashTek - set base address 1238 | int 21h 1239 | else 1240 | mov cx,bx 1241 | mov bx,ax 1242 | mov ax,7 ; set segment base address 1243 | int 31h 1244 | endif 1245 | jc @F 1246 | 1247 | ifdef ?FLASHTEK 1248 | push si 1249 | push di 1250 | pop ecx 1251 | dec ecx ; change size to limit 1252 | mov ax,3505h ; FlashTek - set limit 1253 | int 21h 1254 | else 1255 | mov dx,di 1256 | mov cx,si 1257 | mov ax,8 ; set segment limit 1258 | dec dx ; change size to limit 1259 | sbb cx,0 1260 | int 31h 1261 | endif 1262 | jc @F 1263 | 1264 | mov ax,bx ; return the selector 1265 | clc 1266 | @@: 1267 | pop edx 1268 | ret 1269 | alloc_selector endp 1270 | 1271 | free_selector proc near 1272 | ; BX = selector 1273 | ifdef ?FLASHTEK 1274 | mov ax,3502h 1275 | int 21h 1276 | else 1277 | mov ax,1 1278 | int 31h 1279 | endif 1280 | ret 1281 | free_selector endp 1282 | 1283 | include PORTLIST.INC 1284 | -------------------------------------------------------------------------------- /DRVSRC/HDA.INC: -------------------------------------------------------------------------------- 1 | 2 | ;--- stream descriptors: 32 bytes, starting at offset 80h (input) 3 | 4 | STREAM struct 8 5 | wCtl dw ? ;+0 15:5 rsvd, 4 DEIE desc err int, 3 FEIE fifo err int, 2 IOCE int on compl, 1 RUN, 0 SRST stream reset 6 | bCtl2316 db ? ;+2 23:20 stream number (0=reserved, 1-15 = stream 1-15) 7 | bSts db ? ;+3 8 | dwLinkPos dd ? ;+4 9 | dwBufLen dd ? ;+8 10 | wLastIdx dw ? ;+12 11 | wFIFOmark dw ? ;+14 12 | wFIFOsize dw ? ;+16 13 | wFormat dw ? ;+18 14 | qwBuffer dq ? ;+24 15 | STREAM ends 16 | 17 | ;--- format: 18 | ;--- [15] stream type: 0=PCM, 1=non-PCM 19 | ;--- [14] sample base rate: 0=48 kHz, 1=44.1 kHz 20 | ;--- [13:11]: sample base rate multiple: 0=x1 or less,1=x2,2=x3,3=x4,4-7 reserved 21 | ;--- [10:8]: sample base rate divisor: 0=div 1,1=div 2,2=div 3,3=div 4,4=div 5,5=div 6,6=div 7,7=div 8 22 | ;--- [7]: reserved 23 | ;--- [6:4]: bits per sample: 0=8, 1=16, 2=20, 3=24, 4=32, 5-7 reserved 24 | ;--- [3:0]: number of channels: 0=1, 1=2, ... 15=16 25 | 26 | ;--- gctl: [0] CRST: writing 0 -> controller resets 27 | ;--- writing 1 -> exit reset 28 | 29 | ;--- structure of HDA controller memory-mapped registers 30 | 31 | HDAREGS struct 8 32 | gcap dw ? 33 | vminor db ? 34 | vmajor db ? 35 | opayload dw ? 36 | ipayload dw ? 37 | gctl dd ? 38 | wakeen dw ? 39 | statests dw ? 40 | gsts dd ? ;+10h 41 | dd ?,?,? 42 | intctl dd ? ;+20h 43 | intsts dd ? 44 | dd ? 45 | dd ? 46 | walclk dd ? ;+30h 47 | dd ? 48 | ssync dd ? 49 | dd ? 50 | corbbase dq ? ;+40h size of CORB 256*4 = 1024 51 | corbwp dw ? 52 | corbrp dw ? 53 | corbctl db ? 54 | corbsts db ? 55 | corbsize db ? ;2, 16 or 256 entries (bits 1:0) 56 | db ? 57 | rirbbase dq ? ;+50h size of RIRB 256*8 = 2048 58 | rirbwp dw ? 59 | rirbric dw ? 60 | rirbctl db ? 61 | rirbsts db ? 62 | rirbsize db ? 63 | db ? 64 | ic dd ? ;+60h immediate command register 65 | ir dd ? ;immediate response register 66 | ics dw ? ;immediate command status register 67 | org 80h 68 | stream0 STREAM <> 69 | stream1 STREAM <> 70 | stream2 STREAM <> 71 | stream3 STREAM <> 72 | stream4 STREAM <> 73 | stream5 STREAM <> 74 | stream6 STREAM <> 75 | stream7 STREAM <> 76 | HDAREGS ends 77 | 78 | ;--- bits in ics 79 | ;--- bit 0 (immediate command busy): 1=busy (set to 1 by software to run command) 80 | ;--- bit 1 (immediate result valid): 1=valid new response. 81 | ;--- reset by software (by writing 1 to it!) before a new response is expected. 82 | 83 | ICS_ICB equ 1 84 | ICS_IRV equ 2 85 | 86 | ;--- DMA position in current buffer 87 | ;--- must be aligned to 128-byte 88 | ;--- for stream descriptor 0: dd pos, rsvd 89 | ;--- for stream descriptor 1: dd pos, rsvd, 90 | ;--- .... 91 | 92 | ;--- structure of BDL entry (size 16 bytes) 93 | ;--- start of BDL itself must be 128-byte aligned 94 | 95 | BDLENTRY struct 96 | qwAddr dq ? 97 | dwLen dd ? 98 | dwFlgs dd ? ;bit 0:1=IOC (interrupt on completion) 99 | BDLENTRY ends 100 | 101 | ;--- CORB: buffer must start on a 128-byte boundary, size in CORBSIZE 102 | ;--- each entry is 32-bits 103 | ;--- corb read pointer (CORBRP): write 1 to [15] to reset this pointer to 0 104 | ;--- if reset is complete, [15] will be 1. 105 | ;--- RIRB: buffer must start on a 128-byte boundary, size in RIRBSIZE 106 | ;--- each entry is 64-bits: response, response_ex (0-3: codec, 4: 1=unsolicited response) 107 | 108 | ;--- codec communication. 109 | ;--- 0003: set amplifier gain/mute 110 | ;--- bit 15: set output amp 111 | ;--- bit 14: set input amp 112 | ;--- bit 13: set left amp 113 | ;--- bit 12: set right amp 114 | ;--- bit 11-8: index (usually 0) 115 | ;--- bit 7: mute 116 | ;--- bit 6-0: gain 117 | ; 118 | ;--- default configuration (F1C) 119 | ;--- 31:30 port connectivity 120 | ;--- 29:24 location 121 | ;--- 23:20 default device 122 | ;--- 19:16 connection type 123 | ;--- 15:12 color 124 | ;--- 11:8 misc 125 | ;--- 7:4 def. association 126 | ;--- 3:0 sequence 127 | 128 | ;--- widget types 129 | 130 | WTYPE_AUDIOOUT equ 0 131 | WTYPE_AUDIOIN equ 1 132 | WTYPE_MIXER equ 2 133 | WTYPE_SELECTOR equ 3 134 | WTYPE_PIN equ 4 135 | WTYPE_POWER equ 5 136 | WTYPE_VOLKNOB equ 6 137 | WTYPE_BEEPGEN equ 7 138 | 139 | ;--- pin default device types 140 | 141 | DEFDEV_LINEOUT equ 0 142 | DEFDEV_SPEAKER equ 1 143 | DEFDEV_HEADPHONE equ 2 144 | DEFDEV_SPDIFOUT equ 4 145 | DEFDEV_DIGOUT equ 5 146 | 147 | -------------------------------------------------------------------------------- /DRVSRC/HDA16S.ASM: -------------------------------------------------------------------------------- 1 | ; 16-bit Stereo HD Audio driver for HMIDRV.386 2 | 3 | .386 4 | .model small 5 | include COMDECS.INC 6 | 7 | ?DEBUGLOG equ 0 8 | ?CDAUDIO equ 1 9 | ?SETIRQLINE equ 1 10 | 11 | _TEXT segment use32 12 | assume ds:nothing,es:nothing,gs:nothing,fs:_TEXT 13 | 14 | .code 15 | org 0 16 | hda16s: 17 | jmp entry 18 | 19 | align 4 20 | include COMDATA.INC 21 | lpAuxBufFilled label fword ; Far pointer to aux buffer when main one not 128-byte-aligned 22 | dwLastFillEAX dd 0 ; Value returned in EAX at last call to timer function 23 | dwAuxSelHdl label dword 24 | wAuxSel dw 0 25 | wAuxHdl dw 0 26 | dwAuxDpmiHdl dd 0 27 | 28 | firststreamoff dd 0 29 | 30 | dwMainBufPhys dd 0 ; Physical address of DMA buffer passed from host application 31 | dwMainBufSize dd 0 ; Size of DMA buffer passed from host application 32 | 33 | TicksSinceBCIS dd 0 34 | lpMainBuf label fword 35 | dwMainBufOff dd 0 36 | wMainBufSel dw 0 37 | 38 | mPICeoi db 0 ; EOI signal to send to master PIC 39 | sPICeoi db 0 ; EOI signal (if any) to send to slave PIC 40 | 41 | oldIRQhandler label fword 42 | oldIRQ_off dd 0 43 | oldIRQ_seg dw 0 44 | ifdef ?FLASHTEK 45 | oldIRQ_RM dd 0 46 | endif 47 | 48 | irqvec db 0 ; the actual interrupt vector corresponding to our IRQ 49 | oldpciirq db 0 ; the interrupt line of the controller before we set it 50 | 51 | if ?DEBUGLOG 52 | ; IRQ stats 53 | TotalIRQs dd 0 54 | UncaughtIRQs dd 0 55 | UncaughtPCI dd 0 56 | LastUncaught dd 0 57 | PassedIRQs dd 0 58 | TotalBCISs dd 0 59 | TotalTicks dq 0 60 | LastIRR dw 0 61 | LastISR dw 0 62 | endif 63 | 64 | if ?CDAUDIO 65 | ?CDBUFSIZE equ 8 ; size in sectors 66 | ?CDVOLCTL equ 1 67 | ?CDCHANCTL equ 1 ; support more than two channels? 68 | 69 | CDSECTORSIZE equ 930h ; a constant, defined in the Red Book standard 70 | CDBUFSIZEDWORDS equ ?CDBUFSIZE * CDSECTORSIZE SHR 2 71 | 72 | IOCTLRW struc ; IOCTL read/write request 73 | bLen db ? ; 3 for read, 12 for write 74 | bUnit db ? 75 | bCmd db ? 76 | wStatus dw ? 77 | _resd dq ? 78 | _resd1 db ? ; media descriptor byte = 0 for MSCDEX 79 | wBufOff dw ? 80 | wBufSeg dw ? 81 | wCount dw ? 82 | _resd2 dw ? ; starting sector number = 0 for MSCDEX 83 | _resd3 dd ? ; volume ID = 0 for MSCDEX 84 | IOCTLRW ends 85 | 86 | ReadL struc 87 | bLen db ? 88 | bUnit db ? 89 | bCmd db ? 90 | wStatus dw ? 91 | _resd dq ? 92 | bAMode db ? ; addressing mode (RedBook / High Sierra) 93 | wBufOff dw ? 94 | wBufSeg dw ? 95 | wSectors dw ? 96 | dwStart dd ? ; first sector 97 | bRMode db ? ; read mode (cooked / raw) - use raw to get 930h 98 | bISize db ? ; interleave size 99 | bISkip db ? ; interleave skip factor 100 | ReadL ends 101 | 102 | PlayReq struc 103 | bLen db ? 104 | bUnit db ? 105 | bCmd db ? 106 | wStatus dw ? 107 | _resd dq ? 108 | bAMode db ? ; addressing mode (RedBook / High Sierra) 109 | dwStart dd ? ; first sector 110 | dwSectors dd ? 111 | PlayReq ends 112 | 113 | OutChanInfo struc 114 | bInChan db ? ; input channel for this output channel 115 | bVolume db ? ; volume knob for this output channel 116 | OutChanInfo ends 117 | 118 | AudInfo struc 119 | bCode db ? ; 4 for read, 3 for write 120 | Info OutChanInfo 4 dup () 121 | AudInfo ends 122 | 123 | AudStat struc 124 | bCode db ? ; 15 for read 125 | wStatus dw ? ; Bit 0 = paused, all others reserved 126 | dwStart dd ? 127 | dwEnd dd ? 128 | AudStat ends 129 | 130 | ; dwStat meaning: 131 | ; Bit 0 = door open 132 | ; Bit 1 = door unlocked 133 | ; Bit 2 = supports raw reading (needed to be useful to us) 134 | ; Bit 3 = writeable 135 | ; Bit 4 = can play audio/video 136 | ; Bit 5 = interleaving supported 137 | ; Bit 6 = reserved 138 | ; Bit 7 = supports prefetching (needed to be useful to us) 139 | ; Bit 8 = supports audio channel manipulation 140 | ; Bit 9 = supports Red Book addressing mode 141 | DevStat struc 142 | bCode db ? ; 6 for read 143 | dwStat dd ? 144 | DevStat ends 145 | 146 | QInfo struc 147 | bCode db ? ; 12 for read 148 | bCtlADR db ? 149 | bTrack db ? 150 | bPoint db ? 151 | bMinute db ? 152 | bSecond db ? 153 | bFrame db ? 154 | _resd db ? ; zero 155 | bPMin db ? 156 | bPSec db ? 157 | bPFrame db ? 158 | QInfo ends 159 | 160 | ; layout of our first conventional-memory buffer for querying CD drives 161 | CdRmHeadBuf struc 162 | wFirstS dw ? ; selector of first buffer in linked list 163 | bDrives db ? ; number of drives available 164 | sReq IOCTLRW 165 | sInfo DevStat 166 | sRmCall RMCS <> 167 | CdRmHeadBuf ends 168 | 169 | ; layout of our per-drive conventional-memory buffers for talking to MSCDEX 170 | CdRmDriveBuf struc 171 | wNextS dw ? ; selector of next buffer in linked list 172 | bDrive db ? ; index of drive associated with this buffer 173 | sReq ReadL 174 | sInfo AudInfo 175 | sStat AudStat 176 | sQChan QInfo 177 | wStatus dw ? ; set bit 9 to indicate we're playing 178 | ; also bit 0 to indicate prefetch possible... 179 | dwBufPos dd ? 180 | dwBufEnd dd ? 181 | align 10h ; make it its own segment... 182 | Samples dd CDBUFSIZEDWORDS dup (?) 183 | CdRmDriveBuf ends 184 | 185 | ; Pointer to head buffer 186 | wCdRmBufSel dw ? 187 | wCdRmBufSeg dw ? 188 | 189 | ; selector for entire MiB of Real Mode memory 190 | wRmMemSel dw ? 191 | 192 | ; real-mode callback to set the busy bit on return from an intercepted int 2F 193 | dwSetBusyCB label dword 194 | wSetBusyCBOff dw ? 195 | wSetBusyCBSeg dw ? 196 | 197 | dwOldInt2F label dword 198 | wOldInt2FOff dw ? 199 | wOldInt2FSeg dw ? 200 | 201 | bCdDivider db 1 ; set to 2 if running at 96 kHz (to simulate 32 kHz) 202 | endif 203 | 204 | ; Bit 0 = successfully initialized 205 | ; Bit 1 = timer entered 206 | ; Bit 2 = IRQ entered 207 | ; Bit 3 = Timer entered 208 | ; Bit 4 = Sound paused 209 | ; Bit 5 = Sound temporarily stopped (e.g. for setting rate) 210 | ; Bit 6 = CD Audio possible 211 | ; Bit 7 = XMS needed (linear addresses != physical) 212 | statusword dw 1 SHL 7 213 | ; bitmap representing rates supported by the currently-selected DAC node 214 | ratebitmap dw 0 215 | 216 | CHECK_XMS_NEEDED macro 217 | bt [statusword],7 218 | endm 219 | 220 | ; software rate divider 221 | soft_divider db 1 222 | 223 | pinnode db 0 224 | afgnode db 0 225 | dacnode db 0 226 | 227 | ; number of Controller/Stream Resets attempted during the current timer period 228 | ; (in response to error interrupts) 229 | crst_count db 0 230 | srst_count db 0 231 | ; give up after this many: 232 | ?CRST_MAX equ 3 233 | ?SRST_MAX equ 3 234 | 235 | ; Function table 236 | ftable dd offset drv_init 237 | dd offset drv_uninit 238 | dd offset drv_setrate 239 | dd offset drv_setaction 240 | dd offset drv_start 241 | dd offset drv_stop 242 | dd offset drv_pause 243 | dd offset drv_resume 244 | dd offset drv_capabilities 245 | dd offset drv_foreground 246 | dd offset drv_fillinfo 247 | dd offset drv_getcallfn 248 | dd offset drv_setcallfn 249 | 250 | include COMFUNCS.INC 251 | 252 | if ?DEBUGLOG 253 | if ?CDAUDIO 254 | ; Print nth drive letter to the debug log 255 | curdriveletter db ? 256 | db 0 257 | printdriveletter proc near stdcall uses eax bOut:byte 258 | mov al,[bOut] 259 | add al,'A' 260 | mov [curdriveletter],al 261 | 262 | invoke printtolog, offset curdriveletter 263 | ret 264 | printdriveletter endp 265 | endif 266 | 267 | announcecopy proc near 268 | invoke printtolog, CStr("copying ") 269 | ror edx,10h 270 | invoke printbinword,dx 271 | ror edx,10h 272 | invoke printbinword,dx 273 | 274 | invoke printtolog, CStr("b dwords;",0Dh,0Ah," from: ") 275 | ror esi,10h 276 | invoke printbinword,si 277 | ror esi,10h 278 | invoke printbinword,si 279 | 280 | invoke printtolog, CStr("b (main buffer)",0Dh,0Ah," to: ") 281 | ror edi,10h 282 | invoke printbinword,di 283 | ror edi,10h 284 | invoke printbinword,di 285 | invoke printtolog, CStr("b (aux buffer);",0Dh,0Ah," limit: ") 286 | 287 | push edi 288 | mov edi,es 289 | lsl edi,edi 290 | ror edi,10h 291 | invoke printbinword,di 292 | ror edi,10h 293 | invoke printbinword,di 294 | invoke printtolog, CStr("b (aux buffer)",0Dh,0Ah) 295 | pop edi 296 | 297 | ret 298 | announcecopy endp 299 | endif 300 | 301 | ; ------------------------------------------------------- ; 302 | ; ENUM functions from here (called from host application) ; 303 | ; ------------------------------------------------------- ; 304 | assume ds:_TEXT ; always called from within DS-set portion of entry point 305 | 306 | ; Initialize driver 307 | ; Called with BX = port, CL = IRQ, CH = DMA channel, SI = param 308 | ; Returns nothing 309 | drv_init proc near 310 | if ?DEBUGLOG 311 | ;invoke logtostderr 312 | invoke openlog, CStr("HDA_INIT.LOG"),0 313 | endif 314 | ; fill in the data that the caller gives us 315 | mov [wPort],bx 316 | mov [wIrqDma],cx 317 | mov [wParam],si 318 | if ?DEBUGLOG 319 | invoke printtolog, CStr("Initializing driver: successfully set port/irq/dma/param",0Dh,0Ah) 320 | endif 321 | and [codec],0Fh 322 | 323 | call check_paging 324 | jc @F 325 | btr [statusword],7 326 | 327 | @@: 328 | call alloc_CORB_RIRB 329 | jc @@init_failed_esok 330 | 331 | push es 332 | call get_hdareg_ptr 333 | jc @@init_failed 334 | 335 | call init_cntrlr 336 | jc @@init_failed 337 | 338 | @@: 339 | ; make sure interrupts are off until we set up IRQ... 340 | xor eax,eax 341 | bt es:[edi].HDAREGS.intctl,31 342 | mov es:[edi].HDAREGS.intctl,eax 343 | jnc @@interrupts_off 344 | 345 | mov ecx,10000h 346 | @@: 347 | call wait_timerch2 348 | cmp es:[edi].HDAREGS.intctl,eax 349 | loopnz @B 350 | 351 | @@interrupts_off: 352 | call set_busmaster 353 | jc @@init_failed 354 | 355 | call start_CORB_RIRB 356 | jc @@init_failed 357 | 358 | @@: 359 | if ?DEBUGLOG 360 | invoke printtolog, CStr("CORB/RIRB up and running!",0Dh,0Ah,"Checking attributes of selected widget...",0Dh,0Ah) 361 | endif 362 | 363 | mov ax,0F00h ; get parameter 364 | mov edx,9 ; audio widget capabilities 365 | call send_cmd_wait 366 | bt eax,0 367 | jnc @@init_failed 368 | if ?DEBUGLOG 369 | invoke printtolog, CStr("selected widget is stereo",0Dh,0Ah) 370 | endif 371 | 372 | shr eax,20 373 | and al,0Fh 374 | cmp al,WTYPE_PIN 375 | jne @@init_failed 376 | if ?DEBUGLOG 377 | invoke printtolog, CStr("selected widget is a pin",0Dh,0Ah) 378 | endif 379 | 380 | ; OK, we won't be sending commands to this again for a while... 381 | mov bl,[node] 382 | mov [pinnode],bl 383 | 384 | mov [node],0 ; root node 385 | call get_subnodes 386 | mov ecx,edx 387 | mov [node],al 388 | @@: 389 | call get_subnodes 390 | mov ah,bl 391 | sub ah,al 392 | cmp ah,dl 393 | jb @F ; the pin widget belongs to this functional group 394 | inc [node] 395 | loop @B 396 | 397 | if ?DEBUGLOG 398 | invoke printtolog, CStr("couldn't find function group containing selected widget, aborting",0Dh,0Ah) 399 | endif 400 | jmp @@init_failed 401 | 402 | @@: 403 | if ?DEBUGLOG 404 | invoke printtolog, CStr("found function group containing selected widget",0Dh,0Ah) 405 | endif 406 | ; now, [node] should be a functional group, which contains our [pinnode] 407 | mov ax,0F00h ; get parameter 408 | mov edx,5 ; function group type 409 | call send_cmd_wait 410 | and al,7Fh 411 | cmp al,1 ; audio function group 412 | jne @@init_failed 413 | if ?DEBUGLOG 414 | invoke printtolog, CStr("it is an audio function group",0Dh,0Ah) 415 | endif 416 | 417 | mov al,[node] 418 | mov [afgnode],al 419 | 420 | mov ax,0705h ; set power state 421 | xor edx,edx ; D0 422 | call send_cmd_wait 423 | mov ax,07FFh ; reset 424 | xor edx,edx 425 | call send_cmd_wait 426 | 427 | if ?DEBUGLOG 428 | invoke printtolog, CStr("audio function group reset, now looking for a DAC...",0Dh,0Ah) 429 | endif 430 | 431 | mov al,[pinnode] 432 | mov [node],al 433 | mov al,WTYPE_PIN 434 | mov si,7 ; just look for 48 kHz since we've no more specific instructions for now... 435 | call find_dac_start 436 | mov [dacnode],al 437 | mov [ratebitmap],si 438 | test al,al 439 | jnz @F 440 | 441 | if ?DEBUGLOG 442 | invoke printtolog, CStr("could not route a DAC to selected widget, aborting",0Dh,0Ah) 443 | endif 444 | jmp @@init_failed 445 | 446 | @@: 447 | mov [node],al 448 | mov ax,0F00h ; get parameter 449 | mov edx,9 ; widget type 450 | call send_cmd_wait 451 | shr eax,20 452 | and al,0Fh 453 | cmp al,WTYPE_AUDIOOUT 454 | je @F 455 | 456 | if ?DEBUGLOG 457 | invoke printtolog, CStr("BUGBUG: codec/node ") 458 | invoke printbinword,[wParam] 459 | invoke printtolog, CStr("b is not a DAC (it is a ") 460 | invoke printnodetype,al 461 | invoke printtolog, CStr(")",0Dh,0Ah) 462 | endif 463 | jmp @@init_failed 464 | 465 | @@: 466 | if ?DEBUGLOG 467 | invoke printtolog, CStr("DAC found at codec/node ") 468 | invoke printbinword,[wParam] 469 | invoke printtolog, CStr("b, unmuting...",0Dh,0Ah) 470 | endif 471 | xor eax,eax ; only the output amplifier 472 | call unmute 473 | mov ax,0705h ; set power state 474 | xor edx,edx ; D0 475 | call send_cmd_wait 476 | 477 | mov al,[pinnode] 478 | mov [node],al 479 | ; no need to unmute pin because it already happened during the search 480 | mov ax,0707h ; set pin widget control 481 | mov edx,40h ; only out enable 482 | call send_cmd_wait 483 | 484 | if ?DEBUGLOG 485 | invoke printtolog, CStr("DAC unmuted, pin configured, now resetting output streams",0Dh,0Ah) 486 | endif 487 | 488 | call get_hdareg_ptr 489 | movzx eax,es:[edi].HDAREGS.gcap 490 | mov ecx,eax 491 | shr eax,8 492 | and eax,0Fh ; number of input streams 493 | bts es:[edi].HDAREGS.intctl,eax ; get interrupts from the first output stream 494 | 495 | shr ecx,0Ch 496 | and ecx,0Fh ; number of output streams 497 | shl eax,5 ; EAX *= 32 (size STREAM) 498 | 499 | lea esi,[edi+eax+HDAREGS.stream0] 500 | mov [firststreamoff],esi 501 | @@resetstreams: 502 | push ecx 503 | mov ecx,1000h 504 | or es:[esi].STREAM.wCtl,1 505 | @@: 506 | call wait_timerch2 507 | test es:[esi].STREAM.wCtl,1 508 | loopz @B 509 | mov ecx,1000h 510 | and es:[esi].STREAM.wCtl, not 1 511 | @@: 512 | call wait_timerch2 513 | test es:[esi].STREAM.wCtl,1 514 | loopnz @B 515 | pop ecx 516 | lea esi,[esi+size STREAM] 517 | loop @@resetstreams 518 | 519 | if ?DEBUGLOG 520 | invoke printtolog, CStr("output streams all reset, setting up IRQ...",0Dh,0Ah) 521 | endif 522 | call mask_irq 523 | if ?DEBUGLOG 524 | invoke printtolog, CStr("IRQ masked",0Dh,0Ah) 525 | endif 526 | 527 | mov ax,0B108h ; read configuration byte 528 | mov bx,[wPort] 529 | mov edi,3Ch ; interrupt line 530 | int 1Ah 531 | jc @@init_failed 532 | if ?SETIRQLINE 533 | mov [oldpciirq],cl 534 | 535 | mov ax,0B10Bh ; write configuration byte 536 | mov bx,[wPort] 537 | mov cl,[irq] 538 | mov edi,3Ch ; interrupt line 539 | int 1Ah 540 | jc @@init_failed 541 | if ?DEBUGLOG 542 | invoke printtolog, CStr("PCI IRQ line set to ") 543 | movzx bx,[irq] 544 | invoke printbinword,bx 545 | invoke printtolog, CStr("b (was ") 546 | movzx bx,[oldpciirq] 547 | invoke printbinword,bx 548 | invoke printtolog, CStr("b)",0Dh,0Ah) 549 | 550 | invoke printtolog, CStr("Readback: PCI IRQ line is ") 551 | mov ax,0B108h ; read configuration byte 552 | mov bx,[wPort] 553 | mov edi,3Ch ; interrupt line 554 | int 1Ah 555 | xor ch,ch 556 | invoke printbinword,cx 557 | invoke printtolog, CStr("b",0Dh,0Ah) 558 | endif 559 | 560 | else 561 | ; I've found my device accepts but ignores changes to the Interrupt Line 562 | ; register, so just read it and set our own IRQ accordingly. 563 | mov [irq],cl 564 | if ?DEBUGLOG 565 | invoke printtolog, CStr("set our IRQ to ") 566 | xor ch,ch 567 | invoke printbinword,cx 568 | invoke printtolog, CStr("b, in accordance with PCI config space",0Dh,0Ah) 569 | endif 570 | 571 | endif 572 | 573 | mov bl,[irq] 574 | mov al,bl 575 | add al,8 ; IRQs 0-7 based at interrupt 8 576 | or bl,60h ; EOI for specific interrupt 577 | mov [mPICeoi],bl 578 | cmp al,10h 579 | jb @F 580 | 581 | add al,60h ; IRQs 8-F based at interrupt 70h 582 | and bl,67h ; clear upper bit of first nibble (subtract 8) 583 | mov [sPICeoi],bl 584 | mov [mPICeoi],62h ; EOI for IRQ2 (cascaded interrupt) 585 | @@: 586 | mov [irqvec],al 587 | ifdef ?FLASHTEK 588 | mov cl,al 589 | mov ax,2503h ; Phar Lap / FlashTek: get RM interrupt vector 590 | int 21h 591 | mov [oldIRQ_RM],ebx 592 | mov ax,2502h ; Phar Lap / FlashTek: get interrupt vector 593 | else 594 | mov ah,35h ; get interrupt vector 595 | endif 596 | int 21h 597 | mov [oldIRQ_off],ebx 598 | mov [oldIRQ_seg],es 599 | 600 | push ds 601 | push cs 602 | pop ds 603 | mov edx,offset irq_handler 604 | ifdef ?FLASHTEK 605 | mov ax,2506h ; Phar Lap / FlashTek: set interrupt to gain control in PM 606 | mov cl,[irqvec] 607 | else 608 | mov ah,25h ; set interrupt vector 609 | mov al,[irqvec] 610 | endif 611 | int 21h 612 | pop ds 613 | if ?DEBUGLOG 614 | invoke printtolog, CStr("protected-mode IRQ handler set (vector ") 615 | movzx ax,[irqvec] 616 | invoke printbinword,ax 617 | invoke printtolog, CStr("b)",0Dh,0Ah) 618 | endif 619 | 620 | xor eax,eax ; zero wakeen - we don't want interrupts for this... 621 | call get_hdareg_ptr 622 | xchg es:[edi].HDAREGS.wakeen,ax 623 | .if ax 624 | if ?DEBUGLOG 625 | invoke printtolog, CStr("blanking WAKEEN...",0Dh,0Ah) 626 | endif 627 | mov ecx,1000h 628 | @@: 629 | call wait_timerch2 630 | movzx eax,es:[edi].HDAREGS.wakeen 631 | test eax,eax 632 | loopnz @B 633 | .endif 634 | if ?DEBUGLOG 635 | invoke printtolog, CStr("WAKEEN blanked",0Dh,0Ah) 636 | endif 637 | 638 | movzx eax,es:[edi].HDAREGS.statests 639 | .if eax 640 | if ?DEBUGLOG 641 | invoke printtolog, CStr("blanking STATESTS...",0Dh,0Ah) 642 | endif 643 | mov es:[edi].HDAREGS.statests,ax ; write 1s back to clear 644 | mov ecx,1000h 645 | @@: 646 | call wait_timerch2 647 | movzx eax,es:[edi].HDAREGS.statests 648 | test eax,eax 649 | loopnz @B 650 | .endif 651 | if ?DEBUGLOG 652 | invoke printtolog, CStr("STATESTS blank",0Dh,0Ah) 653 | endif 654 | 655 | or es:[edi].HDAREGS.intctl,0C0000000h ; GIE + CIE 656 | mov ecx,1000h 657 | @@: 658 | call wait_timerch2 659 | test es:[edi].HDAREGS.intctl,0C0000000h 660 | loopz @B 661 | if ?DEBUGLOG 662 | invoke printtolog, CStr("interrupts enabled",0Dh,0Ah) 663 | endif 664 | 665 | bts [statusword],0 666 | if ?DEBUGLOG 667 | invoke printtolog, CStr("initialization completed successfully!",0Dh,0Ah) 668 | endif 669 | 670 | call unmask_irq 671 | if ?DEBUGLOG 672 | invoke printtolog, CStr("interrupts unmasked (in case we're sharing)",0Dh,0Ah) 673 | ; reset IRQ stats 674 | xor eax,eax 675 | mov [TotalIRQs],eax 676 | mov [UncaughtIRQs],eax 677 | mov [PassedIRQs],eax 678 | mov [TotalBCISs],eax 679 | mov dword ptr [TotalTicks],eax 680 | mov dword ptr [TotalTicks+4],eax 681 | endif 682 | 683 | @@init_failed: 684 | pop es 685 | @@init_failed_esok: 686 | if ?DEBUGLOG 687 | invoke closelog 688 | endif 689 | 690 | bt [statusword],0 691 | jnc drv_uninit ; if we didn't successfully init, then clean up our mess! 692 | ret 693 | drv_init endp 694 | 695 | ; Un-initialize driver 696 | ; Takes no parameters 697 | ; Returns nothing 698 | drv_uninit proc near 699 | if ?DEBUGLOG 700 | invoke openlog, CStr("HDA_FINI.LOG"),0 701 | invoke printtolog, CStr("masking interrupts...",0Dh,0Ah) 702 | endif 703 | call mask_irq 704 | btr [statusword],0 705 | 706 | if ?DEBUGLOG 707 | invoke printtolog, CStr("IRQ stats: ") 708 | invoke printbinword,word ptr [TotalIRQs+2] 709 | invoke printbinword,word ptr [TotalIRQs] 710 | invoke printtolog, CStr("b total caught IRQs;",0Dh,0Ah,"Of which ") 711 | invoke printbinword,word ptr [PassedIRQs+2] 712 | invoke printbinword,word ptr [PassedIRQs] 713 | invoke printtolog, CStr("b were passed through;",0Dh,0Ah,"Of which ") 714 | invoke printbinword,word ptr [TotalBCISs+2] 715 | invoke printbinword,word ptr [TotalBCISs] 716 | invoke printtolog, CStr("b were BCIS;",0Dh,0Ah) 717 | invoke printbinword,word ptr [UncaughtIRQs+2] 718 | invoke printbinword,word ptr [UncaughtIRQs] 719 | invoke printtolog, CStr("b UNCAUGHT (timer-detected) IRQs;",0Dh,0Ah) 720 | invoke printbinword,word ptr [UncaughtPCI+2] 721 | invoke printbinword,word ptr [UncaughtPCI] 722 | invoke printtolog, CStr("b of these asserted PCI interrupt status",0Dh,0Ah,"Average ticks per BCIS: ") 723 | mov eax,dword ptr [TotalTicks] 724 | mov edx,dword ptr [TotalTicks+4] 725 | mov ebx,[TotalBCISs] 726 | .if ebx <= edx ; the last thing we need here is a #DE! 727 | invoke printtolog, CStr("Could not be calculated (total ticks:",0Dh,0Ah) 728 | ror edx,10h 729 | invoke printbinword,dx 730 | ror edx,10h 731 | invoke printbinword,dx 732 | ror eax,10h 733 | invoke printbinword,ax 734 | ror eax,10h 735 | invoke printbinword,ax 736 | invoke printtolog, CStr("b)",0Dh,0Ah) 737 | .else 738 | div ebx 739 | ror eax,10h 740 | invoke printbinword,ax 741 | ror eax,10h 742 | invoke printbinword,ax 743 | invoke printtolog, CStr("b",0Dh,0Ah) 744 | .endif 745 | 746 | invoke printtolog, CStr("checking if HDA register far pointer exists...",0Dh,0Ah) 747 | endif 748 | xor eax,eax 749 | .if [hdareg_seg] != ax 750 | push es 751 | call get_hdareg_ptr 752 | 753 | if ?DEBUGLOG 754 | invoke printtolog, CStr("INTCTL was ") 755 | invoke printbinword,word ptr es:[edi+2].HDAREGS.intctl 756 | invoke printbinword,word ptr es:[edi].HDAREGS.intctl 757 | invoke printtolog, CStr("b",0Dh,0Ah) 758 | .if [UncaughtIRQs] 759 | invoke printtolog, CStr("INTSTS was ") 760 | invoke printbinword,word ptr [LastUncaught+2] 761 | invoke printbinword,word ptr [LastUncaught] 762 | invoke printtolog, CStr("b at last uncaught interrupt",0Dh,0Ah,"PIC IRRs were ") 763 | invoke printbinword,[LastIRR] 764 | invoke printtolog, CStr("b at last uncaught interrupt",0Dh,0Ah,"PIC ISRs were ") 765 | invoke printbinword,[LastISR] 766 | invoke printtolog, CStr("b at last uncaught interrupt",0Dh,0Ah) 767 | .endif 768 | endif 769 | 770 | mov es:[edi].HDAREGS.intctl,eax ; EAX = 0 from above 771 | mov ecx,1000h 772 | @@: 773 | call wait_timerch2 774 | test es:[edi].HDAREGS.intctl,080000000h ; GIE 775 | loopnz @B 776 | if ?DEBUGLOG 777 | invoke printtolog, CStr("interrupts disabled",0Dh,0Ah) 778 | endif 779 | 780 | cmp [oldIRQ_seg],ax ; AX = 0 from above 781 | je @F 782 | 783 | mov al,[irqvec] 784 | ifdef ?FLASHTEK 785 | mov cl,al 786 | mov ebx,[oldIRQ_RM] 787 | mov ax,2507h ; Phar Lap / FlashTek: set RM/PM int vectors 788 | else 789 | mov ah,25h ; set interrupt vector 790 | endif 791 | push ds 792 | lds edx,[oldIRQhandler] 793 | int 21h 794 | pop ds 795 | if ?DEBUGLOG 796 | invoke printtolog, CStr("protected-mode IRQ handler reset",0Dh,0Ah) 797 | endif 798 | 799 | xor eax,eax 800 | mov [oldIRQ_seg],ax 801 | mov [oldIRQ_off],eax 802 | 803 | @@: 804 | ; no need for these anymore... 805 | mov [mPICeoi],al 806 | mov [sPICeoi],ah 807 | 808 | if ?SETIRQLINE 809 | cmp [oldpciirq],al 810 | je @F 811 | 812 | mov ax,0B10Bh ; write configuration byte 813 | mov bx,[wPort] 814 | mov cl,[oldpciirq] 815 | mov edi,3Ch ; interrupt line 816 | int 1Ah 817 | if ?DEBUGLOG 818 | invoke printtolog, CStr("PCI IRQ line reset",0Dh,0Ah) 819 | endif 820 | 821 | xor eax,eax 822 | mov [oldpciirq],al 823 | endif 824 | 825 | @@: 826 | call unmask_irq 827 | if ?DEBUGLOG 828 | invoke printtolog, CStr("IRQ unmasked",0Dh,0Ah) 829 | endif 830 | 831 | and es:[edi].HDAREGS.corbctl,not 2 832 | and es:[edi].HDAREGS.rirbctl,not 2 833 | mov ecx,10000h 834 | @@: 835 | call wait_timerch2 836 | test es:[edi].HDAREGS.corbctl,2 837 | loopnz @B 838 | if ?DEBUGLOG 839 | invoke printtolog, CStr("CORB/RIRB stopped",0Dh,0Ah) 840 | endif 841 | 842 | btr es:[edi].HDAREGS.gctl,0 843 | if ?DEBUGLOG 844 | invoke printtolog, CStr("Resetting HDA controller...",0Dh,0Ah) 845 | endif 846 | mov ecx,10000h 847 | @@: 848 | call wait_timerch2 849 | test es:[edi].HDAREGS.gctl,1 850 | loopnz @B 851 | if ?DEBUGLOG 852 | invoke printtolog, CStr("HDA controller reset",0Dh,0Ah) 853 | endif 854 | 855 | mov ebx,es 856 | call free_selector 857 | xor eax,eax 858 | mov [hdareg_seg],ax 859 | if ?DEBUGLOG 860 | invoke printtolog, CStr("far pointer to HDA device registers freed",0Dh,0Ah) 861 | endif 862 | 863 | pop es 864 | .endif 865 | 866 | if ?DEBUGLOG 867 | invoke printtolog, CStr("checking if HDA register linear map exists...",0Dh,0Ah) 868 | endif 869 | mov ecx,[hdareg_linaddr] 870 | cmp ecx,eax 871 | jz @F 872 | 873 | mov ebx,ecx 874 | shr ebx,10h 875 | call unmap_physmem 876 | xor eax,eax 877 | mov [hdareg_linaddr],eax 878 | if ?DEBUGLOG 879 | invoke printtolog, CStr("linear address of HDA device registers unmapped",0Dh,0Ah) 880 | endif 881 | 882 | @@: 883 | mov eax,[dwCorbSelHdl] 884 | test eax,eax 885 | jz @F 886 | 887 | mov ebx,[dwCorbDpmiHdl] 888 | call free_dma_buf 889 | xor eax,eax 890 | mov [dwCorbPhys],eax 891 | mov [dwCorbSelHdl],eax 892 | .if [dwTSRbuf] 893 | mov [dwTSRbufoffset],eax ; reset the bump allocator 894 | .endif 895 | if ?DEBUGLOG 896 | invoke printtolog, CStr("CORB/RIRB buffer freed",0Dh,0Ah) 897 | endif 898 | @@: 899 | 900 | if ?DEBUGLOG 901 | invoke closelog 902 | endif 903 | ret 904 | drv_uninit endp 905 | 906 | ; Set PCM sample rate - only called immediately after init (i.e. not when sound is playing) 907 | ; Takes sample rate in BX 908 | ; Returns nothing 909 | drv_setrate proc near 910 | if ?DEBUGLOG 911 | invoke openlog, CStr("HDA_RATE.LOG"),0 912 | invoke printtolog, CStr("checking if driver is initialized...",0Dh,0Ah) 913 | endif 914 | bt [statusword],0 915 | jnc @@failed 916 | 917 | if ?DEBUGLOG 918 | invoke printtolog, CStr("finding rate index...",0Dh,0Ah) 919 | invoke printtolog, CStr("requested rate: ") 920 | invoke printbinword,bx 921 | invoke printtolog, CStr("b",0Dh,0Ah) 922 | endif 923 | mov ax,bx 924 | .if ax == 10000 ; Rayman 1 uses this non-standard rate... 925 | mov ax,11025 ; So sneakily speed it up to this standard one! 926 | .endif ; (Rayman Designer uses 11025 - so that's why I always thought Rayman 1's sounds were lower-pitched...) 927 | call get_rate_idx 928 | cmp ax,-1 929 | je @@failed 930 | 931 | if ?DEBUGLOG 932 | invoke printtolog, CStr("checking if DAC supports this rate...",0Dh,0Ah) 933 | invoke printtolog, CStr("[rate support bitmap: ") 934 | invoke printbinword,[ratebitmap] 935 | invoke printtolog, CStr("; rate index: ") 936 | invoke printbinword,ax 937 | invoke printtolog, CStr("b]",0Dh,0Ah) 938 | endif 939 | bt [ratebitmap],ax 940 | movzx ebx,ax 941 | jc @F 942 | 943 | if ?DEBUGLOG 944 | invoke printtolog, CStr("nope, searching for another DAC...",0Dh,0Ah) 945 | endif 946 | mov si,ax 947 | mov al,[pinnode] 948 | mov [node],al 949 | mov al,WTYPE_PIN 950 | call find_dac_start 951 | test al,al 952 | jnz @@dacfound 953 | 954 | if ?DEBUGLOG 955 | invoke printtolog, CStr("no DAC found, setting up software conversion...",0Dh,0Ah) 956 | endif 957 | mov bh,FormatHiBytes[ebx-1] 958 | mov bl,bh 959 | and bl,7 ; three lowest bits = divider 960 | inc bl ; convert zero to one, etc. 961 | mov [soft_divider],bl 962 | and bh,not 7 ; nullify the divider 963 | jmp @@setformatlobyte 964 | 965 | @@dacfound: 966 | mov [dacnode],al 967 | mov [ratebitmap],si 968 | mov [node],al 969 | if ?DEBUGLOG 970 | invoke printtolog, CStr("DAC found at codec/node ") 971 | invoke printbinword,[wParam] 972 | invoke printtolog, CStr("b, unmuting...",0Dh,0Ah) 973 | endif 974 | xor eax,eax ; only the output amplifier 975 | call unmute 976 | mov ax,0705h ; set power state 977 | xor edx,edx ; D0 978 | call send_cmd_wait 979 | 980 | @@: 981 | if ?DEBUGLOG 982 | invoke printtolog, CStr("rate supported, configuring stream...",0Dh,0Ah) 983 | endif 984 | mov bh,FormatHiBytes[ebx-1] 985 | @@setformatlobyte: 986 | if ?CDAUDIO 987 | mov bl,bh 988 | shr bl,3 989 | and bl,7 ; three middle bits = multiplier 990 | inc bl ; convert zero to one, etc. 991 | mov [bCdDivider],bl 992 | endif 993 | mov bl,FORMATLOBYTE 994 | if ?DEBUGLOG 995 | invoke printbinword,bx 996 | invoke printtolog, CStr(" is the new stream format word",0Dh,0Ah) 997 | endif 998 | 999 | push es 1000 | call get_hdareg_ptr 1001 | mov edi,[firststreamoff] 1002 | mov es:[edi].STREAM.wFormat,bx 1003 | pop es 1004 | 1005 | if ?DEBUGLOG 1006 | invoke printtolog, CStr("stream configured, programming DAC...",0Dh,0Ah) 1007 | endif 1008 | mov al,[dacnode] 1009 | mov [node],al 1010 | mov ax,2 ; set converter format 1011 | movzx edx,bx 1012 | call send_cmd_wait 1013 | 1014 | @@failed: 1015 | if ?DEBUGLOG 1016 | invoke closelog 1017 | endif 1018 | ret 1019 | drv_setrate endp 1020 | 1021 | ; Set DMA "action" of driver 1022 | ; Takes "action" (4 or 8) in BX 1023 | ; Returns nothing 1024 | drv_setaction proc near 1025 | if ?DEBUGLOG 1026 | invoke openlog, CStr("HDA_ACT.LOG"),0 1027 | endif 1028 | cmp bx,8 ; TRA1 = reading from memory (an ISA DMA mode - see osdev wiki) 1029 | je @F 1030 | 1031 | ; nothing we can do really, apart from complaining in the debug log... 1032 | if ?DEBUGLOG 1033 | invoke printtolog, CStr("Attempted to configure driver for something other than device reading host memory - not supported!",0Dh,0Ah) 1034 | endif 1035 | 1036 | @@: 1037 | if ?DEBUGLOG 1038 | invoke closelog 1039 | endif 1040 | ret 1041 | drv_setaction endp 1042 | 1043 | ; Start playing sound 1044 | ; Takes physical address of DMA buffer in EDI, and its size in ECX 1045 | ; Returns nothing 1046 | drv_start proc near 1047 | if ?DEBUGLOG 1048 | invoke openlog, CStr("HDA_STRT.LOG"),0 1049 | invoke printtolog, CStr("checking if driver is initialized...",0Dh,0Ah) 1050 | endif 1051 | bt [statusword],0 1052 | jnc @@failed 1053 | 1054 | if ?DEBUGLOG 1055 | invoke printtolog, CStr("checking if sound is paused...",0Dh,0Ah) 1056 | endif 1057 | bt [statusword],4 1058 | jc @@failed 1059 | 1060 | mov edx,edi 1061 | push es 1062 | 1063 | call get_hdareg_ptr 1064 | mov edi,[firststreamoff] 1065 | if ?DEBUGLOG 1066 | invoke printtolog, CStr("checking if sound is playing...",0Dh,0Ah) 1067 | endif 1068 | bt es:[edi].STREAM.wCtl,1 ; RUN bit 1069 | jc @@skip 1070 | 1071 | mov [dwMainBufPhys],edx 1072 | mov [dwMainBufSize],ecx 1073 | 1074 | cmp [soft_divider],1 1075 | jna @F 1076 | 1077 | if ?DEBUGLOG 1078 | invoke printtolog, CStr("software divider in operation (") 1079 | movzx ax,[soft_divider] 1080 | invoke printbinword,ax 1081 | invoke printtolog, CStr("b), creating new buffer...",0Dh,0Ah,"[") 1082 | invoke printbinword,word ptr [dwMainBufSize+2] 1083 | invoke printbinword,word ptr [dwMainBufSize] 1084 | invoke printtolog, CStr("b --> ") 1085 | endif 1086 | mov eax,ecx 1087 | movzx ecx,[soft_divider] 1088 | mul ecx ; destroys EDX, but alloc_dma_buf sets it anyway 1089 | mov ecx,eax 1090 | if ?DEBUGLOG 1091 | ror ecx,10h 1092 | invoke printbinword,cx 1093 | ror ecx,10h 1094 | invoke printbinword,cx 1095 | invoke printtolog, CStr("b]",0Dh,0Ah) 1096 | endif 1097 | jmp @@createauxbuf 1098 | 1099 | @@: 1100 | test edx,7Fh ; 128-byte aligned? 1101 | jz @F 1102 | 1103 | if ?DEBUGLOG 1104 | invoke printtolog, CStr("buffer not 128-byte aligned, creating new one...",0Dh,0Ah) 1105 | endif 1106 | mov eax,ecx 1107 | @@createauxbuf: 1108 | call alloc_dma_buf 1109 | jc @@skip 1110 | mov [dwAuxSelHdl],eax 1111 | mov [dwAuxDpmiHdl],ebx 1112 | if ?DEBUGLOG 1113 | invoke printtolog, CStr("128-byte-aligned aux buffer created, clearing...",0Dh,0Ah) 1114 | endif 1115 | 1116 | xor eax,eax 1117 | push edi 1118 | push es 1119 | push ecx 1120 | les edi,[lpAuxBufFilled] 1121 | xor edi,edi ; just in case... 1122 | shr ecx,2 1123 | rep stosd 1124 | pop ecx 1125 | pop es 1126 | pop edi 1127 | if ?DEBUGLOG 1128 | invoke printtolog, CStr("aux buffer cleared",0Dh,0Ah) 1129 | endif 1130 | 1131 | @@: 1132 | push gs 1133 | lgs esi,[lpRirb] 1134 | mov esi,[dwBdlOff] 1135 | if ?DEBUGLOG 1136 | invoke printtolog, CStr("setting up BDL entries...",0Dh,0Ah) 1137 | endif 1138 | xor eax,eax 1139 | mov dword ptr gs:[esi].BDLENTRY.qwAddr,edx 1140 | mov dword ptr gs:[esi+4].BDLENTRY.qwAddr,eax 1141 | mov gs:[esi].BDLENTRY.dwLen,ecx 1142 | mov gs:[esi].BDLENTRY.dwFlgs,1 ; IOC 1143 | mov dword ptr gs:[esi+size BDLENTRY].BDLENTRY.qwAddr,edx 1144 | mov dword ptr gs:[esi+size BDLENTRY+4].BDLENTRY.qwAddr,eax 1145 | mov gs:[esi+size BDLENTRY].BDLENTRY.dwLen,ecx 1146 | mov gs:[esi+size BDLENTRY].BDLENTRY.dwFlgs,1 ; IOC 1147 | pop gs 1148 | 1149 | if ?DEBUGLOG 1150 | invoke printtolog, CStr("setting up stream descriptor...",0Dh,0Ah) 1151 | endif 1152 | add esi,[dwCorbPhys] ; get BDL physical address 1153 | mov dword ptr es:[edi].STREAM.qwBuffer,esi 1154 | mov dword ptr es:[edi+4].STREAM.qwBuffer,0 1155 | mov es:[edi].STREAM.dwBufLen,ecx 1156 | mov es:[edi].STREAM.bCtl2316,10h ; Stream 1 1157 | mov es:[edi].STREAM.dwLinkPos,eax ; start at the beginning 1158 | mov es:[edi].STREAM.wLastIdx,1 ; only two BDL entries 1159 | if ?DEBUGLOG 1160 | invoke printtolog, CStr("starting stream",0Dh,0Ah) 1161 | endif 1162 | or es:[edi].STREAM.wCtl,11110b ; RUN bit, plus all Interrupt Enable bits 1163 | 1164 | if ?DEBUGLOG 1165 | invoke printtolog, CStr("connecting stream to DAC",0Dh,0Ah) 1166 | endif 1167 | mov al,[dacnode] 1168 | mov [node],al 1169 | mov ax,706h ; set stream and channel 1170 | mov edx,10h ; Stream 1, on channel 0 1171 | call send_cmd_wait 1172 | if ?DEBUGLOG 1173 | invoke printtolog, CStr("stream initialized",0Dh,0Ah) 1174 | endif 1175 | 1176 | if ?CDAUDIO 1177 | ifdef ?FLASHTEK 1178 | ; FlashTek doesn't have functions for dynamically allocating Real-Mode 1179 | ; memory and callbacks, so this stuff will only work if there's DPMI 1180 | ; running behind it... 1181 | if ?DEBUGLOG 1182 | invoke printtolog, CStr("checking if running under DPMI...",0Dh,0Ah) 1183 | endif 1184 | mov ax,1686h ; DPMI - detect mode 1185 | int 2Fh 1186 | test ax,ax 1187 | jnz @@skip 1188 | endif 1189 | 1190 | if ?DEBUGLOG 1191 | invoke printtolog, CStr("checking if SMARTDRV is installed...",0Dh,0Ah,"(CD Audio cannot be used with SMARTDRV!)",0Dh,0Ah) 1192 | endif 1193 | push ebp 1194 | mov ax,4A10h ; SMARTDRV v4.00+ installation check 1195 | xor bx,bx 1196 | mov cx,0EBABh ; SMARTDRV v4.00+ installation check 1197 | int 2Fh 1198 | pop ebp 1199 | cmp ax,0BABEh 1200 | je @@skip 1201 | 1202 | if ?DEBUGLOG 1203 | invoke printtolog, CStr("no SMARTDRV, checking if MSCDEX is available...",0Dh,0Ah) 1204 | endif 1205 | mov ax,1500h ; MSCDEX installation check 1206 | xor bx,bx 1207 | int 2Fh 1208 | test bx,bx 1209 | jz @@skip 1210 | 1211 | if ?DEBUGLOG 1212 | invoke printtolog, CStr("MSCDEX installed with ") 1213 | invoke printbinword,bx 1214 | invoke printtolog, CStr("b drives, beginning on ") 1215 | invoke printdriveletter,cl 1216 | invoke printtolog, CStr(":",0Dh,0Ah,"checking the version...",0Dh,0Ah) 1217 | endif 1218 | 1219 | mov si,bx ; save number of drives 1220 | mov ax,150Ch 1221 | xor bx,bx 1222 | int 2Fh 1223 | cmp bx,20Ah ; 2.10 needed for IOCTL requests 1224 | jb @@skip 1225 | 1226 | mov bx,(size CdRmHeadBuf + 0Fh) SHR 4 1227 | if ?DEBUGLOG 1228 | invoke printtolog, CStr("MSCDEX > 2.10 installed, allocating ") 1229 | invoke printbinword,bx 1230 | invoke printtolog, CStr("b-paragraph buffer to check drives...",0Dh,0Ah) 1231 | endif 1232 | mov ax,100h ; allocate DOS memory block 1233 | int 31h 1234 | 1235 | if ?DEBUGLOG 1236 | jnc @F 1237 | cmp ax,8 1238 | jne @@skip 1239 | invoke printtolog, CStr("Insufficient memory - largest MCB is ") 1240 | invoke printbinword,bx 1241 | invoke printtolog, CStr("b paragraphs",0Dh,0Ah) 1242 | jmp @@skip 1243 | @@: 1244 | else 1245 | jc @@skip 1246 | endif 1247 | 1248 | mov [wCdRmBufSeg],ax 1249 | mov [wCdRmBufSel],dx 1250 | mov es,dx 1251 | push gs 1252 | mov gs,dx 1253 | 1254 | mov dx,cx ; save first drive 1255 | if ?DEBUGLOG 1256 | invoke printtolog, CStr("buffer allocated, clearing...",0Dh,0Ah) 1257 | endif 1258 | xor edi,edi 1259 | xor eax,eax 1260 | mov ecx,(size CdRmHeadBuf + 3) SHR 2 1261 | rep stosd 1262 | if ?DEBUGLOG 1263 | invoke printtolog, CStr("buffer cleared, checking drives...",0Dh,0Ah) 1264 | endif 1265 | 1266 | mov ax,[wCdRmBufSeg] 1267 | mov es:[CdRmHeadBuf.sRmCall.rCX],dx 1268 | mov es:[CdRmHeadBuf.sRmCall.rES],ax 1269 | mov es:[CdRmHeadBuf.sRmCall.rBX],CdRmHeadBuf.sReq 1270 | 1271 | mov es:[CdRmHeadBuf.sInfo.bCode],6 ; device status 1272 | 1273 | push ebp 1274 | .while si 1275 | if ?DEBUGLOG 1276 | invoke printtolog, CStr("checking drive ") 1277 | invoke printdriveletter,byte ptr es:[CdRmHeadBuf.sRmCall.rCX] 1278 | invoke printtolog, CStr(": for suitability...",0Dh,0Ah) 1279 | endif 1280 | mov ax,[wCdRmBufSeg] 1281 | mov es:[CdRmHeadBuf.sReq.bLen],size IOCTLRW 1282 | mov es:[CdRmHeadBuf.sReq.bCmd],3 ; IOCTL read 1283 | mov es:[CdRmHeadBuf.sReq.wBufSeg],ax 1284 | mov es:[CdRmHeadBuf.sReq.wBufOff],CdRmHeadBuf.sInfo 1285 | mov es:[CdRmHeadBuf.sReq.wCount],size DevStat 1286 | 1287 | mov es:[CdRmHeadBuf.sRmCall.rAX],1510h ; send device request 1288 | mov ax,300h ; simulate real mode interrupt 1289 | mov bx,2Fh 1290 | xor cx,cx 1291 | mov edi,CdRmHeadBuf.sRmCall 1292 | int 31h 1293 | jc @@next 1294 | bt es:[CdRmHeadBuf.sRmCall.rFlags],0 1295 | jc @@next 1296 | 1297 | if ?DEBUGLOG 1298 | invoke printtolog, CStr("drive status request sent...",0Dh,0Ah) 1299 | endif 1300 | mov eax,es:[CdRmHeadBuf.sInfo.dwStat] 1301 | bt eax,2 1302 | jnc @@next 1303 | if ?DEBUGLOG 1304 | invoke printtolog, CStr("supports raw reading",0Dh,0Ah) 1305 | endif 1306 | xor ebp,ebp 1307 | bt eax,7 1308 | jnc @F 1309 | if ?DEBUGLOG 1310 | invoke printtolog, CStr("supports prefetching",0Dh,0Ah) 1311 | endif 1312 | mov ebp,1 1313 | 1314 | @@: 1315 | mov ax,100h ; allocate DOS memory block 1316 | mov bx,(size CdRmDriveBuf + 0Fh) SHR 4 1317 | if ?DEBUGLOG 1318 | invoke printtolog, CStr("Allocating ") 1319 | invoke printbinword,bx 1320 | invoke printtolog, CStr("b-paragraph drive buffer...",0Dh,0Ah) 1321 | endif 1322 | int 31h 1323 | if ?DEBUGLOG 1324 | jnc @F 1325 | cmp ax,8 1326 | jne @@skip 1327 | invoke printtolog, CStr("Insufficient memory for drive buffer - largest MCB is ") 1328 | invoke printbinword,bx 1329 | invoke printtolog, CStr("b paragraphs",0Dh,0Ah) 1330 | jmp @@next 1331 | @@: 1332 | else 1333 | jc @@next 1334 | endif 1335 | 1336 | push es 1337 | mov es,dx 1338 | mov bx,ax 1339 | if ?DEBUGLOG 1340 | invoke printtolog, CStr("drive buffer allocated, clearing...",0Dh,0Ah) 1341 | endif 1342 | xor edi,edi 1343 | xor eax,eax 1344 | mov ecx,(size CdRmDriveBuf + 3) SHR 2 1345 | rep stosd 1346 | if ?DEBUGLOG 1347 | invoke printtolog, CStr("drive buffer cleared, saving audio channel info...",0Dh,0Ah) 1348 | endif 1349 | pop es 1350 | 1351 | mov gs:[CdRmDriveBuf.wNextS],dx ; or CdRmHeadBuf.wFirstS 1352 | mov gs,dx 1353 | 1354 | mov ax,es:[CdRmHeadBuf.sRmCall.rCX] 1355 | mov gs:[CdRmDriveBuf.bDrive],al 1356 | mov gs:[CdRmDriveBuf.wStatus],bp ; save prefetch ability 1357 | 1358 | mov gs:[CdRmDriveBuf.sReq.bLen],size ReadL 1359 | ;mov gs:[CdRmDriveBuf.sReq.bCmd],80h ; READ LONG 1360 | mov gs:[CdRmDriveBuf.sReq.wBufOff],CdRmDriveBuf.Samples 1361 | mov gs:[CdRmDriveBuf.sReq.wBufSeg],bx 1362 | mov gs:[CdRmDriveBuf.sReq.bRMode],1 ; raw 1363 | 1364 | mov gs:[CdRmDriveBuf.sInfo.bCode],4 ; audio channel info 1365 | mov gs:[CdRmDriveBuf.sQChan.bCode],0Ch ; audio Q-Channel info 1366 | mov gs:[CdRmDriveBuf.sStat.bCode],0Fh ; audio status info 1367 | 1368 | ;mov es:[CdRmHeadBuf.sReq.bLen],size IOCTLRW 1369 | ;mov es:[CdRmHeadBuf.sReq.bCmd],3 ; IOCTL read 1370 | mov es:[CdRmHeadBuf.sReq.wBufSeg],bx 1371 | mov es:[CdRmHeadBuf.sReq.wBufOff],CdRmDriveBuf.sInfo 1372 | mov es:[CdRmHeadBuf.sReq.wCount],size AudInfo 1373 | 1374 | mov es:[CdRmHeadBuf.sRmCall.rAX],1510h ; send device request 1375 | mov ax,300h ; simulate real mode interrupt 1376 | mov bx,2Fh 1377 | xor cx,cx 1378 | mov edi,CdRmHeadBuf.sRmCall 1379 | int 31h 1380 | if ?DEBUGLOG 1381 | invoke printtolog, CStr("audio channel info saved, stopping any current playback...",0Dh,0Ah) 1382 | endif 1383 | 1384 | ; TODO: Seamlessly take over any ongoing audio playback? 1385 | ; (Would need to fill sStat and sQChan, and set wStatus and dwBufPos) 1386 | mov es:[CdRmHeadBuf.sReq.bLen],0Dh 1387 | mov es:[CdRmHeadBuf.sReq.bCmd],133 ; STOP AUDIO 1388 | mov es:[CdRmHeadBuf.sRmCall.rAX],1510h ; send device request 1389 | mov ax,300h ; simulate real mode interrupt 1390 | int 31h 1391 | if ?DEBUGLOG 1392 | invoke printtolog, CStr("drive ready to use!",0Dh,0Ah) 1393 | endif 1394 | inc es:[CdRmHeadBuf.bDrives] 1395 | 1396 | mov eax,dword ptr gs:CdRmDriveBuf.sInfo.Info 1397 | mov ebx,dword ptr gs:CdRmDriveBuf.sInfo.Info+4 1398 | or eax,ebx 1399 | jnz @@next 1400 | 1401 | if ?DEBUGLOG 1402 | invoke printtolog, CStr("drive's audio channel control is null - assuming it doesn't support audio",0Dh,0Ah,"populating our structure with defaults...",0Dh,0Ah) 1403 | endif 1404 | 1405 | mov gs:CdRmDriveBuf.sInfo.Info[0].bVolume,0FFh 1406 | mov gs:CdRmDriveBuf.sInfo.Info[0].bInChan,0 1407 | mov gs:CdRmDriveBuf.sInfo.Info[2].bVolume,0FFh 1408 | mov gs:CdRmDriveBuf.sInfo.Info[2].bInChan,1 1409 | mov gs:CdRmDriveBuf.sInfo.Info[4].bVolume,0FFh 1410 | mov gs:CdRmDriveBuf.sInfo.Info[4].bInChan,2 1411 | mov gs:CdRmDriveBuf.sInfo.Info[6].bVolume,0FFh 1412 | mov gs:CdRmDriveBuf.sInfo.Info[6].bInChan,3 1413 | 1414 | @@next: 1415 | ; next drive 1416 | inc es:[CdRmHeadBuf.sRmCall.rCX] 1417 | dec si 1418 | .endw 1419 | pop ebp 1420 | 1421 | @@cddone: 1422 | pop gs 1423 | .if es:[CdRmHeadBuf.bDrives] 1424 | if ?DEBUGLOG 1425 | invoke printtolog, CStr("drive(s) set up, installing interrupt handler...",0Dh,0Ah) 1426 | endif 1427 | xor cx,cx 1428 | mov bx,cx 1429 | mov di,cx 1430 | mov si,10h ; SI:DI = 100000h = 1 MiB 1431 | call alloc_selector 1432 | jc @@skip 1433 | mov [wRmMemSel],ax 1434 | if ?DEBUGLOG 1435 | invoke printtolog, CStr("allocated selector for real-mode memory",0Dh,0Ah) 1436 | endif 1437 | 1438 | mov ax,200h ; get real mode interrupt vector 1439 | mov bl,2Fh 1440 | int 31h 1441 | mov [wOldInt2FOff],dx 1442 | mov [wOldInt2FSeg],cx 1443 | 1444 | mov ax,303h ; allocate real-mode callback 1445 | push ds 1446 | push cs 1447 | pop ds 1448 | mov esi,offset int2f_setbusy 1449 | mov edi,CdRmHeadBuf.sRmCall 1450 | int 31h 1451 | pop ds 1452 | jc @@skip 1453 | if ?DEBUGLOG 1454 | invoke printtolog, CStr("real-mode callback created to set busy bit",0Dh,0Ah) 1455 | endif 1456 | mov [wSetBusyCBOff],dx 1457 | mov [wSetBusyCBSeg],cx 1458 | 1459 | mov ax,303h ; allocate real-mode callback 1460 | push ds 1461 | push cs 1462 | pop ds 1463 | mov esi,offset int2f_handler 1464 | mov edi,CdRmHeadBuf.sRmCall 1465 | int 31h 1466 | pop ds 1467 | jc @@skip 1468 | if ?DEBUGLOG 1469 | invoke printtolog, CStr("real-mode callback created for int 2F handler",0Dh,0Ah) 1470 | endif 1471 | 1472 | mov ax,201h ; set real mode interrupt vector 1473 | mov bl,2Fh 1474 | int 31h 1475 | if ?DEBUGLOG 1476 | invoke printtolog, CStr("handler installed!",0Dh,0Ah) 1477 | endif 1478 | 1479 | bts [statusword],6 1480 | .else 1481 | if ?DEBUGLOG 1482 | invoke printtolog, CStr("no usable drives, freeing buffer...",0Dh,0Ah) 1483 | endif 1484 | mov ax,101h ; free DOS memory block 1485 | mov dx,es 1486 | xor cx,cx 1487 | mov es,cx ; nullify ES so we don't end up with an invalid selector 1488 | int 31h 1489 | if ?DEBUGLOG 1490 | invoke printtolog, CStr("buffer freed",0Dh,0Ah) 1491 | endif 1492 | xor eax,eax 1493 | mov dword ptr [wCdRmBufSel],eax 1494 | .endif 1495 | endif 1496 | 1497 | @@skip: 1498 | pop es 1499 | 1500 | @@failed: 1501 | if ?DEBUGLOG 1502 | invoke closelog 1503 | endif 1504 | ret 1505 | drv_start endp 1506 | 1507 | ; Stop playing sound 1508 | ; Takes no parameters 1509 | ; Returns nothing 1510 | drv_stop proc near 1511 | if ?DEBUGLOG 1512 | invoke openlog, CStr("HDA_STOP.LOG"),0 1513 | invoke printtolog, CStr("checking if driver is initialized...",0Dh,0Ah) 1514 | endif 1515 | bt [statusword],0 1516 | jnc @@failed 1517 | 1518 | if ?DEBUGLOG 1519 | invoke printtolog, CStr("resetting pause status",0Dh,0Ah) 1520 | endif 1521 | btr [statusword],4 1522 | 1523 | if ?CDAUDIO 1524 | if ?DEBUGLOG 1525 | invoke printtolog, CStr("checking if CD audio is set up...",0Dh,0Ah) 1526 | endif 1527 | btr [statusword],6 1528 | jnc @F 1529 | 1530 | if ?DEBUGLOG 1531 | invoke printtolog, CStr("freeing callbacks...",0Dh,0Ah) 1532 | endif 1533 | mov ax,200h ; get real mode interrupt vector 1534 | mov bl,2Fh 1535 | int 31h 1536 | mov ax,304h ; free real mode callback 1537 | int 31h 1538 | mov dx,[wSetBusyCBOff] 1539 | mov cx,[wSetBusyCBSeg] 1540 | mov ax,304h ; free real mode callback 1541 | int 31h 1542 | if ?DEBUGLOG 1543 | invoke printtolog, CStr("callbacks freed, resetting vector...",0Dh,0Ah) 1544 | endif 1545 | 1546 | mov dx,[wOldInt2FOff] 1547 | mov cx,[wOldInt2FSeg] 1548 | mov ax,201h ; set real mode interrupt vector 1549 | mov bl,2Fh 1550 | int 31h 1551 | if ?DEBUGLOG 1552 | invoke printtolog, CStr("handler uninstalled",0Dh,0Ah) 1553 | endif 1554 | 1555 | @@: 1556 | mov bx,[wRmMemSel] 1557 | test bx,bx 1558 | jz @F 1559 | if ?DEBUGLOG 1560 | invoke printtolog, CStr("freeing selector for real-mode memory...",0Dh,0Ah) 1561 | endif 1562 | call free_selector 1563 | 1564 | @@: 1565 | if ?DEBUGLOG 1566 | invoke printtolog, CStr("checking if CD audio linked list exists...",0Dh,0Ah) 1567 | endif 1568 | cmp dword ptr [wCdRmBufSel],0 1569 | jz @@cddone 1570 | 1571 | if ?DEBUGLOG 1572 | invoke printtolog, CStr("freeing CD buffers...",0Dh,0Ah) 1573 | endif 1574 | push gs 1575 | mov gs,[wCdRmBufSel] 1576 | @@: 1577 | mov dx,gs 1578 | mov gs,gs:[CdRmDriveBuf.wNextS] ; or CdRmHeadBuf.wFirstS 1579 | mov ax,101h ; free DOS memory block 1580 | int 31h 1581 | mov eax,gs 1582 | test eax,eax 1583 | jnz @B 1584 | 1585 | mov dword ptr [wCdRmBufSel],eax 1586 | if ?DEBUGLOG 1587 | invoke printtolog, CStr("CD buffers freed",0Dh,0Ah) 1588 | endif 1589 | pop gs 1590 | @@cddone: 1591 | endif 1592 | 1593 | push es 1594 | call get_hdareg_ptr 1595 | mov edi,[firststreamoff] 1596 | if ?DEBUGLOG 1597 | invoke printtolog, CStr("checking if sound is playing...",0Dh,0Ah) 1598 | invoke printtolog, CStr("wCtl == ") 1599 | invoke printbinword,es:[edi].STREAM.wCtl 1600 | invoke printtolog, CStr("b",0Dh,0Ah) 1601 | endif 1602 | btr es:[edi].STREAM.wCtl,1 ; RUN bit 1603 | jnc @@skip 1604 | 1605 | xor ecx,ecx 1606 | mov [dwMainBufPhys],ecx 1607 | mov [dwMainBufSize],ecx 1608 | 1609 | if ?DEBUGLOG 1610 | invoke printtolog, CStr("Waiting for DMA engine to stop...",0Dh,0Ah) 1611 | invoke printtolog, CStr("wCtl == ") 1612 | invoke printbinword,es:[edi].STREAM.wCtl 1613 | invoke printtolog, CStr("b",0Dh,0Ah) 1614 | endif 1615 | mov ecx,1000h 1616 | @@: 1617 | call wait_timerch2 1618 | test es:[edi].STREAM.wCtl,2 ; RUN bit 1619 | loopnz @B 1620 | 1621 | if ?DEBUGLOG 1622 | invoke printtolog, CStr("wCtl == ") 1623 | invoke printbinword,es:[edi].STREAM.wCtl 1624 | invoke printtolog, CStr("b",0Dh,0Ah,"Saving format and resetting stream...",0Dh,0Ah) 1625 | endif 1626 | push es:[edi].STREAM.wFormat 1627 | 1628 | mov ecx,1000h 1629 | bts es:[edi].STREAM.wCtl,0 ; SRST bit 1630 | @@: 1631 | call wait_timerch2 1632 | test es:[edi].STREAM.wCtl,1 1633 | loopz @B 1634 | mov ecx,1000h 1635 | btr es:[edi].STREAM.wCtl,0 ; SRST bit 1636 | @@: 1637 | call wait_timerch2 1638 | test es:[edi].STREAM.wCtl,1 1639 | loopnz @B 1640 | 1641 | if ?DEBUGLOG 1642 | invoke printtolog, CStr("Stream reset, restoring format",0Dh,0Ah) 1643 | invoke printtolog, CStr("wCtl == ") 1644 | invoke printbinword,es:[edi].STREAM.wCtl 1645 | invoke printtolog, CStr("b",0Dh,0Ah) 1646 | endif 1647 | pop es:[edi].STREAM.wFormat 1648 | 1649 | xor eax,eax 1650 | cmp eax,[dwAuxSelHdl] 1651 | jz @@skip 1652 | 1653 | if ?DEBUGLOG 1654 | invoke printtolog, CStr("Freeing aux buffer...",0Dh,0Ah) 1655 | endif 1656 | xchg eax,[dwAuxSelHdl] 1657 | mov ebx,[dwAuxDpmiHdl] 1658 | call free_dma_buf 1659 | if ?DEBUGLOG 1660 | invoke printtolog, CStr("Done",0Dh,0Ah) 1661 | endif 1662 | 1663 | @@skip: 1664 | pop es 1665 | 1666 | @@failed: 1667 | if ?DEBUGLOG 1668 | invoke closelog 1669 | endif 1670 | ret 1671 | drv_stop endp 1672 | 1673 | ; Pause sound 1674 | ; Takes no parameters 1675 | ; Returns nothing 1676 | drv_pause proc near 1677 | if ?DEBUGLOG 1678 | invoke openlog, CStr("HDAPAUSE.LOG"),0 1679 | invoke printtolog, CStr("checking if driver is initialized...",0Dh,0Ah) 1680 | endif 1681 | bt [statusword],0 1682 | jnc @@failed 1683 | 1684 | if ?DEBUGLOG 1685 | invoke printtolog, CStr("checking if already paused...",0Dh,0Ah) 1686 | endif 1687 | bts [statusword],4 1688 | jc @@failed 1689 | 1690 | push es 1691 | call get_hdareg_ptr 1692 | mov edi,[firststreamoff] 1693 | if ?DEBUGLOG 1694 | invoke printtolog, CStr("checking if sound is playing...",0Dh,0Ah) 1695 | endif 1696 | btr es:[edi].STREAM.wCtl,1 ; RUN bit 1697 | jnc @@skip 1698 | 1699 | if ?DEBUGLOG 1700 | invoke printtolog, CStr("Waiting for DMA engine to pause...",0Dh,0Ah) 1701 | endif 1702 | mov ecx,1000h 1703 | @@: 1704 | call wait_timerch2 1705 | test es:[edi].STREAM.wCtl,2 ; RUN bit 1706 | loopnz @B 1707 | 1708 | @@skip: 1709 | pop es 1710 | 1711 | @@failed: 1712 | if ?DEBUGLOG 1713 | invoke closelog 1714 | endif 1715 | ret 1716 | drv_pause endp 1717 | 1718 | ; Resume sound 1719 | ; Takes no parameters 1720 | ; Returns nothing 1721 | drv_resume proc near 1722 | if ?DEBUGLOG 1723 | invoke openlog, CStr("HDA_RSM.LOG"),0 1724 | invoke printtolog, CStr("checking if driver is initialized...",0Dh,0Ah) 1725 | endif 1726 | bt [statusword],0 1727 | jnc @@failed 1728 | 1729 | if ?DEBUGLOG 1730 | invoke printtolog, CStr("checking if paused...",0Dh,0Ah) 1731 | endif 1732 | btr [statusword],4 1733 | jnc @@failed 1734 | 1735 | push es 1736 | call get_hdareg_ptr 1737 | mov edi,[firststreamoff] 1738 | if ?DEBUGLOG 1739 | invoke printtolog, CStr("checking if sound is playing...",0Dh,0Ah) 1740 | endif 1741 | bts es:[edi].STREAM.wCtl,1 ; RUN bit 1742 | jnc @@skip 1743 | 1744 | if ?DEBUGLOG 1745 | invoke printtolog, CStr("Waiting for DMA engine to resume...",0Dh,0Ah) 1746 | endif 1747 | mov ecx,1000h 1748 | @@: 1749 | call wait_timerch2 1750 | test es:[edi].STREAM.wCtl,2 ; RUN bit 1751 | loopz @B 1752 | 1753 | @@skip: 1754 | pop es 1755 | 1756 | @@failed: 1757 | if ?DEBUGLOG 1758 | invoke closelog 1759 | endif 1760 | ret 1761 | drv_resume endp 1762 | 1763 | ; Empty functions 1764 | drv_foreground: 1765 | drv_fillinfo: 1766 | drv_setcallfn: 1767 | retn 1768 | 1769 | ; Get far pointer to timer callback 1770 | ; Takes no parameters 1771 | ; Returns far pointer in DX:EAX 1772 | drv_getcallfn proc near 1773 | mov eax,offset timer_handler 1774 | mov edx,cs 1775 | ret 1776 | drv_getcallfn endp 1777 | 1778 | ; ------------------------------------------------------------------------------ ; 1779 | ; INTERNAL functions from here (called from within ENUM and interrupt functions) ; 1780 | ; ------------------------------------------------------------------------------ ; 1781 | 1782 | ; Unmute and set the amplifier gain to 0 dB for [node] 1783 | ; If EAX is zero, only does the output amp, otherwise does both input and output 1784 | unmute proc near uses eax edx 1785 | test eax,eax 1786 | jz @F 1787 | 1788 | mov ax,0F00h ; get parameter 1789 | mov edx,0Dh ; input amplifier capabilities 1790 | call send_cmd_wait 1791 | movzx edx,ah ; get the "numsteps" (i.e. max) gain value (bits 8-14) 1792 | if ?DEBUGLOG 1793 | invoke printtolog,CStr("input amp max gain of codec/node ") 1794 | invoke printbinword,[wParam] 1795 | invoke printtolog,CStr("b is: ") 1796 | invoke printbinword,dx 1797 | invoke printtolog,CStr("b",0Dh,0Ah) 1798 | endif 1799 | 1800 | mov ax,3 ; set amplifier 1801 | mov dh,70h ; bits 12-14 - set input amp, left and right 1802 | call send_cmd_wait 1803 | 1804 | @@: 1805 | mov ax,0F00h ; get parameter 1806 | mov edx,12h ; output amplifier capabilities 1807 | call send_cmd_wait 1808 | movzx edx,ah ; get the "numsteps" (i.e. max) gain value (bits 8-14) 1809 | if ?DEBUGLOG 1810 | invoke printtolog,CStr("output amp max gain of codec/node ") 1811 | invoke printbinword,[wParam] 1812 | invoke printtolog,CStr("b is: ") 1813 | invoke printbinword,dx 1814 | invoke printtolog,CStr("b",0Dh,0Ah) 1815 | endif 1816 | 1817 | mov ax,3 ; set amplifier 1818 | mov dh,0B0h ; bits 12-13/15 - set output amp, left and right 1819 | call send_cmd_wait 1820 | 1821 | ret 1822 | unmute endp 1823 | 1824 | ; Take a sample rate in AX, and return its index in the rate list in AX, or -1 if absent. 1825 | get_rate_idx proc near uses ecx edi es 1826 | push ds 1827 | pop es 1828 | 1829 | mov edi,offset RateList 1830 | mov ecx,NUM_RATES 1831 | repne scasw 1832 | sub edi,2 1833 | xor eax,eax 1834 | not eax ; EAX = -1 1835 | scasw ; ES:[EDI] == -1? (This instruction also increments the index, which we want since we count from 1!) 1836 | je @F 1837 | 1838 | sub edi,offset RateList 1839 | mov ax,di 1840 | shr ax,1 1841 | 1842 | @@: 1843 | ret 1844 | get_rate_idx endp 1845 | 1846 | ; Find an audio output converter node attached to the pin widget selected by [node], 1847 | ; with type given by AL, and return the found node in AL. 1848 | ; Index of desired rate can be given in SI, and supported rate bitmap is returned in SI. 1849 | nodes_seen_bmap dd 8 dup (?) 1850 | find_dac_start proc near 1851 | ; clear the bitmap of nodes we've seen 1852 | push es 1853 | push edi 1854 | push ecx 1855 | push eax 1856 | mov ecx,ds 1857 | mov edi,offset nodes_seen_bmap 1858 | mov es,ecx 1859 | mov ecx,8 1860 | xor eax,eax 1861 | rep stosd 1862 | pop eax 1863 | pop ecx 1864 | pop edi 1865 | pop es 1866 | 1867 | call find_dac_node 1868 | ret 1869 | find_dac_start endp 1870 | find_dac_node proc near uses ebx ecx edx edi 1871 | ; Local variables: 1872 | ; BL = current node (potential return value) 1873 | ; BH = current node's type (potential argument to recursive call) 1874 | push eax ; save the node type 1875 | 1876 | ; establish that we've seen the currently-selected node 1877 | movzx eax,[node] 1878 | mov edi,eax 1879 | and eax,1Fh 1880 | shr edi,5 1881 | bts [nodes_seen_bmap+edi*4],eax 1882 | jc @@found_bupkis ; if we've already seen it, don't run again 1883 | 1884 | mov ax,0F00h ; get parameter 1885 | mov edx,0Eh ; number of connections 1886 | call send_cmd_wait 1887 | 1888 | mov ecx,eax 1889 | xor eax,eax 1890 | xor edx,edx 1891 | @@: 1892 | .if !eax 1893 | mov ax,0F02h ; get connection nodes 1894 | call send_cmd_wait 1895 | mov edi,eax 1896 | .endif 1897 | 1898 | push [wParam] ; save current node 1899 | push edx 1900 | mov [node],al 1901 | mov bl,al 1902 | mov ax,0F00h ; get parameter 1903 | mov edx,9 ; widget type 1904 | call send_cmd_wait 1905 | shr eax,20 1906 | and al,0Fh 1907 | cmp al,WTYPE_AUDIOOUT 1908 | mov bh,al 1909 | pop edx 1910 | jne @@not_dac 1911 | 1912 | push edx 1913 | mov ax,0F00h ; get parameter 1914 | mov edx,0Ah ; supported PCM size / rates 1915 | call send_cmd_wait 1916 | bt eax,17 ; supports 16-bit format? 1917 | pop edx 1918 | jnc @@not_suitable_dac 1919 | 1920 | and si,7 ; only look for rates up to number 7 (i.e. 48 kHz) 1921 | dec si ; R1 <=> Bit 0, etc. 1922 | bt ax,si ; supports desired rate? 1923 | lea esi,[esi+1] ; restore value without affecting flags 1924 | jnc @@not_suitable_dac 1925 | 1926 | mov si,ax 1927 | mov al,bl 1928 | jmp @@found_one 1929 | 1930 | @@not_dac: 1931 | mov al,bh 1932 | call find_dac_node ; recurse 1933 | test al,al 1934 | jnz @@found_one 1935 | 1936 | @@not_suitable_dac: 1937 | mov eax,edi 1938 | shr eax,8 1939 | inc edx 1940 | pop [wParam] 1941 | loop @B 1942 | 1943 | @@found_bupkis: 1944 | xor al,al 1945 | lea esp,[esp+4] ; get rid of the node type on the stack 1946 | jmp @@retpoint 1947 | 1948 | @@found_one: 1949 | pop [wParam] ; restore current node 1950 | 1951 | xchg [esp],eax ; save the node we've found, and pull up our node's type 1952 | if ?DEBUGLOG 1953 | invoke printtolog,CStr("unmuting codec/node ") 1954 | invoke printbinword,[wParam] 1955 | invoke printtolog,CStr("b of type ") 1956 | invoke printnodetype,al 1957 | invoke printtolog,CStr("...",0Dh,0Ah) 1958 | endif 1959 | cmp al,WTYPE_MIXER 1960 | je @F 1961 | 1962 | lea eax,[edx+ecx] ; total number of connections 1963 | cmp eax,1 1964 | jg @F 1965 | 1966 | ; there's more than one connection and it's not a mixer, 1967 | ; so we need to select the connection correctly. 1968 | mov ax,701h ; select connection 1969 | call send_cmd_wait 1970 | 1971 | @@: 1972 | mov eax,1 ; include input amp 1973 | call unmute 1974 | mov ax,705h ; set power state 1975 | xor edx,edx ; D0 1976 | call send_cmd_wait 1977 | 1978 | pop eax 1979 | if ?CDAUDIO 1980 | and si,not 11111b ; don't support any rates slower than 44100 1981 | endif 1982 | @@retpoint: 1983 | ret 1984 | find_dac_node endp 1985 | 1986 | mask_irq proc near uses eax ecx edx 1987 | movzx cx,[irq] 1988 | .if cx == 2 1989 | mov cx,9 1990 | .endif 1991 | cmp cx,8 ; high IRQ? 1992 | jb @F 1993 | mov dx,0A1h 1994 | in al,dx 1995 | mov ah,al 1996 | @@: 1997 | mov dx,21h 1998 | in al,dx 1999 | bts ax,cx 2000 | 2001 | cmp cx,8 ; high IRQ? 2002 | jb @F 2003 | mov dx,0A1h 2004 | mov al,ah 2005 | @@: 2006 | out dx,al 2007 | 2008 | ret 2009 | mask_irq endp 2010 | 2011 | unmask_irq proc near uses eax ecx edx 2012 | movzx cx,[irq] 2013 | .if cx == 2 2014 | mov cx,9 2015 | .endif 2016 | cmp cx,8 ; high IRQ? 2017 | jb @F 2018 | mov dx,0A1h 2019 | in al,dx 2020 | mov ah,al 2021 | @@: 2022 | mov dx,21h 2023 | in al,dx 2024 | btr ax,cx 2025 | 2026 | cmp cx,8 ; high IRQ? 2027 | jb @F 2028 | btr ax,2 2029 | out dx,al ; unmask cascaded interrupt 2030 | mov dx,0A1h 2031 | mov al,ah 2032 | @@: 2033 | out dx,al 2034 | 2035 | ret 2036 | unmask_irq endp 2037 | 2038 | send_eoi proc near 2039 | mov al,[mPICeoi] 2040 | mov dx,20h 2041 | out dx,al 2042 | 2043 | mov al,[sPICeoi] 2044 | test al,al 2045 | jz @F 2046 | mov dx,0A0h 2047 | out dx,al 2048 | @@: 2049 | 2050 | ret 2051 | send_eoi endp 2052 | 2053 | handle_rirbois proc near 2054 | ; This may not even show up on the terminal if a game is being played! 2055 | invoke printstderr, CStr(33o,"[31m","Non-fatal: RIRB overrun",33o,"[37m",0Dh,0Ah) 2056 | ret 2057 | handle_rirbois endp 2058 | 2059 | ; Hope we never need this... 2060 | drv_reset proc near 2061 | mov eax,[dwMainBufPhys] 2062 | push eax 2063 | push [dwMainBufSize] 2064 | 2065 | test eax,eax ; buffer set iff start has been called (i.e. sound playing) 2066 | jz @F 2067 | pushad 2068 | call drv_stop 2069 | popad 2070 | @@: 2071 | pushad 2072 | call drv_uninit 2073 | 2074 | cmp [crst_count],?CRST_MAX 2075 | jb @F 2076 | 2077 | ; This may not even show up on the terminal if a game is being played! 2078 | invoke printstderr, CStr(33o,"[31m","FATAL: Too many HDA controller resets!",33o,"[37m",0Dh,0Ah) 2079 | popad 2080 | lea esp,[esp+8] 2081 | ret 2082 | 2083 | @@: 2084 | mov bx,[wPort] 2085 | mov cx,[wIrqDma] 2086 | mov al,[pinnode] 2087 | mov [node],al 2088 | mov si,[wParam] 2089 | call drv_init 2090 | popad 2091 | 2092 | xchg edi,[esp+4] 2093 | xchg ecx,[esp] 2094 | test edi,edi 2095 | jz @F 2096 | pushad 2097 | call drv_start 2098 | popad 2099 | 2100 | @@: 2101 | inc [crst_count] 2102 | pop ecx 2103 | pop edi 2104 | ret 2105 | drv_reset endp 2106 | 2107 | handle_cmei proc near 2108 | if ?DEBUGLOG 2109 | invoke logtostderr 2110 | endif 2111 | invoke printstderr, CStr(33o,"[31m","CORB Memory Error, attempting reset...",33o,"[37m",0Dh,0Ah) 2112 | call drv_reset 2113 | invoke printstderr, CStr(33o,"[31m","Reset complete",33o,"[37m",0Dh,0Ah) 2114 | if ?DEBUGLOG 2115 | call wait_timerch2 2116 | invoke closelog 2117 | endif 2118 | ret 2119 | handle_cmei endp 2120 | 2121 | ; Hope we never need this... 2122 | stream_reset proc near 2123 | mov eax,[dwMainBufPhys] 2124 | push eax 2125 | push [dwMainBufSize] 2126 | pushad 2127 | call drv_stop 2128 | popad 2129 | 2130 | cmp [srst_count],?SRST_MAX 2131 | jb @F 2132 | 2133 | ; This may not even show up on the terminal if a game is being played! 2134 | invoke printstderr, CStr(33o,"[31m","FATAL: Too many HDA stream resets!",33o,"[37m",0Dh,0Ah) 2135 | popad 2136 | lea esp,[esp+8] 2137 | ret 2138 | 2139 | @@: 2140 | xchg edi,[esp+4] 2141 | xchg ecx,[esp] 2142 | pushad 2143 | call drv_start 2144 | popad 2145 | 2146 | inc [srst_count] 2147 | pop ecx 2148 | pop edi 2149 | ret 2150 | stream_reset endp 2151 | 2152 | handle_dese proc near 2153 | if ?DEBUGLOG 2154 | invoke logtostderr 2155 | endif 2156 | invoke printstderr, CStr(33o,"[31m","Stream Descriptor Error, attempting reset...",33o,"[37m",0Dh,0Ah) 2157 | call stream_reset 2158 | invoke printstderr, CStr(33o,"[31m","Stream Reset complete",33o,"[37m",0Dh,0Ah) 2159 | if ?DEBUGLOG 2160 | call wait_timerch2 2161 | invoke closelog 2162 | endif 2163 | ret 2164 | handle_dese endp 2165 | 2166 | handle_fifoe proc near 2167 | ; This may not even show up on the terminal if a game is being played! 2168 | invoke printstderr, CStr(33o,"[31m","Non-fatal: FIFO underrun",33o,"[37m",0Dh,0Ah) 2169 | ret 2170 | handle_fifoe endp 2171 | 2172 | ; Buffer Completion Interrupt 2173 | handle_bcis proc near uses edi 2174 | if ?DEBUGLOG 2175 | inc [TotalBCISs] 2176 | endif 2177 | 2178 | xor edi,edi 2179 | xchg edi,[TicksSinceBCIS] 2180 | test edi,edi 2181 | jz @F 2182 | ret 2183 | 2184 | @@: 2185 | ; If we're here, it means our timer handler isn't running but the DMA 2186 | ; engine still is, so the user may be hearing annoying looping sound. 2187 | ; This codepath is a legitimate scenario, as the host app may shut down 2188 | ; the SOS timer system while leaving the driver running. 2189 | ; E.g. Rayman does this while playing a cutscene so it can reprogram the 2190 | ; PIT to time the video itself. 2191 | push eax 2192 | push ecx 2193 | 2194 | ; Anyway, clear the buffer to stop any looping sound. 2195 | movzx eax,[wAuxSel] 2196 | test eax,eax 2197 | jnz @F 2198 | 2199 | ; No aux buffer, see if we have a pointer to the main buffer 2200 | movzx eax,[wMainBufSel] 2201 | test eax,eax 2202 | mov edi,[dwMainBufOff] 2203 | jz @@skip ; if not, we can't do anything to improve the situation 2204 | 2205 | @@: 2206 | push es 2207 | push edi 2208 | call get_hdareg_ptr 2209 | mov edi,[firststreamoff] 2210 | mov ecx,es:[edi].STREAM.dwBufLen 2211 | 2212 | mov es,eax ; ES:[EDI] = buffer 2213 | pop edi ; (EDI is by definition zero unless using dwMainBufOff) 2214 | shr ecx,2 2215 | 2216 | if ?CDAUDIO 2217 | push ecx 2218 | push edi 2219 | endif 2220 | xor eax,eax 2221 | cld 2222 | rep stosd 2223 | 2224 | if ?CDAUDIO 2225 | pop edi 2226 | pop ecx 2227 | ; We need to keep the CD Audio going, since in the old days one wouldn't 2228 | ; have expected disabling the timer to have any effect on this. 2229 | ; This may call the CD driver several times in one IRQ - not sure what 2230 | ; that will do... 2231 | bt [statusword],6 2232 | jnc @F 2233 | call mixincdaudio 2234 | @@: 2235 | endif 2236 | pop es 2237 | 2238 | @@skip: 2239 | pop ecx 2240 | pop eax 2241 | ret 2242 | handle_bcis endp 2243 | 2244 | if ?CDAUDIO 2245 | ; Takes segment pointing to a CdRmDriveBuf in GS 2246 | fillcdbuf proc near 2247 | call mask_irq 2248 | 2249 | push ebp 2250 | sub esp,size RMCS 2251 | mov ebp,esp 2252 | mov [ebp].RMCS.resvrd,0 2253 | mov [ebp].RMCS.rSSSP,0 2254 | 2255 | mov [ebp].RMCS.rESI,eax ; stash EAX 2256 | mov [ebp].RMCS.rEDX,ebx ; stash EBX 2257 | mov [ebp].RMCS.rEBP,ecx ; stash ECX 2258 | mov [ebp].RMCS.rEDI,edi ; stash EDI 2259 | push es 2260 | 2261 | mov [ebp].RMCS.rAX,1510h ; send device driver request 2262 | movzx ax,gs:[CdRmDriveBuf.bDrive] 2263 | mov bx,gs:[CdRmDriveBuf.sReq.wBufSeg] 2264 | mov ecx,[dwOldInt2F] 2265 | mov [ebp].RMCS.rCX,ax 2266 | mov [ebp].RMCS.rES,bx 2267 | mov [ebp].RMCS.rBX,CdRmDriveBuf.sReq 2268 | mov [ebp].RMCS.rCSIP,ecx 2269 | 2270 | ; These flags are interesting... For int 31h function 0302h, PMODE/W 2271 | ; uses RMCS.rFlags to build the IRET frame, but uses the caller's flags 2272 | ; when it actually switches to Real Mode. So everything's fine until the 2273 | ; Real Mode procedure IRETs (or, in the case of MSCDEX, pops the flags 2274 | ; first, then does some other stuff, then RETF 02s!). Once that IRET / 2275 | ; POPF happens in Real Mode, then suddenly whatever garbage was in the 2276 | ; RMCS starts dictating the machine behaviour. In particular, if bit 8 2277 | ; was set, you start getting #DB exceptions every instruction! What's 2278 | ; worse, PMODE/W, despite ignoring them for the transition to Real Mode, 2279 | ; then carries these newly-set flags through the switch back to 2280 | ; Protected Mode without question. This can result in #DBs within its 2281 | ; kernel code, which in certain versions of Rayman result in very 2282 | ; unceremonious crashes! 2283 | ; The bottom line is, we need to set the flags here instead of accepting 2284 | ; whatever garbage is in the RMCS. I'm slightly ashamed that it took me 2285 | ; so long to realize that... 2286 | pushf 2287 | pop [ebp].RMCS.rFlags 2288 | 2289 | mov ecx,gs:[CdRmDriveBuf.sStat.dwEnd] 2290 | sub ecx,gs:[CdRmDriveBuf.sReq.dwStart] 2291 | .if ecx > ?CDBUFSIZE 2292 | mov ecx,?CDBUFSIZE 2293 | .endif 2294 | 2295 | push gs 2296 | pop es 2297 | 2298 | ; clear the buffer 2299 | push ecx 2300 | mov ecx,CDBUFSIZEDWORDS 2301 | mov edi,CdRmDriveBuf.Samples 2302 | xor eax,eax 2303 | rep stosd 2304 | pop ecx 2305 | 2306 | .if !ecx 2307 | ; reset the "playing" status 2308 | btr gs:[CdRmDriveBuf.wStatus],9 ; busy bit = playing 2309 | jmp @@done 2310 | .endif 2311 | mov gs:[CdRmDriveBuf.sReq.wSectors],cx 2312 | 2313 | push ss 2314 | pop es 2315 | 2316 | push ecx 2317 | mov gs:[CdRmDriveBuf.sReq.bCmd],80h ; READ LONG 2318 | mov gs:[CdRmDriveBuf.sReq.wBufOff],CdRmDriveBuf.Samples 2319 | mov gs:[CdRmDriveBuf.sReq.bRMode],1 ; raw 2320 | mov gs:[CdRmDriveBuf.sReq.bISize],0 ; no interleave 2321 | mov gs:[CdRmDriveBuf.sReq.bISkip],0 ; no interleave 2322 | mov edi,ebp 2323 | xor bx,bx 2324 | mov cx,bx 2325 | mov ax,0302h ; call real-mode interrupt procedure 2326 | int 31h 2327 | pop ecx 2328 | 2329 | if ?DEBUGLOG 2330 | bt gs:[CdRmDriveBuf.sReq.wStatus],15 2331 | jnc @F 2332 | invoke logtostderr 2333 | invoke printbinword,gs:[CdRmDriveBuf.sReq.wStatus] 2334 | int 3 2335 | invoke closelog 2336 | 2337 | @@: 2338 | endif 2339 | imul eax,ecx,CDSECTORSIZE 2340 | add eax,CdRmDriveBuf.Samples 2341 | mov gs:[CdRmDriveBuf.dwBufEnd],eax 2342 | add gs:[CdRmDriveBuf.sReq.dwStart],ecx 2343 | 2344 | ; check if driver advertised prefetch support 2345 | ; if not, there is no point in doing READ LONG PREFETCH, as it will just 2346 | ; seek, to a location which has already been reached by the above read. 2347 | bt gs:[CdRmDriveBuf.wStatus],0 2348 | jnc @F 2349 | 2350 | ; encourage MSCDEX to prefetch the next N sectors so as not to block 2351 | mov gs:[CdRmDriveBuf.sReq.bCmd],82h ; READ LONG PREFETCH 2352 | mov ax,0302h ; call real-mode interrupt procedure 2353 | mov cx,bx 2354 | int 31h 2355 | 2356 | @@: 2357 | ; update Q-Channel info 2358 | mov ax,gs:[CdRmDriveBuf.sReq.wBufSeg] 2359 | mov cx,[wCdRmBufSeg] 2360 | 2361 | push fs 2362 | mov fs,[wCdRmBufSel] 2363 | mov fs:[CdRmHeadBuf.sReq.bCmd],3 ; IOCTL Read 2364 | mov fs:[CdRmHeadBuf.sReq.wCount],size QInfo 2365 | mov fs:[CdRmHeadBuf.sReq.wBufOff],CdRmDriveBuf.sQChan 2366 | mov fs:[CdRmHeadBuf.sReq.wBufSeg],ax 2367 | pop fs 2368 | 2369 | mov [ebp].RMCS.rES,cx 2370 | mov [ebp].RMCS.rBX,CdRmHeadBuf.sReq 2371 | mov ax,0302h ; call real-mode interrupt procedure 2372 | mov cx,bx 2373 | int 31h 2374 | 2375 | @@done: 2376 | ; when the system is not in VM86 mode, the CD driver may carelessly 2377 | ; mask IRQ0, effectively disabling our driver - counteract this here! 2378 | ; (this had me scratching my head for over a week!) 2379 | in al,21h 2380 | btr ax,0 ; unmask IRQ0 = timer 2381 | out 21h,al 2382 | 2383 | mov ebx,[ebp].RMCS.rEDX ; restore EBX 2384 | mov ecx,[ebp].RMCS.rEBP ; restore ECX 2385 | mov edi,[ebp].RMCS.rEDI ; restore EDI 2386 | mov eax,[ebp].RMCS.rESI ; restore EAX 2387 | pop es 2388 | 2389 | lea esp,[ebp+size RMCS] 2390 | pop ebp 2391 | 2392 | call unmask_irq 2393 | ret 2394 | fillcdbuf endp 2395 | 2396 | ; ES:EDI = buffer into which to mix the CD Audio stream 2397 | ; ECX = number of sample pairs to mix 2398 | mixincdaudio proc near uses gs esi ebx eax edx 2399 | mov gs,[wCdRmBufSel] 2400 | push ebp 2401 | 2402 | test ecx,ecx 2403 | jz @@done ; prevent hangs / crashes! 2404 | 2405 | @@driveloop: 2406 | mov ax,gs:[CdRmDriveBuf.wNextS] ; or wFirstS for head buffer 2407 | test ax,ax ; end of linked list? 2408 | jz @@done 2409 | mov gs,ax 2410 | 2411 | bt gs:[CdRmDriveBuf.wStatus],9 ; audio playing? 2412 | jnc @@driveloop ; if not, move to next drive 2413 | 2414 | mov bl,gs:CdRmDriveBuf.sInfo.Info[2].bVolume 2415 | mov bh,bl ; BX = multiplication factor for right channel 2416 | shr bx,1 ; FFFFh --> 7FFFh, etc. (for signed multiplication) 2417 | ror ebx,10h 2418 | mov bl,gs:CdRmDriveBuf.sInfo.Info[0].bVolume 2419 | mov bh,bl ; BX = multiplication factor for left channel 2420 | shr bx,1 ; FFFFh --> 7FFFh, etc. (for signed multiplication) 2421 | 2422 | push ecx 2423 | push edi 2424 | mov ebp,ecx 2425 | mov esi,gs:[CdRmDriveBuf.dwBufPos] 2426 | @@loadloop: 2427 | .if esi >= gs:[CdRmDriveBuf.dwBufEnd] 2428 | call fillcdbuf 2429 | mov esi,CdRmDriveBuf.Samples 2430 | .endif 2431 | 2432 | if ?CDCHANCTL 2433 | mov edx,2 ; step size in words 2434 | .if gs:[CdRmDriveBuf.sQChan.bCtlAdr] & 80h ; four channels? 2435 | mov dl,4 2436 | .endif 2437 | ; pull in the input for right channel 2438 | movzx ecx,gs:CdRmDriveBuf.sInfo.Info[2].bInChan 2439 | .if ecx >= edx 2440 | xor ax,ax 2441 | .else 2442 | mov ax,gs:[esi+ecx*2] 2443 | .endif 2444 | shl eax,10h 2445 | ; and for left channel 2446 | movzx ecx,gs:CdRmDriveBuf.sInfo.Info[0].bInChan 2447 | .if ecx >= edx 2448 | xor ax,ax 2449 | .else 2450 | mov ax,gs:[esi+ecx*2] 2451 | .endif 2452 | ; step forward 2453 | lea esi,[esi+edx*2] 2454 | else 2455 | ; assumes CD is stereo (not four-channel) 2456 | lodsd gs:[esi] 2457 | endif 2458 | 2459 | if ?CDVOLCTL 2460 | xor edx,edx 2461 | imul bx 2462 | shld dx,ax,1 ; undo the SHR we did on BX earlier 2463 | bt ax,0Eh 2464 | adc dx,0 ; increment DX if second-MSB of AX set (i.e. round up) 2465 | 2466 | ror eax,10h ; move to right channel 2467 | ror edx,10h ; move to right channel 2468 | ror ebx,10h ; move to right channel 2469 | 2470 | imul bx 2471 | shld dx,ax,1 ; undo the SHR we did on BX earlier 2472 | bt ax,0Eh 2473 | adc dx,0 ; increment DX if second-MSB of AX set (i.e. round up) 2474 | 2475 | ror edx,10h ; back to left channel 2476 | ror ebx,10h ; back to left channel 2477 | else 2478 | mov edx,eax 2479 | endif 2480 | 2481 | movzx ecx,[bCdDivider] 2482 | @@mixloop: 2483 | mov eax,[es:edi] 2484 | add ax,dx ; mix left channel 2485 | jno @F 2486 | ; handle clipping 2487 | bt ax,0Fh ; check sign bit after overflow 2488 | mov ax,8000h 2489 | sbb ax,0 ; if sign bit was 1, AX becomes 7FFFh, otherwise 8000h 2490 | 2491 | @@: 2492 | ror eax,10h ; move to right channel 2493 | ror edx,10h ; move to right channel 2494 | add ax,dx ; mix right channel 2495 | jno @F 2496 | ; handle clipping 2497 | bt ax,0Fh ; check sign bit after overflow 2498 | mov ax,8000h 2499 | sbb ax,0 ; if sign bit was 1, AX becomes 7FFFh, otherwise 8000h 2500 | 2501 | @@: 2502 | ror eax,10h ; back to left channel 2503 | ror edx,10h ; back to left channel 2504 | stosd 2505 | dec ebp 2506 | jz @F 2507 | loop @@mixloop 2508 | jmp @@loadloop 2509 | 2510 | @@: 2511 | mov gs:[CdRmDriveBuf.dwBufPos],esi 2512 | pop edi 2513 | pop ecx 2514 | jmp @@driveloop 2515 | 2516 | @@done: 2517 | pop ebp 2518 | ret 2519 | mixincdaudio endp 2520 | endif 2521 | 2522 | ; ---------------------------------------------------------------------- ; 2523 | ; EXTERNAL functions from here (called directly from outside our driver) ; 2524 | ; ---------------------------------------------------------------------- ; 2525 | assume ds:nothing,fs:nothing 2526 | 2527 | irq_handler proc 2528 | pushad 2529 | push ds 2530 | push es 2531 | 2532 | mov ds,cs:[lpPortList_seg] 2533 | assume ds:_TEXT 2534 | if ?DEBUGLOG 2535 | inc [TotalIRQs] 2536 | endif 2537 | call get_hdareg_ptr 2538 | movzx eax,es:[edi].HDAREGS.statests ; don't care about this... 2539 | test eax,eax 2540 | jz @F 2541 | mov es:[edi].HDAREGS.statests,ax ; write 1s back to clear the bits 2542 | 2543 | @@: 2544 | mov ebx,es:[edi].HDAREGS.intsts 2545 | bt ebx,31 ; GIS 2546 | jnc @@not_ours 2547 | 2548 | bts [statusword],2 2549 | jc @@skip ; already entered 2550 | 2551 | bt ebx,30 ; CIS 2552 | jnc @@not_rirb 2553 | 2554 | mov dl,es:[edi].HDAREGS.rirbsts 2555 | 2556 | bt dx,2 ; RIRBOIS 2557 | jnc @F 2558 | call handle_rirbois 2559 | 2560 | @@: 2561 | ; don't bother with RINTFL, since we always use send_cmd_wait which polls anyway 2562 | ; - this may change in the future... 2563 | ;bt dx,0 ; RINTFL 2564 | ;jnc @F 2565 | ;call handle_rintfl 2566 | 2567 | ;@@: 2568 | ; write 1s back to the bits we've addressed 2569 | mov es:[edi].HDAREGS.rirbsts,dl 2570 | 2571 | mov dl,es:[edi].HDAREGS.corbsts 2572 | bt dx,0 ; CMEI 2573 | jnc @F 2574 | call handle_cmei 2575 | @@: 2576 | ; write 1s back to the bits we've addressed 2577 | mov es:[edi].HDAREGS.corbsts,dl 2578 | 2579 | @@not_rirb: 2580 | movzx eax,es:[edi].HDAREGS.gcap 2581 | shr eax,8 2582 | and eax,0Fh ; number of input streams 2583 | bt ebx,eax ; SIS from the first output stream 2584 | jnc @@not_stream 2585 | 2586 | mov edi,[firststreamoff] 2587 | mov dl,es:[edi].STREAM.bSts 2588 | 2589 | bt dx,4 ; DESE 2590 | jnc @F 2591 | call handle_dese 2592 | 2593 | @@: 2594 | bt dx,3 ; FIFOE 2595 | jnc @F 2596 | call handle_fifoe 2597 | 2598 | @@: 2599 | bt dx,2 ; BCIS 2600 | jnc @F 2601 | call handle_bcis 2602 | 2603 | @@: 2604 | ; write 1s back to the bits we've addressed 2605 | mov es:[edi].STREAM.bSts,dl 2606 | 2607 | @@not_stream: 2608 | call send_eoi 2609 | btr [statusword],2 2610 | 2611 | @@skip: 2612 | pop es 2613 | pop ds 2614 | assume ds:nothing 2615 | popad 2616 | iretd 2617 | 2618 | @@not_ours: 2619 | if ?DEBUGLOG 2620 | inc [PassedIRQs] 2621 | endif 2622 | pop es 2623 | pop ds 2624 | popad 2625 | jmp cs:[oldIRQhandler] 2626 | irq_handler endp 2627 | 2628 | ; Called from the timer (16-bit stereo pseudo-DMA) 2629 | ; Takes far pointer to DMA buffer in ES:[EDI] 2630 | ; Returns the address at which we next want it filled in EAX, 2631 | ; and the address behind which we want it blanked in EDX. 2632 | timer_handler proc far 2633 | push ds 2634 | xor eax,eax ; return zero by default 2635 | mov edx,eax 2636 | 2637 | mov ds,cs:[lpPortList_seg] 2638 | assume ds:_TEXT 2639 | bts [statusword],3 2640 | jc @@donotenter 2641 | bt [statusword],0 2642 | jnc @@skip 2643 | 2644 | mov [crst_count],al 2645 | mov [srst_count],al 2646 | inc [TicksSinceBCIS] 2647 | if ?DEBUGLOG 2648 | inc dword ptr [TotalTicks] 2649 | jnz @F 2650 | inc dword ptr [TotalTicks+4] 2651 | @@: 2652 | endif 2653 | 2654 | mov [wMainBufSel],es 2655 | mov [dwMainBufOff],edi 2656 | 2657 | push fs 2658 | push esi 2659 | push ecx 2660 | 2661 | push es 2662 | pop fs 2663 | mov esi,edi ; FS:[ESI] points to the main buffer 2664 | 2665 | call get_hdareg_ptr 2666 | if ?DEBUGLOG 2667 | mov edx,es:[edi].HDAREGS.intctl 2668 | mov eax,es:[edi].HDAREGS.intsts 2669 | test edx,eax 2670 | jz @F 2671 | inc [UncaughtIRQs] 2672 | mov [LastUncaught],eax 2673 | 2674 | mov al,0Ah ; read IRR 2675 | out 020h,al 2676 | out 0A0h,al 2677 | in al,0A0h 2678 | mov ah,al 2679 | in al,020h 2680 | mov [LastIRR],ax 2681 | 2682 | mov al,0Bh ; read ISR 2683 | out 020h,al 2684 | out 0A0h,al 2685 | in al,0A0h 2686 | mov ah,al 2687 | in al,020h 2688 | mov [LastISR],ax 2689 | 2690 | push ebx 2691 | mov ax,0B109h ; read configuration word 2692 | mov bx,[wPort] 2693 | mov edi,6 ; status register 2694 | int 1Ah 2695 | bt cx,3 ; interrupt status 2696 | adc [UncaughtPCI],0 2697 | pop ebx 2698 | @@: 2699 | endif 2700 | mov edi,[firststreamoff] 2701 | mov edx,es:[edi].STREAM.dwLinkPos 2702 | mov eax,es:[edi].STREAM.dwBufLen 2703 | 2704 | mov ecx,eax 2705 | shr eax,1 ; Fill halfway through the DMA buffer 2706 | add eax,edx 2707 | cmp eax,ecx 2708 | jb @F 2709 | sub eax,ecx ; Wrap around through beginning of buffer 2710 | @@: 2711 | 2712 | and eax,not 3 ; Ensure timer driver fills aligned dwords 2713 | and edx,not 3 ; Ensure timer driver fills aligned dwords 2714 | 2715 | cmp [dwAuxSelHdl],0 2716 | jz @@noaux ; No aux buffer, OK to return what we have 2717 | 2718 | push gs 2719 | push esi 2720 | 2721 | ;call logtostderr 2722 | 2723 | lgs esi,[lpAuxBufFilled] 2724 | and esi,not 3 ; ensure we're copying full dwords 2725 | mov [dwLastFillEAX],eax 2726 | mov ecx,eax 2727 | sub ecx,esi 2728 | jnb @@nowrap 2729 | 2730 | push es 2731 | push edx 2732 | push eax 2733 | 2734 | mov ecx,es:[edi].STREAM.dwBufLen 2735 | sub ecx,esi ; get the distance to the end of the buffer 2736 | shr ecx,2 ; convert to dwords 2737 | 2738 | push gs 2739 | pop es ; ES points to aux buffer 2740 | mov edi,esi 2741 | mov eax,esi 2742 | xor edx,edx 2743 | movzx esi,[soft_divider] 2744 | ;invoke printtolog, CStr("soft divider is ") 2745 | ;invoke printbinword,si 2746 | ;invoke printtolog, CStr("b, starting pre-wraparound copy",0Dh,0Ah) 2747 | div esi 2748 | mov esi,eax 2749 | 2750 | mov edx,ecx 2751 | and esi,not 3 ; ensure we're copying full dwords 2752 | ;call announcecopy 2753 | 2754 | if ?CDAUDIO 2755 | push edx 2756 | push edi 2757 | endif 2758 | @@: 2759 | movzx ecx,[soft_divider] 2760 | .if edx < ecx 2761 | mov ecx,edx 2762 | .endif 2763 | lodsd fs:[esi] 2764 | sub edx,ecx 2765 | rep stosd ; copy what's been filled in, to the aux buffer 2766 | ja @B ; flags set by subtraction above 2767 | 2768 | if ?CDAUDIO 2769 | pop edi 2770 | pop ecx 2771 | bt [statusword],6 2772 | jnc @F 2773 | call mixincdaudio 2774 | @@: 2775 | endif 2776 | 2777 | ;invoke printtolog, CStr("pre-wraparound copy done",0Dh,0Ah) 2778 | pop eax 2779 | pop edx 2780 | pop es 2781 | 2782 | xor esi,esi ; back to start of the buffer 2783 | mov ecx,eax 2784 | 2785 | @@nowrap: 2786 | push es 2787 | push edx 2788 | push eax 2789 | 2790 | and esi,not 3 ; ensure we're copying full dwords 2791 | shr ecx,2 ; convert to dwords 2792 | push gs 2793 | pop es ; ES points to aux buffer 2794 | mov edi,esi 2795 | mov eax,esi 2796 | xor edx,edx 2797 | movzx esi,[soft_divider] 2798 | ;invoke printtolog, CStr("soft divider is ") 2799 | ;invoke printbinword,si 2800 | ;invoke printtolog, CStr("b, starting post-wraparound copy",0Dh,0Ah) 2801 | div esi 2802 | mov esi,eax 2803 | 2804 | mov edx,ecx 2805 | and esi,not 3 ; ensure we're copying full dwords 2806 | ;call announcecopy 2807 | 2808 | if ?CDAUDIO 2809 | push edx 2810 | push edi 2811 | endif 2812 | @@: 2813 | movzx ecx,[soft_divider] 2814 | .if edx < ecx 2815 | mov ecx,edx 2816 | .endif 2817 | lodsd fs:[esi] 2818 | sub edx,ecx 2819 | rep stosd ; copy what's been filled in to the aux buffer 2820 | ja @B ; flags set by subtraction above 2821 | 2822 | if ?CDAUDIO 2823 | pop edi 2824 | pop ecx 2825 | bt [statusword],6 2826 | jnc @F 2827 | call mixincdaudio 2828 | @@: 2829 | endif 2830 | 2831 | ;invoke printtolog, CStr("post-wraparound copy done",0Dh,0Ah) 2832 | pop eax 2833 | pop edx 2834 | pop es 2835 | 2836 | pop esi 2837 | pop gs 2838 | 2839 | ; convert buffer positions 2840 | movzx ecx,[soft_divider] 2841 | 2842 | push edx 2843 | xor edx,edx 2844 | div ecx 2845 | pop edx 2846 | 2847 | push eax 2848 | mov eax,edx 2849 | xor edx,edx 2850 | div ecx 2851 | mov edx,eax 2852 | pop eax 2853 | 2854 | and eax,not 3 ; Ensure timer driver fills aligned dwords 2855 | and edx,not 3 ; Ensure timer driver fills aligned dwords 2856 | 2857 | ;invoke closelog 2858 | jmp @@done 2859 | 2860 | @@noaux: 2861 | if ?CDAUDIO 2862 | bt [statusword],6 2863 | jnc @@cddone 2864 | push es:[edi].STREAM.dwBufLen 2865 | 2866 | push fs 2867 | pop es ; restore main buffer in ES 2868 | mov edi,[dwLastFillEAX] 2869 | mov ecx,eax 2870 | sub ecx,edi 2871 | jnb @F 2872 | 2873 | mov ecx,[esp] ; load saved buffer length 2874 | sub ecx,edi ; get the distance to the end of the buffer 2875 | shr ecx,2 2876 | call mixincdaudio 2877 | xor edi,edi 2878 | @@: 2879 | add esp,4 ; remove saved buffer length from stack 2880 | shr ecx,2 2881 | call mixincdaudio 2882 | @@cddone: 2883 | endif 2884 | mov [dwLastFillEAX],eax 2885 | @@done: 2886 | mov edi,esi 2887 | push fs 2888 | pop es 2889 | 2890 | pop ecx 2891 | pop esi 2892 | pop fs 2893 | @@skip: 2894 | btr [statusword],3 2895 | @@donotenter: 2896 | pop ds 2897 | assume ds:nothing 2898 | ret 2899 | timer_handler endp 2900 | 2901 | if ?CDAUDIO 2902 | ; Takes RedBook M:S:F address in EAX and returns HSG sector in EAX 2903 | redbook2hsg proc near uses edx ecx 2904 | mov edx,eax 2905 | shr edx,10h ; EDX = minutes 2906 | imul edx,edx,60 2907 | movzx ecx,ah ; seconds 2908 | add edx,ecx 2909 | imul edx,edx,75 2910 | movzx ecx,al ; frames 2911 | lea eax,[ecx+edx-150] 2912 | ret 2913 | redbook2hsg endp 2914 | 2915 | int2f_handler proc 2916 | ; Remember: on entry, ES is set to our head buffer, and EDI points to 2917 | ; sRmCall therein. So we have a readymade pointer to the head buffer! 2918 | ; On the downside, this function is not reentrant, so we can't do any 2919 | ; debug logging (since int 21h may re-call int 2Fh internally). :/ 2920 | mov ax,es:[edi.RMCS.rAX] 2921 | cmp ah,15h ; MSCDEX 2922 | jne @@passthrough 2923 | 2924 | cmp al,8 ; absolute disc read 2925 | je @F 2926 | cmp al,10h ; send device request 2927 | jne @@passthrough ; don't care about anything else 2928 | 2929 | @@: 2930 | mov cx,es:[edi.RMCS.rCX] ; drive number 2931 | mov gs,cs:[wCdRmBufSel] 2932 | @@: 2933 | mov ax,gs:[CdRmDriveBuf.wNextS] ; or CdRmHeadBuf.wFirstS 2934 | test ax,ax 2935 | jz @@passthrough ; we didn't hook this drive 2936 | mov gs,ax 2937 | cmp cl,gs:[CdRmDriveBuf.bDrive] 2938 | jne @B 2939 | 2940 | mov bp,gs:[CdRmDriveBuf.wStatus] 2941 | .if es:[edi.RMCS.rAX] == 1508h ; absolute read 2942 | bt bp,9 ; busy? 2943 | jnc @@passthrough 2944 | 2945 | bts es:[edi.RMCS.rFlags],0 ; set carry 2946 | mov es:[edi.RMCS.rAX],15h ; not ready 2947 | jmp @@return 2948 | .endif 2949 | 2950 | mov fs,cs:[wRmMemSel] 2951 | movzx eax,es:[edi.RMCS.rBX] 2952 | movzx ebx,es:[edi.RMCS.rES] 2953 | shl ebx,4 2954 | add ebx,eax ; FS:EBX = device request 2955 | 2956 | .if fs:[ebx.IOCTLRW.bCmd] == 3 ; IOCTL READ 2957 | push ebx 2958 | movzx eax,fs:[ebx.IOCTLRW.wBufOff] 2959 | movzx ebx,fs:[ebx.IOCTLRW.wBufSeg] 2960 | shl ebx,4 2961 | add ebx,eax ; FS:EBX = read request 2962 | 2963 | .if byte ptr fs:[ebx] == 0Fh ; Audio Status Info 2964 | mov ax,gs:[CdRmDriveBuf.sStat.wStatus] 2965 | mov ecx,gs:[CdRmDriveBuf.sStat.dwStart] 2966 | mov edx,gs:[CdRmDriveBuf.sStat.dwEnd] 2967 | mov fs:[ebx.AudStat.wStatus],ax 2968 | mov fs:[ebx.AudStat.dwStart],ecx 2969 | mov fs:[ebx.AudStat.dwEnd],edx 2970 | 2971 | pop ebx ; FS:EBX = device request 2972 | mov fs:[ebx.IOCTLRW.wStatus],100h ; done 2973 | or fs:[ebx.IOCTLRW.wStatus],bp ; busy? 2974 | jmp @@return 2975 | 2976 | .endif 2977 | pop ebx ; FS:EBX = device request 2978 | .elseif fs:[ebx.IOCTLRW.bCmd] == 0Ch ; IOCTL WRITE 2979 | push ebx 2980 | movzx eax,fs:[ebx.IOCTLRW.wBufOff] 2981 | movzx ebx,fs:[ebx.IOCTLRW.wBufSeg] 2982 | shl ebx,4 2983 | add ebx,eax ; FS:EBX = write request 2984 | 2985 | .if byte ptr fs:[ebx] == 3 ; Audio Channel Control 2986 | mov eax, dword ptr fs:[ebx.AudInfo.Info] 2987 | mov edx, dword ptr fs:[ebx.AudInfo.Info+4] 2988 | mov dword ptr gs:[CdRmDriveBuf.sInfo.Info],eax 2989 | mov dword ptr gs:[CdRmDriveBuf.sInfo.Info+4],edx 2990 | .elseif byte ptr fs:[ebx] == 0 ; Eject Disc 2991 | pop ebx ; FS:EBX = device request 2992 | jmp @@failifbusy 2993 | 2994 | .endif 2995 | pop ebx ; FS:EBX = device request 2996 | .elseif fs:[ebx.IOCTLRW.bCmd] == 80h ; READ LONG 2997 | @@failifbusy: 2998 | bt bp,9 2999 | jnc @@passthrough 3000 | mov fs:[ebx.IOCTLRW.wStatus],8202h ; error, busy, code=2=not ready 3001 | jmp @@return 3002 | 3003 | .elseif fs:[ebx.IOCTLRW.bCmd] == 82h ; READ LONG PREFETCH 3004 | jmp @@failifbusy 3005 | .elseif fs:[ebx.IOCTLRW.bCmd] == 83h ; SEEK 3006 | jmp @@failifbusy 3007 | 3008 | .elseif fs:[ebx.IOCTLRW.bCmd] == 84h ; PLAY AUDIO 3009 | mov eax,fs:[ebx.PlayReq.dwStart] 3010 | .if fs:[ebx.PlayReq.bAMode] ; RedBook? 3011 | call redbook2hsg 3012 | .endif 3013 | mov edx,fs:[ebx.PlayReq.dwSectors] 3014 | add edx,eax 3015 | mov gs:[CdRmDriveBuf.sStat.wStatus],0 3016 | mov gs:[CdRmDriveBuf.sStat.dwStart],eax 3017 | mov gs:[CdRmDriveBuf.sStat.dwEnd],edx 3018 | mov gs:[CdRmDriveBuf.sReq.dwStart],eax ; start reading here 3019 | bts gs:[CdRmDriveBuf.wStatus],9 ; we're busy now! 3020 | mov gs:[CdRmDriveBuf.dwBufPos],0 3021 | mov gs:[CdRmDriveBuf.dwBufEnd],0 ; force refill 3022 | mov fs:[ebx.PlayReq.wStatus],300h ; done and busy 3023 | 3024 | jmp @@return 3025 | 3026 | .elseif fs:[ebx.IOCTLRW.bCmd] == 85h ; STOP AUDIO 3027 | btr gs:[CdRmDriveBuf.wStatus],9 3028 | jnc @F 3029 | mov eax,gs:[CdRmDriveBuf.sReq.dwStart] 3030 | bts gs:[CdRmDriveBuf.sStat.wStatus],0 ; paused 3031 | jmp @@setresume 3032 | @@: 3033 | xor eax,eax 3034 | btr gs:[CdRmDriveBuf.sStat.wStatus],ax ; paused 3035 | mov gs:[CdRmDriveBuf.sStat.dwEnd],eax 3036 | @@setresume: 3037 | mov gs:[CdRmDriveBuf.sStat.dwStart],eax ; (un)set resume point 3038 | mov fs:[ebx.PlayReq.wStatus],100h ; done, not busy 3039 | jmp @@return 3040 | 3041 | .elseif fs:[ebx.IOCTLRW.bCmd] == 88h ; RESUME AUDIO 3042 | btr gs:[CdRmDriveBuf.sStat.wStatus],0 ; paused 3043 | jnc @F 3044 | mov eax,gs:[CdRmDriveBuf.sStat.dwStart] ; get resume point 3045 | bts gs:[CdRmDriveBuf.wStatus],9 ; playing 3046 | mov gs:[CdRmDriveBuf.sReq.dwStart],eax 3047 | mov gs:[CdRmDriveBuf.dwBufPos],0 3048 | mov gs:[CdRmDriveBuf.dwBufEnd],0 ; force refill 3049 | mov fs:[ebx.PlayReq.wStatus],100h ; done, not busy 3050 | 3051 | jmp @@return 3052 | 3053 | @@: 3054 | mov fs:[ebx.IOCTLRW.wStatus],8002h ; error, code=2=not ready 3055 | or fs:[ebx.IOCTLRW.wStatus],bp ; busy? 3056 | jmp @@return 3057 | 3058 | .endif 3059 | 3060 | @@callthrough: 3061 | bt bp,9 3062 | jnc @@passthrough ; return normally and don't set busy bit 3063 | 3064 | ; create a new IRET frame resulting in a return to int2f_setbusy 3065 | sub es:[edi.RMCS.rSP],6 3066 | mov eax,cs:[dwSetBusyCB] 3067 | mov bx,[esi+4] ; get flags 3068 | mov [esi-6],eax 3069 | mov [esi-2],bx 3070 | jmp @@passthrough 3071 | 3072 | @@return: 3073 | lodsd ; get return address from stack 3074 | add es:[edi.RMCS.rSP],6 3075 | btr es:[edi.RMCS.rFlags],0 ; clear carry (pretend we called driver) 3076 | jmp @F 3077 | @@passthrough: 3078 | mov eax,cs:[dwOldInt2F] 3079 | @@: 3080 | mov es:[edi.RMCS.rCSIP],eax 3081 | iretd 3082 | int2f_handler endp 3083 | 3084 | int2f_setbusy proc 3085 | ; last stop on the return path from the old int 2F handler, to set the 3086 | ; busy bit in a device IOCTL request if needed 3087 | 3088 | mov fs,cs:[wRmMemSel] 3089 | movzx eax,es:[edi.RMCS.rBX] 3090 | movzx ebx,es:[edi.RMCS.rES] 3091 | shl ebx,4 3092 | add ebx,eax ; FS:EBX = device request 3093 | 3094 | bts fs:[ebx.IOCTLRW.wStatus],9 ; busy 3095 | 3096 | lodsd ; get return address from stack 3097 | add es:[edi.RMCS.rSP],6 3098 | mov es:[edi.RMCS.rCSIP],eax 3099 | iretd 3100 | int2f_setbusy endp 3101 | endif 3102 | 3103 | _TEXT ends 3104 | 3105 | ; make sure the assembler knows all the CStrs are in the right segment! 3106 | DGROUP group _TEXT, CONST 3107 | 3108 | end hda16s 3109 | -------------------------------------------------------------------------------- /DRVSRC/HDA16SD.ASM: -------------------------------------------------------------------------------- 1 | ; 16-bit Stereo HD Audio detector for HMIDET.386 2 | 3 | .386 4 | .model small 5 | include COMDECS.INC 6 | 7 | ; Debug logging cannot currently be enabled for the detector 8 | ; as it blows up the binary size beyond one page (which is all the lib provides) 9 | ?DEBUGLOG equ 0 10 | 11 | _TEXT segment use32 12 | assume ds:nothing,es:nothing,gs:nothing,fs:_TEXT 13 | 14 | .code 15 | org 0 16 | hda16sd: 17 | jmp entry 18 | 19 | align 4 20 | include COMDATA.INC 21 | 22 | ; env strings - keys 23 | kBus db "HDA_BUS",0 24 | kDevice db "HDA_DEVICE",0 25 | kFunc db "HDA_FUNCTION",0 26 | 27 | kIrq db "HDA_IRQ",0 28 | 29 | kCodec db "HDA_CODEC",0 30 | kWidget db "HDA_WIDGET",0 31 | 32 | pEnvStringKeys: 33 | dd offset kBus 34 | dd offset kDevice 35 | dd offset kFunc 36 | dd offset kIrq 37 | dd offset kCodec 38 | dd offset kWidget 39 | dd -1 40 | 41 | ; env strings - values (pointers thereto) 42 | lpEnvStringVals: 43 | lpvBus df ? 44 | lpvDevice df ? 45 | lpvFunc df ? 46 | lpvIrq df ? 47 | lpvCodec df ? 48 | lpvWidget df ? 49 | dw 3 dup (-1) 50 | 51 | ; Bit 0 = Bus specified 52 | ; Bit 1 = Device specified 53 | ; Bit 2 = Function specified 54 | ; Bit 3 = IRQ specified 55 | ; Bit 4 = Codec specified 56 | ; Bit 5 = Widget specified 57 | ; Bit 6 = XMS status checked 58 | ; Bit 7 = XMS needed (linear addresses != physical) 59 | ; Bit 8 = CORB/RIRB up and running 60 | statusword dw 1 SHL 7 61 | 62 | CHECK_XMS_NEEDED macro 63 | bts [statusword],6 64 | jc @F 65 | call check_paging 66 | jc @F 67 | btr [statusword],7 68 | @@: 69 | bt [statusword],7 70 | endm 71 | 72 | ; Detector function table 73 | ftable dd offset det_exists 74 | dd offset det_getsettings 75 | dd offset drv_capabilities ; same as the main driver 76 | dd offset det_verifysettings 77 | dd offset det_envstringinit 78 | 79 | include COMFUNCS.INC 80 | 81 | ; ------------------------------------------------------- ; 82 | ; ENUM functions from here (called from host application) ; 83 | ; ------------------------------------------------------- ; 84 | assume ds:_TEXT ; always called from within DS-set portion of entry point 85 | 86 | ; Tell the host application if the HD Audio hardware is available 87 | ; Takes no parameters 88 | ; Returns the "port" (i.e. PCI Bus/Device/Function combo) or zero if unavailable 89 | ; (Technically zero is a valid PCI B/D/F, but only for a host bridge...) 90 | det_exists proc near uses gs 91 | if ?DEBUGLOG 92 | invoke openlog, CStr("HDD_EXST.LOG"),0 93 | invoke printtolog, CStr("parsing env variables...",0Dh,0Ah) 94 | endif 95 | 96 | lgs esi,[lpvBus] 97 | test esi,esi 98 | jz @F 99 | 100 | call parseenvstring 101 | mov [pci_bus],bl 102 | bts [statusword],0 103 | 104 | @@: 105 | lgs esi,[lpvDevice] 106 | test esi,esi 107 | jz @F 108 | 109 | call parseenvstring 110 | shl bl,3 ; bits 7-3 of lower byte are device number 111 | mov [pci_dev_func],bl 112 | bts [statusword],1 113 | 114 | @@: 115 | lgs esi,[lpvFunc] 116 | test esi,esi 117 | jz @F 118 | 119 | call parseenvstring 120 | and bl,111b ; bits 2-0 of lower byte are function number 121 | mov bh,[pci_dev_func] 122 | and bh,not 111b 123 | or bl,bh 124 | mov [pci_dev_func],bl 125 | bts [statusword],2 126 | 127 | @@: 128 | lgs esi,[lpvIrq] 129 | test esi,esi 130 | jz @F 131 | 132 | call parseenvstring 133 | mov [irq],bl 134 | bts [statusword],3 135 | 136 | @@: 137 | lgs esi,[lpvCodec] 138 | test esi,esi 139 | jz @F 140 | 141 | call parseenvstring 142 | and bl,0Fh ; only a nibble 143 | mov [codec],bl 144 | bts [statusword],4 145 | 146 | @@: 147 | lgs esi,[lpvWidget] 148 | test esi,esi 149 | jz @F 150 | 151 | call parseenvstring 152 | mov [node],bl 153 | bts [statusword],5 154 | 155 | @@: 156 | if ?DEBUGLOG 157 | invoke printbinword,[statusword] 158 | invoke printtolog, CStr("b is env variable status word",0Dh,0Ah,"now filling 'port' list...",0Dh,0Ah) 159 | endif 160 | 161 | xor eax,eax ; get ready to return nothing... 162 | call fill_portlist 163 | jc @@done 164 | if ?DEBUGLOG 165 | invoke printtolog, CStr("'port' list filled",0Dh,0Ah) 166 | endif 167 | 168 | cmp PortList[0],-1 169 | je @@done 170 | if ?DEBUGLOG 171 | invoke printtolog, CStr("at least one device present!",0Dh,0Ah) 172 | endif 173 | 174 | xor ecx,ecx 175 | @@nextdev: 176 | movsx eax,PortList[ecx*2] 177 | .if eax == -1 178 | if ?DEBUGLOG 179 | invoke printtolog, CStr("no device matches env variables!",0Dh,0Ah) 180 | endif 181 | xor eax,eax 182 | jmp @@done 183 | .endif 184 | test [statusword],111b 185 | jz @@done ; Bus/Device/Function not specified, return first one 186 | inc ecx 187 | 188 | bt [statusword],0 ; Bus specified? 189 | jnc @F 190 | if ?DEBUGLOG 191 | invoke printtolog, CStr("checking Bus against env variable...",0Dh,0Ah) 192 | endif 193 | cmp ah,[pci_bus] 194 | jne @@nextdev ; wrong Bus! 195 | 196 | @@: 197 | bt [statusword],1 ; Device specified? 198 | jnc @F 199 | if ?DEBUGLOG 200 | invoke printtolog, CStr("checking Device against env variable...",0Dh,0Ah) 201 | endif 202 | mov bh,[pci_dev_func] 203 | mov bl,al 204 | and bx,not 707h 205 | cmp bh,bl 206 | jne @@nextdev ; wrong Device! 207 | 208 | @@: 209 | bt [statusword],2 ; Function specified? 210 | jnc @@done 211 | if ?DEBUGLOG 212 | invoke printtolog, CStr("checking Function against env variable...",0Dh,0Ah) 213 | endif 214 | mov bh,[pci_dev_func] 215 | mov bl,al 216 | and bx,707h 217 | cmp bh,bl 218 | jne @@nextdev ; wrong Function! 219 | 220 | @@done: 221 | .if eax 222 | mov [wPort],ax ; make sure B/D/F are fully specified 223 | or [statusword],111b 224 | 225 | ; query the hardware for the IRQ 226 | mov bx,ax 227 | mov ax,0B108h ; read configuration byte 228 | mov edi,3Ch ; interrupt line 229 | int 1Ah 230 | jc @F 231 | 232 | .if cl > 1 && cl < 10h 233 | mov [irq],cl 234 | bts [statusword],3 235 | .endif 236 | @@: 237 | movzx eax,[wPort] 238 | .endif 239 | if ?DEBUGLOG 240 | invoke closelog 241 | endif 242 | ret 243 | det_exists endp 244 | 245 | ; Get the settings for the selected HD Audio device 246 | ; Takes no parameters 247 | ; Returns AX = port, CL = DMA channel, CH = IRQ, DX = param 248 | ; YES, CL/CH are swapped! The values are passed confusingly through the SOS lib 249 | ; (just to confuse reverse engineers perhaps?) 250 | det_getsettings proc near uses es 251 | mov ax,[statusword] 252 | and ax,111b 253 | .if ax != 111b 254 | call det_exists 255 | xor eax,eax 256 | .endif 257 | if ?DEBUGLOG 258 | invoke openlog, CStr("HDD_GETS.LOG"),0 259 | .if !eax 260 | invoke printtolog, CStr("det_exists had to be called from det_getsettings!",0Dh,0Ah,"Did the host application request settings without ensuring hardware existed?",0Dh,0Ah) 261 | .endif 262 | endif 263 | 264 | @@: 265 | bts [statusword],3 266 | jc @F 267 | if ?DEBUGLOG 268 | invoke printtolog, CStr("IRQ not specified, using default...",0Dh,0Ah) 269 | endif 270 | mov ax,IRQList[0] 271 | mov [irq],al 272 | @@: 273 | 274 | bts [statusword],4 275 | jc @@codec_set 276 | if ?DEBUGLOG 277 | invoke printtolog, CStr("codec not specified, querying hardware...",0Dh,0Ah) 278 | endif 279 | 280 | call get_hdareg_ptr 281 | jc @@codec_set 282 | btr es:[edi].HDAREGS.gctl,0 283 | jnc @@in_reset 284 | if ?DEBUGLOG 285 | invoke printtolog, CStr("Resetting HDA controller...",0Dh,0Ah) 286 | endif 287 | mov ecx,10000h 288 | @@: 289 | call wait_timerch2 290 | test es:[edi].HDAREGS.gctl,1 291 | loopnz @B 292 | 293 | @@in_reset: 294 | if ?DEBUGLOG 295 | invoke printtolog, CStr("HDA controller in reset",0Dh,0Ah) 296 | endif 297 | call wait_timerch2 298 | call init_cntrlr 299 | if ?DEBUGLOG 300 | invoke printtolog, CStr("waiting for codecs to appear in STATESTS...",0Dh,0Ah) 301 | endif 302 | call wait_timerch2 ; TODO: long enough? 303 | 304 | mov ecx,0Fh ; there may be up to 15 codecs 305 | mov bx,es:[edi].HDAREGS.statests 306 | @@codec_loop: 307 | shr bx,1 308 | jnc @@next_codec 309 | if ?DEBUGLOG 310 | invoke printtolog, CStr("codec found, checking if widget is already set...",0Dh,0Ah) 311 | endif 312 | bt [statusword],5 313 | jc @@widget_set 314 | 315 | call find_output_pin 316 | jc @@next_codec 317 | mov [node],al 318 | bts [statusword],5 319 | jmp @@widget_set 320 | 321 | @@next_codec: 322 | inc [codec] 323 | loop @@codec_loop 324 | 325 | @@codec_set: 326 | cmp [codec],0Eh 327 | ja @@widget_set ; it's no good... 328 | 329 | bts [statusword],5 330 | jc @@widget_set 331 | 332 | if ?DEBUGLOG 333 | invoke printtolog, CStr("widget not specified, querying hardware...",0Dh,0Ah) 334 | endif 335 | call init_cntrlr 336 | call find_output_pin 337 | mov [node],al 338 | 339 | @@widget_set: 340 | if ?DEBUGLOG 341 | invoke printtolog, CStr("settings obtained, checking if there's any cleanup to do...",0Dh,0Ah) 342 | endif 343 | cmp [hdareg_seg],0 344 | jz @@done 345 | 346 | call get_hdareg_ptr 347 | btr [statusword],8 348 | jnc @@corbstopped 349 | 350 | and es:[edi].HDAREGS.corbctl,not 2 351 | and es:[edi].HDAREGS.rirbctl,not 2 352 | mov ecx,10000h 353 | @@: 354 | call wait_timerch2 355 | test es:[edi].HDAREGS.corbctl,2 356 | loopnz @B 357 | if ?DEBUGLOG 358 | invoke printtolog, CStr("CORB/RIRB stopped",0Dh,0Ah) 359 | endif 360 | 361 | mov eax,[dwCorbSelHdl] 362 | mov ebx,[dwCorbDpmiHdl] 363 | call free_dma_buf 364 | xor eax,eax 365 | mov [dwCorbPhys],eax 366 | mov [dwCorbSelHdl],eax 367 | .if [dwTSRbuf] 368 | mov [dwTSRbufoffset],eax ; reset the bump allocator 369 | .endif 370 | if ?DEBUGLOG 371 | invoke printtolog, CStr("CORB/RIRB buffer freed",0Dh,0Ah) 372 | endif 373 | 374 | call get_hdareg_ptr 375 | @@corbstopped: 376 | btr es:[edi].HDAREGS.gctl,0 377 | jnc @@cntrlr_done 378 | if ?DEBUGLOG 379 | invoke printtolog, CStr("Resetting HDA controller...",0Dh,0Ah) 380 | endif 381 | mov ecx,10000h 382 | @@: 383 | call wait_timerch2 384 | test es:[edi].HDAREGS.gctl,1 385 | loopnz @B 386 | 387 | @@cntrlr_done: 388 | mov ebx,es 389 | call free_selector 390 | xor eax,eax 391 | mov [hdareg_seg],ax 392 | if ?DEBUGLOG 393 | invoke printtolog, CStr("far pointer to HDA device registers freed",0Dh,0Ah) 394 | endif 395 | mov ecx,[hdareg_linaddr] 396 | mov ebx,ecx 397 | shr ebx,10h 398 | call unmap_physmem 399 | xor eax,eax 400 | mov [hdareg_linaddr],eax 401 | if ?DEBUGLOG 402 | invoke printtolog, CStr("linear address of HDA device registers unmapped",0Dh,0Ah) 403 | endif 404 | 405 | @@done: 406 | mov ax,[wPort] 407 | mov cx,[wIrqDma] 408 | xchg cl,ch ; VERY IMPORTANT! 409 | mov dx,[wParam] 410 | if ?DEBUGLOG 411 | invoke closelog 412 | endif 413 | ret 414 | det_getsettings endp 415 | 416 | ; Get the settings for the selected HD Audio device 417 | ; Takes AX = port, CL = DMA channel, CH = IRQ (yes, CL/CH are swapped...) 418 | ; Returns an error code 419 | ; Does NOT take a param, so there's no need to query the hardware... 420 | ; (Pity, because there's no way to cross-check the user specs...) 421 | det_verifysettings proc near 422 | cmp PortList[0],-1 423 | jne @F 424 | 425 | call fill_portlist 426 | 427 | @@: 428 | xor ebx,ebx 429 | @@: 430 | mov dx,PortList[ebx*2] 431 | .if dx == -1 432 | mov eax,10h ; _ERR_INVALID_PORT 433 | ret 434 | .endif 435 | cmp ax,dx 436 | je @F 437 | inc ebx 438 | jmp @B 439 | 440 | @@: 441 | movzx ax,ch 442 | xor ebx,ebx 443 | @@: 444 | mov dx,PortList[ebx*2] 445 | .if dx == -1 446 | mov eax,11h ; _ERR_INVALID_IRQ 447 | ret 448 | .endif 449 | cmp ax,dx 450 | je @F 451 | inc ebx 452 | jmp @B 453 | 454 | @@: 455 | xor eax,eax ; _ERR_NO_ERROR 456 | ret 457 | det_verifysettings endp 458 | 459 | ; Get the environment variables needed by this detector 460 | ; Takes no parameters 461 | ; Returns pointer to list of keys in ESI, and to list of value pointers in EDI 462 | det_envstringinit proc near 463 | if ?DEBUGLOG 464 | invoke openlog, CStr("HDD_ENV.LOG"),0 465 | endif 466 | 467 | mov esi,pEnvStringKeys 468 | mov edi,lpEnvStringVals 469 | 470 | if ?DEBUGLOG 471 | invoke closelog 472 | endif 473 | ret 474 | det_envstringinit endp 475 | 476 | ; ------------------------------------------------------------------------------ ; 477 | ; INTERNAL functions from here (called from within ENUM and interrupt functions) ; 478 | ; ------------------------------------------------------------------------------ ; 479 | 480 | ; Search for a pin widget of type lineout/headphone/speaker on the current codec 481 | ; If found, returns it in AL, with CF clear 482 | ; If not found, CF is set 483 | find_output_pin proc near uses ebx edi esi ebp ecx 484 | if ?DEBUGLOG 485 | invoke printtolog, CStr("widget not specified, looking for a suitable one on this codec...",0Dh,0Ah) 486 | endif 487 | 488 | bts [statusword],8 489 | jc @F 490 | call set_busmaster ; busmaster needed for CORB/RIRB DMA 491 | jc @@failed 492 | call alloc_CORB_RIRB 493 | jc @@failed 494 | call start_CORB_RIRB 495 | jc @@failed 496 | 497 | @@: 498 | xor ebx,ebx ; keep latest candidate here 499 | xor edi,edi ; and its default device type here 500 | 501 | mov [node],0 ; query root node 502 | call get_subnodes 503 | mov ebp,edx 504 | mov [node],al 505 | @@fgloop: 506 | mov ax,0F00h ; get parameter 507 | mov edx,5 ; function group type 508 | call send_cmd_wait 509 | and al,7Fh 510 | cmp al,1 ; audio function group 511 | jne @@next_fg 512 | 513 | call get_subnodes 514 | push [wParam] 515 | mov ecx,edx 516 | mov [node],al 517 | 518 | @@nodeloop: 519 | mov ax,0F00h ; get parameter 520 | mov edx,9 ; audio widget capabilities 521 | call send_cmd_wait 522 | bt eax,0 ; stereo? 523 | jnc @@next_node 524 | 525 | shr eax,20 526 | and al,0Fh 527 | cmp al,WTYPE_PIN 528 | jne @@next_node 529 | 530 | mov ax,0F1Ch ; get default configuration 531 | xor edx,edx 532 | call send_cmd_wait 533 | shr eax,12 534 | and ah,0Fh ; default device type 535 | .if ah != DEFDEV_LINEOUT && ah != DEFDEV_SPEAKER && ah != DEFDEV_HEADPHONE && ah != DEFDEV_SPDIFOUT && ah != DEFDEV_DIGOUT 536 | jmp @@next_node 537 | .endif 538 | and al,0Fh ; colour 539 | movzx esi,ah 540 | 541 | cmp ah,DEFDEV_LINEOUT 542 | jne @F ; prefer speakers / headphones / etc. 543 | 544 | test edi,edi 545 | jz @F ; first one we've found 546 | 547 | cmp esi,edi 548 | jne @@next_node ; don't prefer a line-out to something else 549 | 550 | cmp al,4 551 | jne @@next_node ; prefer "green" line-outs 552 | 553 | @@: 554 | if ?DEBUGLOG 555 | invoke printtolog, CStr("found a stereo output pin, checking if anything's plugged in...",0Dh,0Ah) 556 | endif 557 | mov ax,0F09h ; pin sense 558 | xor edx,edx 559 | call send_cmd_wait 560 | bt eax,1Fh ; presence detect 561 | jnc @@next_node 562 | 563 | if ?DEBUGLOG 564 | invoke printtolog, CStr("got a candidate!",0Dh,0Ah) 565 | endif 566 | mov bl,[node] 567 | mov edi,esi 568 | 569 | @@next_node: 570 | inc [node] 571 | dec ecx 572 | jnz @@nodeloop 573 | pop [wParam] 574 | 575 | @@next_fg: 576 | inc [node] 577 | dec ebp 578 | jnz @@fgloop 579 | 580 | test bl,bl 581 | jnz @F 582 | 583 | if ?DEBUGLOG 584 | invoke printtolog, CStr("couldn't find any output widget!",0Dh,0Ah) 585 | endif 586 | stc 587 | jmp @@failed 588 | 589 | @@: 590 | mov al,bl 591 | clc 592 | @@failed: 593 | ret 594 | find_output_pin endp 595 | 596 | ; Takes pointer to hex string in GS:ESI and returns numeric value in EBX 597 | parseenvstring proc near 598 | xor ebx,ebx 599 | @@: 600 | lodsb gs:[esi] 601 | .if al >= '0' && al <= '9' 602 | sub al,'0' 603 | .elseif al >= 'A' && al <= 'F' 604 | sub al,'A'-0Ah 605 | .elseif al >= 'a' && al <= 'f' 606 | sub al,'a'-0Ah 607 | .else 608 | jmp @F ; non-hex character - we're done 609 | .endif 610 | shl ebx,4 611 | or bl,al 612 | jmp @B 613 | 614 | @@: 615 | ret 616 | parseenvstring endp 617 | 618 | _TEXT ends 619 | 620 | if ?DEBUGLOG 621 | ; make sure the assembler knows all the CStrs are in the right segment! 622 | DGROUP group _TEXT, CONST 623 | endif 624 | 625 | end hda16sd 626 | -------------------------------------------------------------------------------- /DRVSRC/PORTLIST.INC: -------------------------------------------------------------------------------- 1 | ; Code to fill the "port" list using the PCI BIOS 2 | 3 | fill_portlist proc near ; fill in the "port" list with all PCI audio devices detected 4 | push eax 5 | push ebx 6 | push ecx 7 | push edx 8 | 9 | call check_pci_bios 10 | jc @@noPCI 11 | 12 | push esi 13 | xor esi,esi 14 | xor eax,eax 15 | .while (esi < DEVSTOENUMERATE) && (ah != 86h) 16 | mov ax,0B103h ; find PCI class code 17 | mov ecx,040300h ; class=4 (multimedia), subclass=3 (audio), no progif 18 | int 1Ah 19 | jc @F 20 | mov [PortList+esi*2],bx 21 | inc esi 22 | @@: 23 | .endw 24 | 25 | pop esi 26 | clc 27 | 28 | @@noPCI: 29 | pop edx 30 | pop ecx 31 | pop ebx 32 | pop eax 33 | ret 34 | fill_portlist endp 35 | 36 | check_pci_bios proc near ; Spoils EAX,EBX,ECX,EDX... 37 | push edi 38 | 39 | mov ax,0B101h 40 | int 1Ah 41 | test ah,ah 42 | stc 43 | jnz @F 44 | 45 | cmp edx," ICP" 46 | stc 47 | jne @F 48 | 49 | clc 50 | @@: 51 | pop edi 52 | ret 53 | check_pci_bios endp 54 | -------------------------------------------------------------------------------- /HMIAPPND.EXE: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PluMGMK/hmidrv_hdaudio/361c47ead497f5258c4cb542bd0e5fb629f81a3d/HMIAPPND.EXE -------------------------------------------------------------------------------- /RAYTOOLS/EDUGB3.BAT: -------------------------------------------------------------------------------- 1 | @echo off 2 | rem This is an example batch file for getting HD Audio working on Rayman Junior 3 | rem UK Volume 1 (EDUGB3). 4 | rem Assumptions: 5 | rem * The CD is in drive D: 6 | rem * The game is installed in C:\EDUGB3 7 | rem * HMID*.386 in C:\EDUGB3 have already been patched 8 | 9 | rem You should also set the env variables like HDA_BUS, HDA_WIDGET, etc. 10 | 11 | soundbin d:\data\sound.bin c:\edugb3\sound.bin 12 | hmiredir c:\edugb3 sound.bin 13 | d: 14 | cd d:\ 15 | install 16 | -------------------------------------------------------------------------------- /RAYTOOLS/EXAMPLE.BAT: -------------------------------------------------------------------------------- 1 | @echo off 2 | rem This is an example batch file for getting HD Audio working on Rayman Junior 3 | rem UK Volume 1 (EDUGB1). 4 | rem Assumptions: 5 | rem * The CD is in drive D: 6 | rem * The game is installed in C:\EDUGB1 7 | rem * HMID*.386 in C:\EDUGB1 have already been patched (DOSBUILD.BAT does this) 8 | 9 | rem You should also set the env variables like HDA_BUS, HDA_WIDGET, etc. 10 | 11 | soundbin d:\data\sound.bin c:\edugb1\sound.bin 12 | hmiredir c:\edugb1 sound.bin 13 | d: 14 | cd d:\ 15 | install 16 | -------------------------------------------------------------------------------- /RAYTOOLS/EXAMPLE2.BAT: -------------------------------------------------------------------------------- 1 | @echo off 2 | rem This is an example batch file for getting HD Audio working on Rayman 3 | rem Designer (RAYKIT). Much more straightforward than EDU since the utility runs 4 | rem from the hard disk! 5 | rem Assumptions: 6 | rem * The CD is in drive D: 7 | rem * The game is installed in C:\RAYKIT 8 | rem * HMID*.386 in C:\RAYKIT have already been patched (DOSBUILD.BAT does this) 9 | 10 | rem You should also set the env variables like HDA_BUS, HDA_WIDGET, etc. 11 | 12 | soundbin d:\kit\data\sound.bin c:\raykit\data\sound.bin 13 | c: 14 | cd c:\raykit 15 | setsound 16 | -------------------------------------------------------------------------------- /RAYTOOLS/SOUNDBIN.ASM: -------------------------------------------------------------------------------- 1 | ; A tool to add a descriptor for our HD Audio driver to Rayman's SOUND.BIN file, 2 | ; used by some versions of that game's installer. 3 | ; Other games may well need analogous tools! 4 | 5 | .8086 6 | .model tiny 7 | 8 | ?OVERWRITE equ 1 ; overwrite the last descriptor instead of adding one 9 | NUM_DESCRIPTORS equ 20 10 | DEVSTOENUMERATE equ 10 ; set by the Ubisoft structure 11 | XOR_KEY equ 4Dh 12 | 13 | UbiDescriptor struc 14 | wID dw ? 15 | num_ports db ? 16 | num_irqs db ? 17 | num_dmas db ? 18 | device_name db 40 dup (?) 19 | ports dw DEVSTOENUMERATE dup (?) 20 | irqs dw 15 dup (?) 21 | dmas dw 10 dup (?) 22 | wParam dw ? ; used by game installer at runtime 23 | port_idx db ? ; used by game installer at runtime 24 | irq_idx db ? ; used by game installer at runtime 25 | dma_idx db ? ; used by game installer at runtime 26 | UbiDescriptor ends 27 | 28 | .code 29 | org 100h 30 | 31 | soundbin: 32 | xor cx,cx 33 | mov cl,byte ptr ds:[80h] ; number of characters in cmdline args 34 | mov di,81h ; start of cmdline buffer 35 | mov dx,offset input_filename 36 | 37 | @@next_arg: 38 | test cx,cx 39 | jbe @@usage 40 | 41 | mov al,20h 42 | repe scasb 43 | lea si,[di-1] ; beginning of current argument 44 | repne scasb 45 | mov bx,di ; save current position 46 | mov bp,cx ; and number of chars left 47 | 48 | cmp byte ptr [di],0Dh ; at the end? 49 | je @F 50 | dec di 51 | @@: 52 | sub di,si 53 | mov cx,di ; length of current argument 54 | 55 | mov di,dx 56 | rep movsb 57 | xor al,al 58 | stosb ; null-terminate 59 | 60 | mov di,bx 61 | mov cx,bp 62 | 63 | cmp dx,offset input_filename 64 | jne @F 65 | mov dx,offset output_filename 66 | jmp @@next_arg 67 | 68 | @@: 69 | mov dx,offset openingmsg1 70 | mov ah,9 71 | int 21h 72 | mov dx,offset input_filename 73 | mov ax,3D00h ; open read-only 74 | int 21h 75 | jc @@failure 76 | 77 | mov bx,ax 78 | mov dx,offset readingmsg 79 | mov ah,9 80 | int 21h 81 | 82 | mov ah,3Fh ; read 83 | mov cx,dataend - databuf 84 | mov dx,offset databuf 85 | int 21h 86 | jc @@failure 87 | 88 | mov ah,3Eh ; close 89 | int 21h 90 | 91 | mov dx,offset decryptingmsg 92 | mov ah,9 93 | int 21h 94 | 95 | mov si,offset payload 96 | mov di,si 97 | mov cx,dataend - payload 98 | @@: 99 | lodsb 100 | sub [checksum],al 101 | xor al,XOR_KEY 102 | stosb 103 | loop @B 104 | 105 | mov bl,[checksum] 106 | test bl,bl 107 | mov dx,offset badchecksum 108 | jnz @@failure_custommsg 109 | 110 | cmp [numdrivers],NUM_DESCRIPTORS 111 | mov dx,offset toomanymsg 112 | jnb @@printandfail 113 | 114 | mov dx,offset filllistmsg 115 | mov ah,9 116 | int 21h 117 | 118 | call fill_portlist 119 | mov dx,offset nopcimsg 120 | jc @@printandfail 121 | cmp PortList[0],-1 122 | mov dx,offset nohdaudiomsg 123 | je @@printandfail 124 | 125 | mov dx,offset makedescmsg 126 | mov ah,9 127 | int 21h 128 | 129 | mov al,[numdrivers] 130 | if ?OVERWRITE 131 | else 132 | inc [numdrivers] 133 | endif 134 | mov cl,sizeof UbiDescriptor 135 | mul cl 136 | mov bx,offset descriptors 137 | add bx,ax 138 | 139 | if ?OVERWRITE 140 | mov si,bx 141 | lea di,[bx-size UbiDescriptor] 142 | mov cx,sizeof UbiDescriptor 143 | rep movsb ; copy an empty descriptor 144 | lea bx,[bx-size UbiDescriptor] 145 | endif 146 | 147 | mov [bx].UbiDescriptor.wID,0E040h 148 | 149 | ; calculate number of ports 150 | mov di,offset PortList 151 | xor dx,dx 152 | mov ax,-1 153 | @@: 154 | scasw 155 | je @F 156 | inc dx 157 | jmp @B 158 | 159 | @@: 160 | mov [bx].UbiDescriptor.num_ports,dl 161 | mov [bx].UbiDescriptor.num_irqs,NUM_IRQs 162 | mov [bx].UbiDescriptor.num_dmas,NUM_DMAs 163 | 164 | mov si,offset our_devname 165 | mov cx,sizeof our_devname 166 | lea di,[bx].UbiDescriptor.device_name 167 | rep movsb 168 | 169 | mov si,offset PortList 170 | mov cl,[bx].UbiDescriptor.num_ports 171 | lea di,[bx].UbiDescriptor.ports 172 | rep movsw 173 | 174 | mov si,offset IRQList 175 | mov cl,[bx].UbiDescriptor.num_irqs 176 | lea di,[bx].UbiDescriptor.irqs 177 | rep movsw 178 | 179 | mov si,offset DMAList 180 | mov cl,[bx].UbiDescriptor.num_dmas 181 | lea di,[bx].UbiDescriptor.dmas 182 | rep movsw 183 | 184 | mov dx,offset encryptingmsg 185 | mov ah,9 186 | int 21h 187 | 188 | mov si,offset payload 189 | mov di,si 190 | mov cx,dataend - payload 191 | @@: 192 | lodsb 193 | xor al,XOR_KEY 194 | add [checksum],al 195 | stosb 196 | loop @B 197 | 198 | mov dx,offset openingmsg2 199 | mov ah,9 200 | int 21h 201 | mov dx,offset output_filename 202 | mov ah,3Ch ; create 203 | xor cx,cx ; no special attributes 204 | int 21h 205 | jc @@failure 206 | 207 | mov bx,ax 208 | mov dx,offset writingmsg 209 | mov ah,9 210 | int 21h 211 | 212 | mov ah,40h ; write 213 | mov cx,dataend - databuf 214 | mov dx,offset databuf 215 | int 21h 216 | jc @@failure 217 | 218 | mov ah,3Eh ; close 219 | int 21h 220 | mov ax,4C00h ; exit with success 221 | int 21h 222 | 223 | @@usage: 224 | mov dx,offset usagemsg 225 | jmp @@printandfail 226 | 227 | @@failure: 228 | mov dx,offset failuremsg 229 | mov bx,ax ; save error code 230 | @@failure_custommsg: 231 | mov ah,9 232 | int 21h 233 | 234 | @@print_bl: 235 | mov dl,bl 236 | mov cl,4 237 | shr dl,cl 238 | .if dl > 9 239 | add dl,'A' - 0Ah 240 | .else 241 | add dl,'0' 242 | .endif 243 | mov ah,2 ; character output 244 | int 21h 245 | 246 | mov dl,bl 247 | and dl,0Fh 248 | .if dl > 9 249 | add dl,'A' - 0Ah 250 | .else 251 | add dl,'0' 252 | .endif 253 | mov ah,2 ; character output 254 | int 21h 255 | 256 | mov dl,'h' 257 | mov ah,2 ; character output 258 | int 21h 259 | 260 | mov dx,offset eol 261 | 262 | @@printandfail: 263 | mov ah,9 264 | int 21h 265 | mov ax,4CFFh ; exit with failure 266 | int 21h 267 | 268 | .386 ; PCI BIOS code uses 32-bit regs... 269 | include PORTLIST.INC 270 | 271 | usagemsg: 272 | db "You need to specify two arguments on the command line.",0Dh,0Ah 273 | db "The first is an input file to be patched.",0Dh,0Ah 274 | db "The second is the output file to receive the patched data." 275 | eol: 276 | db 0Dh,0Ah,"$" 277 | 278 | failuremsg: 279 | db "Syscall failed - error code: ","$" 280 | 281 | openingmsg1: 282 | db "Opening input file...",0Dh,0Ah,"$" 283 | openingmsg2: 284 | db "Creating output file...",0Dh,0Ah,"$" 285 | 286 | readingmsg: 287 | db "Reading input file...",0Dh,0Ah,"$" 288 | writingmsg: 289 | db "Writing output file...",0Dh,0Ah,"$" 290 | 291 | decryptingmsg: 292 | db "Decrypting...",0Dh,0Ah,"$" 293 | 294 | encryptingmsg: 295 | db "Re-encrypting...",0Dh,0Ah,"$" 296 | 297 | badchecksum: 298 | db "Input file has bad checksum:",0Dh,0Ah 299 | db "Should have ended up with zero, but got ","$" 300 | 301 | toomanymsg: 302 | db "Input file is full - no room to add extra driver",0Dh,0Ah,"$" 303 | 304 | filllistmsg: 305 | db "Detecting HD Audio hardware...",0Dh,0Ah,"$" 306 | 307 | makedescmsg: 308 | db "Creating new descriptor...",0Dh,0Ah,"$" 309 | 310 | nopcimsg: 311 | db "No PCI BIOS available, cannot detect HD Audio hardware!",0Dh,0Ah,"$" 312 | 313 | nohdaudiomsg: 314 | db "No HD Audio hardware detected!",0Dh,0Ah,"$" 315 | 316 | our_devname db "Intel High Definition Audio",0 317 | 318 | include CAPLISTS.INC 319 | 320 | .data? 321 | 322 | input_filename db 80h dup (?) 323 | output_filename db 80h dup (?) 324 | 325 | databuf label byte 326 | ubiheader1 db 5 dup (?) 327 | ubiheader2 db 5 dup (?) 328 | ubiheaderword dw ? 329 | checksum db ? 330 | 331 | payload label byte 332 | numdrivers db ? 333 | runtimeidx db ? ; not used here 334 | 335 | descriptors UbiDescriptor NUM_DESCRIPTORS dup () 336 | dataend label byte 337 | 338 | end soundbin 339 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HMIDRV_HDAudio 2 | This is a driver for [Intel High Definition Audio](https://en.wikipedia.org/wiki/Intel_High_Definition_Audio) controllers, for use with Human Machine Interface's (HMI's) Sound Operating System **version 3**, perhaps better known as `HMIDRV.386`. 3 | The project was inspired by [Japheth's `HDAUtils`](https://github.com/Baron-von-Riedesel/HDAutils), and the idea is to give old DOS programs / games (such as Rayman) the ability to play sound through modern hardware. 4 | So far, it has been tested with the aforementioned Rayman, on MS-DOS 6.2 running on a PC from 2014 with [this motherboard](https://us.msi.com/Motherboard/Z97-GAMING-3) (to clarify, I'm booting DOS *natively* on the system, not with QEMU or the like). 5 | 6 | The driver can interface with `HDATSR.EXE` from the Windows 3.1 HDA sound driver by Watlers World, available [here](http://turkeys4me.byethost4.com/programs/index.htm). 7 | If this TSR is not present, the driver can attempt to allocate its own buffers using XMS (or directly from the DOS extender if paging is off). 8 | That said, the TSR is the most sure-fire way to ensure the driver has memory available for all the necessary buffers. 9 | 10 | The driver can also hook MSCDEX to mix CD Audio into the stream, since modern optical drives tend not to have built-in analogue music players with direct connections to the sound card anymore. 11 | 12 | At this point, the performance of the detector and driver seems satisfactory on *Rayman Designer*, which can detect arbitrary sound cards on startup using `HMIDET.386`. 13 | When both `HMIDRV.386` and `HMIDET.386` are patched, a fresh install of Rayman Designer can detect HD Audio hardware, and after closing and reopening, play sound and CD Audio through it. 14 | Further patching is required for *Rayman Junior*, which needs changes to `SOUND.BIN` (using the `SOUNDBIN.ASM` utility in the `RAYTOOLS` folder), and has an installer that only runs from CD, necessitating the use of the `HMIREDIR` tool (see `RAYTOOLS/EXAMPLE.BAT` for how I did it). 15 | Other games will likely have quirks of their own! 16 | 17 | ## Limitations 18 | * Looping sounds unfortunately skip when played through this driver, even though non-looping sounds are fine. 19 | * I suspect this is a feature of all "pseudo-DMA" SOS3 drivers - at some point, I'll check out the Gravis UltraSound driver under Dosbox and see if it has the same problem. 20 | * Only tested with `RATIONAL`-type (i.e. DOS/4GW-like) DOS extenders, not `FLASHTECK` [*[sic]*](https://github.com/Wohlstand/SOSPLAY/blob/master/sos3/include/sos.h#L574) (i.e. FlashTek X-32VM and similar). 21 | * CD Audio won't work under FlashTek unless there is a DPMI host running behind it. 22 | * Currently only supports 16-bit stereo playback 23 | * CD Audio requires your drive to support raw reading 24 | * Cannot run under Windows 3.1/9x with the aforementioned HDA sound driver running - they will conflict 25 | * Detector doesn't seem to be useful in combination with certain game installers which contain a fixed list of known sound cards (so they will basically ignore this new unknown one even if it's detected) 26 | * Most versions of Rayman have an external file that can be modified to include references to this driver. If other games do this, then each game will need some work to get it working with this driver (i.e. it's unfortunately not plug and play). 27 | 28 | ## Build / Usage instructions 29 | Example build scripts are included for DOS (`DOSBUILD.BAT`) and Linux (`unixbld.sh`), but it could also be built on Windows without much additional effort (but at any rate, the driver itself is only useful on DOS!). 30 | 31 | Essentially, the steps are: 32 | * If building yourself: 33 | * Assemble the driver, `drvsrc/hda16s.asm` (with the `-D?FLASHTEK` option if targeting that DOS extender), as a binary (`.BIN`) file, using [JWASM](https://www.japheth.de/JWasm.html) or similar. 34 | * Likewise assemble the detector, `drvsrc/HDA16SD.ASM`. 35 | * You can also build `HMIAPPND` if you have a C compiler handy (example build scripts are `BOOTSTRP.BAT` for DOS, using Open Watcom, and `BOOTSTRP.SH` for Linux, using GCC), but this isn't necessary as a DOS EXE of this patcher is included in the source repo. 36 | * If not building yourself, you can grab a zip from the releases page, and extract the `BIN` files and `HMIAPPND.EXE`, but you'll still need to patch your games `HMI*.386` files yourself. 37 | * Use `HMIAPPND` to patch your game's `HMIDRV.386` and `HMIDET.386` files (syntax for this can be seen in the build scripts). 38 | * To assist the detector in finding your hardware, it's advisable to set the environment variables `HDA_BUS`, `HDA_DEVICE`, `HDA_FUNCTION`, `HDA_CODEC` and `HDA_WIDGET`. [Japheth's `HDAUtils`](https://github.com/Baron-von-Riedesel/HDAutils) can help you enumerate your hardware and find the appropriate values for these. They should all be specified in hex **without** leading `0x` or anything like that. 39 | * If you want CD Audio to play through the driver, make sure `SMARTDRV` isn't running on your system. 40 | * The driver will not play CD Audio if it detects `SMARTDRV`. If it did, it would lead to buffer underflow and hence unpleasant crackling. 41 | * If your game has an installer that detects the sound card which must be run from a CD (i.e. you can't directly modify its copy of `HMIDET.386`), you'll need to run `HMIREDIR` with the path to the directory containing your patched `HMIDET.386`. `HMIREDIR` should be assembled as a `.BIN` file and then renamed to `HMIREDIR.COM` to run it as a TSR. 42 | * There may be other complications with game installers, like custom file formats containing a list of sound cards (instead of just reading them from SOS itself). The `RAYTOOLS` folder contains source for another program to deal with one such binary format, and an `EXAMPLE.BAT` file showing how to deal with it for one particular *Rayman* game. Other Rayman iterations have text formats, and still others have built-in lists in the installer `EXE` itself. 43 | 44 | ## Wishlist (for future versions / projects) 45 | * Use EMS if available for CD Audio buffers 46 | * Right now, any game that hogs Conventional Memory can't play CD Audio through this driver at all. 47 | * Rayman Junior is an interesting case, in that I've found when `EMM386.EXE` is installed, I can play CD Audio with an 8-sector buffer, but this is simply impossible without an EMM. 48 | * More tools for Rayman and for other games (possibly less invasive, command-line-based tools) 49 | * Actually test the Flashtek version 50 | * 8-bit / mono versions, for games / hardware that need those 51 | * Similar drivers for other abstraction layers, e.g. 52 | * SOS 4 (as opposed to SOS 3, which this version supports) 53 | * [MSS / AIL](https://en.wikipedia.org/wiki/Miles_Sound_System) 54 | * Support for pre-emphasis in CD Audio 55 | -------------------------------------------------------------------------------- /TOOLSRC/HMIAPPND.C: -------------------------------------------------------------------------------- 1 | /* HMIAPPND.C 2 | Append new driver(s) to the end of a HMI driver blob 3 | Written in C since this is really the only sane way to get something running 4 | on both Unix and DOS, at least at the moment... 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #ifdef __unix__ 12 | /* TODO: Find a better define for this?? */ 13 | #define stricmp strcasecmp 14 | #define strnicmp strncasecmp 15 | #include 16 | #endif 17 | 18 | #define INFILEARG "oldfile=" 19 | #define DRVFILEARG "drvfile=" 20 | #define OUTFILEARG "newfile=" 21 | 22 | #define DEVARG "drv" 23 | #define RATIONALCHAR 'r' 24 | #define FLASHTEKCHAR 'f' 25 | #define BOTHEXTCHAR 'b' 26 | 27 | #define MINDEVID 0xE000 28 | #define MAXDEVID 0xE200 29 | 30 | #define EXT_FLASHTEK 0x4000 31 | #define EXT_RATIONAL 0x8000 32 | 33 | #define COPYBUF_SIZE 0x1000 /* One page... */ 34 | char copy_buffer[COPYBUF_SIZE]; /* Too big for stack! */ 35 | 36 | int main(int argc, char **argv, const char **envp) { 37 | char *infile = NULL, *outfile = NULL; 38 | uint32_t wDeviceID = 0, wExtenderType = EXT_RATIONAL; 39 | 40 | /* Declarations for later... */ 41 | char szName[32], drvfile[32], drvtype; 42 | long copy_filesize; 43 | uint32_t hdrstart, wDrivers, lNextDriver, wSize; 44 | FILE *inhdl, *outhdl, *drvhdl; 45 | 46 | int drvstoappend = 0; 47 | int i; 48 | for(i = 0; i < argc; i++) { 49 | if(!strnicmp(argv[i], INFILEARG, strlen(INFILEARG))) 50 | infile = argv[i] + strlen(INFILEARG); 51 | else if(!strnicmp(argv[i], OUTFILEARG, strlen(OUTFILEARG))) 52 | outfile = argv[i] + strlen(OUTFILEARG); 53 | else if(sscanf(argv[i], DEVARG"%c:%4x=%31s", 54 | &drvtype, &wDeviceID, drvfile) == 3 55 | && (drvtype == RATIONALCHAR || 56 | drvtype == FLASHTEKCHAR || 57 | drvtype == BOTHEXTCHAR) 58 | && wDeviceID>=MINDEVID && wDeviceID<=MAXDEVID) 59 | drvstoappend++; 60 | } 61 | 62 | if(infile == NULL || outfile == NULL || !drvstoappend) { 63 | printf("Usage:\t%s" 64 | " \\\n\t%s" 65 | " \\\n\t%s" 66 | " \\\n\t%s[%c|%c|%c]:=" 67 | "\nYou can specify as many device files as needed." 68 | "\nThe %dth character of each argument specifies the DOS extender type:" 69 | "\n\tRational, FlashTek, or both." 70 | "\nValid Device IDs are from %4X to %4X.\n", 71 | argv[0], 72 | INFILEARG, OUTFILEARG, 73 | DEVARG, RATIONALCHAR, FLASHTEKCHAR, BOTHEXTCHAR, 74 | strlen(DEVARG) + 1, 75 | MINDEVID, MAXDEVID); 76 | return 1; 77 | } 78 | 79 | inhdl = fopen(infile, "rb"); 80 | if(!inhdl) { 81 | perror("Coudn't open input file"); 82 | return -1; 83 | } 84 | 85 | outhdl = fopen(outfile, "wb"); 86 | if(!outhdl) { 87 | perror("Coudn't open output file"); 88 | return -1; 89 | } 90 | 91 | /* Copy over the "szName" field of the file header */ 92 | fread(szName, sizeof(char), 32, inhdl); 93 | fwrite(szName, sizeof(char), 32, outhdl); 94 | 95 | /* Number of drivers in the file */ 96 | fread(&wDrivers, sizeof(uint32_t), 1, inhdl); 97 | /* wDrivers += drvstoappend; */ 98 | fwrite(&wDrivers, sizeof(uint32_t), 1, outhdl); 99 | 100 | printf("szName and wDrivers written...\n"); 101 | 102 | /* Figure out how much stuff to copy to the new file */ 103 | fseek(inhdl, 0, SEEK_END); 104 | copy_filesize = ftell(inhdl); 105 | fseek(inhdl, ftell(outhdl), SEEK_SET); /* Back to where we were... */ 106 | 107 | /* Copy it */ 108 | while(ftell(inhdl) < copy_filesize) 109 | fwrite(copy_buffer, sizeof(char), 110 | fread(copy_buffer, sizeof(char), COPYBUF_SIZE, inhdl), 111 | outhdl); 112 | printf("Old HMIDRV file copied to new one\n"); 113 | 114 | for(i = 0; i < argc && drvstoappend; i++) { 115 | if(sscanf(argv[i], DEVARG"%c:%4x=%31s", 116 | &drvtype, &wDeviceID, drvfile) < 3) 117 | continue; 118 | 119 | switch(drvtype) { 120 | case RATIONALCHAR: 121 | wExtenderType = EXT_RATIONAL; 122 | break; 123 | case FLASHTEKCHAR: 124 | wExtenderType = EXT_FLASHTEK; 125 | break; 126 | case BOTHEXTCHAR: 127 | wExtenderType = EXT_RATIONAL | EXT_FLASHTEK; 128 | break; 129 | default: 130 | continue; 131 | } 132 | 133 | if(wDeviceID < MINDEVID || wDeviceID > MAXDEVID) 134 | continue; 135 | 136 | printf("\nProcessing argument '%s'\n", argv[i]); 137 | 138 | drvhdl = fopen(drvfile, "rb"); 139 | if(!drvhdl) { 140 | perror("Couldn't open driver file"); 141 | continue; 142 | } 143 | 144 | /* Construct the new driver header */ 145 | /* Where is the current header starting? */ 146 | hdrstart = ftell(outhdl); 147 | /* Size of the new driver */ 148 | fseek(drvhdl, 0, SEEK_END); 149 | wSize = ftell(drvhdl); 150 | fseek(drvhdl, 0, SEEK_SET); 151 | /* End of the driver in the file 152 | (header size is 32 + 4*4 = 48 bytes) */ 153 | lNextDriver = hdrstart + wSize + 48; 154 | 155 | memset(szName, 0, 32); /* Make sure it's null-padded */ 156 | strncpy(szName, drvfile, 32); 157 | fwrite(szName, sizeof(char), 32, outhdl); 158 | fwrite(&lNextDriver, sizeof(uint32_t), 1, outhdl); 159 | fwrite(&wSize, sizeof(uint32_t), 1, outhdl); 160 | fwrite(&wDeviceID, sizeof(uint32_t), 1, outhdl); 161 | fwrite(&wExtenderType, sizeof(uint32_t), 1, outhdl); 162 | printf("New driver header written\n"); 163 | 164 | /* Copy it */ 165 | while(ftell(drvhdl) < wSize) 166 | fwrite(copy_buffer, sizeof(char), 167 | fread(copy_buffer, sizeof(char), COPYBUF_SIZE, drvhdl), 168 | outhdl); 169 | printf("Data copied!\n"); 170 | 171 | fclose(drvhdl); 172 | 173 | wDrivers++; 174 | drvstoappend--; 175 | } 176 | 177 | /* Write the updated wDrivers into the output file header */ 178 | fseek(outhdl, 32, SEEK_SET); 179 | fwrite(&wDrivers, sizeof(uint32_t), 1, outhdl); 180 | 181 | printf("\nAll done!\n"); 182 | return 0; 183 | } 184 | 185 | -------------------------------------------------------------------------------- /TOOLSRC/HMIREDIR.ASM: -------------------------------------------------------------------------------- 1 | ; Some game installers refuse to run from the hard drive, so we need to redirect 2 | ; file opens of HMID{ET,RV}.386 to a known folder with our patched version. 3 | ; This is a little TSR to do just that. 4 | .8086 5 | .model tiny 6 | 7 | fn_record struc 8 | len db ? 9 | fn db 0Ch dup (?) ; 8.3 is at most 0Ch = 12 characters! 10 | fn_record ends 11 | 12 | .code 13 | org 100h 14 | 15 | hmiredir: 16 | jmp entrypoint 17 | 18 | handler proc 19 | cmp ax,3D00h ; open read-only 20 | je @F 21 | jmp cs:[old_int21] 22 | 23 | @@: 24 | push es 25 | push di 26 | push ax 27 | push cx 28 | push si 29 | push bx 30 | 31 | mov di,ds 32 | mov es,di 33 | mov di,dx 34 | 35 | xor cx,cx 36 | dec cx 37 | xor al,al 38 | cld 39 | repne scasb 40 | 41 | mov bx,offset fn_records 42 | mov cx,cs 43 | mov es,cx 44 | lea si,[di-2] 45 | @@fn_loop: 46 | push si 47 | mov cl,cs:[bx].fn_record.len 48 | xor ch,ch 49 | lea di,[bx].fn_record.fn - 1 50 | add di,cx 51 | std 52 | @@: 53 | lodsb 54 | or al,20h ; make sure it's lowercase 55 | scasb 56 | loope @B 57 | pop si 58 | jne @F 59 | 60 | mov di,cs:[directory_end] 61 | lea si,[bx].fn_record.fn 62 | mov cl,cs:[bx].fn_record.len 63 | xor ch,ch 64 | cld 65 | rep movsb es:[di],cs:[si] 66 | xor al,al 67 | stosb 68 | jmp @@redirect 69 | 70 | @@: 71 | lea bx,[bx+size fn_record] 72 | cmp bx,offset end_fn_records 73 | jnb @@passthrough 74 | cmp cs:[bx].fn_record.len,0 75 | je @@passthrough 76 | jmp @@fn_loop 77 | 78 | @@redirect: 79 | pop bx 80 | pop si 81 | pop cx 82 | pop ax 83 | pop di 84 | pop es 85 | 86 | push ds 87 | push dx 88 | mov dx,cs 89 | mov ds,dx 90 | mov dx,offset directory 91 | pushf 92 | call cs:[old_int21] 93 | pop dx 94 | pop ds 95 | 96 | ; propagate up the carry flag 97 | push bp 98 | mov bp,sp 99 | jc @F 100 | and byte ptr [bp+6],not 1 101 | pop bp 102 | iret 103 | 104 | @@: 105 | or byte ptr [bp+6],1 106 | pop bp 107 | iret 108 | 109 | @@passthrough: 110 | pop bx 111 | pop si 112 | pop cx 113 | pop ax 114 | pop di 115 | pop es 116 | 117 | jmp cs:[old_int21] 118 | handler endp 119 | 120 | fn_records: 121 | drv_fnrec fn_record <0Ah,"hmidrv.386"> 122 | det_fnrec fn_record <0Ah,"hmidet.386"> 123 | more_fnrecs fn_record 8 dup () 124 | end_fn_records: 125 | 126 | align 2 127 | old_int21 label dword 128 | old_int21_off dw ? 129 | old_int21_seg dw ? 130 | directory_end dw ? 131 | dirlen db ? 132 | directory db 80h dup (?) 133 | 134 | align 2 135 | entrypoint: 136 | xor cx,cx 137 | mov cl,byte ptr ds:[80h] ; number of characters in cmdline args 138 | mov di,81h 139 | mov bx,offset dirlen 140 | 141 | @@next_arg: 142 | test cx,cx 143 | jbe @@args_done 144 | 145 | mov al,20h 146 | repe scasb 147 | lea si,[di-1] ; beginning of current argument 148 | repne scasb 149 | mov dx,di ; save current position 150 | mov bp,cx ; and number of chars left 151 | 152 | cmp byte ptr [di],0Dh ; at the end? 153 | je @F 154 | dec di 155 | @@: 156 | sub di,si 157 | mov cx,di ; length of current argument 158 | .if bx != offset dirlen 159 | cmp cx,0Ch 160 | ja @@skip_arg ; skip if too long to be a filename... 161 | .endif 162 | 163 | mov [bx].fn_record.len,cl 164 | lea di,[bx].fn_record.fn 165 | @@: 166 | lodsb 167 | .if al >= 'A' && al <= 'Z' 168 | or al,20h ; make sure it's lowercase 169 | .endif 170 | stosb 171 | loop @B 172 | .if bx == offset dirlen 173 | xor al,al 174 | stosb ; null-terminate 175 | .endif 176 | 177 | @@skip_arg: 178 | mov di,dx 179 | mov cx,bp 180 | 181 | cmp bx,offset dirlen 182 | jne @F 183 | mov bx,offset more_fnrecs 184 | jmp @@next_arg 185 | 186 | @@: 187 | cmp [bx].fn_record.len,0 188 | jz @@next_arg 189 | lea bx,[bx+size fn_record] 190 | cmp bx,end_fn_records 191 | jb @@next_arg 192 | 193 | @@args_done: 194 | cmp [dirlen],0 195 | jz @@usage 196 | 197 | cmp [directory+1],':' 198 | jne @@usage 199 | 200 | mov dx,offset directory 201 | mov ah,3Bh ; CHDIR (make sure it's a valid dirspec) 202 | int 21h 203 | jc @@usage 204 | 205 | mov di,dx 206 | mov cl,[dirlen] 207 | xor ch,ch 208 | add di,cx 209 | mov al,'\' 210 | sub di,1 211 | scasb 212 | je @F 213 | stosb ; add trailing slash if needed 214 | @@: 215 | mov [directory_end],di 216 | 217 | mov ax,3521h ; get int 21h vector 218 | int 21h 219 | mov [old_int21_off],bx 220 | mov [old_int21_seg],es 221 | 222 | mov ax,2521h ; set int 21h vector 223 | mov dx,offset handler 224 | int 21h 225 | 226 | mov es,word ptr ds:[2Ch] ; env segment 227 | mov ah,49h ; free memory 228 | int 21h 229 | 230 | mov ax,3100h ; TSR with success code 231 | mov dx,offset entrypoint 232 | add dx,0Fh 233 | mov cx,4 234 | shr dx,cl 235 | int 21h 236 | 237 | @@usage: 238 | mov dx,offset usagemsg 239 | mov ah,9 240 | int 21h 241 | mov ax,4CFFh 242 | int 21h 243 | 244 | usagemsg: 245 | db "You need to specify an absolute directory path on the command line." 246 | db 0Dh,0Ah,"(No trailing slash unless it's the root of a drive!)" 247 | db 0Dh,0Ah,"You may also specify up to eight additional files to redirect." 248 | db 0Dh,0Ah,"(Above and beyond the usual HMID*.386 files)" 249 | db 0Dh,0Ah,"$" 250 | 251 | end hmiredir 252 | -------------------------------------------------------------------------------- /unixbld.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | uasm -bin DRVSRC/hda16s.asm 4 | mv -v hd{a,w}16s.BIN 5 | uasm -bin -D?FLASHTEK=1 DRVSRC/hda16s.asm 6 | ./hmiappnd oldfile=$HOME/Games/rayman-forever/Rayman/HMIDRV.OLD newfile=$HOME/Games/rayman-forever/Rayman/HMIDRV.386 drvf:E040=hda16s.BIN drvr:E040=hdw16s.BIN 7 | 8 | uasm -bin DRVSRC/HDA16SD.ASM 9 | mv -v HD{A,W}16SD.BIN 10 | uasm -bin -D?FLASHTEK=1 DRVSRC/HDA16SD.ASM 11 | ./hmiappnd oldfile=$HOME/Games/rayman-forever/Rayman/HMIDET.OLD newfile=$HOME/Games/rayman-forever/Rayman/HMIDET.386 drvf:E040=HDA16SD.BIN drvr:E040=HDW16SD.BIN 12 | 13 | uasm -bin TOOLSRC/HMIREDIR.ASM 14 | mv -v HMIREDIR.{BIN,COM} 15 | 16 | uasm -IDRVSRC -bin RAYTOOLS/SOUNDBIN.ASM 17 | mv -v SOUNDBIN.{BIN,COM} 18 | --------------------------------------------------------------------------------