; MINT.ASM Version 1.0 ; (c) 1990 Eletech Electronics, Inc. All rights UNreserved. ; ; Creation : 3-13-90 by Wei Lu. ; Function : Works with MIMIC.C to play Eletech Digicorder recorded voice ; file thru PC's internal speaker at specified sampling rate. ; Usage : See MIMIC.C. ; Assembler: Microsoft MASM 5.0 and up. ; Assembling Options: None. ; ; Operation: MINT.ASM is an interrupt handler. After MIMIC.C re-directs clock ; interrupt, subsequent timer 0 interrupts are intercepted and ; handled by MINT.ASM. ; ; MINT.ASM has two responsibilities: ; ; a. Output voice data bit by bit to PC's speaker upon each timer ; 0 interrupt. ; ; b. Maintain DOS clock operation by doing "real" clock interrupts ; 18.2 times per second. ; ; MINT.ASM assumes that MIMIC.C has done the following setup: ; ; a. Load voice data into play buffer pointed to by play_addr. ; ; b. Calculate and set play_ptr, play_length and tack_count (see ; MIMIC.C for explanations.) ; ; c. Re-direct timer 0 (clock) interrupt and re-program timer 0 ; interrupt rate to equal sampling rate. ; ; Upon each timer 0 interrupt, MINT.ASM will be activated and do ; one of the following: ; ; a. If there is no more data to play, do a "real" clock interrupt ; if it's time to, then terminate. ; ; Otherwise, ; ; b. If it does not need a new byte of data, output a bit and ; terminate. ; ; c. If it needs a new byte of data but it's not time yet to do a ; "real" clock interrupt, get a byte, output a bit and ; terminate. ; ; d. If it needs a new byte of data and it's also time to do a ; "real" clock interrupt, get a byte, output a bit, do a "real" ; clock interrupt and terminate. ; ; The following codes are written so as to minimize the execution ; time of case b above. See MIMIC.C for more operation details. .model small public _int_handler public _play_addr public _play_ptr public _play_length public _tack_count speaker equ 97 ; speaker I/O port ocw2 equ 20h ; 8259 control I/O port eoi equ 20h ; "End Of Interrupt" control word clock_irq equ 255 ; "real" clock new interrupt # .data _play_addr label dword ; play buffer is 64K bytes _play_ptr label word ; actually the offset of play_addr play_addr label dword ; (set and changed by MIMIC.C) play_addr_off dw ? play_addr_seg dw ? ; does not change once set _play_length label word ; number of bytes to play play_length dw ? _tack_count label byte ; see MIMIC.C tack_count db ? data_byte db 55 ; a byte of garbage doesn't hurt bit_ptr db 80h ; bits go from left to right t_count db ? ; a working copy of tack_count .code _int_handler proc far push ax push ds mov ax,@data mov ds,ax cmp play_length,0 ; are we out of voice data? je no_more mov al,data_byte mov ah,bit_ptr ; bit_ptr used as a mask and al,ah ; is this bit 0 or 1? jz zero one: nop ; so that "one" and "zero" take nop ; the same amount of time to do mov al,01001010b ; (optimized for AT) out speaker,al ror ah,1 ; will set Carry if rotate around mov bit_ptr,ah ; update bit_ptr jc adjust mov al,eoi out ocw2,al ; re-enable interrupt requests pop ds pop ax iret zero: mov al,01001000b out speaker,al ror ah,1 mov bit_ptr,ah jc adjust mov al,eoi out ocw2,al pop ds pop ax iret adjust: ; now we have to get a new byte sub play_length,1 ; also update play_length push si push es les si,dword ptr ds:play_addr mov al,byte ptr es:[si] mov data_byte,al ; the new byte is in inc si mov play_addr_off,si ; update play_ptr dec t_count ; is it time to do clock int? jz do_interrupt mov al,eoi out ocw2,al pop es pop si pop ds pop ax iret do_interrupt: mov al,tack_count mov t_count,al ; reset t_count to tack_count int clock_irq ; do clock interrupt pop es ; no need to re-enable interrupt pop si ; requests since it's already pop ds ; been done in clock interrupt pop ax ; service routine iret no_more: ror bit_ptr,1 ; still have to know when to do jc adj ; clock interrupt mov al,eoi out ocw2,al pop ds pop ax iret adj: dec t_count jz do_int mov al,eoi out ocw2,al pop ds pop ax iret do_int: mov al,tack_count mov t_count,al int clock_irq pop ds pop ax iret _int_handler endp end