; This File: PUMPSND.S ; Author: Glyn H. Anderson; modified by Duncan Blanchard ; Created: 10-Nov-87 12:53 ; Last Edit: 23-Nov-87 14:13; 23-Feb-88 ; register conventions: in interrupt code, must preserve everything, ; elsewhere, preserve all except a0/d0 BIOS equ 1 ;BIOS trap number GISelect equ $FFFF8800 ;GI sound chip register select GIRead equ GISelect ;GI sound chip register read GIWrite equ GISelect+2 ;GI sound chip register write CLKFRQ equ 8000000 ;ST clock freq (8 MHz) LOOPLEN equ 220 ;was 286, 214, 238-5 WASTELEN equ 10 ;length of waste loop bss *----------------------- * interrupt globals *----------------------- i_samp ds.l 1 ;address of sound samples i_reps ds.w 1 ;repeat count i_p ds.l 1 ;pointer to next sample i_n ds.w 1 ;number of samples remaining in buffer i_waste ds.w 1 ;tens of cycles to waste i_done ds.w 1 ;end-of-sound flag oldSSP ds.l 1 ;saved supervisor stack pointer oldSR ds.w 1 ;saved status register text xdef pump_sound *----------------------- * pump_sound *----------------------- pump_sound move.l 4(a7),a0 ;get pointer to data buffer move.w 8+2(a7),d0 ;get caller's repeat count bsr ps_init bsr ps_loop bsr ps_cleanup rts ;from pump_sound *----------------------- * ps_init *----------------------- ; setup for sound; a0 -> buffer, d0.w = reps ps_init move.l a0,i_samp ;store *** clr.w d0 *** move.b 2(a0),d0 ;get data's repeat count (byte) move.w d0,i_reps ;store move.l #CLKFRQ,d0 ;ST clock freq divu 4(a0),d0 ;over sample freq -> ticks per sample sub.w #LOOPLEN,d0 ;take out loop's length bpl.s psix1 ;branch if we've got time to waste... moveq #0,d0 ;else don't waste any psix1 and.l #$0000FFFF,d0 ;clear upper bits divu #WASTELEN,d0 ;length of waste loop move.w d0,i_waste ;save waste count ; enter Super mode, disable interrupts clr.l -(sp) ;arg is 0L move.w #$20,-(sp) ;routine no. is $20 trap #BIOS ;call Super( 0L ) addq.l #6,sp ;fix stack move.l d0,oldSSP ;save SSP move sr,d0 ;get SR move.w d0,oldSR ;save it or.w #$0700,d0 ;disable interrupts move d0,sr ; atomically adjust mixer control bits (don't disturb I/O control bits) move.l #GISelect,a0 move.b #$07,(a0) ;request mixer controls move.b (a0),d0 ;get controls ori.b #$3F,d0 ;disable all noise and tones move.b d0,2(a0) ;set controls clr.w i_done ;reset flag, and fall thru ... *----------------------- * rep_init *----------------------- rep_init move.l i_samp,a0 move.w 8(a0),d0 addq.w #1,d0 move.w d0,i_n ;setup length counter (+1) adda.w #10,a0 move.l a0,i_p ;setup pointer to start of data rts *----------------------- * ps_loop *----------------------- ; interrupts come here. ; Each pass through the critical section uses 282 ticks, so an 18k sample ; rate (one every 434 ticks at 8 MHz) consumes 65 percent of total cpu time, ; plus interrupt overhead. Careful. ps_loop movem.l d0/a0-a1,-(sp) ;[38] subq.w #1,i_n ;[16] [20?] ;reached end of cycle? beq.s pslx3 ;[ 8] [10 tk] ;yes ;[[ 62]] pslx1 move.l i_p,a0 ;[16] [?] ;pointer to next sample move.b (a0)+,d0 ; [8] ;get it ext.w d0 ; [4] ;signed byte move.l a0,i_p ;[16] [?] ;update pointer lea volmap(pc),a1 ; [8] ;address of conversion table adda.w d0,a1 ; [8] add.w d0,d0 ; [4] adda.w d0,a1 ; [8] ;offset x3 ;[[ 72]] ; update volume on each channel (as simultaneously as possible) move.l #GISelect,a0 ;[12] move.b #8,(a0) ;[12] move.b (a1)+,2(a0) ;[16] ;write A vol move.b #9,(a0) ;[12] move.b (a1)+,2(a0) ;[16] ;write B vol move.b #10,(a0) ;[12] move.b (a1)+,2(a0) ;[16] ;write C vol ;[[ 96]] pslx2 movem.l (sp)+,d0/a0-a1 ;[36] ;exit rts ;[16] ;total 282 ;[[ 52]] ; handle end of cycle. This code runs in parallel with the above, and so ; consumes no more interrupt cycles than usual. pslx3 tst.w i_done ;been here before? bne.s pslx2 ;just waiting until ints disabled subq.w #1,i_reps ;any more reps? beq.s pslx4 ;no bsr rep_init ;yes, set up next cycle bra.s pslx2 pslx4 move.w #1,i_done ;done, raise flag bra.s pslx2 *----------------------- * ps_cleanup *----------------------- ps_cleanup move oldSR,sr ;restore interrupt level move.l oldSSP,-(sp) ;push old SSP move.w #$20,-(sp) ;function #$20 trap #BIOS ;call Super( SSP ) addq.l #6,sp ;fix stack rts text ; keep read-only data in code seg for quick access ; This table maps signed bytes (-128..127) into amplitude inputs for the ; sound chip. Each digit is the 4-bit amplitude for one channel. ; In the Atari ST the three channels are not weighted equally; the values ; in the table were determined empirically. ; the table's entry point is in the middle, corresponding to signed '0' dc.b $01,$1,$1,$01,$1,$2,$01,$1,$3,$00,$3,$3 ;4 groups per line dc.b $01,$1,$4,$01,$2,$4,$01,$4,$2,$01,$4,$3 dc.b $01,$5,$2,$03,$3,$4,$04,$3,$3,$05,$3,$2 dc.b $05,$3,$3,$05,$4,$2,$05,$3,$4,$03,$6,$2 dc.b $03,$6,$3,$03,$4,$6,$03,$6,$4,$03,$5,$6 dc.b $04,$6,$4,$05,$6,$3,$04,$6,$5,$04,$7,$3 dc.b $05,$7,$2,$05,$7,$3,$04,$7,$5,$05,$7,$4 dc.b $05,$6,$6,$05,$7,$5,$04,$7,$6,$03,$7,$7 dc.b $05,$7,$6,$05,$8,$3,$04,$8,$5,$03,$8,$6 dc.b $07,$6,$6,$07,$7,$5,$07,$8,$0,$05,$9,$0 dc.b $05,$9,$1,$05,$9,$2,$05,$9,$3,$04,$9,$5 dc.b $06,$9,$1,$06,$9,$2,$06,$9,$3,$07,$9,$0 dc.b $08,$8,$2,$08,$8,$3,$09,$6,$5,$09,$0,$8 dc.b $09,$7,$4,$09,$6,$6,$09,$7,$5,$09,$8,$0 dc.b $08,$9,$1,$08,$9,$2,$08,$9,$3,$08,$8,$7 dc.b $07,$9,$7,$08,$9,$5,$06,$A,$3,$09,$6,$8 dc.b $09,$4,$9,$08,$7,$9,$09,$5,$9,$09,$9,$3 dc.b $09,$9,$4,$0A,$6,$6,$0A,$7,$5,$0A,$3,$8 dc.b $08,$A,$1,$08,$A,$2,$04,$B,$2,$04,$B,$3 dc.b $03,$B,$5,$03,$A,$9,$02,$B,$6,$03,$B,$6 dc.b $06,$B,$2,$06,$B,$3,$06,$B,$4,$07,$B,$1 dc.b $07,$B,$2,$07,$B,$3,$07,$B,$4,$07,$B,$5 dc.b $07,$A,$9,$02,$A,$A,$03,$A,$A,$09,$2,$B dc.b $09,$3,$B,$08,$9,$A,$08,$7,$B,$05,$9,$B dc.b $03,$3,$C,$03,$4,$C,$05,$2,$C,$05,$3,$C dc.b $06,$1,$C,$07,$9,$B,$06,$3,$C,$04,$6,$C dc.b $06,$4,$C,$07,$2,$C,$07,$3,$C,$07,$4,$C dc.b $07,$5,$C,$05,$B,$A,$08,$B,$9,$0A,$6,$B dc.b $09,$B,$8,$09,$A,$A,$09,$9,$B,$08,$5,$C dc.b $06,$C,$5,$07,$C,$3,$05,$C,$7,$03,$C,$8 dc.b $03,$B,$B,$08,$7,$C,$04,$B,$B,$05,$C,$8 dc.b $05,$B,$B,$06,$9,$C,$06,$C,$8,$06,$B,$B volmap dc.b $0A,$9,$B,$09,$B,$A,$0B,$9,$A,$0B,$B,$2 dc.b $0B,$A,$9,$09,$C,$4,$09,$C,$5,$0A,$4,$C dc.b $0A,$5,$C,$0B,$8,$B,$0C,$1,$A,$0C,$2,$A dc.b $0C,$3,$A,$0C,$4,$A,$0C,$5,$A,$0C,$8,$9 dc.b $0C,$9,$8,$0C,$6,$A,$0C,$A,$1,$0C,$A,$3 dc.b $0C,$A,$5,$0C,$9,$9,$0C,$A,$6,$0C,$1,$B dc.b $0C,$A,$7,$0D,$2,$5,$0D,$3,$5,$0D,$5,$3 dc.b $0D,$5,$4,$0D,$5,$5,$0D,$6,$3,$0D,$6,$4 dc.b $0D,$6,$5,$0D,$7,$2,$0D,$6,$6,$0D,$7,$4 dc.b $0D,$7,$5,$0D,$7,$6,$0D,$8,$0,$0D,$7,$7 dc.b $0D,$6,$8,$0D,$8,$5,$0D,$8,$6,$0D,$8,$7 dc.b $0D,$9,$0,$0D,$9,$2,$0D,$9,$4,$0D,$9,$5 dc.b $0D,$9,$6,$0D,$3,$A,$0D,$4,$A,$0D,$5,$A dc.b $0D,$6,$A,$0B,$3,$D,$0B,$4,$D,$0B,$5,$D dc.b $0D,$A,$1,$0D,$A,$3,$0D,$A,$5,$0D,$A,$6 dc.b $0D,$A,$7,$0D,$4,$B,$0D,$5,$B,$0D,$A,$8 dc.b $0D,$6,$B,$0D,$7,$B,$0D,$A,$9,$0D,$8,$B dc.b $0D,$B,$3,$0D,$B,$5,$0D,$B,$6,$0D,$B,$7 dc.b $0D,$9,$B,$0D,$B,$8,$0B,$A,$D,$0C,$0,$D dc.b $0C,$2,$D,$0C,$4,$D,$0C,$5,$D,$0D,$0,$C dc.b $0D,$2,$C,$0D,$3,$C,$0D,$4,$C,$0D,$5,$C dc.b $0D,$6,$C,$0D,$B,$A,$0D,$7,$C,$0D,$8,$C dc.b $0C,$9,$D,$0D,$9,$C,$0D,$B,$B,$0C,$D,$3 dc.b $0C,$D,$6,$0C,$D,$7,$0D,$C,$7,$0C,$D,$8 dc.b $0D,$C,$8,$0A,$D,$C,$0A,$1,$E,$0A,$3,$E dc.b $0A,$5,$E,$0A,$6,$E,$0A,$7,$E,$0C,$D,$A dc.b $06,$D,$D,$07,$D,$D,$08,$E,$6,$0A,$9,$E dc.b $08,$D,$D,$0C,$D,$B,$0E,$7,$8,$09,$D,$D dc.b $0A,$7,$E,$0C,$D,$A,$0D,$C,$A,$0B,$C,$D dc.b $0C,$B,$D,$05,$D,$D,$07,$E,$8,$0B,$D,$C dc.b $0D,$5,$D,$0E,$4,$8,$0D,$6,$D,$0E,$7,$6 dc.b $0D,$7,$D,$0D,$C,$B,$0D,$8,$D,$09,$D,$D end ;of assembly text *----------------------- * DEAD CODE *----------------------- LLEN2 equ 106 SYNCCNT equ 100 ;resync every n samples ; from setup ... sub.w #LLEN2/10,d0 ;reduced waste after sync section bpl.s psx2 moveq #0,d0 psx2 move.w d0,w2 clr.w sync ;resync first time thru ; synchronize with beginning of a new square wave ; For smoother sq wave (and reduced cpu load), do this only every nth pass subq.w #1,sync ;[16] ;time to resync? bgt.s plx2 ;[10] ;no [8 if not taken] ;[[ 26]] move.w #SYNCCNT,sync ;[16] move.w w2,w_use ;[20] ;adjust waste val ; move.b #0,(a2) ;chan A period = longest poss ; move.b #$FF,(a3) ;(ignore 8 ls period bits) move.b #1,(a2) ;[12] move.b #$0F,(a3) ;[12] ; move.b #2,(a2) ;chan B period = longest poss ; move.b #$FF,(a3) move.b #3,(a2) ;[12] move.b #$0F,(a3) ;[12] ; move.b #4,(a2) ;chan C period = longest poss ; move.b #$FF,(a3) move.b #5,(a2) ;[12] move.b #$0F,(a3) ;[12] ;72+36-2 ;[[106]] ; fetch and unpack 3 amplitude values clr.w d0 move.b (a0)+,d0 ; [8] ;get sample eor.b #$80,d0 ; [8] ;offset the sample lsl.w #1,d0 ; [8] ;index into WORDs move.w 0(a1,d0.w),d0 ;[14] ;get 12-bit version move.w d0,d1 ; [4] ;copy it move.w d0,d2 ; [4] ;twice lsr.w #8,d0 ;[22] ;get A lsr.b #4,d1 ;[14] ;get B and.b #$0F,d2 ; [8] ;get C [66] ;; move.w 0(a1,d0.w),d2 ;[14] ;get 12-bit version ;; move.w d2,d1 ; [4] ;; and.b #$0F,d2 ; [8] ;extract C ;; lsr.b #4,d1 ;[14] ;shift B into position ;; move.w d1,d0 ; [4] ;; and.b #$0F,d1 ; [8] ;extract B ;; lsr.b #4,d0 ;[14] ;shift A into position [66] ; (after pump_next) move.b #$07,GISelect ;done -- disable all tone and noise [dbb] move.b GIRead,d0 or.b #$3F,d0 move.b d0,GIWrite end