home *** CD-ROM | disk | FTP | other *** search
- ;
- ; SAMPLASM.ASM
- ; ------------
- ;
- ; One voice sampler playback system using fractional addition 16/3/89
- ;
- ; with optional patch to drive PC speaker using PWM
- ;
- ; For use with SAMPLER.PAS V2
- ;
- ;
- ;
- ;
- ; (C) Copyright 1989 by Rowan McKenzie
- ;
- ; You may copy these files or use the source code only
- ; for non-profit purposes. Please contact me if you wish to use any
- ; part of the package for commercial purposes.
- ;
- ;
- ; Rowan McKenzie
- ; 35 Moore Ave,
- ; Croydon 3136
- ; Vic Australia
- ;
- ;
- ;
- ; This code provides the assembler section of SAMPLER.PAS
- ;
- ; This version uses the system timer for sample timing, and is therefore
- ; clock speed independent, although the CPU must be able to keep up with
- ; the interrupt rate (20kHz!).
- ;
- ;
- ;
- ; Notes:
- ; ------
- ;
- ; To assemble the D to A converter version, set pwm equ false
- ;
- ; To assemble the PWM version, set pwm equ true
- ;
- ; The D/A version uses the system timer (at 20kHz) to synchronise output
- ; samples (and to make the code clock speed independent)
- ;
- ; The PWM version uses the system timer in the same way, but triggers the
- ; speaker timer channel as a monostable whose pulse length is proportional
- ; to the sample value. Note that time constants are all scaled assuming
- ; 20kHz sample rate. If another sample rate is used, these will need to be
- ; recalculated. Also, faster sampling rates mean lower effective resolution
- ; from the one shot because it is clocked at a fixed frequency. The current
- ; setup gives the equivalent of a 6 bit d/a converter (I know that's not
- ; very good, but it's better than most PC speakers can justify!)
- ;
- ;
- ;
- ; Variables defined in Pascal source:
- ; -----------------------------------
- ;
- ; release is a boolean variable indicating whether key releases should
- ; terminate sound playback
- ;
- ; loop is a boolean variable indicating that the loop section of the sample
- ; should be played continuously after playback is complete
- ;
- ; daout is an integer variable containing the d/a converter port
- ; (for D/A version)
- ;
- ; increment is an integer variable containing a fractional increment constant which
- ; determines the pitch of the note to be played
- ;
- ; bufloop is an integer variable containing the offset of the start of the
- ; loop section
- ;
- ; bufend is an integer variable containing the offset of the end of the sample
- ; (for when the sample code is implemented)
- ;
- ; bufstart is an integer variable containing the offset of the start of the
- ; sample
- ;
- ; kbdmode is a boolean variable indicating whether keyboard service routine
- ; should pass control to bios (false) or handle the key itself (true)
- ;
- ; kbdflag is a byte variable to indicate a key has been pressed during replay
- ;
- ; keyval is a byte variable which will contain the scan code of a key pressed
- ; during replay
- ;
- ; tconstant is a byte variable which sets the system timer rate (20kHz)
- ;
- ; timer is a boolean variable which indicates the timer should be used
- ; to determine how long notes should last
- ;
- ; tinterval is an integer variable indicating how long a note should be
- ; played for (no. interrupts mod modulus)
- ;
- ; modulus is a byte variable which determines after how many interrupts the
- ; tinterval counter should be decremented
- ;
- ; song is a boolean variable indicating whether a song is being played
- ;
- ; trigger is an integer variable describing the minimum input level required
- ; to begin sampling
- ;
-
- page 255,132
- title sampler asm module
-
- ; ===== Equates =====
-
- true equ 1
- false equ 0
-
- pwm equ false ;pwm version if true else d/a version
-
- tdelay equ 2 ;delay constant between samples while
- ; waiting for trigger
- lshiftr equ 170 ;scan code for left shift release
- rshiftr equ 182
- tccont equ 43h ;control reg of 8253 timer chip
- tccount equ 42h ;counter reg 2 (speaker)
-
- speaker equ 97 ;speaker port for PWM version
-
- data segment word public
- ;
- ; data
- ;
-
- extrn tconstant:word,keyval:word,kbdflag:word,bufstart:word
- extrn bufend:word,bufloop:word,increment:word,daout:word,loop:word
- extrn release:word,timer:word,tinterval:word,modulus:word,song:word
- extrn trigger:word,kbdmode:word,quickexit:word,buffer:word
- extrn bufferw:word,bufflen:word
-
- sysold dd ? ;temp storage for old system int vector
- sptemp dw ? ;storage for sp for quick exit
- duration dw ? ;duration counter
- modul db ? ;local modulus counter
-
- data ends
-
- code segment byte public
- assume cs:code,ds:data
-
- public restore,sample,initial,replay,replayt,scalewave,echo
-
- kbdold dd ? ;temp storage for old kbd int vector
- dstemp dw ? ;holds ds incase it's needed
-
-
- ;************************************************************
- ;
- ; Restore - restore int vectors and exits
- ;
- ;************************************************************
-
- restore proc near
- cli ;restore old kbd interrupt
- push ds
- mov dx,word ptr cs:[kbdold]
- mov ds,word ptr cs:[kbdold+2]
- mov ax,2509h
- int 21h ;restore old kbd vector
- pop ds
-
- if pwm
- mov al,0b6h
- out tccont,al ;change timer to square wave gen mode
-
- in al,speaker
- and al,0fch
- out speaker,al ;speaker one shot disable
- endif
-
- sti
- ret
- restore endp
-
- ;***********************************************************************
- ;
- ; Initialise - save int vector contents, replace with my version
- ;
- ;***********************************************************************
-
- initial proc near
- cli
- mov cs:[dstemp],ds
- mov ax,3509h ;get old kbd int
- int 21h
- mov word ptr cs:[kbdold],bx ;save it
- mov word ptr cs:[kbdold+2],es
- mov dx,offset kbdint
- push ds
- mov ax,cs
- mov ds,ax
- mov ax,2509h
- int 21h ;replace with mine
- pop ds
- mov byte ptr [kbdflag],0 ;no key activity
-
- if pwm
- mov al,0b6h
- out tccont,al ;change timer to square wave gen mode
- mov al,byte ptr[tconstant]
- shr al,1
- out tccount,al ;produce mean equivalent dc voltage
- xor al,al
- out tccount,al
- in al,speaker
- or al,3
- out speaker,al ;speaker one shot enable
- else
- mov dx,[daout]
- mov al,127
- out dx,al
- endif
-
- sti
- ret
- initial endp
-
-
- ;******************************************************************************
- ;
- ; Sample data from A/D port
- ;
- ;******************************************************************************
-
- sample proc near
- cli
-
- in al,21h
- or al,98h ;disable comms, printer interrupts
- out 21h,al
-
- mov ax,3508h
- int 21h ;get system timer vector
- mov word ptr [sysold],bx
- mov word ptr [sysold+2],es ;and save it
-
- mov dx,offset sysint
- push ds
- mov ax,cs
- mov ds,ax
- mov ax,2508h
- int 21h ;replace with new one
- pop ds
-
- mov ah,byte ptr [trigger]
- mov dx,[daout]
- trig: in al,dx ;wait for trigger level
- mov cx,tdelay
- tdel: loop tdel ;delay before next sample taken
- cmp al,ah
- jc trig
-
- mov al,36h
- out 43h,al
- mov al,byte ptr [tconstant]
- out 40h,al ;set to ~20kHz system timer!
- mov al,0
- out 40h,al
- sti
-
- mov bx,[bufstart]
- mov di,[bufend]
-
- mov es,[buffer+2]
-
- sync: hlt ;wait for system interrupt
- in al,dx
- mov es:[bx],al ;store sample
- inc bx
- cmp bx,di
- jbe sync ;until buffer full
-
- call vrestore
-
- cli
- in al,21h
- and al,67h
- out 21h,al
- sti
-
- ret
- sample endp
-
-
- ;******************************************************************************
- ;
- ; Scale waveform for PWM replay
- ;
- ;******************************************************************************
-
- scalewave proc near
-
- push ds
-
- mov es,[bufferw+2] ;copy from buffer to bufferw with scale
- mov si,[bufferw]
- mov cx,bufflen
- inc cx
- mov di,[buffer]
- mov ds,[buffer+2]
-
- sloop: mov al,[di] ;div 4 +1
- shr al,1
- shr al,1
- inc al
- mov es:[si],al
- inc si
- inc di
- loop sloop
-
- pop ds
- ret
- scalewave endp
-
-
- ;*************************************************************************
- ;
- ; Echo - echos A/D to D/A for monitoring
- ;
- ;*************************************************************************
-
- echo proc near
- cli
-
- push bp
- in al,21h
- or al,98h ;disable comms, printer interrupts
- out 21h,al
-
- mov [sptemp],sp ;remember sp incase kbd abort
- mov al,1
- mov byte ptr [quickexit],al
- mov ax,3508h
- int 21h ;get system timer vector
- mov word ptr [sysold],bx
- mov word ptr [sysold+2],es ;and save it
-
- mov dx,offset sysint
- push ds
- mov ax,cs
- mov ds,ax
- mov ax,2508h
- int 21h ;replace with new one
- pop ds
-
- if pwm
- mov al,0b0h
- out tccont,al ;change timer to one shot mode
- mov al,255
- out tccount,al
- mov al,0
- out tccount,al
- mov al,90h
- out tccont,al
- endif
-
- mov al,36h
- out 43h,al
- mov al,byte ptr [tconstant]
- out 40h,al ;set to ~20kHz system timer!
- mov al,0
- out 40h,al
- sti
-
- if pwm
- in al,speaker
- or al,3
- out speaker,al ;speaker one shot enable
- else
- mov dx,[daout]
- endif
-
-
- eloop:
- if pwm
- mov dx,[daout]
- endif
-
- in al,dx
-
- if pwm
- shr al,1
- shr al,1
- inc al
- mov dx,tccount
- out dx,al ;activate one shot
- out dx,al ;seems to need this else distortion
- else
- out dx,al
- endif
-
- hlt ;wait for system interrupt
-
- jmp eloop ;exit via exitr
-
- echo endp
-
-
- ;*************************************************************************
- ;
- ; Replay - plays stored sound under interrupt control using fractional
- ; addition, handles loop function, looks for keypresses
- ;
- ;*************************************************************************
-
- replay proc near
- cli
-
- in al,21h
- or al,98h ;disable comms, printer interrupts
- out 21h,al
-
- push bp
- mov [sptemp],sp ;remember sp incase kbd abort
- mov al,1
- mov byte ptr [quickexit],al
- mov ax,3508h
- int 21h ;get system timer vector
- mov word ptr [sysold],bx
- mov word ptr [sysold+2],es ;and save it
-
- mov dx,offset sysint
- push ds
- mov ax,cs
- mov ds,ax
- mov ax,2508h
- int 21h ;replace with new one
- pop ds
-
- if pwm
- mov al,0b0h
- out tccont,al ;change timer to one shot mode
- mov al,255
- out tccount,al
- mov al,0
- out tccount,al
- mov al,90h
- out tccont,al
- endif
-
- mov al,36h
- out 43h,al
- mov al,byte ptr [tconstant]
- out 40h,al ;set to ~20kHz system timer!
- mov al,0
- out 40h,al
- sti
-
- if pwm
- in al,speaker
- or al,3
- out speaker,al ;speaker one shot enable
- endif
-
- loopa: mov bx,[tinterval]
- mov si,bx ;local note duration counter
- mov bl,byte ptr [modulus]
- mov [modul],bl ;local modulus counter
-
- if pwm
- mov es,[bufferw+2]
- else
- mov es,[buffer+2]
- endif
-
- mov bx,[bufstart]
- mov bp,[increment]
- mov ah,byte ptr [loop]
-
- ife pwm
- mov dx,[daout]
- else
- mov dx,tccount
- endif
-
- repeat: mov di,[bufend]
- mov ch,bl
- mov cl,0
-
- loops: add cx,bp ; fractional add (ch.cl is result)
- mov bl,ch ; lsnib of buffer pointer
- adc bh,0
- waiti: mov al,es:[bx] ; get sample
-
- hlt ;wait for system interrupt
-
- if pwm
- out dx,al ;activate one shot
- out dx,al ;seems to need this else distortion
- else
- out dx,al
- endif
-
- testlop: cmp bx,di
- jb loops
- test ah,ah ;loop mode?
- jz exitr
- mov bx,[bufloop]
- jmp repeat ;yes, restart from loop position
-
- exitr: mov al,0
- mov byte ptr [quickexit],al
- call vrestore
-
- cli
- in al,21h ;enable serial/parallel ints
- and al,67h
- out 21h,al
- sti
-
- pop bp
- ret
- replay endp
-
-
- ;*************************************************************************
- ;
- ; Replayt - plays stored sound under interrupt control using fractional
- ; addition, handles loop function, looks for keypresses. this
- ; version uses timer control
- ;
- ;*************************************************************************
-
- replayt proc near
- cli
-
- in al,21h
- or al,98h ;disable comms, printer interrupts
- out 21h,al
-
- push bp
- mov [sptemp],sp ;remember sp incase kbd abort
- mov al,1
- mov byte ptr [quickexit],al
- mov ax,3508h
- int 21h ;get system timer vector
- mov word ptr [sysold],bx
- mov word ptr [sysold+2],es ;and save it
-
- mov dx,offset sysint
- push ds
- mov ax,cs
- mov ds,ax
- mov ax,2508h
- int 21h ;replace with new one
- pop ds
-
- if pwm
- mov al,0b0h
- out tccont,al ;change timer to one shot mode
- mov al,255
- out tccount,al
- mov al,0
- out tccount,al
- mov al,90h
- out tccont,al
- endif
-
- mov al,36h
- out 43h,al
- mov al,byte ptr [tconstant]
- out 40h,al ;set to ~20kHz system timer!
- mov al,0
- out 40h,al
- sti
-
- if pwm
- in al,speaker
- or al,3
- out speaker,al ;speaker one shot enable
- endif
-
- loopat: mov si,[tinterval] ;local note duration counter
- mov bl,byte ptr [modulus]
- mov [modul],bl ;local modulus counter
-
- if pwm
- mov es,[bufferw+2]
- else
- mov es,[buffer+2]
- endif
-
- mov bx,[bufstart]
- mov bp,[increment]
- mov ah,[modul]
-
- ife pwm
- mov dx,[daout]
- else
- mov dx,tccount
- endif
-
- repeatt: mov di,[bufend]
- mov ch,bl
- mov cl,0
-
- loopst: add cx,bp ; fractional add (ch.cl is result)
- mov bl,ch ; lsnib of buffer pointer
- adc bh,0
- waitit: mov al,es:[bx] ; get sample
-
- hlt ;wait for system interrupt
-
- if pwm
- out dx,al ;activate one shot
- out dx,al ;seems to need this else distortion
- else
- out dx,al
- endif
-
- dec ah ;time to dec tinterval?
- jz ntestlop
- cmp bx,di
- jb loopst
- test byte ptr [loop],0ffh ;loop mode?
- jz waith
- mov bx,[bufloop]
- jmp repeatt ;yes, restart from loop position
-
- ntestlop: dec si ;see if time up
- mov al,byte ptr [modulus] ;reset modulus counter
- mov ah,al
- js timeupt ; yep, go and check if song mode
- cmp bx,di ;2nd copy of testlop avoids jump
- jb loopst
- test byte ptr [loop],0ffh ;loop mode?
- jz waith
- mov bx,[bufloop]
- jmp repeatt ;yes, restart from loop position
-
- timeupt: test byte ptr [song],0ffh ;song mode?
- jnz exitrt ;yes, immediate exit
- jmp loopat ;no, loop back to start of sound
-
- waith: test si,0ffffh ;wait for full duration
- jns waitit ;wait for keyboard or timer
- exitrt: jmp exitr
-
- replayt endp
-
-
- ;
- ; local procedures
- ;
-
-
- ;
- ; Speaker interrupt service routine
- ;
- ; Does nothing except synchronise output operations
- ;
-
- sysint proc far
- push ax
- mov al,20h
- out 20h,al ;EOI to int controller
- pop ax
-
- iret
- sysint endp
-
- ;
- ; Comm1,2 interrupt service routine
- ;
- ; Prevents mouse activity from slowing cpu during sampling/replay
- ;
-
- comint proc far
- push ax
- mov al,20h
- out 20h,al ;EOI to int controller
- pop ax
-
- iret
- comint endp
-
- ;
- ; Custom keyboard interrupt handler - handles keypresses, key releases (if
- ; desired), modifies kbd flags. Note if TP4 kbd or screen
- ; procedure was in progress, DS may have been changed, so restore
- ; from dstemp.
- ;
-
- kbdint proc far ;custom keyboard interrupt handler
- push ds
- mov ds,cs:[dstemp]
- test byte ptr [kbdmode],0ffh ;handle key here?
- jnz kserv
- pop ds
- jmp cs:kbdold ;no, give to bios
- kserv: push ax
- in al,60h ;get key code
- cmp al,lshiftr ;always note shift releases
- jz notrel
- cmp al,rshiftr
- jz notrel
- test byte ptr [release],0ffh ;release sensitive?
- jnz isrel
- test al,al ;no, ignore if key released
- js pressd
- jmp notrel
- isrel: ;release sensitive
- cmp al,byte ptr [keyval] ;ignore if same key as last time
- jz pressd ; ie ignore autorepeat
- test al,al
- jns notrel ;if keydown, go record it
- mov ah,al
- xor ah,byte ptr [keyval]
- cmp ah,129 ;record release of last key but
- jns pressd ; not release of older key
- notrel: mov byte ptr [keyval],al ;record keypressed
- mov byte ptr [kbdflag],al
- pressd: mov al,0ffh
- in al,61h
- or al,80h
- out 61h,al
- and al,7fh
- out 61h,al
- mov al,20h ;EOI
- out 20h,al
- pop ax
- test byte ptr [quickexit],0ffh
- jz noquick
- test byte ptr [kbdflag],0ffh
- jz noquick
- mov es,[sptemp]
- pop ds
- push es
- pop sp
- jmp exitr
-
- noquick: pop ds
- iret
- kbdint endp
-
-
- vrestore proc near ;restore hardware, system timer vector
- cli
- mov al,36h
- out 43h,al
- xor al,al
- out 40h,al ;restore system timer frequency
- out 40h,al
-
- mov al,0b6h
- out tccont,al ;change timer to square wave gen mode
- mov al,byte ptr[tconstant]
- shr al,1
- out tccount,al ;produce mean equivalent dc voltage
- xor al,al
- out tccount,al
-
- push ds
- lds dx,sysold
- mov ax,2508h ;restore old system timer vec
- int 21h
- pop ds
- sti
- ret
- vrestore endp
-
- code ends
- end