; ****************************************************************************
; TRDOS386.ASM (TRDOS 386 Kernel) - v2.0.0 - audio.s
; ----------------------------------------------------------------------------
; Last Update: 23/04/2017
; ----------------------------------------------------------------------------
; Beginning: 03/04/2017
; ----------------------------------------------------------------------------
; Assembler: NASM version 2.11 (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
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_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_REF_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.

; each codec/mixer register is 16bits

CODEC_RESET_REG                 equ     00      ; reset codec
CODEC_MASTER_VOL_REG            equ     02      ; master volume
CODEC_HP_VOL_REG                equ     04      ; headphone volume
CODEC_MASTER_MONO_VOL_REG       equ     06      ; master mono volume
CODEC_MASTER_TONE_REG           equ     08      ; master tone (R+L)
CODEC_PCBEEP_VOL_REG            equ     0ah     ; PC beep volume
CODEC_PHONE_VOL_REG             equ     0bh     ; phone volume
CODEC_MIC_VOL_REG               equ     0eh     ; MIC volume
CODEC_LINE_IN_VOL_REG           equ     10h     ; line input volume
CODEC_CD_VOL_REG                equ     12h     ; CD volume
CODEC_VID_VOL_REG               equ     14h     ; video volume
CODEC_AUX_VOL_REG               equ     16h     ; aux volume
CODEC_PCM_OUT_REG               equ     18h     ; PCM output volume
CODEC_RECORD_SELECT_REG         equ     1ah     ; record select input
CODEC_RECORD_VOL_REG            equ     1ch     ; record volume
CODEC_RECORD_MIC_VOL_REG        equ     1eh     ; record mic volume
CODEC_GP_REG                    equ     20h     ; general purpose
CODEC_3D_CONTROL_REG            equ     22h     ; 3D control
; 24h is reserved
CODEC_POWER_CTRL_REG            equ     26h     ; powerdown control
CODEC_EXT_AUDIO_REG             equ     28h     ; extended audio
CODEC_EXT_AUDIO_CTRL_REG        equ     2ah     ; extended audio control
CODEC_PCM_FRONT_DACRATE_REG     equ     2ch     ; PCM out sample rate
CODEC_PCM_SURND_DACRATE_REG     equ     2eh     ; surround sound sample rate
CODEC_PCM_LFE_DACRATE_REG       equ     30h     ; LFE sample rate
CODEC_LR_ADCRATE_REG            equ     32h     ; PCM in sample rate
CODEC_MIC_ADCRATE_REG           equ     34h     ; mic in sample rate

; VT8233 SGD bits (21/04/2017)
FLAG	EQU BIT30
EOL	EQU BIT31

;=============================================================================
;               CODE
;=============================================================================

; CODE for VIA VT8233 AUDIO CONTROLLER

DetectVT8233:
	; 03/04/2017
	mov     eax, (VT8233_DID << 16) + VIA_VID
        call    pciFindDevice
        jnc     short d_vt8233_0
; couldn't find the audio device!
	retn
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)

	; eax = BUS/DEV/FN
	;	00000000BBBBBBBBDDDDDFFF00000000
	; edx = DEV/VENDOR
	;	DDDDDDDDDDDDDDDDVVVVVVVVVVVVVVVV

	mov	[audio_dev_id], eax
	mov	[audio_vendor], edx

	; 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)
	call	pciRegRead32

	and     dx, 0FFC0h	; IO_ADDR_MASK (0FFFE) ?
        mov     [audio_io_base], dx

	mov	al, AC97_INT_LINE ; Interrupt line register (3Ch)
	call	pciRegRead32

	and 	edx, 0FFh
  	mov     [audio_intr], dl

	; (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
	out	21h, al  ; enable interrupt (if irq <= 7)
	mov	al, ah
	out	0A1h, al ; enable interrupt (if irq > 7)

	mov	dx, 4D1h	; 8259 ELCR1
    	in	al, dx
	mov	ah, al
	;mov	dx, 4D0h 
	dec	dl
	in	al, dx
	bts	ax, cx
	;mov	dx, 4D0h
	out	dx, al		; set level-triggered mode
	;mov	dx, 4D1h
	inc	dl
	out	dx, al		; set level-triggered mode

	xor	eax, eax ; 0

	retn

; CODE for PCI

pciFindDevice:
	; 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, esi, edi, cl
	;

	;push	ecx
	push	eax
	;push	esi
	;push	edi

        mov     esi, eax                ; save off vend+device ID
        mov     edi, (80000000h - 100h) ; start with bus 0, dev 0 func 0

nextPCIdevice:
        add     edi, 100h
        cmp     edi, 80FFF800h		; scanned all devices?
        stc
        je      short PCIScanExit       ; not found

        mov     eax, edi                ; read PCI registers
        call    pciRegRead32
        cmp     edx, esi                ; found device?
        jne     short nextPCIdevice
        clc

PCIScanExit:
	pushf
	mov	eax, NOT_BIT31 	; 19/03/2017
	and	eax, edi	; return only bus/dev/fn #
	popf

	;pop	edi
	;pop	esi
	pop	edx
	;pop	ecx
	retn

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

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:
        xor     eax, eax
        ;mov	al, VIA_ACLINK_C00_READY ; 1
        inc	al
	retn

cold_reset:
	; 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

_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:
	; 24/03/2017 ('codec.asm')
	; wait 100 ms
	mov	ecx, 25
_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
;

	; 23/04/2017
	; 05/03/2017 (TRDOS 386)
	; ('UTILS.ASM')
delay1_4ms:
        push    eax 
        push    ecx
        mov	cl, 16		; close enough.

	in	al, PORTB
		
	and	al, REFRESH_STATUS
	mov	ch, al		; Start toggle state
	or	cl, cl
	jz	short _d4ms1
	inc	cl		; Throwaway first toggle
_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

        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
        retn

codec_read:
	; 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

_cr_ok:
	; wait 25 ms
	call	delay1_4ms
	call	delay1_4ms
	call	delay1_4ms
	call	delay1_4ms
	call	delay1_4ms

        call    codec_io_r16
        and     eax, 0FFFFh
        retn

codec_write:
	; 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

        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:
	; 12/11/2016 - Erdogan Tan (Ref: KolibriOS, vt823x.asm)

_codec_check_ready:
	mov	ecx, 20	; total 2s
_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

channel_reset:
	; 23/03/2017
	; 14/11/2016 - Erdogan Tan
	; 12/11/2016 - Erdogan Tan (Ref: KolibriOS, vt823x.asm)
        mov	edx, VIA_REG_OFFSET_CONTROL
        mov     eax, VIA_REG_CTRL_PAUSE + VIA_REG_CTRL_TERMINATE + VIA_REG_CTRL_RESET
        call    ctrl_io_w8

        ;mov	edx, VIA_REG_OFFSET_CONTROL
        ;call   ctrl_io_r8

	mov	ecx, 12 ; 50 ms	
_ch_rst_wait:
	call	delay1_4ms
	dec	ecx
	jnz	short _ch_rst_wait     

        ; disable interrupts
        mov	edx, VIA_REG_OFFSET_CONTROL
        xor     eax, eax
        call    ctrl_io_w8

        ; clear interrupts
        mov	edx, VIA_REG_OFFSET_STATUS
	mov	eax, 3
        call	ctrl_io_w8

	;mov	edx, VIA_REG_OFFSET_CURR_PTR
	;xor	eax, eax
	;call	ctrl_io_w32

        retn

codec_config:
	; 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
	; 21/04/2017
	mov	[audio_master_volume], ax
	mov	ax, 3131h
	mov	edx, 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)
	call	codec_write
	;jc	short cconfig_error
      
 	;mov    eax, 0202h
	mov	ax, 0202h
	mov	edx, CODEC_AUX_VOL ; 04h ; CODEC_HP_VOL_REG ; HeadPhone
	call	codec_write
	;jc	short cconfig_error

 	;mov    eax, 08h
        mov     ax,  08h
        mov	edx, 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)	
	call	codec_write
	;jc	short cconfig_error

 	;mov    eax, 0808h
	mov     ax,  0808h
        mov	edx, 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)
	call	codec_write
	;jc	short cconfig_error

	; 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:
	; Interrupt Handler for VIA VT8237R Audio Controller
	; Note: called by 'dev_irq_service' 
	; 21/04/2017 (TRDOS 386 kernel, 'audio.s')
	; 24/03/2017 - 'PLAYER.COM' ('player.asm') 

	push	eax
	push	edx
	push	ecx
	;push	ebx
	push	esi
	push	edi

	cmp	byte [audio_busy], 1
	jnb	short _ih_busy  ; busy !

	mov	byte [audio_busy], 1

	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

        and     al, VIA_REG_STAT_EOL + VIA_REG_STAT_FLAG + VIA_REG_STAT_STOPPED
	mov	[audio_flag_eol], al
        jz	short _ih0

	call	vt8233_tuneLoop
_ih0:
	mov	byte [audio_busy], 0
;_p_i_retn:
;	mov	al, 20h
;	test	byte [audio_intr], 8
;	jz	short _ih_1
;	out 	0A0h, al ; 20h ; EOI
;_ih_1:
;	out	20h, al  ; 20h ; EOI
;_ih_2:
	pop	edi
	pop	esi
	;pop	ebx
	pop	ecx
	pop	edx
	pop	eax
;	iretd
	retn

_ih_busy:
        mov     al, [audio_flag_eol]   ;; ack ;;
        mov     dx, VIADEV_PLAYBACK + VIA_REG_OFFSET_STATUS
        call    ctrl_io_w8

	;test	byte [audio_flag_eol], VIA_REG_STAT_FLAG 
	test	al,  VIA_REG_STAT_FLAG 
	jz	short _ih0

	mov	al, VIA_REG_CTRL_INT
       	or	al, VIA_REG_CTRL_START
       	mov     dx, VIADEV_PLAYBACK + VIA_REG_OFFSET_CONTROL
        call    ctrl_io_w8

	jmp	short _ih0

vt8233_tuneLoop:
	; 21/04/2017 (TRDOS 386 kernel, 'audio.s')
	; 24/03/2017 - 'PLAYER.COM' ('via_wav.asm' - 29/11/2016) 
	; 08/12/2016
	; 28/11/2016 - Erdogan Tan
	
	cmp	byte [audio_play_cmd], 1
	jz	short _tlp4 ; stop command !

	mov	edi, [audio_dma_buff]
	mov	ecx, [audio_dmabuff_size]

	mov	byte [audio_flag], 0 ; Buffer 1 (next buffer)

	test	byte [audio_flag_eol], VIA_REG_STAT_EOL 
	jz	short _tlp1 ; FLAG
	
	; EOL
	inc	byte [audio_flag] ; 1 ; Buffer 2 (next buffer)
	add	edi, ecx
_tlp1: 
	mov	esi, [audio_p_buffer]
	;rep	movsb
	shr	ecx, 2
	rep	movsd 

        mov     al, [audio_flag_eol]   ;; ack ;;
        mov     dx, VIADEV_PLAYBACK + VIA_REG_OFFSET_STATUS
        call    ctrl_io_w8

	test	byte [audio_flag_eol], VIA_REG_STAT_FLAG 
	jz	short _tlp2

	mov	al, VIA_REG_CTRL_INT
       	or	al, VIA_REG_CTRL_START
       	mov     dx, VIADEV_PLAYBACK + VIA_REG_OFFSET_CONTROL
        call    ctrl_io_w8

	call	codec_check_ready
_tlp2:	
	retn
vt8233_stop: ; 22/04/2017
	mov	byte [audio_play_cmd], 0 ; stop !
_tlp4:
        ; 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	channel_reset

set_vt8233_bdl: ; Set VT8237R Buffer Descriptor List
	; 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]

	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

	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
	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
	or	eax, EOL
	;or	eax, FLAG
	stosd

	loop    s_vt8233_bdl0
	
	retn


vt8233_start_play:  
	; start to play audio data via VT8233 audio controller 
	; 21/04/2017 (TRDOS 386 kernel, 'audio.s')
	; 24/03/2017 - 'PLAYER.COM' ('via_wav.asm' - 29/11/2016) 
	; write buffer descriptor list address
	;
        mov	eax, audio_bdl_buff
	shl	eax, 4
  
	; 12/11/2016 - Erdogan Tan 
	; (Ref: KolibriOS, vt823x.asm, 'create_primary_buff')
	mov	edx, VIADEV_PLAYBACK + VIA_REG_OFFSET_TABLE_PTR
        call	ctrl_io_w32

	call	codec_check_ready

  	mov	dx, VIADEV_PLAYBACK + VIA_REG_OFS_PLAYBACK_VOLUME_L
        ;mov	eax, 2   ;31
        mov	al, [audio_master_volume_l] ; 21/04/2017
	call	ctrl_io_w8

	call	codec_check_ready

        mov     dx, VIADEV_PLAYBACK + VIA_REG_OFS_PLAYBACK_VOLUME_R
        ;mov	ax, 2   ;31
        mov	al, [audio_master_volume_r] ; 21/04/2017
	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
	call    set_VT8233_LastValidIndex

	mov	byte [audio_play_cmd], 1 ; play command (do not stop) !

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
	;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


;input AL = index # to stop on
set_VT8233_LastValidIndex:
	; 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	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 
	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
        call    ctrl_io_w32
	call	codec_check_ready
	;pop	edx
	retn

vt8233_pause: ; pause
	; 22/04/2017
	mov     al, VIA_REG_CTRL_INT
        or      al, VIA_REG_CTRL_PAUSE
	mov     dx, VIADEV_PLAYBACK + VIA_REG_OFFSET_CONTROL
        call    ctrl_io_w8
	;call	codec_check_ready
	;retn
	jmp	codec_check_ready

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:
	retn

vt8233_volume:
	; set VT8237R (vt8233) sound volume level
	; 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, 3131h
	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
	call	codec_write
vt8233_vol_1:
	retn