ftp.nice.ch/peanuts/GeneralData/Usenet/news/1989/CSN-89.tar.gz#/comp-sys-next/1989/Nov/sine-wave-synthesis-on-Next

This is sine-wave-synthesis-on-Next in view mode; [Up]


Date: Sun 24-Nov-1989 18:39:38 From: Unknown Subject: sine wave synthesis on Next There have been a number of requests for my source code which allows you to synthesize a sine wave on the NeXT's DAC. Enclosed is a copy of the source. Make sure you separate the code into three different files: sine.c, sine.asm, and ioequ.asm. Compile sine.c with: cc -ansi -lm -lsys_s -O. Assemble sine.asm with: asm56000 -a -b -l; ioequ.asm is included by sine.asm. Leonard Manzara (manzara@cpsc.UCalgary.CA) /********************************************************************************* * * PROGRAM: sine.c * * AUTHOR: Leonard Manzara * * THIS PROGRAM GENERATES A SINE WAVE ON THE DSP AND PLAYS IT THROUGH * THE NeXT DAC. BASED HEAVILY ON dsp_example_3. * COMPILE THIS WITH: cc sine.c -ansi -lm -lsys_s -O * THE DSP ASSEMBLY PROGRAM IS: sine.asm * *********************************************************************************/ #import <sound/sound.h> #import <sound/sounddriver.h> #import <mach.h> #import <stdlib.h> #import <stdio.h> static int read_count; main() { int s_err; kern_return_t k_err; port_t dev_port, owner_port, cmd_port, temp_port, stream_port, read_port, reply_port; SNDSoundStruct *dspStruct; int reply, read_buf_size, read_width, low_water, high_water, protocol; /* INITIALIZE DMA STREAMING VARIABLES */ low_water = 48*1024; high_water = 64*1024; read_width = 2; read_buf_size = 512; /* GET THE DEVICE PORT FOR THE SOUND/DSP DRIVER ON THE LOCAL MACHINE */ k_err = netname_look_up(name_server_port,"","sound",&dev_port); if (k_err != KERN_SUCCESS) { mach_error("netname lookup failure ",k_err); exit(1); } /* GET THE OWNER PORT */ k_err = port_allocate(task_self(),&owner_port); if (k_err != KERN_SUCCESS) { mach_error("Cannot allocate owner port ",k_err); exit(1); } /* BECOME OWNER OF DSP RESOURCES */ temp_port = owner_port; k_err = snddriver_set_dsp_owner_port(dev_port,owner_port,&temp_port); if (k_err != KERN_SUCCESS) { mach_error("Cannot become owner of dsp resources ",k_err); exit(1); } /* GET THE DSP COMMAND PORT */ k_err = snddriver_get_dsp_cmd_port(dev_port,owner_port,&cmd_port); if (k_err != KERN_SUCCESS) { mach_error("Cannot acquire command port ",k_err); exit(1); } /* ACQUIRE OWNERSHIP OF THE SOUND OUTPUT DEVICE */ k_err = snddriver_set_sndout_owner_port(dev_port,owner_port,&temp_port); if (k_err != KERN_SUCCESS) { mach_error("Cannot become owner of sound out device ",k_err); exit(1); } /* SET UP TO STREAM DATA FROM THE DSP TO THE SOUND DEVICE */ protocol = 0; k_err = snddriver_stream_setup(dev_port,owner_port,SNDDRIVER_STREAM_DSP_TO_SNDOUT_44, read_buf_size,read_width,low_water,high_water, &protocol,&read_port); if (k_err != KERN_SUCCESS) { mach_error("Cannot set up stream from DSP ",k_err); exit(1); } /* SET THE DSP PROTOCOL */ k_err = snddriver_dsp_protocol(dev_port,owner_port,protocol); if (k_err != KERN_SUCCESS) { mach_error("Cannot set up DSP protocol ",k_err); exit(1); } /* ALLOCATE A PORT FOR REPLIES */ k_err = port_allocate(task_self(),&reply_port); if (k_err != KERN_SUCCESS) { mach_error("Cannot allocate reply port ",k_err); exit(1); } /* PARSE THE .LOD ASSEMBLY FILE AND BOOT THE DSP WITH IT */ s_err = SNDReadDSPfile("sine.lod",&dspStruct,NULL); if (s_err != SND_ERR_NONE) { fprintf(stderr,"Cannot parse DSP load image: %s\n",SNDSoundError(s_err)); exit(1); } s_err = SNDBootDSP(dev_port,owner_port,dspStruct); if (s_err != SND_ERR_NONE) { fprintf(stderr,"Cannot boot dsp: %s\n",SNDSoundError(s_err)); exit(1); } /* START STREAM FROM THE DSP */ k_err = snddriver_stream_start_reading(read_port,NULL, read_count,0, 0,0,0,0,0,0,reply_port); if (k_err != KERN_SUCCESS) { mach_error("Cannot enqueue read request ",k_err); exit(1); } /* SEND INTIAL TABLE VALUE TO DSP */ k_err = snddriver_dspcmd_req_condition(cmd_port,SNDDRIVER_ISR_HF3,SNDDRIVER_ISR_HF3, SNDDRIVER_HIGH_PRIORITY,PORT_NULL); if (k_err != KERN_SUCCESS) { mach_error("Cannot request condition ",k_err); exit(1); } printf("Enter table value: "); scanf("%d",&reply); getchar(); k_err = snddriver_dsp_write(cmd_port,&reply,1,4,SNDDRIVER_MED_PRIORITY); if (k_err != KERN_SUCCESS) { mach_error("Cannot write to DSP ",k_err); exit(1); } k_err = snddriver_dspcmd_req_condition(cmd_port,SNDDRIVER_ISR_HF3,0, SNDDRIVER_HIGH_PRIORITY,PORT_NULL); if (k_err != KERN_SUCCESS) { mach_error("Cannot request condition ",k_err); exit(1); } /* SEND HOST COMMAND TO THE DSP CHIP; VECTOR = P:$0026 CAUSES PROGRAM CONTROL TO JMP TO MAIN, WHERE SOUND SAMPLES ARE SENT OUT. NOTE THAT THE HOST COMMAND IS THE VECTOR ADDRESS DIVIDED BY 2 */ printf("Push return to start sine wave:"); reply = getchar(); k_err = snddriver_dsp_host_cmd(cmd_port,(u_int)0x13,SNDDRIVER_HIGH_PRIORITY); if (k_err != KERN_SUCCESS) { mach_error("Cannot send host command ",k_err); exit(1); } /* PLAY THE SINE TONE UNTIL RETURN PUSHED AGAIN. VECTOR = p:$0028 CAUSES PROGRAM CONTROL TO JMP TO RESET, EFFECTIVELY STOPPING SAMPLE OUT */ printf("Push return to stop sine wave:"); reply = getchar(); k_err = snddriver_dsp_host_cmd(cmd_port,(u_int)0x14,SNDDRIVER_HIGH_PRIORITY); if (k_err != KERN_SUCCESS) { mach_error("Cannot send host command ",k_err); exit(1); } } ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; PROGRAM: sine.asm ;; ;; AUTHOR: Leonard Manzara ;; ;; THIS PROGRAM, WHEN USED IN CONJUNCTION WITH sine.c, PRODUCES A SINE TONE THROUGH ;; THE NeXT'S D/A CONVERTER. ASSEMBLE THIS WITH: asm56000 -a -b -l sine.asm ;; BASED HEAVILY ON dsp_example_3. ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; include 'ioequ.asm' ;; MEMORY STORAGE DMA_DONE equ 0 x_DMA_flags equ $00ff x_OutputValue equ $00fe y_DMA_size equ $00ff y_Buffer_count equ $00fe XRAMLO equ 8192 YTABLERAM equ 8192 TABLE_LENGTH equ 1 DMA_R_REQ equ $050001 ; message to host to request DMA transfer ;; INTERRUPT VECTORS org p:$0 jmp <reset ; reset interrupt org p:$0024 ; set flag when dma transfer done bset #DMA_DONE,x:x_DMA_flags org p:$0026 ; host command to start sine wave send jmp <main org p:$0028 ; host command to stop sine wave send jmp <reset ;; RESET SUBROUTINE org p:$40 reset movec #6,omr ; chip set to mode 2; ROM enabled bset #0,x:m_pbc ; configure port B so it acts as host interface bset #3,x:m_pcddr ; program pin 3 (pc3) of port C to be output bclr #3,x:m_pcd ; zero to enable the external ram movep #>$000000,x:m_bcr ; set 0 wait states for all external RAM movep #>$00b400,x:m_ipr ; set interrupt priority register to SSI=0, SCI=1, HOST=0 move #>$0200,a move a,y:y_DMA_size ; set buffersize to 512 move #>XRAMLO,r7 ; set pointer to beginning of DMA buffer clr a move a,x:x_DMA_flags ; clear DMA flags move a,y:y_Buffer_count ; set Buffer count to 0 jsr read_table ; read initial table value(s) move y:YTABLERAM,n2 ; intialize n2, for pitch nop ; waste a cycle to let n2 set bset #m_hcie,x:m_hcr ; enable host command interrupts move #0,sr ; unmask interrupts _loop jmp _loop ; loop until interrupt sends us to main ;; MAIN ROUTINE main move #>$0100,r2 ; load in origin of sine table into r2 move #>$0100-1,m2 ; make it mod 256 nop ; waste one cycle to allow r2 and m2 to be initialized _toploop move y:(r2)+n2,a ; read value from sine table rep #8 asr a ; shift the value right to avoid clipping jsr putHost ; since stereo put it into jsr putHost ; DMA buffer twice jmp _toploop ; loop forever ;; PUTHOST SUBROUTINE putHost move a,x:x_OutputValue ; store a, so we can restore it at end of routine move a,x:(r7)+ ; put output value into DMA register, increment pointer move y:y_Buffer_count,b move #>1,a add a,b move b,y:y_Buffer_count ; increment buffer count by 1 move y:y_DMA_size,a cmp b,a jgt _exit ; if Buffer count < DMA size, exit ; (only send DMA buffer when it is full) _transfer jclr #m_htde,x:m_hsr,_transfer ; loop until htde bit HSR is set movep #DMA_R_REQ,x:m_htx ; send DMA initiate read message to host _ackBegin jclr #m_hf1,x:m_hsr,_ackBegin ; loop until host acknowledges (HF1=1) move #>XRAMLO,r7 ; point to beginning of DMA buffer move y:y_DMA_size,b ; initialize loop size do b,_send_loop ; top of DMA buffer send loop _send jclr #m_htde,x:m_hsr,_send ; loop until htde bit of HSR is set movep x:(r7)+,x:m_htx ; send buffer element to host _send_loop btst #DMA_DONE,x:x_DMA_flags ; if interrupt has set flags, then go to end jcs _endDMA jclr #m_htde,x:m_hsr,_send_loop ; else keep sending zeroes until interrupt sets flags movep #0,x:m_htx jmp _send_loop _endDMA bclr #DMA_DONE,x:x_DMA_flags ; reset flag to 0, we know we are done--be ready to ; set flag on next interrupt _ackEnd jset #m_hf1,x:m_hsr,_ackEnd ; loop until host acknowledge has ended (HF1=0) move #>XRAMLO,r7 ; reset DMA buffer pointer to beginning clr a ; reset buffer count move a,y:y_Buffer_count _exit move x:x_OutputValue,a ; put output value into a, in case it's needed by the rts ; calling routine read_table bset #m_hf3,x:m_hcr ; set HF3 (tells host that DSP ready to receive data) move #>YTABLERAM,r3 ; reset pointer to beginning of table do #TABLE_LENGTH,_tableloop ; loop until table filled _loop jclr #m_hrdf,x:m_hsr,_loop ; loop until hrdf bit of HSR becomes set movep x:m_hrx,y:(r3)+ ; move data into y memory; increment pointer _tableloop bclr #m_hf3,x:m_hcr ; clear HF3 (tells host that DSP finished reading data) rts; ;************************************************************************ ; ; ioequ.asm ; ; This program originally available on the Motorola DSP bulletin board. ; It is provided under a DISCLAMER OF WARRANTY available from ; Motorola DSP Operation, 6501 Wm. Cannon Drive W., Austin, Tx., 78735. ; ; Motorola Standard I/O Equates (lower case). ; ; Last Update 25 Aug 87 Version 1.1 (fixed m_of) ; ;************************************************************************ ; ; EQUATES for DSP56000 I/O registers and ports ; ;************************************************************************ ioequlc ident 1,0 ;------------------------------------------------------------------------ ; ; EQUATES for I/O Port Programming ; ;------------------------------------------------------------------------ ; Register Addresses m_bcr EQU $FFFE ; Port A Bus Control Register m_pbc EQU $FFE0 ; Port B Control Register m_pbddr EQU $FFE2 ; Port B Data Direction Register m_pbd EQU $FFE4 ; Port B Data Register m_pcc EQU $FFE1 ; Port C Control Register m_pcddr EQU $FFE3 ; Port C Data Direction Register m_pcd EQU $FFE5 ; Port C Data Register ;------------------------------------------------------------------------ ; ; EQUATES for Host Interface ; ;------------------------------------------------------------------------ ; Register Addresses m_hcr EQU $FFE8 ; Host Control Register m_hsr EQU $FFE9 ; Host Status Register m_hrx EQU $FFEB ; Host Receive Data Register m_htx EQU $FFEB ; Host Transmit Data Register ; Host Control Register Bit Flags m_hrie EQU 0 ; Host Receive Interrupt Enable m_htie EQU 1 ; Host Transmit Interrupt Enable m_hcie EQU 2 ; Host Command Interrupt Enable m_hf2 EQU 3 ; Host Flag 2 m_hf3 EQU 4 ; Host Flag 3 ; Host Status Register Bit Flags m_hrdf EQU 0 ; Host Receive Data Full m_htde EQU 1 ; Host Transmit Data Empty m_hcp EQU 2 ; Host Command Pending m_hf EQU $18 ; Host Flag Mask m_hf0 EQU 3 ; Host Flag 0 m_hf1 EQU 4 ; Host Flag 1 m_dma EQU 7 ; DMA Status ;------------------------------------------------------------------------ ; ; EQUATES for Serial Communications Interface (SCI) ; ;------------------------------------------------------------------------ ; Register Addresses m_srxl EQU $FFF4 ; SCI Receive Data Register (low) m_srxm EQU $FFF5 ; SCI Receive Data Register (middle) m_srxh EQU $FFF6 ; SCI Receive Data Register (high) m_stxl EQU $FFF4 ; SCI Transmit Data Register (low) m_stxm EQU $FFF5 ; SCI Transmit Data Register (middle) m_stxh EQU $FFF6 ; SCI Transmit Data Register (high) m_stxa EQU $FFF3 ; SCI Transmit Data Address Register m_scr EQU $FFF0 ; SCI Control Register m_ssr EQU $FFF1 ; SCI Status Register m_sccr EQU $FFF2 ; SCI Clock Control Register ; SCI Control Register Bit Flags m_wds EQU $3 ; Word Select Mask m_wds0 EQU 0 ; Word Select 0 m_wds1 EQU 1 ; Word Select 1 m_wds2 EQU 2 ; Word Select 2 m_sbk EQU 4 ; Send Break m_wake EQU 5 ; Wake-up Mode Select m_rwi EQU 6 ; Receiver Wake-up Enable m_woms EQU 7 ; Wired-OR Mode Select m_re EQU 8 ; Receiver Enable m_te EQU 9 ; Transmitter Enable m_ilie EQU 10 ; Idle Line Interrupt Enable m_rie EQU 11 ; Receive Interrupt Enable m_tie EQU 12 ; Transmit Interrupt Enable m_tmie EQU 13 ; Timer Interrupt Enable ; SCI Status Register Bit Flags m_trne EQU 0 ; Transmitter Empty m_tdre EQU 1 ; Transmit Data Register Empty m_rdrf EQU 2 ; Receive Data Register Full m_idle EQU 3 ; Idle Line m_or EQU 4 ; Overrun Error m_pe EQU 5 ; Parity Error m_fe EQU 6 ; Framing Error m_r8 EQU 7 ; Received Bit 8 ; SCI Clock Control Register Bit Flags m_cd EQU $FFF ; Clock Divider Mask m_cod EQU 12 ; Clock Out Divider m_scp EQU 13 ; Clock Prescaler m_rcm EQU 14 ; Receive Clock Source m_tcm EQU 15 ; Transmit Clock Source ;------------------------------------------------------------------------ ; ; EQUATES for Synchronous Serial Interface (SSI) ; ;------------------------------------------------------------------------ ; Register Addresses m_rx EQU $FFEF ; Serial Receive Data Register m_tx EQU $FFEF ; Serial Transmit Data Register m_cra EQU $FFEC ; SSI Control Register A m_crb EQU $FFED ; SSI Control Register B m_sr EQU $FFEE ; SSI Status Register m_tsr EQU $FFEE ; SSI Time Slot Register ; SSI Control Register A Bit Flags m_pm EQU $FF ; Prescale Modulus Select Mask m_dc EQU $1F00 ; Frame Rate Divider Control Mask m_wl EQU $6000 ; Word Length Control Mask m_wl0 EQU 13 ; Word Length Control 0 m_wl1 EQU 14 ; Word Length Control 1 m_psr EQU 15 ; Prescaler Range ; SSI Control Register B Bit Flags m_of EQU $3 ; Serial Output Flag Mask m_of0 EQU 0 ; Serial Output Flag 0 m_of1 EQU 1 ; Serial Output Flag 1 m_scd EQU $1C ; Serial Control Direction Mask m_scd0 EQU 2 ; Serial Control 0 Direction m_scd1 EQU 3 ; Serial Control 1 Direction m_scd2 EQU 4 ; Serial Control 2 Direction m_sckd EQU 5 ; Clock Source Direction m_fsl EQU 8 ; Frame Sync Length m_syn EQU 9 ; Sync/Async Control m_gck EQU 10 ; Gated Clock Control m_mod EQU 11 ; Mode Select m_ste EQU 12 ; SSI Transmit Enable m_sre EQU 13 ; SSI Receive Enable m_stie EQU 14 ; SSI Transmit Interrupt Enable m_srie EQU 15 ; SSI Receive Interrupt Enable ; SSI Status Register Bit Flags m_if EQU $2 ; Serial Input Flag Mask m_if0 EQU 0 ; Serial Input Flag 0 m_if1 EQU 1 ; Serial Input Flag 1 m_tfs EQU 2 ; Transmit Frame Sync m_rfs EQU 3 ; Receive Frame Sync m_tue EQU 4 ; Transmitter Underrun Error m_roe EQU 5 ; Receiver Overrun Error m_tde EQU 6 ; Transmit Data Register Empty m_rdf EQU 7 ; Receive Data Register Full ;------------------------------------------------------------------------ ; ; EQUATES for Exception Processing ; ;------------------------------------------------------------------------ ; Register Addresses m_ipr EQU $FFFF ; Interrupt Priority Register ; Interrupt Priority Register Bit Flags m_ial EQU $7 ; IRQA Mode Mask m_ial0 EQU 0 ; IRQA Mode Interrupt Priority Level (low) m_ial1 EQU 1 ; IRQA Mode Interrupt Priority Level (high) m_ial2 EQU 2 ; IRQA Mode Trigger Mode m_ibl EQU $38 ; IRQB Mode Mask m_ibl0 EQU 3 ; IRQB Mode Interrupt Priority Level (low) m_ibl1 EQU 4 ; IRQB Mode Interrupt Priority Level (high) m_ibl2 EQU 5 ; IRQB Mode Trigger Mode m_hpl EQU $C00 ; Host Interrupt Priority Level Mask m_hpl0 EQU 10 ; Host Interrupt Priority Level Mask (low) m_hpl1 EQU 11 ; Host Interrupt Priority Level Mask (high) m_ssl EQU $3000 ; SSI Interrupt Priority Level Mask m_ssl0 EQU 12 ; SSI Interrupt Priority Level Mask (low) m_ssl1 EQU 13 ; SSI Interrupt Priority Level Mask (high) m_scl EQU $C000 ; SCI Interrupt Priority Level Mask m_scl0 EQU 14 ; SCI Interrupt Priority Level Mask (low) m_scl1 EQU 15 ; SCI Interrupt Priority Level Mask (high) >From: jmunkki@kampi.hut.fi (Juri Munkki)

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Marcel Waldvogel and Netfuture.ch.