; vi:filetype=nasm ai:
; hardcoded A220 I5 D1

DMA_ADDR equ 02h
DMA_COUNT equ 03h
DMA_MASK equ 0ah
DMA_MODE equ 0bh
DMA_BPTR equ 0ch
DMA_PAGE equ 83h

REG_RST equ 226h
REG_RDATA equ 22ah
REG_WDATA equ 22ch
REG_WSTAT equ 22ch
REG_RSTAT equ 22eh
REG_INTACK equ 22eh

CMD_PAUSE8 equ 0d0h
CMD_EXIT8 equ 0dah
CMD_START8 equ 0c0h
CMD_SETRATE equ 41h

	bits 16
	section code
..start:
	mov ax, data
	mov ds, ax

	mov ax, stack
	mov ss, ax
	mov sp, stacktop

	call reset_dsp
	mov ax, 0900h
	mov dx, msg_rstok
	int 21h

	; install ISR
	cli
	mov ax, 350dh	; get int 13 (IRQ 5) vector
	int 21h
	mov [prev_isr_seg], es
	mov [prev_isr_off], bx
	mov dx, intr_handler
	push ds
	mov ax, cs
	mov ds, ax
	mov ax, 250dh	; set int 13 (IRQ 5) vector
	int 21h
	pop ds
	sti

	call prog_dma	; program DMA

	; set sampling rate
	mov ax, 22050
	call set_rate

	; program DSP for DMA transfer
	mov ax, pcmdata_end - pcmdata
	call prog_dsp

mainloop:
	in al, 60h
	dec al
	jnz mainloop

	; restore original ISR
	cli
	push ds
	mov ax, [prev_isr_seg]
	mov ds, ax
	mov dx, [prev_isr_off]
	mov ax, 250dh	; restore int 13 (IRQ 5) vector
	int 21h
	pop ds
	sti

exit:
	mov ax, 4c00h
	int 21h

reset_dsp:
	mov dx, REG_RST
	mov al, 1
	out dx, al
	xor al, al
	out 80h, al
	out 80h, al
	out 80h, al
	out dx, al
	mov dx, REG_RSTAT
.waitdata:
	in al, dx
	and al, 80h
	jz .waitdata
	mov dx, REG_RDATA
.waitaa:
	in al, dx
	cmp al, 0aah
	jnz .waitaa
	ret

pause_dsp8:
	mov al, CMD_PAUSE8
	call write_dsp
	ret

write_dsp:
	push dx
	mov ah, al
	mov dx, REG_WSTAT
.wait:
	in al, dx
	and al, 80h
	jnz .wait
	mov al, ah
	out dx, al
	pop dx
	ret

read_dsp:
	push dx
	mov dx, REG_RSTAT
.wait:
	in al, dx
	and al, 80h
	jz .wait
	mov dx, REG_RDATA
	in al, dx
	pop dx
	ret

prog_dsp:
	push cx
	dec ax			; transfer length -1
	mov cx, ax		; save length-1 to cx
	mov al, CMD_START8
	call write_dsp
	xor al, al		; transfer mode 0 (mono, unsigned)
	call write_dsp
	mov al, cl
	call write_dsp
	mov al, ch
	call write_dsp
	pop cx
	ret

intr_handler:
	pusha
	call pause_dsp8
	mov dx, REG_INTACK
	in al, dx		; ack DSP interrupt
	mov al, 20h
	out 20h, al		; ack PIC interrupt
	popa
	iret

set_rate:
	push cx
	mov cx, ax
	mov al, CMD_SETRATE
	call write_dsp
	mov al, ch
	call write_dsp
	mov al, cl
	call write_dsp
	pop cx
	ret

prog_dma:
	push dx
	; mask DMA channel
	mov dx, DMA_MASK
	mov al, 5	; DMA 1 and mask bit
	out dx, al
	; clear byte pointer flip-flop
	mov dx, DMA_BPTR
	out dx, al
	; write DMA mode
	mov dx, DMA_MODE
	mov al, 49h	; mode:single, transfer:read from mem, channel:1
	out dx, al
	; write address low/high
	xor ebx, ebx
	mov bx, data		; data segment
	shl ebx, 4
	xor eax, eax
	mov ax, pcmdata
	add eax, ebx		; eax now has the linear address
	mov ebx, eax		; save result for later
	mov dx, DMA_ADDR
	out dx, al
	mov al, ah
	out dx, al
	; write page
	mov dx, DMA_PAGE
	mov eax, ebx
	shr eax, 16
	out dx, al
	; write length-1 low/high
	mov ax, pcmdata_end - pcmdata - 1
	mov dx, DMA_COUNT
	out dx, al
	mov al, ah
	out dx, al
	; unmask channel
	mov dx, DMA_MASK
	mov al, 1	; DMA 1, mask bit 0
	out dx, al
	pop dx
	ret

	section data
msg_rstok db 'DSP reset',13,10,'$'
prev_isr_seg resw 1
prev_isr_off resw 1

	align 16384
pcmdata:
	incbin "click.pcm"
pcmdata_end:

	segment stack stack
resb 64
stacktop: