; ****************************************************************************
; TRDOS386.ASM (TRDOS 386 Kernel) - v2.0.10 - audio.s
; ----------------------------------------------------------------------------
; Last Update: 28/01/2025  (Previous: 06/06/2024 - Kernel v2.0.9)
; ----------------------------------------------------------------------------
; Beginning: 03/04/2017
; ----------------------------------------------------------------------------
; Assembler: NASM version 2.15 (trdos386.s)
; ****************************************************************************

; AUDIO CONTROLLER & CODEC DEFINITIONS & CODE FOR TRDOS 386

;=============================================================================
;               EQUATES
;=============================================================================

; PCI EQUATES

BIT0  EQU 1
BIT1  EQU 2
BIT2  EQU 4
BIT3  EQU 8
BIT4  EQU 10h
BIT5  EQU 20h
BIT6  EQU 40h
BIT7  EQU 80h
BIT8  EQU 100h
BIT9  EQU 200h
BIT10 EQU 400h
BIT11 EQU 800h
BIT12 EQU 1000h
BIT13 EQU 2000h
BIT14 EQU 4000h
BIT15 EQU 8000h
BIT16 EQU 10000h
BIT17 EQU 20000h
BIT18 EQU 40000h
BIT19 EQU 80000h
BIT20 EQU 100000h
BIT21 EQU 200000h
BIT22 EQU 400000h
BIT23 EQU 800000h
BIT24 EQU 1000000h
BIT25 EQU 2000000h
BIT26 EQU 4000000h
BIT27 EQU 8000000h
BIT28 EQU 10000000h
BIT29 EQU 20000000h
BIT30 EQU 40000000h
BIT31 EQU 80000000h
NOT_BIT31 EQU 7FFFFFFFh

; PCI equates
; PCI function address (PFA)
; bit 31 = 1
; bit 23:16 = bus number     (0-255)
; bit 15:11 = device number  (0-31)
; bit 10:8 = function number (0-7)
; bit 7:0 = register number  (0-255)

IO_ADDR_MASK    EQU     0FFFEh	; mask off bit 0 for reading BARs
PCI_INDEX_PORT  EQU     0CF8h
PCI_DATA_PORT   EQU     0CFCh
PCI32           EQU     BIT31	; bitflag to signal 32bit access
PCI16           EQU     BIT30	; bitflag for 16bit access
NOT_PCI32_PCI16	EQU	03FFFFFFFh ; NOT BIT31+BIT30 ; 19/03/2017

PCI_FN0         EQU     0 << 8
PCI_FN1         EQU     1 << 8
PCI_FN2         EQU     2 << 8
PCI_FN3         EQU     3 << 8
PCI_FN4         EQU     4 << 8
PCI_FN5         EQU     5 << 8
PCI_FN6         EQU     6 << 8
PCI_FN7         EQU     7 << 8

PCI_CMD_REG	EQU	04h	; reg 04, command reg
IO_ENA		EQU	BIT0	; i/o decode enable
MEM_ENA		EQU	BIT1	; memory decode enable
BM_ENA		EQU     BIT2	; bus master enable

; VIA VT8233 EQUATES

VIA_VID		equ 1106h	; VIA's PCI vendor ID
VT8233_DID      equ 3059h	; VT8233 (VT8235) device ID
		
PCI_IO_BASE          equ 10h
AC97_INT_LINE        equ 3Ch
VIA_ACLINK_CTRL      equ 41h
VIA_ACLINK_STAT      equ 40h
VIA_ACLINK_C00_READY equ 01h ; primary codec ready
	
VIA_REG_AC97	     equ 80h ; dword

VIA_ACLINK_CTRL_ENABLE	equ   80h ; 0: disable, 1: enable
VIA_ACLINK_CTRL_RESET	equ   40h ; 0: assert, 1: de-assert
VIA_ACLINK_CTRL_SYNC	equ   20h ; 0: release SYNC, 1: force SYNC hi
VIA_ACLINK_CTRL_VRA	equ   08h ; 0: disable VRA, 1: enable VRA
VIA_ACLINK_CTRL_PCM	equ   04h ; 0: disable PCM, 1: enable PCM
					; 3D Audio Channel slots 3/4
VIA_ACLINK_CTRL_INIT	equ  (VIA_ACLINK_CTRL_ENABLE + \
                              VIA_ACLINK_CTRL_RESET + \
                              VIA_ACLINK_CTRL_PCM + \
                              VIA_ACLINK_CTRL_VRA)

CODEC_AUX_VOL		equ   04h
VIA_REG_AC97_BUSY	equ   01000000h ;(1<<24)
VIA_REG_AC97_CMD_SHIFT	equ   10h ; 16
VIA_REG_AC97_PRIMARY_VALID equ 02000000h ;(1<<25)
VIA_REG_AC97_READ	equ   00800000h ;(1<<23)
VIA_REG_AC97_CODEC_ID_SHIFT   equ  1Eh ; 30
VIA_REG_AC97_CODEC_ID_PRIMARY equ  0
VIA_REG_AC97_DATA_SHIFT equ   0
VIADEV_PLAYBACK         equ   0
VIA_REG_OFFSET_STATUS   equ   0    ;; byte - channel status
VIA_REG_OFFSET_CONTROL  equ   01h  ;; byte - channel control
VIA_REG_CTRL_START	equ   80h  ;; WO
VIA_REG_CTRL_TERMINATE  equ   40h  ;; WO
VIA_REG_CTRL_PAUSE      equ   08h  ;; RW
VIA_REG_CTRL_RESET      equ   01h  ;; RW - probably reset? undocumented
VIA_REG_OFFSET_STOP_IDX equ   08h  ;; dword - stop index, channel type, sample rate
VIA8233_REG_TYPE_16BIT  equ   200000h ;; RW
VIA8233_REG_TYPE_STEREO equ   100000h ;; RW
VIA_REG_OFFSET_CURR_INDEX equ 0Fh ;; byte - channel current index (for via8233 only)
VIA_REG_OFFSET_TABLE_PTR equ  04h  ;; dword - channel table pointer
VIA_REG_OFFSET_CURR_PTR equ   04h  ;; dword - channel current pointer
VIA_REG_OFS_PLAYBACK_VOLUME_L equ  02h ;; byte
VIA_REG_OFS_PLAYBACK_VOLUME_R equ  03h ;; byte
VIA_REG_CTRL_AUTOSTART	equ   20h
VIA_REG_CTRL_INT_EOL	equ   02h
VIA_REG_CTRL_INT_FLAG	equ   01h
VIA_REG_CTRL_INT	equ  (VIA_REG_CTRL_INT_FLAG + \
                              VIA_REG_CTRL_INT_EOL + \
                              VIA_REG_CTRL_AUTOSTART)

VIA_REG_STAT_STOP_IDX	equ   10h    ;; RO ; 27/07/2020
				     ; current index = stop index
VIA_REG_STAT_STOPPED	equ   04h    ;; RWC
VIA_REG_STAT_EOL	equ   02h    ;; RWC
VIA_REG_STAT_FLAG	equ   01h    ;; RWC
VIA_REG_STAT_ACTIVE	equ   80h    ;; RO
; 28/11/2016
VIA_REG_STAT_LAST	equ   40h    ;; RO
VIA_REG_STAT_TRIGGER_QUEUED equ 08h  ;; RO
VIA_REG_CTRL_INT_STOP	equ   04h  ; Interrupt on Current Index = Stop Index
		   		   ; and End of Block

VIA_REG_OFFSET_CURR_COUNT equ 0Ch ;; dword - channel current count, index

PORTB		EQU	061h
REFRESH_STATUS	EQU	010h	; Refresh signal status

; AC97 Codec registers.

; 22/07/2020
; REALTEK ALC655 and ADI SOUNDMAX AD1980 CODEC MIXER REGISTERS

; each codec/mixer register is 16bits

CODEC_RESET_REG                 equ     00h	; reset codec
CODEC_MASTER_VOL_REG            equ     02h	; master volume
CODEC_HP_VOL_REG                equ     04h	; headphone volume ; AD1980
CODEC_MASTER_MONO_VOL_REG       equ     06h	; master mono volume (mono-out)
;CODEC_MASTER_TONE_REG          equ     08h	; master tone (R+L) ; (not used)
CODEC_PCBEEP_VOL_REG            equ     0Ah	; PC beep volume ; ALC655
CODEC_PHONE_VOL_REG             equ     0Ch	; phone volume
CODEC_MIC_VOL_REG               equ     0Eh	; mic volume
CODEC_LINE_IN_VOL_REG           equ     10h	; line in volume
CODEC_CD_VOL_REG                equ     12h	; CD volume
;CODEC_VID_VOL_REG              equ     14h	; video volume ; (not used)
CODEC_AUX_VOL_REG               equ     16h	; aux volume
CODEC_PCM_OUT_REG               equ     18h	; PCM out volume
CODEC_RECORD_SELECT_REG         equ     1Ah	; record select
CODEC_RECORD_VOL_REG            equ     1Ch	; record volume (record gain)
;CODEC_RECORD_MIC_VOL_REG       equ     1Eh	; record mic volume ; (not used)
CODEC_GP_REG                    equ     20h	; general purpose
;CODEC_3D_CONTROL_REG           equ     22h	; 3D control
;;CODEC_AUDIO_INT_PAGING_REG    equ	24h	; audio int & paging ; (not used)
CODEC_POWER_CTRL_REG            equ     26h	; power down control
CODEC_EXT_AUDIO_REG             equ     28h	; extended audio ID
CODEC_EXT_AUDIO_CTRL_REG        equ     2Ah	; extended audio status/control
CODEC_PCM_FRONT_DACRATE_REG     equ     2Ch	; PCM front sample rate
CODEC_PCM_SURND_DACRATE_REG     equ     2Eh	; PCM surround sample rate
CODEC_PCM_LFE_DACRATE_REG       equ     30h	; PCM Center/LFE sample rate
CODEC_LR_ADCRATE_REG            equ     32h	; PCM input sample rate
CODEC_MIC_ADCRATE_REG           equ     34h	; mic in sample rate  ; AD1980
CODEC_PCM_LFE_VOL_REG           equ     36h	; PCM Center/LFE volume
CODEC_PCM_SURND_VOL_REG         equ     38h	; PCM surround volume
;CODEC_SPDIF_CTRL_REG           equ     3Ah	; S/PDIF control
; 22/07/2020
CODEC_MISC_CRTL_BITS_REG	equ	76h	; misc control bits ; AD1980
;	
CODEC_VENDOR_ID1		equ	7Ch	; REALTEK: 414Ch, ADI: 4144h
CODEC_VENDOR_ID2		equ	7Eh	; REALTEK: 4760h, ADI: 5370h

; VT8233 SGD bits (21/04/2017)
FLAG	EQU BIT30
EOL	EQU BIT31

; INTEL ICH EQUATES
; 28/05/2017
INTEL_VID	equ	8086h	; Intel's PCI vendor ID
; 20/11/2023
; (playwav2.com, ac97.inc, Erdogan Tan, 11/11/2023)
; 03/11/2023 - Erdogan Tan (Ref: MenuetOS AC97 WAV Player source code, 2004)
SIS_VID		equ	1039h
NVIDIA_VID	equ	10DEh	 ; Ref: MPXPLAY/SBEMU/KOLIBRIOS AC97 source c.
AMD_VID		equ	1022h
;
ICH_DID		equ	2415h	; ICH (82801AA) device ID
; 20/11/2023
; (playwav2.com, ac97.inc, Erdogan Tan, 11/11/2023)
; 17/02/2017 (Erdogan Tan, ref: ALSA Device IDs, ALSA project)
ICH0_DID        equ     2425h	; ICH0
ICH2_DID        equ     2445h 	; ICH2
ICH3_DID	equ     2485h	; ICH3
ICH4_DID        equ     24C5h	; ICH4
ICH5_DID	equ     24D5h	; ICH5
ICH6_DID	equ     266Eh	; ICH6
ESB6300_DID	equ     25A6h	; 6300ESB
ESB631X_DID	equ     2698h	; 631XESB
ICH7_DID	equ	27DEh	; ICH7
; 03/11/2023 - Erdogan Tan (Ref: MenuetOS AC97 WAV Player source code, 2004)
MX82440_DID	equ	7195h
SI7012_DID	equ	7012h
NFORCE_DID	equ	01B1h
NFORCE2_DID	equ	006Ah
AMD8111_DID	equ	746Dh
AMD768_DID	equ	7445h
; 03/11/2023 - Erdogan Tan - Ref: MPXPLAY/SBEMU/KOLIBRIOS AC97 source code
CK804_DID	equ	0059h	; NFORCE4
MCP04_DID	equ	003Ah
CK8_DID		equ	008Ah
NFORCE3_DID	equ	00DAh
CK8S_DID	equ	00EAh

NAMBAR_REG      equ	10h	; native audio mixer Base Address Register
NABMBAR_REG     equ	14h	; native audio bus mastering Base Addr Reg

PI_CR_REG       equ     0Bh     ; PCM in Control Register
PO_CR_REG	equ     1Bh     ; PCM out Control Register
MC_CR_REG	equ     2Bh     ; MIC in Control Register

PI_SR_REG	equ     6       ; PCM in Status register
PO_SR_REG	equ     16h     ; PCM out Status register
MC_SR_REG	equ     26h     ; MIC in Status register

IOCE 		equ     BIT4    ; interrupt on complete enable.
FEIFE		equ     BIT3    ; set if you want an interrupt to fire
LVBIE		equ     BIT2    ; last valid buffer interrupt enable.
RR 		equ     BIT1    ; reset registers. Nukes all regs
                                ; except bits 4:2 of this register.
                                ; Only set this bit if BIT 0 is 0
RPBM		equ     BIT0    ; Run/Pause
				; set this bit to start the codec!

PI_BDBAR_REG	equ     0       ; PCM in buffer descriptor BAR
PO_BDBAR_REG	equ     10h     ; PCM out buffer descriptor BAR
MC_BDBAR_REG	equ     20h     ; MIC in buffer descriptor BAR

PI_CIV_REG	equ     4       ; PCM in current Index value (RO)
PO_CIV_REG	equ     14h     ; PCM out current Index value (RO)
MC_CIV_REG 	equ     24h     ; MIC in current Index value (RO)

PI_LVI_REG	equ     5       ; PCM in Last Valid Index
PO_LVI_REG	equ     15h     ; PCM out Last Valid Index
MC_LVI_REG	equ     25h     ; MIC in Last Valid Index

IOC		equ     BIT31	; Fire an interrupt whenever this
                		; buffer is complete.
BUP		equ     BIT30	; Buffer Underrun Policy.

GLOB_CNT_REG	equ     2Ch     ; Global Control Register
GLOB_STS_REG	equ     30h     ; Global Status register (RO)

CTRL_ST_CREADY	equ   BIT8+BIT9+BIT28 ; Primary Codec Ready

CODEC_REG_POWERDOWN   equ 26h
CODEC_REG_ST          equ 26h

; 22/06/2017
PO_PICB_REG	equ 18h	; PCM Out Position In Current Buffer Register

; 19/11/2023
AC97_EA_VRA	equ BIT0
; 24/11/2023
BCIS		equ BIT3  ; Buffer Completion Interrupt Status

;=============================================================================
;               CODE
;=============================================================================

; CODE for INTEL ICH AC'97 AUDIO CONTROLLER

DetectICH:
	; 22/11/2023
	; 19/11/2023
	; 01/11/2023 - TRDOS 386 Kernel v2.0.7
	;; 10/06/2017
	;; 05/06/2017
	;; 29/05/2017
	;; 28/05/2017
	;mov	eax, (ICH_DID << 16) + INTEL_VID
        ;call	pciFindDevice
        ;jnc	short d_ac97_1
	;
	;; 01/11/2023
	;mov	eax, (NFORCE_DID << 16) + NFORCE_VID
        ;call	pciFindDevice
        ;jnc	short d_ac97_1

	; 19/11/2023
	mov	esi, valid_ids	; address of Valid ICH (AC97) Device IDs
	mov	ecx, valid_id_count
pfd_1:
	lodsd
	call	pciFindDevice
	jnc	short d_ac97_1
	loop	pfd_1
	;stc

d_ac97_0:
; couldn't find the audio device!
	retn

; CODE for VIA VT8233 AUDIO CONTROLLER

DetectVT8233:
	; 22/11/2023
	; 02/11/2023
	; 01/11/2023 - TRDOS 386 Kernel v2.0.7
	; 06/08/2022 - TRDOS 386 Kernel v2.0.5
	; 10/06/2017
	; 05/06/2017
	; 29/05/2017
	; 03/04/2017
	mov     eax, (VT8233_DID << 16) + VIA_VID
        call    pciFindDevice
;       jnc     short d_vt8233_0
; couldn't find the audio device!
;	retn
	jc	short d_ac97_0  ; 28/05/2017
d_vt8233_0:
	; 24/03/2017 ('player.asm')
	; 12/11/2016 
	; Erdogan Tan - 8/11/2016
	; References: Kolibrios - vt823x.asm (2016)
	;	      VIA VT8235 V-Link South Bridge (VT8235-VIA.PDF)(2002)
	;	      lowlevel.eu - AC97 (2016)
	;	      .wav player for DOS by Jeff Leyda (2002) -this file-
	;	      Linux kernel - via82xx.c (2016)
d_ac97_1:
	; eax = BUS/DEV/FN
	;	00000000BBBBBBBBDDDDDFFF00000000
	; edx = DEV/VENDOR
	;	DDDDDDDDDDDDDDDDVVVVVVVVVVVVVVVV

	mov	[audio_dev_id], eax
	mov	[audio_vendor], edx

	; 22/11/2023
	;; init controller
	;mov	al, PCI_CMD_REG ; command register (04h)
	;call	pciRegRead32
	;
	;; eax = BUS/DEV/FN/REG
	;; edx = STATUS/COMMAND
	;; 	SSSSSSSSSSSSSSSSCCCCCCCCCCCCCCCC
	;mov	[audio_stats_cmd], edx

	mov	al, PCI_IO_BASE ; IO base address register (10h)
	;mov	al, NAMBAR_REG	; Native Audio Mixer BAR (10h)
	call	pciRegRead32

	;cmp	word [audio_vendor], INTEL_VID ; 8086h ; AC'97 ?
	;;jne	short d_vt8233_1
	;; 01/11/2023
	;je	short d_ac97_3
	;cmp	word [audio_vendor], NFORCE_VID ; 10DEh ; AC'97
	;jne	short d_vt8233_1
	; 02/11/2023
	cmp	word [audio_vendor], VIA_VID	; 1106h ; VT8233-VT8237R 
	je	short d_vt8233_1
d_ac97_3:
	;and	dx, 0FFFEh ; Audio Codec IO_ADDR_MASK
	; 06/08/2022
	and	dl, 0FEh
	mov	[NAMBAR], dx

	mov	al, NABMBAR_REG ; Native Audio Bus Mastering BAR (14h)
	call	pciRegRead32

	;;and	dx, 0FFC0h ; Audio Controller IO_ADDR_MASK
	;; 06/08/2022
	;and	dl, 0C0h
	;mov	[NABMBAR], dx
        ;;mov	[audio_io_base], dx
	;
	;jmp	short d_ac97_2
	; 02/11/2023
	; NABMBAR = audio_io_base

d_vt8233_1:
	;and	dx, 0FFC0h ; Audio Controller IO_ADDR_MASK 
	; 06/08/2022
	and	dl, 0C0h
        mov     [audio_io_base], dx

d_ac97_2:
	; 10/06/2017
	mov	al, AC97_INT_LINE ; Interrupt Line Register (3Ch)
	;call	pciRegRead32
	call	pciRegRead8

	;;and 	edx, 0FFh
	; 06/08/2022
	;and	dx, 0FFh

	mov     [audio_intr], dl

	retn

	; (Note: Interrupts are already enabled by TRDOS 386 kernel!)
	;mov	cx, dx
	;
	;in	al, 0A1h ; irq 8-15
	;mov	ah, al
	;in	al, 21h  ; irq 0-7 
	;btr	ax, dx	 ; unmask ; 17/03/2017
	;;bts	ax, dx   ; MASK interrupt ; 10/06/2017
	;out	21h, al  ; irq <= 7
	;mov	al, ah
	;out	0A1h, al ; irq > 7
	;
	
	; 10/06/2017
	; === Intel ICH I/O Controller Hub Datasheet, Section 8.1.16 ===
	; PRQ[n]_ROUT Register (61h, PRQB) Bit 7:
	; Interrupt Routing Enable (IRQEN).
	; 0 = The corresponding PIRQ is routed to one of the ISA-compatible
	;     interrupts specified in bits[3:0].
	; 1 = The PIRQ is not routed to the 8259.
	; Note: If the PIRQ is intended to cause an interrupt to the ICH’s
	;	integrated I/O APIC, then this bit should be set to 0 and
	;	the APIC_EN bit should be set to 1. 
	;	The IRQEN must be set to 0 and the PIRQ routed to 
	;	an 8259 interrupt via the IRQ Routing filed (bits[3:0).
	;	The corresponding 8259 interrupt must be masked via the 
	;	appropriated bit in the 8259’s OCW1 (Interrupt Mask)
	;	register. The IOAPIC must then be enabled by setting 
	;	the APIC_EN bit in the GEN_CNTL register.

	;mov	eax, 0F861h  ; D31:F0
	;	; AL=61h : PIRQ[B] Routing Control Reg, LPC interface
	;;mov	dl, [audio_intr]
	;call	pciRegWrite8
	;;mov	al, 0D0h	; General Control Register (GEN_CTL)
	;;call	pciRegRead32
	;;or	edx, 100h	; Bit 8, APIC_EN (Enable I/O APIC)
	;;call	pciRegWrite32
	;;and	edx, ~100h
	;;call	pciRegWrite32	; Bit 8, APIC_EN (Disable I/O APIC)
	;

	;mov	dx, 4D1h	; 8259 ELCR2
    	;in	al, dx
	;mov	ah, al
	;;mov	dx, 4D0h 	; 8259 ELCR1
	;dec	dl
	;in	al, dx
	;bts	ax, cx
	;;mov	dx, 4D0h
	;out	dx, al		; set level-triggered mode
	;mov	al, ah ; 29/05/2017
	;;mov	dx, 4D1h
	;inc	dl
	;out	dx, al		; set level-triggered mode

	;xor	eax, eax ; 0

	;retn

; CODE for PCI

pciFindDevice:
	; 19/11/2023
	; 03/04/2017 ('pci.asm', 20/03/2017)
	;
	; scan through PCI space looking for a device+vendor ID
	;
	; Entry: EAX=Device+Vendor ID
	;
	; Exit: EAX=PCI address if device found
	;	EDX=Device+Vendor ID
	;       CY clear if found, set if not found. EAX invalid if CY set.
	;
	; Destroys: ebx, edi ; 19/11/2023

	;push	ecx
	;push	eax ; * ; 19/11/2023
	;push	esi
	;push	edi
	;
	;mov	esi, eax		; save off vend+device ID
        ; 19/11/2023
	mov	ebx, eax
	;mov	edi, (80000000h - 100h)	; start with bus 0, dev 0 func 0
	mov	edi, 80000000h
nextPCIdevice:
	;add	edi, 100h
	;cmp	edi, 80FFF800h		; scanned all devices?
	;stc
	;je	short PCIScanExit	; not found

	mov 	eax, edi		; read PCI registers
	call	pciRegRead32
	; 19/11/2023
	cmp	edx, ebx
	;cmp	edx, esi		; found device?
	;jne	short nextPCIdevice
	;;clc
	je	short PCIScanExit	; found
	; 19/11/2023
	cmp	edi, 80FFF800h
	jnb	short pfd_nf		; not found
	add	edi, 100h
	jmp	short nextPCIdevice
pfd_nf:
	stc
	retn
PCIScanExit:
	;pushf
	mov	eax, NOT_BIT31 	; 19/03/2017
	and	eax, edi	; return only bus/dev/fn #
	;popf
	;
	;pop	edi
	;pop	esi
	;pop	edx ; * ; 19/11/2023
	;pop	ecx
	;
	retn

; 26/11/2023
%if 1

pciRegRead:
	; 03/04/2017 ('pci.asm', 20/03/2017)
	;
	; 8/16/32bit PCI reader
	;
	; Entry: EAX=PCI Bus/Device/fn/register number
	;           BIT30 set if 32 bit access requested
	;           BIT29 set if 16 bit access requested
	;           otherwise defaults to 8 bit read
	;
	; Exit:  DL,DX,EDX register data depending on requested read size
	;
	; Note1: this routine is meant to be called via pciRegRead8,
	;	 pciRegread16 or pciRegRead32, listed below.
	;
	; Note2: don't attempt to read 32 bits of data from a non dword
	;	 aligned reg number. Likewise, don't do 16 bit reads from
	;	 non word aligned reg #
	
	push	ebx
	push	ecx
        mov     ebx, eax		; save eax, dh
        mov     cl, dh

        and     eax, NOT_PCI32_PCI16	; clear out data size request
        or      eax, BIT31		; make a PCI access request
        and     al, ~3 ; NOT 3		; force index to be dword

        mov     dx, PCI_INDEX_PORT
        out	dx, eax			; write PCI selector
	
        mov     dx, PCI_DATA_PORT
        mov     al, bl
        and     al, 3			; figure out which port to
        add     dl, al			; read to

	test    ebx, PCI32+PCI16
        jnz     short _pregr0
	in	al, dx			; return 8 bits of data
        mov	dl, al
	mov     dh, cl			; restore dh for 8 bit read
	jmp	short _pregr2
_pregr0:	
	test    ebx, PCI32
        jnz	short _pregr1
	in	ax, dx
        mov     dx, ax			; return 16 bits of data
	jmp	short _pregr2
_pregr1:
	in	eax, dx			; return 32 bits of data
	mov	edx, eax
_pregr2:
	mov     eax, ebx		; restore eax
        and     eax, NOT_PCI32_PCI16	; clear out data size request
	pop	ecx
	pop	ebx
	retn

pciRegRead8:
        and     eax, NOT_PCI32_PCI16	; set up 8 bit read size
        jmp     short pciRegRead	; call generic PCI access

pciRegRead16:
        and     eax, NOT_PCI32_PCI16	; set up 16 bit read size
        or      eax, PCI16		; call generic PCI access
        jmp     short pciRegRead

pciRegRead32:
        and     eax, NOT_PCI32_PCI16	; set up 32 bit read size
        or      eax, PCI32		; call generic PCI access
        jmp     pciRegRead

pciRegWrite:
	; 03/04/2017 ('pci.asm', 29/11/2016)
	;
	; 8/16/32bit PCI writer
	;
	; Entry: EAX=PCI Bus/Device/fn/register number
	;           BIT31 set if 32 bit access requested
	;           BIT30 set if 16 bit access requested
	;           otherwise defaults to 8bit read
	;        DL/DX/EDX data to write depending on size
	;
	; Note1: this routine is meant to be called via pciRegWrite8, 
	;	 pciRegWrite16 or pciRegWrite32 as detailed below.
	;
	; Note2: don't attempt to write 32bits of data from a non dword
	;	 aligned reg number. Likewise, don't do 16 bit writes from
	;	 non word aligned reg #

	push	ebx
	push	ecx
        mov     ebx, eax		; save eax, edx
        mov     ecx, edx
	and     eax, NOT_PCI32_PCI16	; clear out data size request
        or      eax, BIT31		; make a PCI access request
        and     al, ~3 ; NOT 3		; force index to be dword

        mov     dx, PCI_INDEX_PORT
        out	dx, eax			; write PCI selector
	
        mov     dx, PCI_DATA_PORT
        mov     al, bl
        and     al, 3			; figure out which port to
        add     dl, al			; write to

	test    ebx, PCI32+PCI16
        jnz     short _pregw0
	mov	al, cl 			; put data into al
	out	dx, al
	jmp	short _pregw2
_pregw0:
	test    ebx, PCI32
        jnz     short _pregw1
	mov	ax, cx			; put data into ax
	out	dx, ax
	jmp	short _pregw2
_pregw1:
	mov	eax, ecx		; put data into eax
	out	dx, eax
_pregw2:
        mov     eax, ebx		; restore eax
        and     eax, NOT_PCI32_PCI16	; clear out data size request
        mov     edx, ecx		; restore dx
	pop	ecx
	pop	ebx
	retn

pciRegWrite8:
        and     eax, NOT_PCI32_PCI16	; set up 8 bit write size
        jmp	short pciRegWrite	; call generic PCI access

pciRegWrite16:
        and     eax, NOT_PCI32_PCI16	; set up 16 bit write size
        or      eax, PCI16		; call generic PCI access
        jmp	short pciRegWrite

pciRegWrite32:
        and     eax, NOT_PCI32_PCI16	; set up 32 bit write size
        or      eax, PCI32		; call generic PCI access
        jmp	pciRegWrite

%endif

; 26/11/2023 - temporary
%if 0

; PLAYWAV3.COM, ac97_vra.asm, 19/11/2023, Erdogan Tan

;===============================================================
; 8/16/32bit PCI reader
;
; Entry: EAX=PCI Bus/Device/fn/register number
;           BIT30 set if 32 bit access requested
;           BIT29 set if 16 bit access requested
;           otherwise defaults to 8 bit read
;
; Exit:  DL,DX,EDX register data depending on requested read size
;
; Note: this routine is meant to be called via pciRegRead8, pciRegread16,
;	or pciRegRead32, listed below.
;
; Note2: don't attempt to read 32bits of data from a non dword aligned reg
;	 number. Likewise, don't do 16bit reads from non word aligned reg #
; 
pciRegRead:
	push	ebx
	push	ecx
        mov     ebx, eax                        ; save eax, dh
        mov     cl, dh
        and     eax, (~PCI32)+PCI16             ; clear out data size request
        or      eax, BIT31                      ; make a PCI access request
        and     al, ~3 ; NOT 3                  ; force index to be dword

        mov     dx, PCI_INDEX_PORT
        out     dx, eax                         ; write PCI selector

        mov     dx, PCI_DATA_PORT
        mov     al, bl
        and     al, 3                           ; figure out which port to
        add     dl, al                          ; read to

	in      eax, dx                         ; do 32bit read
        test    ebx, PCI32
        jz      short _pregr1

        mov     edx, eax                        ; return 32bits of data
_pregr1:
	mov     dx, ax                          ; return 16bits of data
        test    ebx, PCI32+PCI16
        jnz     short _pregr2
        mov     dh, cl                          ; restore dh for 8 bit read
_pregr2:
        mov     eax, ebx                        ; restore eax
        and     eax, (~PCI32)+PCI16             ; clear out data size request
	pop	ecx
	pop	ebx
	retn

pciRegRead8:
        and     eax, (~PCI16)+PCI32             ; set up 8 bit read size
        jmp     short pciRegRead		; call generic PCI access

pciRegRead16:
        and     eax, (~PCI16)+PCI32		; set up 16 bit read size
        or      eax, PCI16			; call generic PCI access
        jmp     short pciRegRead

pciRegRead32:
        and     eax, (~PCI16)+PCI32		; set up 32 bit read size
        or      eax, PCI32			; call generic PCI access
        jmp     short pciRegRead

;===============================================================
; 8/16/32bit PCI writer
;
; Entry: EAX=PCI Bus/Device/fn/register number
;           BIT31 set if 32 bit access requested
;           BIT30 set if 16 bit access requested
;           otherwise defaults to 8bit read
;        DL/DX/EDX data to write depending on size
;
;
; note: this routine is meant to be called via pciRegWrite8, pciRegWrite16,
; 	or pciRegWrite32 as detailed below.
;
; Note2: don't attempt to write 32bits of data from a non dword aligned reg
;	 number. Likewise, don't do 16bit writes from non word aligned reg #
;
pciRegWrite:
	push	ebx
	push	ecx
        mov     ebx, eax                        ; save eax, dx
        mov     cx, dx
        or      eax, BIT31                      ; make a PCI access request
        and     eax, ~PCI16 ; NOT PCI16         ; clear out data size request
        and     al, ~3 ; NOT 3                  ; force index to be dword

        mov     dx, PCI_INDEX_PORT
        out     dx, eax                         ; write PCI selector

        mov     dx, PCI_DATA_PORT
        mov     al, bl
        and     al, 3                           ; figure out which port to
        add     dl, al                          ; write to

        mov     eax, edx                        ; put data into eax
        mov     ax, cx

        out     dx, al
        test    ebx, PCI16+PCI32                ; only 8bit access? bail
        jz      short _pregw1

        out     dx, ax                          ; write 16 bit value
        test    ebx, PCI16                      ; 16bit requested?  bail
        jnz     short _pregw1

        out     dx, eax                         ; write full 32bit
_pregw1:
        mov     eax, ebx                        ; restore eax
        and     eax, (~PCI32)+PCI16             ; clear out data size request
        mov     dx, cx                          ; restore dx
	pop	ecx
	pop	ebx
	ret

pciRegWrite8:
        and     eax, (~PCI16)+PCI32		; set up 8 bit write size
        jmp     short pciRegWrite		; call generic PCI access

pciRegWrite16:
        and     eax, (~PCI16)+PCI32		; set up 16 bit write size
        or      eax, PCI16			; call generic PCI access
        jmp     short pciRegWrite

pciRegWrite32:
        and     eax, (~PCI16)+PCI32		; set up 32 bit write size
        or      eax, PCI32			; call generic PCI access
        jmp     short pciRegWrite

;===============================================================

%endif

init_codec:
	; 06/08/2022 - TRDOS 386 v2.0.5
	; 05/06/2017
	; 28/05/2017 - Erdogan Tan (Ref: KolibriOS, vt823x.asm)
	;
	mov	eax, [audio_dev_id]
	mov	al, VIA_ACLINK_CTRL
	call	pciRegRead8
	; ?
	mov	al, VIA_ACLINK_STAT
	call	pciRegRead8
	test	dl, VIA_ACLINK_C00_READY
        jnz     short _codec_ready_1
	call	reset_codec
	jnc	short _codec_ready_2 ; eax = 1
	retn
_codec_ready_1:
	;mov	eax, 1
	; 06/08/2022
	sub	eax, eax
	inc	al
	; eax = 1
_codec_ready_2:
	call	codec_io_w16
detect_codec:
	retn

reset_codec:
	; 16/04/2017
	; 23/03/2017 
	; ('codec.asm')
	; 12/11/2016 - Erdogan Tan (Ref: KolibriOS, vt823x.asm)
	mov	eax, [audio_dev_id]
 	mov	al, VIA_ACLINK_CTRL
       	mov	dl, VIA_ACLINK_CTRL_ENABLE + VIA_ACLINK_CTRL_RESET + VIA_ACLINK_CTRL_SYNC
	call	pciRegWrite8

	call	delay_100ms 	; wait 100 ms
_rc_cold:
        call    cold_reset
        jnc     short _reset_codec_ok
	
	; 16/04/2017
        ;xor	eax, eax	; timeout error
       	;stc
	retn

_reset_codec_ok:
	; 01/09/2020
	; 15/08/2020
	; 27/07/2020
	; also reset codec by using index control register 0 of AD1980 or ALC655
	; (to fix line out -2 channels audio playing- problem on AD1980 codec)  

	sub	eax, eax
	; 02/11/2023
	;mov	edx, CODEC_RESET_REG ; 00h ; Reset register
	xor	edx, edx ; 00h ; Reset register
	call	codec_write

	;sub	eax, eax
	; 01/09/2020
	; 15/08/2020
	; AD1980 BugFix
	; (set HPSEL -headphone amp to be driven from mixer- and
	;      CLDIS -center and LFE disable- bits)
	;mov	eax, 0C00h ; HPSEL = bit 10, CLDIS = bit 11 ; 01/09/2020
 	;mov	edx, CODEC_MISC_CRTL_BITS_REG ; 76h ; Misc Ctrl Bits ; AD1980
	;call	codec_write

        xor     eax, eax
        ;mov	al, VIA_ACLINK_C00_READY ; 1
        inc	al
	retn

cold_reset:
	; 06/08/2022 - TRDOS 386 v2.0.5
	; 16/04/2017
	; 23/03/2017
	; ('codec.asm')
	; 12/11/2016 - Erdogan Tan (Ref: KolibriOS, vt823x.asm)
	;mov	eax, [audio_dev_id]
	;mov	al, VIA_ACLINK_CTRL
	xor	dl, dl ; 0
	call	pciRegWrite8

	call	delay_100ms	; wait 100 ms

	;; ACLink on, deassert ACLink reset, VSR, SGD data out
        ;; note - FM data out has trouble with non VRA codecs !!
        
	;mov	eax, [audio_dev_id]
	;mov	al, VIA_ACLINK_CTRL
	mov	dl, VIA_ACLINK_CTRL_INIT
	call	pciRegWrite8

	;mov	ecx, 16	; total 2s
	; 06/08/2022
	;sub	ecx, ecx
	; 22/11/2023
	; ecx = 0 (from 'delay_100ms')
	mov	cl, 16
_crst_wait:
	;mov	eax, [audio_dev_id]
	mov	al, VIA_ACLINK_STAT
	call	pciRegRead8

        test    dl, VIA_ACLINK_C00_READY
        jnz     short _crst_ok

	push	ecx
	call	delay_100ms
	pop	ecx

        dec     ecx
        jnz     short _crst_wait

_crst_fail:
        stc
_crst_ok:
	retn

delay_100ms:
	; 29/05/2017
	; 24/03/2017 ('codec.asm')
	; wait 100 ms
	mov	ecx, 400  ; 400*0.25ms
_delay_x_ms:
	call	delay1_4ms
        loop	_delay_x_ms
	retn

;       delay1_4ms - Delay for 1/4 millisecond.
;	    1ms = 1000us
;       Entry:
;         None
;       Exit:
;	  None
;
;       Modified:
;         None

	; 29/05/2017
	; 23/04/2017
	; 05/03/2017 (TRDOS 386)
	; ('UTILS.ASM')
delay1_4ms:
        push    eax 
        push    ecx
        mov	cl, 16		; close enough.

	in	al, PORTB ; 61h
		
	and	al, REFRESH_STATUS ; 10h
	mov	ch, al		; Start toggle state
_d4ms1:	
	in	al, PORTB	; Read system control port
	
	and	al, REFRESH_STATUS ; Refresh toggles 15.085 microseconds
	cmp	ch, al
	je	short _d4ms1	; Wait for state change

	mov	ch, al		; Update with new state
	dec	cl
	jnz	short _d4ms1

	clc	; 29/05/2017

        pop     ecx
        pop     eax
        retn

; 10/04/2017 (TRDOS 386)
; 12/11/2016

codec_io_w16: ;w32
	; ('codec.asm')
        mov	dx, [audio_io_base]
        add     dx, VIA_REG_AC97
	out	dx, eax
        retn

codec_io_r16: ;r32
	; ('codec.asm')
        mov     dx, [audio_io_base]
        add     dx, VIA_REG_AC97
        in	eax, dx
        retn

ctrl_io_w8:
	; ('codec.asm')
        add     dx, [audio_io_base]
        out	dx, al
        retn

ctrl_io_r8:
	; ('codec.asm')
        add     dx, [audio_io_base]
        in	al, dx
        retn

ctrl_io_w32:
	; ('codec.asm')
        add     dx, [audio_io_base]
        out	dx, eax
        retn

ctrl_io_r32:
	; ('codec.asm')
        add	dx, [audio_io_base]
	in	eax, dx
_cr_not_rdy:	; 06/08/2022
        retn

codec_read:
	; 06/08/2022 - TRDOS 386 v2.0.5
	; 12/11/2016 - Erdogan Tan (Ref: KolibriOS, vt823x.asm)
        ; Use only primary codec.
        ; eax = register
        shl     eax, VIA_REG_AC97_CMD_SHIFT
        or      eax, VIA_REG_AC97_PRIMARY_VALID + VIA_REG_AC97_READ

	call    codec_io_w16

      	; codec_valid
	call	codec_check_ready
	;jnc	short _cr_ok
	;retn
	; 06/08/2022
	jc	short _cr_not_rdy
	; ecx <= 20
_cr_ok:
	; wait 25 ms
	;mov	ecx, 80 ; (100*0.25 ms)
	; 06/08/2022
	;xor	ecx, ecx
	mov	cl, 80
	; ecx = 80
_cr_wloop:
	call	delay1_4ms
	loop	_cr_wloop

        call    codec_io_r16
	; 06/08/2022
	;and	eax, 0FFFFh
        retn

codec_write:
	; 06/08/2022 - TRDOS 386 v2.0.5
	; 12/11/2016 - Erdogan Tan (Ref: KolibriOS, vt823x.asm)
        ; Use only primary codec.
        
	; eax = data (volume)
	; edx = register (mixer register)
	
	shl     edx, VIA_REG_AC97_CMD_SHIFT

	; 02/11/2023 (shl eax, 0)
	;shl	eax, VIA_REG_AC97_DATA_SHIFT ; shl eax, 0
        or      edx, eax

        mov     eax, VIA_REG_AC97_CODEC_ID_PRIMARY
        shl     eax, VIA_REG_AC97_CODEC_ID_SHIFT
        or      eax, edx

        call    codec_io_w16
        ;mov    [codec.regs+esi], ax

        ;call	codec_check_ready
       	;retn
	;jmp	short _codec_check_ready

codec_check_ready:
	; 06/08/2022 - TRDOS 386 v2.0.5
	; 12/11/2016 - Erdogan Tan (Ref: KolibriOS, vt823x.asm)

_codec_check_ready:
	;mov	ecx, 20	; total 2s
	; 06/08/2022
	sub	ecx, ecx
	;mov	cl, 20
	; 02/11/2023
	mov	cl, 10	; wait 1s
_ccr_wait:
	push	ecx

        call    codec_io_r16
        test    eax, VIA_REG_AC97_BUSY
        jz      short _ccr_ok

	call	delay_100ms

	pop	ecx

	dec     ecx
        jnz     short _ccr_wait

        stc
        retn

_ccr_ok:
	pop	ecx
	and     eax, 0FFFFh
        retn

codec_config:
	; 02/11/2023 - TRDOS 386 Kernel v2.0.7
	; 06/08/2022 - TRDOS 386 Kernel v2.0.5
	; 10/06/2017
	; 29/05/2017
	; 24/04/2017
	; 21/04/2017
	; 16/04/2017 (TRDOS 386 Kernel) 
	; 15/11/2016 ('codec.asm', 'player.com')
	; 14/11/2016
	; 12/11/2016 - Erdogan Tan
	;	     (Ref: KolibriOS, 'setup_codec', codec.inc)

	mov     eax, 0202h
	mov	[audio_master_volume], ax

	;mov	ax, 1F1Fh ; 31,31
	; 02/11/2023
	mov	ax, 0B0Bh
	;mov	edx, CODEC_MASTER_VOL_REG ; 02h ; Line Out
	; 06/08/2022
	sub	edx, edx
	mov	dl, CODEC_MASTER_VOL_REG ; 02h ; Line Out
	call	codec_write
	;jc	short cconfig_error

 	;mov    eax, 0202h
	mov     ax, 0202h
	;mov	edx, CODEC_PCM_OUT_REG ; 18h ; Wave Output (Stereo)
	; 06/08/2022
	sub	edx, edx
	mov	dl, CODEC_PCM_OUT_REG ; 18h ; Wave Output (Stereo)
	call	codec_write
	;jc	short cconfig_error
      
 	;mov    eax, 0202h
	mov	ax, 0202h
	;mov	edx, CODEC_AUX_VOL ; 04h ; CODEC_HP_VOL_REG ; HeadPhone
	; 06/08/2022
	sub	edx, edx
	mov	dl, CODEC_AUX_VOL ; 04h ; CODEC_HP_VOL_REG ; HeadPhone
	call	codec_write
	;jc	short cconfig_error

 	;mov    eax, 08h
        ;mov    ax, 08h
	mov	ax, 8008h ; Mute
	;mov	edx, 0Ch  ; AC97_PHONE_VOL ; TAD Input (Mono)
	; 06/08/2022
	sub	edx, edx
	mov	dl, 0Ch	  ; AC97_PHONE_VOL ; TAD Input (Mono)
	call	codec_write
	;jc	short cconfig_error

 	;mov    eax, 0808h
	mov	ax, 0808h
	;mov	edx, CODEC_LINE_IN_VOL_REG ; 10h ; Line Input (Stereo)	
	; 06/08/2022
	sub	edx, edx
	mov	dl, CODEC_LINE_IN_VOL_REG ; 10h ; Line Input (Stereo)	
	call	codec_write
	;jc	short cconfig_error

 	;mov    eax, 0808h
	mov	ax, 0808h
	;mov	edx, CODEC_CD_VOL_REG ; 12h ; CR Input (Stereo)
	; 06/08/2022
	sub	edx, edx ; 02/11/2023
	mov	dl, CODEC_CD_VOL_REG ; 12h ; CR Input (Stereo)
	call	codec_write
	;jc	short cconfig_error

 	;mov    eax, 0808h
	mov     ax, 0808h
        ;mov	edx, CODEC_AUX_VOL_REG ; 16h ; Aux Input (Stereo)
	; 06/08/2022
	sub	edx, edx
	mov	dl, CODEC_AUX_VOL_REG ; 16h ; Aux Input (Stereo)
	;call	codec_write
	;;jc	short cconfig_error
	jmp	codec_write ; 10/06/2017

;	; Extended Audio Status (2Ah)
;	mov	eax, CODEC_EXT_AUDIO_CTRL_REG ; 2Ah 
;	call	codec_read
;	and     eax, 0FFFFh - 2		; clear DRA (BIT1)
;	;or     eax, 1			; set VRA (BIT0)
;	or	eax, 5  	; VRA (BIT0) & S/PDIF (BIT2) ; 14/11/2016
;	mov	edx, CODEC_EXT_AUDIO_CTRL_REG
;	call	codec_write
;	;jc	short cconfig_error
;
;set_sample_rate:
;	;movzx	eax, word [audio_freq]
;	mov	ax, [audio_freq]
;	mov	edx, CODEC_PCM_FRONT_DACRATE_REG ; 2Ch ; PCM Front DAC Rate
;	;call	codec_write
;	;retn
;	jmp	codec_write
	
;cconfig_error:
;	retn

vt8233_int_handler:
	; 05/06/2024
	; 04/06/2024 - TRDOS 386 v2.0.8
	; 27/07/2020
	; 22/07/2020
	; Interrupt Handler for VIA VT8237R Audio Controller
	; Note: called by 'dev_IRQ_service'
	; 14/10/2017 
	; 09/10/2017, 10/10/2017, 12/10/2017
	; 13/06/2017
	; 21/04/2017 (TRDOS 386 kernel, 'audio.s')
	; 24/03/2017 - 'PLAYER.COM' ('player.asm')

	;push	eax ; * must be saved !
	;push	edx
	;push	ecx
	;push	ebx ; * must be saved !
	;push	esi
	;push	edi

	;cmp	byte [audio_busy], 1
	;jnb	short _ih0 ; 09/10/2017

	;mov	byte [audio_flag_eol], 0

        mov     dx, VIADEV_PLAYBACK + VIA_REG_OFFSET_STATUS
        call    ctrl_io_r8

	test    al, VIA_REG_STAT_ACTIVE
        jz      short _ih0 ; 09/10/2017

        and     al, VIA_REG_STAT_EOL + VIA_REG_STAT_FLAG + VIA_REG_STAT_STOPPED
	mov	[audio_flag_eol], al
        jz	short _ih0 ; 09/10/2017

	; 09/10/2017
	;mov	byte [audio_busy], 1

	cmp	byte [audio_play_cmd], 1
	jnb	short _ih1 ; 10/10/2017

	call	channel_reset
_ih0:
	; 09/10/2017
        mov     al, [audio_flag_eol]   ;; ack ;;
        mov     dx, VIADEV_PLAYBACK + VIA_REG_OFFSET_STATUS
        call    ctrl_io_w8
	jmp	short _ih4
_ih1:
vt8233_tuneLoop:
        mov     al, [audio_flag_eol]   ;; ack ;;
        mov     dx, VIADEV_PLAYBACK + VIA_REG_OFFSET_STATUS
        call    ctrl_io_w8

	; 22/07/2020
	;; 12/10/2017
	;mov	byte [audio_flag], 0 ; Reset

	; 10/10/2017
	; 09/10/2017
	;test	byte [audio_flag_eol], VIA_REG_STAT_FLAG
	;jz	short _ih2 ; EOL

	; 22/07/2020
	; 14/10/2017
	;test	byte [audio_flag_eol], VIA_REG_STAT_EOL
	;jnz	short _ih2 ; EOL
	;		   ; (Half Buffer 2 has been completed 
	;		   ; and Half Buffer 1 will be played.)
	
	; FLAG
	; (Half Buffer 1 has been completed
	;  and Half Buffer 2 will be played.)

	; 14/10/2017
	;; (Continue to play.)
	;mov	al, VIA_REG_CTRL_INT
       	;or	al, VIA_REG_CTRL_START
       	;mov	dx, VIADEV_PLAYBACK + VIA_REG_OFFSET_CONTROL
        ;call	ctrl_io_w8
	; 12/10/2017
	;mov	byte [audio_flag], 1 

	; 22/07/2020
	;inc	byte [audio_flag] ; = 1
_ih2: 
	; 10/10/2017
	mov	edi, [audio_dma_buff]
	
	; 05/06/2024
	; 04/06/2024
	;;mov	ecx, [audio_dmabuff_size]
	;;shr	ecx, 1 ; dma buff size / 2 = half buffer size
	;mov	ecx, [audio_buff_size]
	;and	cl, ~1 ; word aligned
	; 05/06/2024
	mov	ecx, [dma_hbuff_size] ; half buffer size

	; 22/07/2020
	; 12/10/2017
	;cmp	byte [audio_flag], 0
	;ja	short _ih3 ; Playing Half Buffer 2 (Current: FLAG)
	
	; 27/07/2020
	; 22/07/2020
	test	byte [audio_flag], 1  ; Current flag value
	jz	short _ih3 ; Half Buffer 1 must be filled

	; Half Buffer 2 must be filled
	add	edi, ecx
_ih3:
	; Update half buffer 2 while playing half buffer 1
	; Update half buffer 1 while playing half buffer 2

	mov	esi, [audio_p_buffer] ; phy addr of audio buff
	; 05/06/2024
	;shr	ecx, 2 ; half buff size / 4
	;rep	movsd
	shr	ecx, 1	; word aligned buffer
	rep	movsw

	; switch flag value ;
	xor	byte [audio_flag], 1
	; 12/10/2017
	; [audio_flag] = 0 : Playing dma half buffer 2
			   ; Next buffer (to update) is dma half buff 1
	; 	       = 1 : Playing dma half buffer 1
			   ; Next buffer (to update) is dma half buff 2
_ih4:	
	; 28/05/2017
	;mov	byte [audio_busy], 0 ; 09/10/2017
	;
	;pop	edi
	;pop	esi
	;pop	ebx ; * must be restored !
	;pop	ecx
	;pop	edx
	;pop	eax ; * must be restored !

	retn

channel_reset:
	; 06/08/2022 - TRDOS 386 Kernel v2.0.5
	; 24/06/2017
	; 29/05/2017
	; 23/03/2017
	; 14/11/2016 - Erdogan Tan
	; 12/11/2016 - Erdogan Tan (Ref: KolibriOS, vt823x.asm)
        ;mov	edx, VIA_REG_OFFSET_CONTROL
	; 06/08/2022
	sub	edx, edx
	mov	dl, VIA_REG_OFFSET_CONTROL 
	;mov	eax, VIA_REG_CTRL_PAUSE + VIA_REG_CTRL_TERMINATE + VIA_REG_CTRL_RESET
        mov	eax, VIA_REG_CTRL_PAUSE + VIA_REG_CTRL_TERMINATE ; 24/06/2017
	call    ctrl_io_w8

        ;mov	edx, VIA_REG_OFFSET_CONTROL
        ;call   ctrl_io_r8

	; wait for 50 ms
	;mov	ecx, 160 ; (200*0.25 ms) ; 29/05/2017
	; 06/08/2022
	xor	ecx, ecx
	mov	cl, 160
_ch_rst_wait:
	call	delay1_4ms
	dec	ecx
	jnz	short _ch_rst_wait

        ; disable interrupts
	;mov	edx, VIA_REG_OFFSET_CONTROL
        ; 06/08/2022
	sub	edx, edx
	mov	dl, VIA_REG_OFFSET_CONTROL
 	xor     eax, eax
        call    ctrl_io_w8

        ; clear interrupts
        ;mov	edx, VIA_REG_OFFSET_STATUS
	; 06/08/2022
	sub	edx, edx
	;mov	dl, VIA_REG_OFFSET_STATUS ; 0
	; edx = 0
	;mov	eax, 3
        ; 06/08/2022
	sub	eax, eax
	mov	al, 3
	; eax = 3
	call	ctrl_io_w8

	;mov	edx, VIA_REG_OFFSET_CURR_PTR
	;xor	eax, eax
	;call	ctrl_io_w32

        retn	

vt8233_stop: ; 22/04/2017
	mov	byte [audio_play_cmd], 0 ; stop !
_tlp2:
	; 24/06/2017
        ; finished with song, stop everything
	;mov	al, VIA_REG_CTRL_INT
        ;or	al, VIA_REG_CTRL_TERMINATE
	;mov	dx, VIADEV_PLAYBACK + VIA_REG_OFFSET_CONTROL
        ;call	ctrl_io_w8

        ;call	channel_reset
	;retn

	jmp	short channel_reset

set_vt8233_bdl: ; Set VT8237R Buffer Descriptor List
	; 04/06/2024 - TRDOS 386 v2.0.8
	; 06/08/2022 - TRDOS 386 v2.0.5
	; 22/07/2020 - TRDOS 386 v2.0.2
	; 28/05/2017
	; 21/04/2017 (TRDOS 386 kernel, 'audio.s')
	; 24/03/2017 - 'PLAYER.COM' ('via_wav.asm' - 29/11/2016)
	
	; eax = dma buffer address = [audio_DMA_buff]
	; ecx = dma buffer buffer size = [audio_dmabuff_size]

	; 04/06/2024
	; ecx = DMA half buffer size (same as audio buffer size)

	;shr	ecx, 1 ; dma half buffer size
	mov	esi, ecx

        mov     edi, audio_bdl_buff	; get BDL address
	;mov	ecx, 32 / 2		; make 32 entries in BDL
	; 06/08/2022
	sub	ecx, ecx
	mov	cl, 16

	jmp	short s_vt8233_bdl1 

s_vt8233_bdl0:
	; set buffer descriptor 0 to start of data file in memory

 	mov	eax, [audio_dma_buff]	; Physical address of DMA buffer
 
s_vt8233_bdl1:
	stosd				; store dmabuffer1 address

	mov	edx, eax

; VIA VT8235.PDF: (Page 110) (Erdogan Tan, 29/11/2016)
	;
	; 	Audio SGD Table Format
	;	-------------------------------
	;	63   62    61-56    55-32  31-0
	;	--   --   --------  -----  ----
	;	EOL FLAG -reserved- Base   Base
	;		    	    Count  Address
	;		            [23:0] [31:0]
	;	EOL: End Of Link. 
	;	     1 indicates this block is the last of the link.
	;	     If the channel “Interrupt on EOL” bit is set, then
	;	     an interrupt is generated at the end of the transfer.
	;
	;	FLAG: Block Flag. If set, transfer pauses at the end of this
	;	      block. If the channel “Interrupt on FLAG” bit is set,
	;	      then an interrupt is generated at the end of this block.

	mov	eax, esi ; DMA half buffer size

		; 04/06/2024 - Erdogan Tan
		; NOTE: I have changed DMA half buffer size to
		; (word aligned) audio buffer size for smooth audio playing
		; (it was page border aligned before June 4, 2024)
		;
		; For example:
		;    For 65416 bytes audio buffer (22kHz, 16bit samples)
		;       (user's audio buff virtual addr is mapped to physical)
		;	((kernel's and user's audio buff pages are same)) 
		;    1) Memory allocation (of it) is 65536 bytes
		;    2) DMA half buffer size is 65416 bytes
		;	(it would be 65536 bytes before this modification)
		;	((additional 140 bytes would cause to a noise))	     
		;    3) Total DMA buffer size is 131072 bytes
		;       (the last 240 bytes will not be used for playing)

	add	edx, eax
	or	eax, FLAG
	;or	eax, EOL
	stosd

; 2nd buffer:

        mov	eax, edx ; Physical address of the 2nd half of DMA buffer
	stosd		 ; store dmabuffer2 address

; set length to [audio_dmabuff_size]/2
; Set control (bits 31:16) to BUP, bits 15:0=number of samples
; 
	mov	eax, esi ; DMA half buffer size
	; 22/07/2020
	;or	eax, EOL
	or	eax, FLAG
	stosd

	loop    s_vt8233_bdl0

	; 22/07/2020
	or	dword [edi-4], EOL
	
	retn

vt8233_start_play:
	; 06/08/2022 - TRDOS 386 Kernel v2.0.5
	; 01/09/2020
	; 22/07/2020
	; start to play audio data via VT8233 audio controller
	; 13/06/2017
	; 10/06/2017
	; 24/04/2017
	; 21/04/2017 (TRDOS 386 kernel, 'audio.s')
	; 24/03/2017 - 'PLAYER.COM' ('via_wav.asm' - 29/11/2016)
	; write buffer descriptor list address

	; Extended Audio Status (2Ah)
	mov	eax, CODEC_EXT_AUDIO_CTRL_REG ; 2Ah
	call	codec_read
	and     eax, 0FFFFh - 2		; clear DRA (BIT1)
	;or     eax, 1			; set VRA (BIT0)
	;or	eax, 5  	; VRA (BIT0) & S/PDIF (BIT2) ; 14/11/2016
	or	al, 5
	; 01/09/2020
	;or	eax, 3805h ; AD1980 (PRK, PRJ, PRI = 1 .. only front DAC)
	; 01/09/2020
	;mov	edx, CODEC_EXT_AUDIO_CTRL_REG
	;cmp	word [audio_freq], 0BB80h ; 48 kHz
	;jne	short set_extd_audio_status_1
	;and	al, 0FEh ; disable VRA bit (set sample rate to 48000 Hz)
	;jmp	short set_extd_audio_status_2
;set_extd_audio_status_1:
	mov	edx, CODEC_EXT_AUDIO_CTRL_REG
	call	codec_write
	;jc	short cconfig_error

set_sample_rate:
	;movzx	eax, word [audio_freq]
	mov	ax, [audio_freq]
	mov	edx, CODEC_PCM_FRONT_DACRATE_REG ; 2Ch ; PCM Front DAC Rate
;set_extd_audio_status_2:
	call	codec_write

	; 01/09/2020
	; set AD1980 MCB register (Index 76h) to 0C00h
	; (CLDIS, HPSEL)
	;mov	ax, 0C00h
	;mov	edx, CODEC_MISC_CRTL_BITS_REG ; 76h 
	;			; Miscellaneous Control Bit Register
	;call	codec_write
	;

        mov	eax, audio_bdl_buff
  
	; 12/11/2016 - Erdogan Tan 
	; (Ref: KolibriOS, vt823x.asm, 'create_primary_buff')
	;mov	edx, VIADEV_PLAYBACK + VIA_REG_OFFSET_TABLE_PTR
        ; 06/08/2022
	sub	edx, edx
	;mov	dl, VIADEV_PLAYBACK + VIA_REG_OFFSET_TABLE_PTR
	; edx = 0
	call	ctrl_io_w32

	;call	codec_check_ready

  	mov	dx, VIADEV_PLAYBACK + VIA_REG_OFS_PLAYBACK_VOLUME_L
        ;mov	eax, 2	; 31
	mov	al, 31
        sub	al, [audio_master_volume_l]
	call	ctrl_io_w8

	;call	codec_check_ready

        mov     dx, VIADEV_PLAYBACK + VIA_REG_OFS_PLAYBACK_VOLUME_R
        ;mov	ax, 2	; 31
	mov	al, 31
        sub	al, [audio_master_volume_r]
	call    ctrl_io_w8

	;call	codec_check_ready
;
;
; All set. Let's play some music.
;
;
       	;mov    dx, VIADEV_PLAYBACK + VIA_REG_OFFSET_STOP_IDX
        ;mov    ax, VIA8233_REG_TYPE_16BIT or VIA8233_REG_TYPE_STEREO or 0xfffff or 0xff000000
        ;call   ctrl_io_w32

	;call	codec_check_ready

	; 08/12/2016
	; 07/10/2016
        ;;mov    al, 1
        ;mov	al, 31
	; 22/07/2020
	mov	al, 0FFh
	call    set_VT8233_LastValidIndex

	mov	byte [audio_play_cmd], 1 ; play command (do not stop) !

	; 22/07/2020
	;mov	byte [audio_flag], 0  ; clear half buffer flag

vt8233_play: ; continue to play
	; 22/04/2017
	;mov	al, VIA_REG_CTRL_INT
       	;or	al, VIA_REG_CTRL_START
        ;;mov	al, VIA_REG_CTRL_AUTOSTART + VIA_REG_CTRL_START
	; 22/07/2020	
	mov	al, VIA_REG_CTRL_AUTOSTART + VIA_REG_CTRL_START + VIA_REG_CTRL_INT_FLAG

	mov     dx, VIADEV_PLAYBACK + VIA_REG_OFFSET_CONTROL
        call    ctrl_io_w8
	;call	codec_check_ready
	;retn
	;jmp	codec_check_ready
	retn

;input AL = index # to stop on
set_VT8233_LastValidIndex:
	; 06/08/2022 - TRDOS 386 Kernel v2.0.5
	; 23/07/2020
	; 10/06/2017
	; 21/04/2017 (TRDOS 386 kernel, 'audio.s')
	; 24/03/2017 - 'PLAYER.COM' ('via_wav.asm' - 29/11/2016) 
	; 19/11/2016
	; 14/11/2016 - Erdogan Tan (Ref: VIA VT8235.PDF, Page 110)
	; 12/11/2016 - Erdogan Tan
	; (Ref: KolibriOS, vt823x.asm, 'create_primary_buff')
	;push	edx
	;push	ax
	push	eax ; 23/07/2020
	;push	ecx
	movzx	eax, word [audio_freq] ; Hertz
	mov	edx, 100000h ; 2^20 = 1048576
	mul	edx
	mov	ecx, 48000
	div	ecx
	;and	eax, 0FFFFFh
	;pop	ecx
	;pop	dx 
	pop	edx ; 23/07/2020
	shl	edx, 24  ; STOP Index Setting: Bit 24 to 31
	or	eax, edx
	; 19/11/2016
	cmp	byte [audio_bps], 16
	jne	short sLVI_1
	or	eax, VIA8233_REG_TYPE_16BIT
sLVI_1:
	cmp	byte [audio_stmo], 2
	jne	short sLVI_2
	or	eax, VIA8233_REG_TYPE_STEREO
sLVI_2:
	;mov	edx, VIADEV_PLAYBACK + VIA_REG_OFFSET_STOP_IDX
	; 06/08/2022
	sub	edx, edx
	mov	dl, VIADEV_PLAYBACK + VIA_REG_OFFSET_STOP_IDX
	call    ctrl_io_w32
	;call	codec_check_ready
	;pop	edx
	retn

vt8233_pause: ; pause
	; 10/06/2017
	; 22/04/2017
	;mov	al, VIA_REG_CTRL_INT
        ;or	al, VIA_REG_CTRL_PAUSE
	; 23/07/2020
	mov	al, VIA_REG_CTRL_PAUSE+VIA_REG_CTRL_INT_FLAG+VIA_REG_CTRL_AUTOSTART
	
	mov     dx, VIADEV_PLAYBACK + VIA_REG_OFFSET_CONTROL
        call    ctrl_io_w8
	;call	codec_check_ready
	;retn
	;jmp	codec_check_ready
	retn

vt8233_reset:
	; 22/04/2017
	; reset VT8237R (vt8233) Audio Controller
	;cmp	byte [audio_play_cmd], 1
	;jna	short vt8233_rst_0
	mov	byte [audio_play_cmd], 0 ; stop !
vt8233_rst_0:
	call	reset_codec
	jc	short vt8233_rst_1 ; codec error !
	; eax = 1
	call	codec_io_w16 ; w32
	call	channel_reset
vt8233_rst_1:
vt8233_vol_1:	; 06/08/2022
	retn

vt8233_volume:
	; set VT8237R (vt8233) sound volume level
	; 06/08/2022 - TRDOS 386 v2.0.5
	; 24/04/2017
	; 22/04/2017
	; bl = component (0 = master/playback/lineout volume)
	; cl = left channel volume level (0 to 31)
	; ch = right channel volume level (0 to 31)

	or	bl, bl
	jnz	short vt8233_vol_1 ; temporary !
	mov	ax, 1F1Fh ; 31,31
	cmp	cl, al
	ja	short vt8233_vol_1 ; temporary !
	cmp	ch, ah
	ja	short vt8233_vol_1 ; temporary !
	mov	[audio_master_volume], cx
	sub	ax, cx
	;mov	edx, CODEC_MASTER_VOL_REG ; 02h ; Line Out
	; 06/08/2022
	sub	edx, edx
	mov	dl, CODEC_MASTER_VOL_REG ; 02h ; Line Out
	; 06/08/2022
	jmp	codec_write
	;call	codec_write
;vt8233_vol_1:
	;retn

; CODE for SOUND BLASTER 16

DetectSB:
	; 06/08/2022 - TRDOS 386 v2.0.5
	; 24/04/2017
	;pushad
ScanPort:
	; 06/08/2022
	mov	bx, 0210h	; start scanning ports
				; 210h, 220h, .. 260h
	; 06/08/2022
	xor	ecx, ecx
	mov	dh, bh
ResetDSP:       
	;mov	dx, bx		; try to reset the DSP.
	;add	dx, 06h
	; 06/08/2022
	mov	dl, bl
	add	dl, 06h

	mov	al, 1
	out	dx, al

	in	al, dx
	in	al, dx
	in	al, dx
	in	al, dx

	xor     al, al
	out	dx, al

	;add	dx, 08h
	; 06/08/2022
	add	dl, 08h
	;mov	cx, 100
	mov	cl, 100
WaitID:
	in	al, dx
	or      al, al
	js      short GetID
	loop    WaitID
	jmp     short NextPort
GetID:          
	;sub	dx, 04h
	; 06/08/2022
	sub	dl, 04h
	in	al, dx
	cmp     al, 0AAh
	je      short Found
	;add	dx, 04h
	; 06/08/2022
	add	dl, 04h
	loop    WaitID
NextPort:
	;add	bx, 10h		; if not response,
	; 06/08/2022
	add	bl, 10h
	;cmp	bx, 260h	; try the next port.
	cmp	bl, 60h
	jbe     short ResetDSP
	stc
	retn
Found:
	mov     [audio_io_base], bx	; SB Port Address Found!
ScanIRQ:
SetIrqs:
	sub 	al, al ; 0
	mov 	[IRQnum], al ; reset
	mov	[audio_intr], al ; reset

	; ah > 0 -> set IRQ vector
	; al = IRQ number
	;mov	ax, 103h ; IRQ 3
	;call	set_hardware_int_vector
	;mov	ax, 104h ; IRQ 4
	;call	set_hardware_int_vector
	mov	ax, 105h ; IRQ 5
	call	set_hardware_int_vector
	mov	ax, 107h ; IRQ 7
	call	set_hardware_int_vector

	mov     dx, [audio_io_base] ; tells to the SB to
	;add	dx, 0Ch		    ; generate a IRQ!
	; 06/08/2022
	add	dl, 0Ch
WaitSb:
	in	al, dx
	or      al, al
	js      short WaitSb
	mov     al, 0F2h
	out	dx, al

	xor     ecx, ecx	; wait until IRQ level
WaitIRQ: 
	mov	al, [IRQnum]
	cmp     al, 0 ; is changed or timeout.
	ja	short IrqOk
	dec	cx
	jnz	short WaitIRQ
	jmp	short RestoreIrqs
IrqOk:
	mov	[audio_intr], al ; set
	mov     dx, [audio_io_base]
	;add	dx, 0Eh
	; 06/08/2022
	add	dl, 0Eh
	in	al, dx	; SB acknowledge.
	mov	al, 20h
	out	20h, al	; Hardware acknowledge.

RestoreIrqs:
	; ah = 0 -> reset IRQ vector
	; al = IRQ number
	;mov	ax, 3 ; IRQ 3
	;call	set_hardware_int_vector
	;mov	ax, 4 ; IRQ 4
	;call	set_hardware_int_vector
	mov	ax, 5 ; IRQ 5
	call	set_hardware_int_vector
	mov	ax, 7 ; IRQ 7
	call	set_hardware_int_vector

	xor	edx, edx
	mov	[audio_dev_id], edx ; 0
	mov	[audio_vendor], edx ; 0
	; 22/11/2023
	;mov	[audio_stats_cmd], edx ; 0

	;popad

	cmp     byte [audio_intr], 1 ; IRQ level was changed?
	
	retn

%macro	SbOut	1
%%Wait:
	in	al, dx
	or	al, al
	js	short %%Wait
	mov	al, %1
	out	dx, al
%endmacro

SbInit_play:
	; 28/01/2025 - TRDOS 386 Kernel v2.0.10
	; 06/08/2022 - TRDOS 386 Kernel v2.0.5
	; 22/10/2017
	; 20/10/2017
	; 06/10/2017
	; 13/07/2017 - 09/08/2017
	; 24/04/2017 - 15/05/2017 - 24/06/2017
	;pushad
SetBuffer:
	;mov	byte [DmaFlag], 0

	mov	ebx, [audio_dma_buff] ; physical addr of DMA buff
	mov	edi, ebx
	;mov	ecx, [audio_dmabuff_size]
	; 28/01/2025 (BugFix)
	mov	ecx, [dma_hbuff_size]
	;shl	ecx, 1 ; * 2 ; *!*

	cmp	byte [audio_bps], 16
	jne	short sbInit_0 ; set 8 bit DMA buffer
	
	; 09/08/2017
	; convert byte count to word count
	; 28/01/2025
	;shr	ecx, 1 ; *!*
	
	dec	ecx ; word count - 1
	; convert byte offset to word offset
	shr	ebx, 1

	; 16 bit DMA buffer setting (DMA channel 5)
	mov     al, 05h  ; set mask bit for channel 5  (4+1)
	out	0D4h, al
	
	xor     al, al   ; stops all DMA processes on selected channel
	out	0D8h, al ; clear selected channel register

	mov     al, bl	 ; byte 0 of DMA buffer offset in words (physical)
	out	0C4h, al ; DMA channel 5 port number

	mov     al, bh   ; byte 1 of DMA buffer offset in words (physical)
	out	0C4h, al
	
	; 09/08/2017
	shr	ebx, 15	 ; complete 16 bit shift
	and	bl, 0FEh ; clear bit 0 (not necessary, it will be ignored)

	mov     al, bl   ; byte 2 of DMA buffer address (physical)
	out	8Bh, al  ; page register port addr for channel 5 ; 13/07/2017

	mov     al, cl   ; low byte of DMA count - 1
	out	0C6h, al ; count register port addr for channel 1

	mov     al, ch   ; high byte of DMA count - 1
	out	0C6h, al

	; channel 5, read, autoinitialized, single mode
	;mov	al, 49h
	mov	al, 59h  ; 06/10/2017 
	out	0D6h, al ; DMA mode register port address

	mov     al, 01h  ; clear mask bit for channel 1
	out	0D4h, al ; DMA mask register port address

	jmp	short ClearBuffer

sbInit_0:
	; 28/01/2025
	shl	ecx, 1 ; half buffer size * 2 ; *!*

	dec     ecx	; 09/08/2017

	; 8 bit DMA buffer setting (DMA channel 1)
	mov     al, 05h ; set mask bit for channel 1  (4+1)
	out	0Ah, al ; DMA mask register

	xor     al, al  ; stops all DMA processes on selected channel
	out	0Ch, al ; clear selected channel register

	mov     al, bl	; byte 0 of DMA buffer address (physical)
	out	02h, al ; DMA channel 1 port number

	mov     al, bh  ; byte 1 of DMA buffer address (physical)
	out	02h, al

	shr	ebx, 16

	mov     al, bl  ; byte 2 of DMA buffer address (physical)
	out	83h, al ; page register port addr for channel 1

	mov     al, cl  ; low byte of DMA count - 1
	out	03h, al ; count register port addr for channel 1

	mov     al, ch  ; high byte of DMA count - 1
	out	03h, al

	; channel 1, read, autoinitialized, single mode
	;mov	al, 49h
	mov	al, 59h ; 06/10/2017 
	out	0Bh, al ; DMA mode register port address

	mov     al, 01h ; clear mask bit for channel 1
	out	0Ah, al ; DMA mask register port address

ClearBuffer:
	;;mov	edi, [audio_dma_buff]
	;;mov	ecx, [audio_dmabuff_size]
	;inc	ecx
	;mov	al, 80h
	;;cld
	;rep	stosb
SetIrq:
	;mov	ebx, SbIrqhandler
	;mov	al, [audio_intr] ; IRQ number
	;call	set_dev_IRQ_service
	;; SETUP (audio) INTERRUPT CALLBACK SERVICE
	;mov	bl, [audio_intr] ; IRQ number
	;mov	bh, [audio_cb_mode]
	;inc	bh  ; 1 = Signal Response Byte method (fixed value)
	;	    ; 2 = Callback service method
	;	    ; 3 = Auto Increment S.R.B. method
	;mov	cl, [audio_srb]
	;mov	edx, [audio_cb_addr]
	;mov	al, [audio_user]
 	;call	set_irq_callback_service
ResetDsp:
	mov     dx, [audio_io_base]
	;add	dx, 06h
	; 06/08/2022
	add	dl, 06h
	mov     al, 1
	out	dx, al

	in	al, dx
	in	al, dx
	in	al, dx
	in	al, dx

	xor     al, al
	out	dx, al

	;mov	cx, 100
	; 06/08/2022
	sub	ecx, ecx
	mov	cl, 100
	sub	ah, ah ; 0
WaitId:         
	mov     dx, [audio_io_base]
	;add	dx, 0Eh
	; 06/08/2022
	add	dl, 0Eh
	in	al, dx
	or      al, al
	js      short sb_GetId
	loop    WaitId
	jmp     sb_Exit
sb_GetId:
	mov     dx, [audio_io_base]
	;add	dx, 0Ah
	; 06/08/2022
	add	dl, 0Ah
	in	al, dx
	cmp     al, 0AAh
	je      short SbOk
	loop    WaitId
	jmp	sb_Exit
SbOk:
	mov     dx, [audio_io_base]
	;add	dx, 0Ch
	; 06/08/2022
	add	dl, 0Ch
	SbOut   0D1h ; Turn on speaker
	SbOut   41h ; 8h bit or 16 bit transfer
	mov	bx, [audio_freq] ; sampling rate (Hz)
	SbOut	bh ; sampling rate high byte
	SbOut	bl ; sampling rate low byte

	; 22/05/2017
	call	sb16_volume_initial ; 15/05/2017
	; 20/05/2017
	;call	sb16_volume

StartDma: 
	; autoinitialized mode
	cmp	byte [audio_bps], 16 ; 16 bit samples
	je	short sb_play_1
	; 8 bit samples
	mov	bx, 0C6h ; 8 bit output (0C6h)
	cmp	byte [audio_stmo], 2 ; 1 = mono, 2 = stereo
	jb	short sb_play_2
	mov	bh, 20h	; 8 bit stereo (20h)
	jmp	short sb_play_2
sb_play_1:
	; 16 bit samples
	mov	bx, 10B6h ; 16 bit output (0B6h)
	cmp	byte [audio_stmo], 2 ; 1 = mono, 2 = stereo
	jb	short sb_play_2
	add	bh, 20h	; 16 bit stereo (30h)
sb_play_2:     
	; PCM output (8/16 bit mono autoinitialized transfer)
	SbOut   bl ; bCommand
	SbOut	bh ; bMode
	mov	ebx, [audio_dmabuff_size]  ; 15/05/2017
	shr	ebx, 1 ; half buffer size
	; 20/10/2017	
	cmp	byte [audio_bps], 16 ; 16 bit DMA
	jne	short sb_play_3
	shr	ebx, 1 ; byte count to word count
sb_play_3: 
	;dec	bx  ; wBlkSize is one less than the actual size
	; 06/08/2022
	dec	ebx
	SbOut   bl
	SbOut   bh

	mov	byte [audio_play_cmd], 1 ; playing !

	;; Set Voice and master volumes
	;mov	dx, [audio_io_base]
	;add	dl, 4 ; Mixer chip Register Address Port
	;SbOut	30h   ; select Master Volume Register (L)
	;inc	dl    ; Mixer chip Register Data Port
	;SbOut	0F8h  ; Max. volume value is 31 (31*8)
	;dec	dl
	;SbOut	31h   ; select Master Volume Register (R)
	;inc	dl
	;SbOut	0F8h  ; Max. volume value is 31 (31*8)
	;dec	dl
	;SbOut	32h   ; select Voice Volume Register (L)
	;inc	dl
	;SbOut	0F8h  ; Max. volume value is 31 (31*8)
	;dec	dl
	;SbOut	33h   ; select Voice Volume Register (R)
	;inc	dl
	;SbOut	0F8h  ; Max. volume value is 31 (31*8)
	;;
	;dec	dl
	;SbOut	44h   ; select Treble Register (L)
	;inc	dl
	;SbOut	0F0h  ; Max. Treble value is 15 (15*16)
	;dec	dl
	;SbOut	45h   ; select Treble Register (R)
	;inc	dl
	;SbOut	0F0h  ; Max. Treble value is 15 (15*16)
	;dec	dl
	;SbOut	46h   ; select Bass Register (L)
	;inc	dl
	;SbOut	0F0h  ; Max. Bass value is 15 (15*16)
	;dec	dl
	;SbOut	47h   ; select Bass Register (R)
	;inc	dl
	;SbOut	0F0h  ; Max. Bass value is 15 (15*16)

sb_Exit:
	;popad
	retn

sb16_int_handler:
	; Interrupt Handler for Sound Blaster 16 Audio Card
	; Note: called by 'dev_IRQ_service'
	; 28/01/2025
	;	TRDOS 386 Kernel v2.0.10
	; 20/10/2017
	; 12/10/2017
	; 10/10/2017 
	; 12/05/2017, 09/10/2017
	; 24/04/2017 (TRDOS 386 kernel, 'audio.s')
	; 10/03/2017 - 'PLAYWAV.PRG' ('playwav.s')

	;push	eax ; * must be saved !
	;push	ebx ; * must be saved !
	;push	ecx
	;push	edx
	;push	esi
	;push	edi

	mov     dx, [audio_io_base]
	; 20/10/2017
	add     dl, 0Fh ; 2xFh (DSP 16 bit intr ack)
	cmp	byte [audio_bps], 16
	je	short sb_irq_16bit_ack
sb_irq_8bit_ack:
	dec	dl  ; 2xEh (DSP 8 bit intr ack)
sb_irq_16bit_ack:
	in	al, dx

	;cmp	byte [audio_busy], 0
	;ja	short sb_irq_h3

	;mov	byte [audio_busy], 1

	cmp	byte [audio_play_cmd], 1
	jnb	short sb_irq_h1
sb_irq_h0:
	call	sb16_stop
	jmp	short sb_irq_h3
sb_irq_h1:
	;call	sb16_tuneloop
	; 09/10/2017
sb16_tuneloop:
	mov	edi, [audio_dma_buff]
	;mov	ecx, [audio_dmabuff_size]
	;shr	ecx, 1 ; dma buff size / 2 = half buffer size
	; 28/01/2025 (BugFix)
	mov	ecx, [dma_hbuff_size]

	; 22/05/2017
	test	byte [audio_flag], 1  ; Current flag value
	jz	short sb_tlp1 ; EOL (Half Buffer 1 must be filled)
	; FLAG (Half Buffer 2 must be filled)
	add	edi, ecx
	; 15/05/2017
sb_tlp1: 
	mov	esi, [audio_p_buffer] ; phy addr of audio buff
	;rep	movsb
	shr	ecx, 2 ; half buff size / 4
	rep	movsd
	;retn

	; 10/10/2017
	; switch flag value
	xor	byte [audio_flag], 1

	; 12/10/2017
	; [audio_flag] = 0 : Playing dma half buffer 2 (odd intr count)
			   ; Next buffer (to update) is dma half buff 1
	; 	       = 1 : Playing dma half buffer 1 (even intr count)
			   ; Next buffer (to update) is dma half buff 2

sb_irq_h3:
	;mov	byte [audio_busy], 0

	;pop	edi
	;pop	esi
	;pop	edx
	;pop	ecx
	;pop	ebx ; * must be restored !
	;pop	eax ; * must be restored !
	
	retn

sb16_volume:
	; 06/08/2022 (TRDOS 386 v2.0.5)
	; 22/10/2017
	; mov [audio_master_volume_l], cl
	; mov [audio_master_volume_h], ch
	mov	[audio_master_volume], cx
sb16_volume_initial:
	;push	dx ; DX (port address) must be saved
	; 06/08/2022
	push	edx
	mov	dx, [audio_io_base]
	;add	dx, 4 ; Mixer chip address port
	; 06/08/2022
	add	dl, 4
	mov	al, 22h ; master volume
	out	dx, al
	;inc	dx
	; 06/08/2022
	inc	edx
	mov	ah, [audio_master_volume_l]
	shr	ah, 2 ; 32 -> 8 level
	shl	ah, 5 ; bit 5 to 7
	mov	al, [audio_master_volume_r]
	shr	al, 2 ; 32 -> 8 level
	;and	al, 0Fh
	shl	al, 1 ; bit 1 to 3
	or	al, ah
	out	dx, al
	;pop	dx ; DX (port address) must be restored
	; 06/08/2022
	pop	edx
	retn

sb16_pause:
	; 06/08/2022 (TRDOS 386 v2.0.5)
	mov	dx, [audio_io_base]
	;add	dx, 0Ch ; Command & Data Port
	; 06/08/2022
	add	dl, 0Ch
	cmp	byte [audio_bps], 16 ; 16 bit samples
	je	short sb_pause_1
	; 8 bit samples
	mov	bl, 0D0h ; 8 bit DMA mode
	jmp	short sb_pause_2
sb_pause_1:
	; 16 bit samples
	mov	bl, 0D5h ; 16 bit DMA mode
sb_pause_2:
	SbOut   bl ; bCommand
sb_pause_3:
	retn

sb16_continue:
	; 06/08/2022 (TRDOS 386 v2.0.5)
	mov	dx, [audio_io_base]
	;add	dx, 0Ch ; Command & Data Port
	; 06/08/2022
	add	dl, 0Ch
	cmp	byte [audio_bps], 16 ; 16 bit samples
	je	short sb_cont_1
	; 8 bit samples
	mov	bl, 0D4h ; 8 bit DMA mode
	jmp	short sb_cont_2
sb_cont_1:
	; 16 bit samples
	mov	bl, 0D6h ; 16 bit DMA mode
sb_cont_2:     
	SbOut   bl ; bCommand
sb_cont_3:
	retn

sb16_stop:
	; 06/08/2022 (TRDOS 386 v2.0.5)
	; 24/04/2017
	cmp	byte [audio_play_cmd], 0
	jna	short sb16_stop_4

	; 22/05/2017
	mov	dx, [audio_io_base]
	;add	dx, 0Ch
	; 06/08/2022
	add	dl, 0Ch

	mov	bl, 0D9h ; exit auto-initialize 16 bit transfer
	; stop  autoinitialized DMA transfer mode 
	cmp	byte [audio_bps], 16 ; 16 bit samples
	je	short sb16_stop_1
	;mov	bl, 0DAh ; exit auto-initialize 8 bit transfer
	inc	bl
sb16_stop_1:
	SbOut	bl ; exit auto-initialize transfer command

	xor     al, al ; stops all DMA processes on selected channel

	cmp	byte [audio_bps], 16 ; 16 bit samples
	je	short sb16_stop_2
	out	0Ch, al ; clear selected channel register
	jmp	short sb16_stop_3

sb16_stop_2:
	out	0D8h, al ; clear selected channel register

sb16_stop_3:
	mov	byte [audio_play_cmd], 0 ; stop !
SbDone:
	;mov	dx, [audio_io_base]
	;add	dx, 0Ch
	SbOut   0D0h
	SbOut   0D3h
sb16_stop_4:
	retn

sb16_reset:
	; 06/08/2022 (TRDOS 386 v2.0.5)
	; 24/04/2017
	mov     dx, [audio_io_base] ; try to reset the DSP.
	;add	dx, 06h
	; 06/08/2022
	add	dl, 06h
	mov	al, 1
	out	dx, al

	in	al, dx
	in	al, dx
	in	al, dx
	in	al, dx

	xor     al, al
	out	dx, al

	;add	dx, 08h
	; 06/08/2022
	add	dl, 08h
	;mov	cx, 100
	sub	ecx, ecx
	mov	cl, 100
sbrstWaitID:
	in	al, dx
	or      al, al
	js      short sbrstGetID
	loop    sbrstWaitID
	stc
	retn
sbrstGetID:
	;sub	dx, 04h
	; 06/08/2022
	sub	dl, 04h
	in	al, dx
	cmp     al, 0AAh
	je      short sb_rst_retn
	;add	dx, 04h
	; 06/08/2022
	add	dl, 04h
	loop    sbrstWaitID
sb_rst_retn:
	retn

ac97_codec_config:
	; 06/06/2024
	; 04/06/2024
	; 03/06/2024
	; 02/06/2024
	; 01/06/2024 (TRDOS 386 v2.0.8)
	; 26/11/2023
	; 21/11/2023
	; 20/11/2023
	; 19/11/2023 (TRDOS 386 v2.0.7)
	; 10/06/2017
	; 05/06/2017
	; 29/05/2017
	; 28/05/2017 (TRDOS 386, 'audio.s')
	; 07/11/2016 (Erdogan Tan)
	; Derived from 'codecConfig' procedure in 'CODEC.ASM'
	; .wav player for DOS by Jeff Leyda (02/09/2002)

	;; 'PLAYER.ASM'
	;; get ICH base address regs for mixer and bus master

init_ac97_controller: ; 10/06/2017
	mov	eax, [audio_dev_id]
        ;mov	al, NAMBAR_REG
        ;;call  pciRegRead16			; read PCI registers 10-11
        ;call	pciRegRead32
	;and	dx, IO_ADDR_MASK 		; mask off BIT0
	;;and	edx, IO_ADDR_MASK

        ;mov	[NAMBAR], dx			; save audio mixer base addr

        ;mov	al, NABMBAR_REG
        ;;call	pciRegRead16
        ;call	pciRegRead32
	;and	dx, 0FFC0h ; IO_ADDR_MASK
	;;and	edx, 0FFC0h

        ;mov    [NABMBAR], dx			; save bus master base addr

	;mov	eax, [audio_dev_id]
        mov	al, PCI_CMD_REG
        ;call	pciRegRead8			; read PCI command register
        call	pciRegRead16
	or	dl, IO_ENA+BM_ENA               ; enable IO and bus master
        ;call	pciRegWrite8
	call	pciRegWrite16

	; 'CODEC.ASM'

	; enable codec, unmute stuff, set output rate
;	; entry: [audio_freq] = desired sample rate
		
;	mov	dx, [NAMBAR]
;	add	dx, CODEC_EXT_AUDIO_CTRL_REG  	; 2Ah
;	in	ax, dx
;	or	ax, 1
;	out	dx, ax 				; Enable variable rate audio

;       ;call	delay1_4ms
;       ;call	delay1_4ms
;       ;call	delay1_4ms
;       ;call	delay1_4ms

;	mov	ax, [audio_freq]		; sample rate

;	mov	dx, [NAMBAR]               	
;	add	dx, CODEC_PCM_FRONT_DACRATE_REG	; 2Ch
;	out	dx, ax 				; out sample rate
		
;       ;call	delay1_4ms
;       ;call	delay1_4ms
;       ;call	delay1_4ms
;       ;call	delay1_4ms

	;mov	dx, [NAMBAR]			; mixer base address
        ;add	dx, CODEC_RESET_REG  		; reset register
        ;mov	ax, 42
	;out	dx, ax                          ; reset

	;mov	dx, [NABMBAR]			; bus master base address
        ;add	dx, GLOB_STS_REG
        ;mov	ax, 2
	;out	dx, ax

	; 01/06/2024
	; 16/05/2024
	; 02/12/2023
	;call	delay_100ms ; 29/05/2017

	; 02/12/2023
	;call	delay1_4ms
	;call	delay1_4ms
	;call	delay1_4ms
	;call	delay1_4ms

init_ac97_codec:
	; 26/11/2023
	; 19/11/2023
	;   (playwav3.com, ac97_vra.asm, Erdogan Tan, 19/11/2023)
	; 10/06/2017
	; 29/05/2017
	; 28/05/2017 - Erdogan Tan (Ref: KolibriOS, intelac97.asm)

	;; 23/11/2023 - temporary
	;push	ebx
	;mov	ebx, 0B8000h
	;mov	al, '?'
	;mov	ah, 4Eh
	;mov	[ebx], ax
	;pop	ebx

	; 01/06/2024
	mov	byte [reset], 1

	; 19/11/2023
	mov	ebp, 40	; 21/11/2023
_initc_1:
	; 26/05/2024
	mov	dx, GLOB_STS_REG ; 30h
	add	dx, [NABMBAR]
	in	eax, dx

	; 01/06/2024
	; 02/12/2023
	;call	delay1_4ms

	cmp	eax, 0FFFFFFFFh ; -1
	jne	short _initc_3
_initc_2:
	dec	ebp	; 21/11/2023
	jz	short _ac97_codec_ready

	call	delay_100ms
	jmp	short _initc_1
_initc_3:
	test	eax, CTRL_ST_CREADY
	jnz	short _ac97_codec_ready

	; 01/06/2024
	cmp	byte [reset], 0
	jna	short _initc_2

	call	reset_ac97_codec
	;jc	short _initc_2
	; 01/06/2024
	mov	byte [reset], 0
	; 26/11/2023
	jmp	short _initc_2

_ac97_codec_ready:
	mov	dx, [NAMBAR]
	;add	dx, 0 ; ac_reg_0 ; reset register
	out	dx, ax

	; 06/06/2024
	; 01/06/2024
	;;;
	;call	delay1_4ms
	;call	delay1_4ms
	;call	delay1_4ms
	;call	delay1_4ms
	;;;

	; 01/06/2024
	; 26/05/2024
	; 19/11/2023
	;call	delay_100ms
	; 24/11/2023 - temporary
	;call	delay_100ms
	;call	delay_100ms
	;call	delay_100ms

	; 01/06/2024
	;;;
	or	ebp, ebp  ; 21/11/2023
	jnz	short _ac97_codec_init_ok
	;;;

	xor	eax, eax ; 0
	mov	dx, [NAMBAR]
	add	dx, CODEC_REG_POWERDOWN
	out	dx, ax

	; 10/06/2017
	; 29/05/2017
	; wait for 1 second
	; 01/06/2024
	; 16/05/2024
	mov	ecx, 1000 ; 1000*0.25ms
	; 20/11/2023
	;mov	ecx, 10
	; 23/05/2024
	;mov	cl, 10
	; 24/11/2023 - temporary
	;mov	cl, 40
	; ecx = 40 ; 23/05/2024
_ac97_codec_rloop:
	; 01/06/2024
	call	delay1_4ms
	; 06/06/2024
	call	delay1_4ms
	call	delay1_4ms
	call	delay1_4ms
	; 01/06/2024
	; 19/11/2023
	; 22/11/2023
	;push	ecx
	;call	delay_100ms
	;pop	ecx

	; 01/06/2024
	;;;
	mov	dx, [NAMBAR]
	add	dx, CODEC_REG_POWERDOWN
	;;;
	in	ax, dx

	; 06/06/2024
	;call	delay1_4ms

	;and	ax, 0Fh
	; 21/11/2023
	and	al, 0Fh
	cmp	al, 0Fh
	je	short _ac97_codec_init_ok
	; 24/11/2023 - temporary
	;je	short _ac97_codec_init_ok_
_ac97_codec_yloop:	
	loop	_ac97_codec_rloop 
	; 22/11/2023
	; cf = 1
init_ac97_codec_err1:
	;stc
init_ac97_codec_err2:
	retn

;_ac97_codec_init_ok_:
	;mov	dx, GLOB_STS_REG ; 30h
	;add	dx, [NABMBAR]
	;in	eax, dx
	;test	eax, CTRL_ST_CREADY
	;jnz	short _ac97_codec_init_ok
	;stc 
	;retn

_ac97_codec_init_ok:
	; 06/06/2024 (temporary)
	; (this may not be needed)
	;;;
	; 24/05/2024
	; 26/11/2023
	; 23/11/2023
	xor	eax, eax
	; 21/11/2023 - temporary
	; 19/11/2023
	mov	al, 2 ; force set 16-bit 2-channel PCM
	mov	dx, GLOB_CNT_REG ; 2Ch
	add	dx, [NABMBAR]
	out	dx, eax
	;
	call	delay1_4ms
	;;;

	; 23/11/2023 - temporary
	;call	delay_100ms
	; 06/06/2024
	;call	delay1_4ms

	; 10/06/2017
	call 	reset_ac97_controller

	; 21/11/2023
	;call	delay1_4ms
	
	; 01/06/2024
	; 24/05/2024
	; 23/05/2024
	; 21/11/2023 - temporary
	call	delay_100ms ; 06/06/2024

;	call 	setup_ac97_codec
;
;detect_ac97_codec:
;	retn

setup_ac97_codec:
	; 06/06/2024 - TRDOS 386 v2.0.8
	;;;
	mov	dx, [NAMBAR]
	add	dx, CODEC_EXT_AUDIO_CTRL_REG ; 2Ah
	in	ax, dx

	;and	al, ~BIT1 ; Clear DRA
	and	al, ~(BIT1+BIT0) ; Clear DRA+VRA
	;or	al, BIT0  ; Set VRA

	mov	dx, [NAMBAR]
	add	dx, CODEC_EXT_AUDIO_CTRL_REG ; 2Ah
	out	dx, ax
	;;;

	; 24/11/2023
	; 19/11/2023 - TRDOS 386 v2.0.7
	mov	byte [VRA], 1

	; 25/11/2023 - temporary
	;jmp	short vra_not_supported

	; 23/05/2024
	; 18/05/2024
	;call	delay1_4ms ; 06/06/2024
	;call	delay_100ms

; 06/06/2024
; 01/06/2024
%if 0
	; 24/05/2024
	; 23/05/2024
	mov	dx, [NAMBAR]
	add	dx, CODEC_EXT_AUDIO_REG	; 28h
	in	ax, dx
	
	; 18/05/2024
	; 02/12/2023
	call	delay1_4ms
	; 17/05/2024
	;call	delay_100ms
	;
	test	al, 1 ; BIT0 ; Variable Rate Audio bit
	jz	short vra_not_supported

	mov	dx, [NAMBAR]
	add	dx, CODEC_EXT_AUDIO_CTRL_REG ; 2Ah
	in	ax, dx

	; 06/06/2024
	; 23/05/2024
	; 02/12/2023
	;call	delay1_4ms
	;call	delay_100ms ; 18/05/2024
	; 24/05/2024
	;call	delay1_4ms
	;call	delay1_4ms
	;call	delay1_4ms

	;and	al, ~BIT1 ; Clear DRA
	;;;
	; 01/06/2024
	and	al, ~(BIT1+BIT0) ; Clear DRA+VRA
	;;;
	; 04/06/2024
	;mov	dx, [NAMBAR]
	;add	dx, CODEC_EXT_AUDIO_CTRL_REG ; 2Ah
	;;;
	out	dx, ax
%endif

	; 04/06/2024
	call	delay1_4ms
	
	; 01/06/2024
	; check VRA
	mov	dx, [NAMBAR]
	add	dx, CODEC_EXT_AUDIO_REG	; 28h
	in	ax, dx

	; 06/06/2024
	;call	delay1_4ms
	
	test	al, 1 ; BIT0	; Variable Rate Audio bit
	jz	short vra_not_supported

	inc	ax ; 0FFh -> 0
	jnz	short not_alc ; skip ALC850 (and ALC655) checking
	; ax = -1 -> 0 

	;;;;
	; 06/06/2024 (temporary!?)
	; check ALC850
	; (which does not support VRA but it may not be detected)
	;
	call	delay1_4ms
	;
	mov	dx, [NAMBAR]
	add	dx, CODEC_VENDOR_ID1 ; 7Ch
	in	ax, dx
	shl	eax, 16 ; *
	;cmp	ax, 414Ch ; VENDOR_ID - 'AL'
	;jne	short not_alc
	mov	dx, [NAMBAR]
	add	dx, CODEC_VENDOR_ID2 ; 7Eh
	in	ax, dx
	;inc	edx
	;inc	edx
	;in	ax, dx ; VENDOR ID2

	inc	eax ; 0FFFFFFFFh -> 0 ; 0 -> 1
	jz	short vra_not_supported
	dec	eax ; 1 -> 0
	jz	short vra_not_supported	

	ror	eax, 16
	cmp	ax, 414Ch ; VENDOR_ID - 'AL'
	jne	short not_alc
	rol	eax, 16
	
	; ALC850
	cmp	ah, 47h ; 'G'
	jne	short not_alc
	cmp	al, 90h
	;cmp	ax, 4790h ; VENDOR ID - 'G' ; bit 8-15
			  ; CHIP ID - 1001b ; bit 4-7
			  ; Version Number - 0 ; bit 0-3
	;jne	short not_alc850 ; not_alc
	je	short vra_not_supported
	;
; 06/06/2024
%if 0
not_alc850:
	; ALC655
	cmp	al, 60h
	;cmp	ax, 4760h ; VENDOR ID - 'G' ; bit 8-15
			  ; CHIP ID - 0110b ; bit 4-7
			  ; Version Number - 0 ; bit 0-3
	;jne	short not_alc655
	je	short vra_not_supported
	;
not_alc655:
%endif
not_alc:
	; 06/06/2024

	mov	dx, [NAMBAR]
	add	dx, CODEC_EXT_AUDIO_CTRL_REG  	; 2Ah
	in	ax, dx

	;and	al, ~BIT1 ; Clear DRA

	or	al, AC97_EA_VRA ; 1 ; 04/11/2023
	
	; 06/06/2024
	; 04/06/2024
	;mov	dx, CODEC_EXT_AUDIO_CTRL_REG  	; 2Ah
	;add	dx, [NAMBAR]

	out	dx, ax		; Enable variable rate audio

	; 01/06/2024
	mov	ecx, 10
	; 21/11/2023
	;mov	cl, 10
check_vra:
	; 01/06/2024
	;call	delay_100ms
	call	delay1_4ms

	;;;
	; 04/06/2024
	mov	dx, [NAMBAR]
	add	dx, CODEC_EXT_AUDIO_CTRL_REG  	; 2Ah
	;;;

	in	ax, dx

	; 24/05/2024
	; 18/05/2024
	; 02/12/2023
	;call	delay1_4ms

	;test	al, AC97_EA_VRA ; 1
	; 04/06/2024
	and	al, AC97_EA_VRA ; 1
	jnz	short vra_ok

	loop	check_vra

vra_not_supported:	; 24/11/2023
	; VRA is not usable
	;mov	byte [VRA], 0
	dec	byte [VRA]

; 03/06/2024
%if 0
	; 02/06/2024
	jmp	short set_volume

	; 02/06/2024
set_sampling_rate:
	mov	dx, [NAMBAR]
	add	dx, CODEC_PCM_FRONT_DACRATE_REG
	out	dx, ax
	retn

	; 02/06/2024
get_sampling_rate:
	mov	dx, CODEC_PCM_FRONT_DACRATE_REG
	add	dx, [NAMBAR]
	in	ax, dx
	retn
%endif

vra_ok:

; 03/06/2024
%if 0
	; 02/06/2024
	; a second test for verifying VRA status
	; (may be needed for ALC850 codec and CK804 controller)
	
	call	get_sampling_rate
	or	ax, ax
	jz	short vra_not_supported
	cmp	ax, 48000
	jne	short set_volume
	mov	ax, 24000
	call	set_sampling_rate
	call	get_sampling_rate
	cmp	ax, 48000
	je	short vra_not_supported
	mov	ax, 48000
	call	set_sampling_rate
	;call	get_sampling_rate
%endif

	; 02/06/2024
set_volume:
	; 24/05/2024
	; 18/05/2024 - TRDOS 386 v2.0.8
	; 20/11/2023
	; 19/11/2023 - TRDOS 386 v2.0.7
	; 06/08/2022 - TRDOS 386 v2.0.5
	; 22/07/2020
	; 10/06/2017
	; 29/05/2017

	;mov	eax, 0202h
	; 21/11/2023
	;mov	eax, 0B0Bh
	; 23/11/2023
	mov	eax, 1D1Dh
	mov	[audio_master_volume], ax
	; 24/05/2024
	mov	[audio_pcmo_volume], ax
	;mov	ax, 1F1Fh ; 31, 31

	; 21/11/2023
	;push	eax
	
	; 20/11/2023
	;mov	ax, 0B0Bh
	; 23/11/2023
	mov	ax, 0202h

  	mov	dx, [NAMBAR]
  	add	dx, CODEC_MASTER_VOL_REG	;02h
	;xor	ax, ax 	; volume attenuation = 0 (max. volume)
	; 06/08/2022
	;xor	eax, eax
	; 19/11/2023
	out	dx, ax

	; 01/06/2024
	; 23/05/2024
	; 16/05/2024
	; 20/11/2023
	call	delay1_4ms
	;call	delay1_4ms
	;call	delay1_4ms
	;call	delay1_4ms
	; 21/11/2023 temporary
	;call	delay_100ms ; 18/05/2024

	; 21/11/2023
	;pop	eax
	
	; 23/11/2023
	; 21/11/2023
	;mov	ax, 0202h

  	mov	dx, [NAMBAR]
  	add	dx, CODEC_PCM_OUT_REG		;18h
  	;xor	ax, ax
  	out	dx, ax

	; 01/06/2024
	; 23/05/2024
	; 16/05/2024
	; 20/11/2023
        ;call	delay1_4ms
        ;call	delay1_4ms
        ;call	delay1_4ms
	;call	delay1_4ms
	; 21/11/2023 - temporary
	;call	delay_100ms ; 18/05/2024

; 23/11/2023
%if 0
  	mov	dx, [NAMBAR]
  	add	dx, CODEC_MASTER_MONO_VOL_REG	;06h
	;xor	ax, ax
  	out	dx, ax

        ;call	delay1_4ms
        ;call	delay1_4ms
        ;call	delay1_4ms
	;call	delay1_4ms

  	mov	dx, [NAMBAR]
  	add	dx, CODEC_PCBEEP_VOL_REG	;0Ah
  	;xor	ax, ax
  	out	dx, ax

        ;call	delay1_4ms
        ;call	delay1_4ms
        ;call	delay1_4ms
	;call	delay1_4ms

	mov	ax, 8008h ; Mute
  	mov	dx, [NAMBAR]
	; 22/07/2020
	add	dx, CODEC_PHONE_VOL_REG		;0Ch
				 ; AC97_PHONE_VOL ; TAD Input (Mono)
  	out	dx, ax

        ;call	delay1_4ms
        ;call	delay1_4ms
        ;call	delay1_4ms
	;call	delay1_4ms

        mov	ax, 0808h
  	mov	dx, [NAMBAR]
        add	dx, CODEC_LINE_IN_VOL_REG ;10h ; Line Input (Stereo)
  	out	dx, ax

        ;call	delay1_4ms
        ;call	delay1_4ms
        ;call	delay1_4ms
	;call	delay1_4ms

	;mov	ax, 0808h
  	mov	dx, [NAMBAR]
        add	dx, CODEC_CD_VOL_REG ;12h ; CR Input (Stereo)
  	out	dx, ax

        ;call	delay1_4ms
        ;call	delay1_4ms
        ;call	delay1_4ms
	;call	delay1_4ms

	;mov	ax, 0808h
  	mov	dx, [NAMBAR]
        add	dx, CODEC_AUX_VOL_REG ;16h ; Aux Input (Stereo)
  	out	dx, ax

        ;call	delay1_4ms
        ;call	delay1_4ms
        ;call	delay1_4ms
        ;call	delay1_4ms

	; 21/11/2023 - temporary
	;call	delay_100ms
%endif
	; 16/05/2024
	;clc

set_volume_ok:

;detect_ac97_codec:
	retn

set_ac97_bdl: ; Set AC97 (ICH) Buffer Descriptor List
	; 04/06/2024 - TRDOS 386 v2.0.8
	; 06/08/2022 - TRDOS 386 v2.0.5
	; 17/06/2017
	; 11/06/2017
	; 28/05/2017
	; eax = dma buffer address = [audio_DMA_buff]
	;; ecx = dma buffer buffer size = [audio_dmabuff_size]

	; 04/06/2024
	; ecx = DMA half buffer size as truncated for 8 byte alignment

	;shr	ecx, 1 ; dma half buffer size
	mov	esi, ecx

        mov     edi, audio_bdl_buff	; get BDL address
	;mov	ecx, 32 / 2		; make 32 entries in BDL
	; 06/08/2022
	sub	ecx, ecx
	mov	cl, 16

	jmp	short s_ac97_bdl1

s_ac97_bdl0:
	; set buffer descriptor 0 to start of data file in memory

 	mov	eax, [audio_dma_buff]	; Physical address of DMA buffer
 
s_ac97_bdl1:
	stosd				; store dmabuffer1 address

	mov	edx, eax

;
; Buffer Descriptors List
; As stated earlier, each buffer descriptor list is a set of (up to) 32
; descriptors, each 8 bytes in length. Bytes 0-3 of a descriptor entry point
; to a chunk of memory to either play from or record to. Bytes 4-7 of an
; entry describe various control things detailed below.
; 
; Buffer pointers must always be aligned on a Dword boundry.
;

;IOC	equ     BIT31			; Fire an interrupt whenever this
					; buffer is complete.

;BUP	equ     BIT30			; Buffer Underrun Policy.
					; if this buffer is the last buffer
					; in a playback, fill the remaining
					; samples with 0 (silence) or not.
  					; It's a good idea to set this to 1
					; for the last buffer in playback,
					; otherwise you're likely to get a lot
					; of noise at the end of the sound.

;
; Bits 15:0 contain the length of the buffer, in number of samples, which
; are 16 bits each, coupled in left and right pairs, or 32bits each.
; Luckily for us, that's the same format as .wav files.
;
; A value of FFFF is 65536 samples. Running at 44.1Khz, that's just about
; 1.5 seconds of sample time. FFFF * 32bits is 1FFFFh bytes or 128k of data.
;
; A value of 0 in these bits means play no samples.
;

	mov	eax, esi ; DMA half buffer size

		; 04/06/2024 - Erdogan Tan
		; NOTE: I have changed DMA half buffer size to truncated
		; (8 byte aligned) audio buffer size for smooth audio playing
		; (it was page border aligned before June 4, 2024)
		;
		; For example:
		;    For 65416 bytes audio buffer (22kHz, 16bit samples)
		;       (user's audio buff virtual addr is mapped to physical)
		;	((kernel's and user's audio buff pages are same)) 
		;    1) Memory allocation (of it) is 65536 bytes
		;    2) DMA half buffer size is 65416 bytes
		;	(it would be 65536 bytes before this modification)
		;	((additional 140 bytes would cause to a noise))	     
		;    3) Total DMA buffer size is 131072 bytes
		;       (the last 240 bytes will not be used for playing)
		;
		; (buffer will be truncated if the size is not a multiple of 8) 

	add	edx, eax
	shr	eax, 1	; count of 16 bit samples
	; 19/11/2023
	or	eax, IOC+BUP
	;or	eax, IOC ; 11/06/2017
	stosd

; 2nd buffer:

        mov	eax, edx ; Physical address of the 2nd half of DMA buffer
	stosd		 ; store dmabuffer2 address

; set length to [audio_dmabuff_size]/2
; Set control (bits 31:16) to BUP, bits 15:0=number of samples
; 
	mov	eax, esi ; DMA half buffer size
	shr	eax, 1 ; count of 16 bit samples
	; 19/11/2023
	or	eax, IOC+BUP
	;or	eax, IOC ; 11/06/2017
	stosd

	loop    s_ac97_bdl0
	
	retn

ac97_start_play:
	; 26/05/2024 - TRDOS 386 v2.0.8
	; 26/11/2023
	; 20/11/2023  
	; 19/11/2023 - TRDOS 386 v2.0.7
	; 06/08/2022 - TRDOS 386 v2.0.5
	; 28/05/2017
	; Derived from 'playWav' procedure in 'ICHWAV.ASM'
	; .wav player for DOS by Jeff Leyda (02/09/2002)

	; set output rate
	; entry: [audio_freq] = desired sample rate

	; 21/11/2023 - temporary
	;call	ac97_codec_config

; 20/11/2023
%if 0
	AC97_EA_VRA equ 0001h ; 04/11/2023
		
	mov	dx, [NAMBAR]
	add	dx, CODEC_EXT_AUDIO_CTRL_REG  	; 2Ah
	in	ax, dx

	; 23/05/2024
	call	delay1_4ms

	and	al, ~BIT1 ; Clear DRA
	or	al, AC97_EA_VRA ; 1 ; 04/11/2023
	out	dx, ax			; Enable variable rate audio

       ;call	delay1_4ms
       ;call	delay1_4ms
       ;call	delay1_4ms
       ;call	delay1_4ms

	; 23/05/2024
	call	delay_100ms
%endif

	; 26/11/2023
	; 24/11/2023 - temporary
	;mov	ax, 48000

	; 20/11/2023
	cmp	byte [VRA], 1
	jb	short skip_set_rate

	mov	ax, [audio_freq]		; sample rate

;skip_set_rate:	; 24/11/2023
	mov	dx, [NAMBAR]
	add	dx, CODEC_PCM_FRONT_DACRATE_REG	; 2Ch
	out	dx, ax 				; out sample rate
	
	; 01/06/2024
	; 27/11/2023	
	;call	delay1_4ms
	;call	delay1_4ms
	;call	delay1_4ms
	;call	delay1_4ms
	; 27/11/2023 - temporary
	;call	delay_100ms

	; 26/11/2023
skip_set_rate:	; 20/11/2023

	;; 23/11/2023 - temporary
	;push	ebx
	;mov	ebx, 0B8000h
	;mov	al, 'x'
	;mov	ah, 4Eh
	;mov	[ebx], ax
	;pop	ebx
;
; register reset the DMA engine. This may cause a pop noise on the output
; lines when the device is reset. Prolly a better idea to mute output, then
; reset.
;
	;; 21/11/2023 - temporary
	;; 20/11/2023
	;mov	dx, [NABMBAR]
	;add	dx, PO_CR_REG			; set pointer to Cntl reg
	;mov	al, RR				; set reset
	;out	dx, al				; self clearing bit

	; 23/11/2023 - temporary
	;call	delay_100ms

;	mov	edi, audio_bdl_buff
;	mov	edx, [audio_dmabuff_size]
;	shr	edx, 1
;	mov	ecx, 32/2
;ac97_set_bdl_buffer:
;	; 1st half of DMA buffer
;	mov	eax, [audio_dma_buff]
;	push	eax
;	stosd
;	mov	eax, edx ; dma buffer size / 2
;	or	eax, IOC+BUP
;	stosd
;	pop	eax
;	; 2nd half of DMA buffer
;	add	eax, edx
;	stosd
;	mov	eax, edx ; dma buffer size / 2
;	or	eax, IOC+BUP
;	stosd
;	loop	ac97_set_bdl_buffer
 	
; tell the DMA engine where to find our list of Buffer Descriptors.
; this 32bit value is a flat mode memory offset (ie no segment:offset)
;
; write NABMBAR+10h with offset of buffer descriptor list
;
        mov	eax, audio_bdl_buff
	mov	dx, [NABMBAR]
	add	dx, PO_BDBAR_REG
	out	dx, eax

	; 01/06/2024
	; 24/05/2024
	; 23/11/2023 - temporary
	;call	delay_100ms
	; 23/05/2024
	;call	delay1_4ms
;
; All set. Let's play some music.
;
	;mov	eax, 31
	; 06/08/2022
	sub	eax, eax
	mov	al, 31
	call    set_ac97_LastValidIndex

	; 01/06/2024
	; 24/05/2024
	; 23/11/2023 - temporary
	;call	delay_100ms
	; 23/05/2024
	;call	delay1_4ms
	; 27/05/2024
	;call	delay1_4ms
	;call	delay1_4ms
	;call	delay1_4ms

	mov	byte [audio_play_cmd], 1 ; play command (do not stop) !

ac97_play: ; continue to play (after pause)
	; 27/05/2024
	;;;
	; 19/05/2024 - temporary
	; 24/11/2023 - temporary
	;mov	ax, 0202h
	; 23/05/2024
	mov	ax, 1F1Fh
	sub	ax, [audio_master_volume]
	
	mov	dx, [NAMBAR]
  	add	dx, CODEC_MASTER_VOL_REG  ; 02h ; Line Out 
  	out	dx, ax

	; 01/06/2024
	; 19/05/2024 - temporary
	;call	delay_100ms
	; 23/05/2024
	;call	delay1_4ms
	; 24/05/2024
	;call	delay1_4ms
	;call	delay1_4ms
	;call	delay1_4ms

	;mov	ax, 0202h
	; 23/05/2024
	;mov	ax, 1F1Fh
	;sub	ax, [audio_master_volume]

	; 27/05/2024
	mov	ax, 1F1Fh
	sub	ax, [audio_pcmo_volume]
	
	mov	dx, [NAMBAR]
  	add	dx, CODEC_PCM_OUT_REG ; 18h ; PCM Out 
  	out	dx, ax

	; 01/06/2024
	; 24/05/2024
	; 23/05/2024
	;call	delay1_4ms
	;call	delay1_4ms
	;call	delay1_4ms
	;call	delay1_4ms
	; 24/05/2024
	;call	delay_100ms
	;;;

	; 11/06/2017
	; 29/05/2017
	; 28/05/2017
        mov     dx, [NABMBAR]
        add     dx, PO_CR_REG		; PCM out control register
	; 26/11/2023
	mov	al, IOCE+RPBM ; 29/05/2017
	; 24/11/2023
	;mov	al, 1Dh ; (Ref: KolibriOS, intelac97.asm, 'play:')
	out     dx, al			; set start!

	;mov	byte [audio_play_cmd], 1 ; play command (do not stop) !

	retn

;input AL = index # to stop on
set_ac97_LastValidIndex:
	; 28/05/2017
	; Derived from 'setLastValidIndex' procedure in 'ICHWAV.ASM'
	; .wav player for DOS by Jeff Leyda (02/09/2002)
	mov	dx, [NABMBAR]
	add	dx, PO_LVI_REG
        out     dx, al
	;mov	[audio_lvi], al ; for ac97_int_handler
	retn

ac97_volume:
	; 23/05/2024
	; 28/05/2017
	; bl = component (0 = master/playback/lineout volume)
	;		(1 = PCM Out volume) ; 23/05/2024
	; cl = left channel volume level (0 to 31)
	; ch = right channel volume level (0 to 31)

	; 24/05/2024
	cmp	bl, 1
	ja	short ac97_vol_2 ; temporary !

	mov	ax, 1F1Fh ; 31,31
	cmp	cl, al
	ja	short ac97_vol_2
	cmp	ch, ah
	ja	short ac97_vol_2

	or	bl, bl
	jnz	short ac97_vol_1

	; 23/05/2024
	mov	dx, CODEC_MASTER_VOL_REG  ; 02h ; Line Out
	; 24/05/2024
	mov	[audio_master_volume], cx
ac97_vol_0:
	sub	ax, cx
	;mov	dx, [NAMBAR]
	;add	dx, CODEC_MASTER_VOL_REG
  	; 23/05/2024
	add	dx, [NAMBAR]
  	out     dx, ax
	; 23/05/2024
ac97_vol_2:
	; 21/11/2023
;_ac97_ih5:	; 06/08/2022
	retn

ac97_vol_1:
	; 24/05/2024
	; 23/05/2024
	;cmp	bl, 1
	;ja	short ac97_vol_2 ; temporary !
	; BL = 1
	; PCM OUT Volume
	mov	dx, CODEC_PCM_OUT_REG	; 18h ; PCM out
	; 24/05/2024
	mov	[audio_pcmo_volume], cx
	jmp	short ac97_vol_0

ac97_int_handler:
	; 04/06/2024
	; 03/06/2024
	; 02/06/2024 - TRDOS 386 v2.0.8
	; 27/11/2023
	; 24/11/2023
	; 21/11/2023
	; 20/11/2023 - TRDOS 386 v2.0.7
	; 06/08/2022 - TRDOS 386 v2.0.5
	; 12/10/2017
	; 10/10/2017
	; 09/10/2017
	; 13/06/2017, 13/06/2017
	; 10/06/2017, 11/06/2017
	; Interrupt Handler for AC97 (ICH) Audio Controller
	; Note: called by 'dev_IRQ_service'
	; 28/05/2017

	;push	eax ; * must be saved !
	;push	edx
	;push	ecx
	;push	ebx ; * must be saved !
	;push	esi
	;push	edi

; 02/06/2024
%if 0

	;cmp	byte [audio_busy], 1
	;jnb	_ac97_ih2 ; busy !

	; 24/11/2023
        ;mov	dx, [NABMBAR]
	;mov	dx, PO_SR_REG
	;in	ax, dx
	;test	al, BCIS ; bit 3, 8
	;jz	short _ac97_ih5

	; 24/11/2023
	mov	dx, GLOB_STS_REG
        add	dx, [NABMBAR]
	in	eax, dx

	cmp	eax, 0FFFFFFFFh ; -1
	;je	_ac97_ih3 ; exit
	; 06/08/2022
	je	short _ac97_ih5

	;test	eax, 40h ; PCM Out Interrupt
	; 06/08/2022
	test	al, 40h
	jnz	short _ac97_ih0

	; 24/11/2023
	test	eax, eax
	;jz	_ac97_ih3 ; exit
	; 06/08/2022
	jz	short _ac97_ih5

 	;mov	dx, GLOB_STS_REG
        ;add	dx, [NABMBAR]
	out	dx, eax
	
	;jmp	_ac97_ih3 ; exit
	
	; 06/08/2022
	retn
	; 21/11/2023
	;jmp	short _ac97_ih5

_ac97_ih0:
	; 24/11/2023
	;push	eax
	; 09/10/2017
	cmp	byte [audio_play_cmd], 1
	;jb	short _ac97_ih4 ; stop command !
	; 24/11/2023
	jnb	short _ac97_ih4

	;mov	dx, GLOB_STS_REG
        ;add	dx, [NABMBAR]
	out	dx, eax
	;jmp	short _ac97_stop

	; 24/11/2023
ac97_stop: 
	; 28/05/2017
	mov	byte [audio_play_cmd], 0 ; stop !

%else
	; 02/06/2024
_ac97_ih0:
	cmp	byte [audio_play_cmd], 1
	jnb	short _ac97_ih4	
%endif

_ac97_stop: ; 09/10/2017
	; 29/05/2017
	;mov	dx, [NABMBAR]
	;add	dx, PO_CR_REG
	;mov	al, 0
	;out	dx, al

	; 11/06/2017
	xor	al, al ; 0
	call	ac97_po_cmd

	; (Ref: KolibriOS, intelac97.asm, 'stop:')
	; Clear FIFOE, BCIS, LVBCI (Ref: Intel ICH hub manual)
	mov     ax, 1Ch
	mov     dx, [NABMBAR]
	add     dx, PO_SR_REG
	out     dx, ax

	;retn

	; 11/06/2017
	mov     al, RR
ac97_po_cmd:
	 ;11/06/2017
	; 29/05/2017
	mov     dx, [NABMBAR]
        add     dx, PO_CR_REG		; PCM out control register
	out	dx, al
	retn

; 02/06/2024
%if 1
	; 24/11/2023
ac97_stop: 
	; 28/05/2017
	mov	byte [audio_play_cmd], 0 ; stop !
	jmp	short _ac97_stop
%endif

ac97_pause:
	; 11/06/2017
	; 29/05/2017
	mov 	al, IOCE
	jmp	short ac97_po_cmd

_ac97_ih4:
	;mov	byte [audio_busy], 1

; 02/06/2024
%if 1
	; 24/11/2023 (TRDOS386 'audio.s')
        mov	dx, [NABMBAR]
	add	dx, PO_SR_REG
	in	ax, dx

	test	al, BCIS ; bit 3, 8
	jz	short _ac97_ih2	; 02/06/2024

	; 02/06/2024
	push	eax ; *
%else
	; 27/05/2024
	; 24/11/2023
	push	eax

	mov	ax, 1Ch ; FIFOE(=16)+BCIS(=8)+LVBCI(=4)
	mov	dx, PO_SR_REG
        add	dx, [NABMBAR]
	out	dx, ax

	mov	dx, PO_CIV_REG
        add	dx, [NABMBAR]
	in	al, dx

	;cmp	al, [audio_civ] ; [audio_flag]
	;je	short _ac97_ih2

	; 21/11/2023
	;mov	[audio_civ], al
	; 20/11/2023
	mov	ah, al
	dec	al
	;inc	al ; 11/06/2017
	and	al, 1Fh
%endif
	; 02/06/2024
	mov	dx, PO_CIV_REG
        add	dx, [NABMBAR]
	;in	ax, dx
	; 03/06/2024
	in	al, dx
			; al = CVI, ah = LVI
	;xchg	ah, al
	;		; al = LVI, ah = CVI
	;cmp	al, ah
	;jne	short _ac97_ih5
	; 03/06/2024
	mov	ah, al

	dec	al
	and	al, 1Fh

	;mov	dx, PO_LVI_REG
	;add	dx, [NABMBAR]
        inc	dx	; 02/06/2024	
	out	dx, al

	; 12/10/2017
	;mov	al, [audio_civ]
	;inc	al
	;and	al, 1
	;mov	[audio_flag], al 
	; 27/11/2023
	; 20/11/2023
	;inc	ah
_ac97_ih5:	; 02/06/2024
	and	ah, 1
	mov	[audio_flag], ah

	;; [audio_flag] : 0 = Buffer 1, 1 = Buffer 2
	;
	; 21/11/2023
	;call	ac97_tuneloop

	; 24/11/2023
ac97_tuneloop:
	; 09/10/2017
	mov	edi, [audio_dma_buff]
	; 04/06/2024
	;mov	ecx, [audio_dmabuff_size]
	;shr	ecx, 1 ; dma buff size / 2 = half buffer size
	mov	ecx, [audio_buff_size]
	and	cl, ~7 ; 8 byte aligned

	; 12/10/2017
	cmp 	byte [audio_flag], 0
	ja	short _ac97_ih1  ; Playing Half Buffer 2 (Current: FLAG)
	; Playing Half Buffer 1 (Current: EOL)	
	add	edi, ecx
_ac97_ih1: 
	; Update half buffer 2 while playing half buffer 1 (next: FLAG)
	; Update half buffer 1 while playing half buffer 2 (next: EOL)

	mov	esi, [audio_p_buffer] ; phy addr of audio buff
	shr	ecx, 2 ; half buff size / 4
	rep	movsd

	; 10/10/2017
	; switch flag value
	xor	byte [audio_flag], 1

; 02/06/2024
%if 1
	; 02/06/2024
	pop	eax ; *
_ac97_ih2:
	;mov	ax, 1Ch ; FIFOE(=16)+BCIS(=8)+LVBCI(=4)
	mov	dx, PO_SR_REG
	add	dx, [NABMBAR]
	out	dx, ax
%else
	; 27/11/2023
	; 21/11/2023 - temporary
	;push	ebx
	;mov	ebx, 0B8002h
	;mov	al, [audio_flag]
	;add	al, '1'
	;mov	ah, 0Fh
	;mov	[ebx], ax
	;pop	ebx
	
	; 12/10/2017
	; [audio_flag] = 0 : Playing dma half buffer 2 (even index value)
			   ; Next buffer (to update) is dma half buff 1
	; 	       = 1 : Playing dma half buffer 1 (odd index value)
			   ; Next buffer (to update) is dma half buff 2
	; 24/11/2023
	;retn

	;
	; 24/11/2023
	pop	eax
	; 24/11/2023
	; 20/11/2023
	;and	eax, 40h
        mov	dx, [NABMBAR]
	add	dx, GLOB_STS_REG
	out	dx, eax

	;; 13/06/2017
	;mov	al, 11h ; IOCE + RPBM
	;mov	dx, PO_CR_REG
        ;add	dx, [NABMBAR]
	;out	dx, al

	; 24/11/2023
	;mov	dx, [NABMBAR]
	;add	dx, PO_SR_REG	; set pointer to Status reg
	;mov	ax, 1Ch
	;out	dx, ax

	; 24/11/2023
_ac97_ih2:
	;mov	byte [audio_busy], 0
%endif

_ac97_ih3:
	;pop	edi
	;pop	esi
	;pop	ebx ; * must be restored !
	;pop	ecx
	;pop	edx
	;pop	eax ; * must be restored !

	retn

reset_ac97_controller:
	; 06/06/2024
	; 01/06/2024
	; 27/05/2024
	; 16/05/2024
	; 10/06/2017
	; 29/05/2017
	; 28/05/2017
	; reset AC97 audio controller registers
	xor     eax, eax
        mov	dx, PI_CR_REG
	add	dx, [NABMBAR]
	out     dx, al

	; 16/05/2024
	;call	delay1_4ms

        mov     dx, PO_CR_REG
	add	dx, [NABMBAR]
	out     dx, al

	; 16/05/2024
	;call	delay1_4ms

        mov     dx, MC_CR_REG
	add	dx, [NABMBAR]
	out     dx, al

	; 16/05/2024
	;call	delay1_4ms

        mov     al, RR
        mov     dx, PI_CR_REG
	add	dx, [NABMBAR]
	out     dx, al

	; 16/05/2024
	;call	delay1_4ms

        mov     dx, PO_CR_REG
	add	dx, [NABMBAR]
	out     dx, al

	; 16/05/2024
	;call	delay1_4ms

        mov     dx, MC_CR_REG
	add	dx, [NABMBAR]
	out     dx, al

	; 27/05/2024
	; 16/05/2024
	;call	delay1_4ms

	retn

ac97_reset:
	; 27/05/2024 - TRDOS 386 v2.0.8
	; 06/08/2022 - TRDOS 386 v2.0.5
	; 10/06/2017
	; 29/05/2017
	; 28/05/2017
	call	reset_ac97_controller
	; 29/05/2017
	;jmp	reset_ac97_codec
	; 27/05/2024
	call	delay_100ms
reset_ac97_codec:
	; 28/05/2017 - Erdogan Tan (Ref: KolibriOS, intelac97.asm)
	mov	dx, GLOB_CNT_REG ; 2Ch
	add	dx, [NABMBAR]
	in	eax, dx

	;test	eax, 2
	; 06/08/2022
	test	al, 2
	jz	short _r_ac97codec_cold

	call	warm_ac97codec_reset
	jnc	short _r_ac97codec_ok
_r_ac97codec_cold:
        call    cold_ac97codec_reset
        jnc     short _r_ac97codec_ok
	
	; 16/04/2017
        ;xor	eax, eax	; timeout error
       	;stc
	retn

_r_ac97codec_ok:
        xor     eax, eax
        ;mov	al, VIA_ACLINK_C00_READY ; 1
        inc	al
	retn

warm_ac97codec_reset:
	; 06/08/2022 - TRDOS 386 v2.0.5
	; 28/05/2017 - Erdogan Tan (Ref: KolibriOS, intelac97.asm)
        ;mov	eax, 6
	; 06/08/2022
	sub	eax, eax
	mov	al, 6
	mov	dx, GLOB_CNT_REG ; 2Ch
	add	dx, [NABMBAR]
	out	dx, eax

	;mov	ecx, 10	; total 1s
	; 06/08/2022
	xor	ecx, ecx
	mov	cl, 10
_warm_ac97c_rst_wait:
	push	ecx
	call	delay_100ms
	pop	ecx

	mov	dx, GLOB_STS_REG ; 30h
	add	dx, [NABMBAR]
	in	eax, dx

	test	eax, CTRL_ST_CREADY
	jnz	short _warm_ac97c_rst_ok

        dec     ecx
        jnz     short _warm_ac97c_rst_wait

_warm_ac97c_rst_fail:
        stc
_warm_ac97c_rst_ok:
	retn

cold_ac97codec_reset:
	; 06/08/2022 - TRDOS 386 v2.0.5
	; 28/05/2017 - Erdogan Tan (Ref: KolibriOS, intelac97.asm)
        ;mov	eax, 2
	; 06/08/2022
	xor	eax, eax
	mov	al, 2
	mov	dx, GLOB_CNT_REG ; 2Ch
	add	dx, [NABMBAR]
	out	dx, eax

	call	delay_100ms 	; wait 100 ms
	call	delay_100ms 	; wait 100 ms
	call	delay_100ms 	; wait 100 ms
	call	delay_100ms 	; wait 100 ms

	;mov	ecx, 16	; total 20*100 ms = 2s
	; 06/08/2022
	xor	ecx, ecx
	mov	cl, 16
_cold_ac97c_rst_wait:
	mov	dx, GLOB_STS_REG ; 30h
	add	dx, [NABMBAR]
	in	eax, dx

	test	eax, CTRL_ST_CREADY
	jnz	short _cold_ac97c_rst_ok

	push	ecx
	call	delay_100ms
	pop	ecx

        dec     ecx
        jnz     short _cold_ac97c_rst_wait

_cold_ac97c_rst_fail:
        stc
_cold_ac97c_rst_ok:
	retn

sb16_current_sound_data:
	; 05/06/2024
	; 04/06/2024 - TRDOS 386 v2.0.8
	; 06/08/2022 - TRDOS 386 v2.0.5
	; 20/08/2017
	; 24/06/2017
	; 22/06/2017
	; get current sound (PCM out) data for graphics
	; (for Sound Blaster 16)
	; ebx = Physical address (on page boundary)
	; ecx = Byte count
	; [audio_buff_size]

	;;mov	edi, [audio_buff_size]
	;mov	edi, [audio_dmabuff_size]
	;mov	esi, [audio_dma_buff]

	cmp	edi, ecx
	jnb	short sb16_gcd_0
	mov	ecx, edi
sb16_gcd_0:
	; 06/08/2022
	xor	eax, eax
	; 20/08/2017
	cmp	byte [audio_bps], 16
	jne	short sb16_gcd_1 ; 8 bit DMA channel
	in	al, 0C6h ; DMA channel 5 count register
	;mov	dl, al
	; 06/08/2022
	mov	ah, al
	in	al, 0C6h
	;mov	dh, al
	;movzx	eax, dx
	; 06/08/2022
	xchg	ah, al
	shl	eax, 1 ; word count -> byte count
	jmp	short sb16_gcd_2
sb16_gcd_1:
	in	al, 03h ; DMA channel 1 count register
	;mov	dl, al
	; 06/08/2022
	mov	ah, al
	in	al, 03h
	;mov	dh, al
	;movzx	eax, dx
	; 06/08/2022
	xchg	ah, al
	jmp	short sb16_gcd_2
;sb16_gcd_2:
;	cmp	eax, ecx
;	jnb	short sb16_gcd_3
;	; remain count < graphics bytes
;	mov	eax, ecx ; fix remain count to data size
;sb16_gcd_3:
;	sub	edi, eax
;	jna	short sb16_gcd_4
;	add	esi, edi ; dma buffer offset
;sb16_gcd_4:
;	mov	edi, ebx ; buffer address (for graphics) 
;	mov	[u.r0], ecx
;	rep	movsb
;	retn

get_current_sound_data:
	; 05/06/2024
	; 04/06/2024
	; 24/06/2017
	; 22/06/2017
	; get current sound (PCM out) data for graphics
	;
	; ebx = Physical address (on page boundary)
	; ecx = Byte count
	; [audio_buff_size]

	;mov	edi, [audio_buff_size]
	;;; 
	; 04/06/2024
	;mov	edi, [audio_dmabuff_size]
	; 05/06/2024
	;mov	edi, [audio_buff_size]
	mov	edi, [dma_hbuff_size]
	;;;
	mov	esi, [audio_dma_buff]

	;;;
	; 04/06/2024
	;cmp	byte [audio_device], 2
	;jb	short sb16_current_sound_data ; = 1
	;shr	edi, 1

	; 04/06/2024
	cmp	byte [audio_device], 1
	ja	short gcd_1
	shl	edi, 1 ; [audio_dmabuff_size] = 2 * edi
	; 05/06/2024
	; edi = [dma_hbuff_size] * 2
	jmp	short sb16_current_sound_data
gcd_1:
	;;;

; 04/06/2024
%if 0
	cmp	edi, ecx
	jnb	short gcd_0
	mov	ecx, edi
gcd_0:
%endif
	cmp	byte [audio_device], 3
	jb	short ac97_current_sound_data ; = 2
	; = 3
vt8233_current_sound_data:
	; 05/06/2024
	; 04/06/2024 - TRDOS 386 v2.0.8
	; 06/08/2022 - TRDOS 386 v2.0.5
	; 22/06/2017
	; 21/06/2017
	; get current sound (PCM out) data for graphics
	; (for VT 8233, VT 8237R)
	; ebx = Physical address (on page boundary)
	; ecx = Byte count
	; [audio_buff_size]
	
	;;mov	edi, [audio_buff_size]
	;mov	edi, [audio_dmabuff_size]
	;mov	esi, [audio_dma_buff]
	;shr	edi, 1
	;cmp	edi, ecx
	;jnb	short vt8233_gcd_1
	;mov	ecx, edi

	;;;
	; 04/06/2024
	; edi = [audio_buff_size]
	; 05/06/2024
	;and	di, ~1	; word alignment
	; edi = [dma_hbuff_size]
	cmp	edi, ecx
	jnb	short vt8233_gcd_1
	mov	ecx, edi
	;;;

vt8233_gcd_1:
	;mov	edx, VIA_REG_OFFSET_CURR_COUNT
	; 06/08/2022
	xor	edx, edx
	mov	dl, VIA_REG_OFFSET_CURR_COUNT
	call	ctrl_io_r32
	mov	edx, eax ; remain count (bits 23-0),
			 ; SGD index (bits 31-24) 
	and	edx, 1000000h ; SGD index (0 = 1st half)
	jz	short vt8233_gcd_2
	; the second half of DMA buffer
	add	esi, edi 
vt8233_gcd_2:
	and	eax, 0FFFFFFh ; bits 23-0
ac97_gcd_2:
sb16_gcd_2:
	cmp	eax, ecx
	jnb	short vt8233_gcd_3 
	; remain count < graphics bytes
	mov	eax, ecx ; fix remain count to data size
vt8233_gcd_3:
	sub	edi, eax
	jna	short vt8233_gcd_4
	add	esi, edi ; dma buffer offset
vt8233_gcd_4:
	mov	edi, ebx ; buffer address (for graphics)
	mov	[u.r0], ecx
	rep	movsb
vt8233_gcd_5:
	retn

ac97_current_sound_data:
	; 05/06/2024
	; 04/06/2024 - TRDOS 386 v2.0.8
	; 23/06/2017
	; 22/06/2017
	; get current sound (PCM out) data for graphics
	; (for AC'97, ICH)
	; ebx = Physical address (on page boundary)
	; ecx = Byte count
	; [audio_buff_size]
	
	;;mov	edi, [audio_buff_size]
	;mov	edi, [audio_dmabuff_size]
	;mov	esi, [audio_dma_buff]
	;shr	edi, 1
	;cmp	edi, ecx
	;jnb	short ac97_gcd_0
	;mov	ecx, edi

	;;;
	; 04/06/2024
	; edi = [audio_buff_size]
	; 05/06/2024
	;and	di, ~7	; 8 byte alignment
	; edi = [dma_hbuff_size]
	cmp	edi, ecx
	jnb	short ac97_gcd_0
	mov	ecx, edi
	;;;

ac97_gcd_0:
	mov	dx, PO_CIV_REG ; Position In Current Buff Reg
	add	dx, [NABMBAR]
	in	al, dx ; current index value
	test	al, 1
	jz	short ac97_gcd_1
	add	esi, edi
ac97_gcd_1:
	xor	eax, eax
	mov	dx, PO_PICB_REG ; Position In Current Buff Reg
	add	dx, [NABMBAR]
	;in	ax, dx ; remain dwords
	;shl	eax, 2 ; remain bytes ; 23/06/2017
	;;;
	; 04/06/2024
	in	ax, dx ; remain words
	shl	eax, 1 ; remain bytes	
	;;;
	jmp	short ac97_gcd_2
;	cmp	eax, ecx
;	jnb	short ac97_gcd_2 
;	; remain count < graphics bytes
;	mov	eax, ecx ; fix remain count to data size
;ac97_gcd_2:
;	sub	edi, eax
;	jna	short ac97_gcd_3
;	add	esi, edi ; dma buffer offset
;ac97_gcd_3:
;	mov	edi, ebx ; buffer address (for graphics)
;	mov	[u.r0], ecx
;	rep	movsb
;	retn

sb16_get_dma_buff_off:
	; 05/06/2024
	; 04/06/2024 - TRDOS 386 v2.0.8
	; 28/10/2017
	; 24/06/2017
	; 22/06/2017
	; get current (PCM OUT DMA buffer) pointer
	; (for Sound Blaster 16)

	;mov	ecx, [audio_dmabuff_size]
	;xor	ebx, ebx
	;shr	ecx, 1
	
	;;;
	; 04/06/2024
	; ecx = audio buffer size
	; 05/06/2024
	; ecx = DMA half buffer size
	shl	ecx, 1 ; * 2
	; ecx = DMA buffer size
	;;;

sb16_gdmabo_0:
	; 28/10/2017
	cmp	byte [audio_bps], 16
	jne	short sb16_gdmabo_1 ; 8 bit DMA channel
	; 16 bit DMA channel
	in	al, 0C6h ; DMA channel 5 count register
	mov	dl, al	
	in	al, 0C6h
	mov     dh, al
	movzx	eax, dx
	shl	eax, 1 ; word count -> byte count
	jmp	short sb16_gdmabo_2
sb16_gdmabo_1:
	in	al, 03h ; DMA channel 1 count register
	mov	dl, al	
	in	al, 03h
	mov     dh, al
	movzx	eax, dx
	jmp	short sb16_gdmabo_2

get_dma_buffer_offset:
	; 05/06/2024
	; 04/06/2024
	; 24/06/2017
	; 22/06/2017
	; get current sound (PCM out) data for graphics
	;
	; ebx = Physical address (on page boundary)
	; ecx = Byte count
	; [audio_buff_size]

	;mov	ecx, [audio_dmabuff_size]
	;;;
	; 04/06/2024
	;mov	ecx, [audio_buff_size]
	; 05/06/2024
	mov	ecx, [dma_hbuff_size]
	;;;
	xor	ebx, ebx
gdmabo_0:
	cmp	byte [audio_device], 2
	jb	short sb16_get_dma_buff_off
	je	short ac97_get_dma_buff_off

vt8233_get_dma_buff_off:
	; 05/06/2024
	; 04/06/2024 - TRDOS 386 v2.0.8
	; 06/08/2022 - TRDOS 386 v2.0.5
	; 24/06/2017
	; 22/06/2017
	; get current (PCM OUT DMA buffer) pointer
	; (for VT 8233, VT 8237R)
	
	;mov	ecx, [audio_dmabuff_size]
	;xor	ebx, ebx
	;;;
	; 04/06/2024
	;shr	ecx, 1
	; 05/06/2024
	;and	cl, ~1
	; ecx = DMA Half Buffer Size (word aligned)
	;;;
vt8233_gdmabo_0:
	;mov	edx, VIA_REG_OFFSET_CURR_COUNT
	; 06/08/2022
	xor	edx, edx
	mov	dl, VIA_REG_OFFSET_CURR_COUNT
	call	ctrl_io_r32
	mov	edx, eax ; remain count (bits 23-0),
			 ; SGD index (bits 31-24) 
	and	edx, 1000000h ; SGD index (0 = 1st half)
	jz	short vt8233_gdmabo_1
	; the second half of DMA buffer
	mov	ebx, ecx
vt8233_gdmabo_1:
	and	eax, 0FFFFFFh ; bits 23-0
sb16_gdmabo_2:
ac97_gdmabo_2:
	sub	ecx, eax
	jna	short vt8233_gdmabo_2
	add	ebx, ecx ; dma buffer offset
vt8233_gdmabo_2:
	mov	[u.r0], ebx
	retn

ac97_get_dma_buff_off:
	; 06/06/2024
	; 05/06/2024
	; 04/06/2024 - TRDOS 386 v2.0.8
	; 24/06/2017
	; 22/06/2017
	; get current (PCM OUT DMA buffer) pointer
	; (for AC'97, ICH)
	; ebx = Physical address (on page boundary)
	; ecx = Byte count
	; [audio_buff_size]
	
	;mov	ecx, [audio_dmabuff_size]
	;xor	ebx, ebx
	;;;
	; 04/06/2024
	;shr	ecx, 1
	; 05/06/2024
	;and	cl, ~7 ; (truncate bytes if out of 8x)
	; ecx = DMA Half Buffer Size (8 byte aligned)
	;;;
ac97_gdmabo_0:
	mov	dx, PO_CIV_REG ; Position In Current Buff Reg
	add	dx, [NABMBAR]
	in	al, dx ; current index value
	test	al, 1
	jz	short ac97_gdmabo_1
	mov	ebx, ecx
ac97_gdmabo_1:
	xor	eax, eax
	mov	dx, PO_PICB_REG ; Position In Current Buff Reg
	add	dx, [NABMBAR]
	in	ax, dx ; remain words (samples)
	;;;
	; 06/06/2024
	; audio samples are counted as words
	; 04/06/2024 (BugFix)
	shl	eax, 1 ; remain bytes
	;;;
	jmp	short ac97_gdmabo_2

ac97_codec_info:
	; 06/06/2024
	; 05/06/2024 - TRDOS 386 v2.0.8
	; 19/11/2023
	; ENTRY: none
	; RETURN: 
	;	eax = Extended Audio ID (MX28) in ax
	;	 ax bit 0 - VRA bit
	;	 hw of eax = PCM output sample rate (48000)
	;	ebx = VENDOR ID 1, VENDOR ID 2 (bx)
	;	cf = 1 -> error

	; 06/06/2024
	;;;
	mov	dx, GLOB_STS_REG ; 30h
	add	dx, [NABMBAR]
	in	eax, dx

	;cmp	eax, 0FFFFFFFFh ; -1
	inc	eax
	jz	short ac97_c_inf_err
	dec	eax

	and	eax, CTRL_ST_CREADY
	jnz	short ac97_c_inf_1

ac97_c_inf_err:	
	stc
	retn
	;;;

ac97_c_inf_1:
	mov	dx, [NAMBAR]
	add	dx, CODEC_PCM_FRONT_DACRATE_REG 
	in	ax, dx	; PCM Output Sample Rate
			; 48000 Hz
	shl	eax, 16
	call	delay_100ms
	mov	dx, [NAMBAR]
	add	dx, CODEC_EXT_AUDIO_REG ; 28h
	in	ax, dx
	push	eax
	call	delay_100ms
	mov	dx, [NAMBAR]
	add	dx, CODEC_VENDOR_ID1
	in	ax, dx
	shl	eax, 16
	mov	dx, [NAMBAR]
	add	dx, CODEC_VENDOR_ID2
	in	ax, dx
	mov	ebx, eax
	pop	eax
	;;;;
	; 06/06/2024 (temporary!?)
	; (ALC850 & ALC655 BugFix)
	cmp	ebx, 0FFFFFFFFh ; -1
	je	short ac97_c_inf_err ; invalid
	or	ebx, ebx
	jz	short ac97_c_inf_err ; invalid
	cmp	ebx, 414C4790h ; ALC850
	;je	short ac97_c_inf_2
	;cmp	ebx, 414C4760h ; ALC655
	jne	short ac97_c_inf_3	
	;mov	eax, 0BB8009C6h ; AC655 default (Read only)
	;retn
ac97_c_inf_2:
	; ref: ALC850 & ALC655 data sheet
	mov	eax, 0BB8009C4h ; AC850 default (Read only)
ac97_c_inf_3:
	;;;;
	retn

; 19/11/2023
; Valid ICH device IDs

valid_ids:
dd	(ICH_DID << 16) + INTEL_VID  	 ; 8086h:2415h
dd	(ICH0_DID << 16) + INTEL_VID 	 ; 8086h:2425h
dd	(ICH2_DID << 16) + INTEL_VID 	 ; 8086h:2445h
dd	(ICH3_DID << 16) + INTEL_VID 	 ; 8086h:2485h
dd	(ICH4_DID << 16) + INTEL_VID 	 ; 8086h:24C5h
dd	(ICH5_DID << 16) + INTEL_VID 	 ; 8086h:24D5h
dd	(ICH6_DID << 16) + INTEL_VID 	 ; 8086h:266Eh
dd	(ESB6300_DID << 16) + INTEL_VID  ; 8086h:25A6h
dd	(ESB631X_DID << 16) + INTEL_VID  ; 8086h:2698h
dd	(ICH7_DID << 16) + INTEL_VID 	 ; 8086h:27DEh
dd	(MX82440_DID << 16) + INTEL_VID  ; 8086h:7195h
dd	(SI7012_DID << 16)  + SIS_VID	 ; 1039h:7012h
dd 	(NFORCE_DID << 16)  + NVIDIA_VID ; 10DEh:01B1h
dd 	(NFORCE2_DID << 16) + NVIDIA_VID ; 10DEh:006Ah
dd 	(AMD8111_DID << 16) + AMD_VID 	 ; 1022h:746Dh
dd 	(AMD768_DID << 16)  + AMD_VID 	 ; 1022h:7445h
dd 	(CK804_DID << 16) + NVIDIA_VID	 ; 10DEh:0059h
dd 	(MCP04_DID << 16) + NVIDIA_VID	 ; 10DEh:003Ah
dd 	(CK8_DID << 16) + NVIDIA_VID	 ; 1022h:008Ah
dd 	(NFORCE3_DID << 16) + NVIDIA_VID ; 10DEh:00DAh
dd 	(CK8S_DID << 16) + NVIDIA_VID	 ; 10DEh:00EAh

valid_id_count:	equ ($ - valid_ids)>>2 ; 19/11/2023
