dropped aas, moved to maxmod
[gbajam21] / libs / maxmod / mm_effect.S
1 /****************************************************************************
2  *                                                          __              *
3  *                ____ ___  ____ __  ______ ___  ____  ____/ /              *
4  *               / __ `__ \/ __ `/ |/ / __ `__ \/ __ \/ __  /               *
5  *              / / / / / / /_/ />  </ / / / / / /_/ / /_/ /                *
6  *             /_/ /_/ /_/\__,_/_/|_/_/ /_/ /_/\____/\__,_/                 *
7  *                                                                          *
8  *                           Sound Effect System                            *
9  *                                                                          *
10  *         Copyright (c) 2008, Mukunda Johnson (mukunda@maxmod.org)         *
11  *                                                                          *
12  * Permission to use, copy, modify, and/or distribute this software for any *
13  * purpose with or without fee is hereby granted, provided that the above   *
14  * copyright notice and this permission notice appear in all copies.        *
15  *                                                                          *
16  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES *
17  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF         *
18  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR  *
19  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES   *
20  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN    *
21  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF  *
22  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.           *
23  ****************************************************************************/
24
25 #include        "mp_defs.inc"
26 #include        "mp_mas.inc"
27 #include        "mp_mas_structs.inc"
28 #include        "mp_format_mas.inc"
29 #include        "mp_macros.inc"
30
31 #ifdef SYS_GBA
32 #include        "mp_mixer_gba.inc"
33 #endif
34
35 #ifdef SYS_NDS
36 #include        "mp_mixer_ds.inc"
37 #endif
38
39 /***********************************************************************
40  *
41  * Definitions
42  *
43  ***********************************************************************/
44
45 .struct 0                                       // mm_sound_effect
46 MM_SFX_SOURCE:  .space 4                        // word: source
47 MM_SFX_RATE:    .space 2                        // hword: rate
48 MM_SFX_HANDLE:  .space 2                        // byte:  handle
49 MM_SFX_VOLUME:  .space 1                        // byte:  volume
50 MM_SFX_PANNING: .space 1                        // byte:  panning
51 MM_SFX_SIZE:                                    // 8 bytes total
52
53 .equ    channelCount, 16
54 .equ    releaseLevel, 200
55
56 /***********************************************************************
57  *
58  * Memory
59  *
60  ***********************************************************************/
61
62         .BSS
63         .ALIGN 2
64         .GLOBAL mm_sfx_bitmask, mm_sfx_clearmask
65
66 mm_sfx_mastervolume:    .space 4
67 mm_sfx_channels:        .space 2*channelCount
68 mm_sfx_bitmask:         .space 4
69 mm_sfx_clearmask:       .space 4
70
71 mm_sfx_counter:         .space 1
72
73 /***********************************************************************
74  *
75  * Program
76  *
77  ***********************************************************************/
78  
79         .TEXT
80         .THUMB
81         .ALIGN 2
82         
83 /***********************************************************************
84  * mmResetEffects
85  ***********************************************************************/
86                                                 .global mmResetEffects
87                                                 .thumb_func
88 mmResetEffects:
89         
90         mov     r0, #0
91         mov     r1, #channelCount
92         ldr     r2,=mm_sfx_channels
93         
94 1:      strh    r0, [r2]
95         add     r2, #2
96         sub     r1, #1
97         bne     1b
98         
99         ldr     r2,=mm_sfx_bitmask
100         str     r0, [r2]
101         
102         bx      lr
103         
104 /***********************************************************************
105  * mmGetFreeEffectChannel()
106  *
107  * Return index to free effect channel
108  ***********************************************************************/
109                                                 .thumb_func
110 mmGetFreeEffectChannel:
111         
112         ldr     r0,=mm_sfx_bitmask              // r0 = bitmask
113         ldr     r0, [r0]                        //
114         mov     r1, #1                          // r1 = channel counter
115         
116 .channel_search:                                // shift out bits until we find a cleared one
117         lsr     r0, #1                          //
118         bcc     .found_channel                  //
119         add     r1, #1                          // r1 = index value
120         b       .channel_search                 //
121         
122 .found_channel:
123         
124         cmp     r1, #channelCount+1             // if r1 == cc+1 then r1 = 0 (no handles avail.)
125         bne     .found_valid_channel            //
126         mov     r1, #0                          //
127 .found_valid_channel:                           //
128         
129         mov     r0, r1                          // return value
130         bx      lr                              //
131
132 /***********************************************************************
133  * mmEffect( id )
134  *
135  * Play sound effect with default parameters
136  ***********************************************************************/
137                                                 .global mmEffect
138                                                 .thumb_func
139 mmEffect:
140         push    {lr}
141                                                 // r0 = ssssssss
142         mov     r1, #1                          // r1 = hhhhrrrr
143         lsl     r1, #10                         //
144         ldr     r2,=0x000080FF                  // r2 = ----ppvv
145         
146         push    {r0-r2}                         // mmEffectEx( sound )
147         mov     r0, sp                          // 
148         bl      mmEffectEx                      //
149         add     sp, #12                         //
150         pop     {r3}                            //
151         bx      r3                              //
152         
153 /***********************************************************************
154  * mmEffectEx( sound )
155  *
156  * Play sound effect with specified parameters
157  ***********************************************************************/
158                                                 .global mmEffectEx
159                                                 .thumb_func
160 mmEffectEx:
161         
162         push    {r4-r6, lr}
163         
164         mov     r4, r0
165         ldrh    r5, [r4, #MM_SFX_HANDLE]        // test if handle was given
166         
167         cmp     r5, #255
168         bne     1f
169         mov     r5, #0
170         b       .got_handle
171         
172 1:      cmp     r5, #0                          //
173         beq     .generate_new_handle            //
174         
175         lsl     r1, r5, #24                     // check if channel is in use
176         lsr     r1, #23                         //
177         sub     r1, #2                          //
178         ldr     r0,=mm_sfx_channels             //
179         ldrb    r0, [r0, r1]                    //
180         cmp     r0, #0                          //
181         beq     .got_handle                     // [valid handle otherwise]
182         
183         mov     r0, r5
184         bl      mmEffectCancel                  // attempt to stop active channel
185         cmp     r0, #0                          //
186         bne     .got_handle                     //
187                                                 // failure: generate new handle
188 .generate_new_handle:
189         
190         bl      mmGetFreeEffectChannel          // generate handle
191         mov     r5, r0                          //
192         beq     .got_handle                     //-(no available channels)
193                                                 //
194         ldr     r0,=mm_sfx_counter              // (counter = ((counter+1) & 255))
195         ldrb    r1, [r0]                        //
196         add     r1, #1                          //
197         strb    r1, [r0]                        //
198         lsl     r1, #8
199         orr     r5, r1
200         lsl     r5, #16
201         lsr     r5, #16
202         
203 .got_handle:
204         
205         ldr     r1,=mmAllocChannel              // allocate new channel
206         bl      mpp_call_r1                     //
207         cmp     r0, #255                        //
208         bge     .no_available_channels          //
209         
210         mov     r6, r0
211         
212         cmp     r5, #0
213         beq     1f 
214         
215         ldr     r1,=mm_sfx_channels             // register data
216         sub     r2, r5, #1                      // r3 = bit
217         lsl     r2, #24                         //
218         lsr     r2, #24                         //
219         mov     r3, #1                          //
220         lsl     r3, r2                          //
221         lsl     r2, #1                          //
222         add     r1, r2                          //
223         add     r2, r0, #1                      //
224         strb    r2, [r1, #0]                    //
225         lsr     r2, r5, #8                      //
226         strb    r2, [r1, #1]                    //
227         
228         ldr     r1,=mm_sfx_bitmask              // set bit
229         ldr     r2, [r1]                        //
230         orr     r2, r3                          //
231         str     r2, [r1]                        //
232         
233 1:      
234
235         ldr     r1,=mm_achannels                // setup channel
236         ldr     r1, [r1]                        //
237         mov     r2, #MCA_SIZE                   //
238         mul     r2, r0                          //
239         add     r1, r2                          //
240                                                 //
241         mov     r2, #releaseLevel               //
242         strb    r2, [r1, #MCA_FVOL]             //
243         
244         cmp     r5, #0                          //
245         bne     1f                              //
246         mov     r2, #ACHN_BACKGROUND            //
247         b       2f                              //
248 1:      mov     r2, #ACHN_CUSTOM                //
249 2:                                              //
250         strb    r2, [r1, #MCA_TYPE]             //
251         mov     r2, #MCAF_EFFECT                //
252         strb    r2, [r1, #MCA_FLAGS]            //
253         
254         GET_MIXCH r1                            // setup voice
255         mov     r2, #MIXER_CHN_SIZE             //
256         mul     r2, r0                          //
257         add     r3, r1, r2                      //
258         
259 #ifdef SYS_GBA
260
261         ldr     r0,=mp_solution                 // set sample data address
262         ldr     r0, [r0]                        //
263         ldrh    r1, [r4, #MM_SFX_SOURCE]        //
264         lsl     r1, #2                          //
265         add     r1, #12                         //
266         ldr     r1, [r0, r1]                    //
267         add     r1, r0                          //
268         ldrh    r2, [r1, #8+C_SAMPLEC_DFREQ]    //
269         add     r1, #8+C_SAMPLE_DATA            //
270         str     r1, [r3, #MIXER_CHN_SRC]        //
271         
272         ldrh    r0, [r4, #MM_SFX_RATE]          // set pitch to original * pitch
273         mul     r2, r0                          //
274         lsr     r2, #10-2                       //
275         str     r2, [r3, #MIXER_CHN_FREQ]       //
276         
277         mov     r1, #0                          // reset read position
278         str     r1, [r3, #MIXER_CHN_READ]       //
279         
280         ldrb    r0, [r4, #MM_SFX_VOLUME]        // set volume
281         
282         ldr     r1,=mm_sfx_mastervolume
283         ldr     r1, [r1]
284         mul     r0, r1
285         lsr     r0, #10
286         
287         strb    r0, [r3, #MIXER_CHN_VOL]        //
288         
289         ldrb    r0, [r4, #MM_SFX_PANNING]       // set panning
290         strb    r0, [r3, #MIXER_CHN_PAN]        //
291         
292 #else
293         ldr     r1, [r4, #MM_SFX_SOURCE]        // set sample address
294         lsr     r0, r1, #16                     // > 0x10000 = external sample
295         bne     1f                              //
296         ldr     r0,=mmSampleBank                // ID# otherwise
297         ldr     r0, [r0]                        //
298         lsl     r1, #2                          //
299         ldr     r1, [r0, r1]                    //
300         lsl     r1, #8                          //
301         lsr     r1, #8                          //
302         beq     .invalid_sample
303         add     r1, #8                          //
304         ldr     r0,=0x2000000                   //
305         add     r1, r0                          //
306 1:                                              //
307         str     r1, [r3, #MIXER_CHN_SAMP]       //
308
309         
310         ldrh    r0, [r4, #MM_SFX_RATE]          // set pitch to original * pitch
311         ldrh    r1, [r1, #C_SAMPLEC_DFREQ]      //
312         mul     r1, r0                          //
313         lsr     r1, #10                         //
314         strh    r1, [r3, #MIXER_CHN_FREQ]       //
315         
316         mov     r1, #0                          // clear sample offset
317         str     r1, [r3, #MIXER_CHN_READ]       //
318         
319         ldrb    r1, [r4, #MM_SFX_PANNING]       // set panning + startbit
320         lsr     r1, #1                          //
321         add     r1, #0x80                       //
322         strb    r1, [r3, #MIXER_CHN_CNT]        //
323         
324         ldrb    r1, [r4, #MM_SFX_VOLUME]        // set volume
325         ldr     r0,=mm_sfx_mastervolume
326         ldr     r0, [r0]
327         mul     r1, r0
328 //      lsr     r0, #10
329         lsr     r2, r1, #2
330 //      lsl     r2, r1, #8
331
332         strh    r2, [r3, #MIXER_CHN_VOL]        //
333         
334         b       .valid_sample
335 .invalid_sample:
336         mov     r0, #0
337         str     r0, [r3, #MIXER_CHN_SAMP]
338         
339 .valid_sample:
340         
341 #endif
342
343         mov     r0, r5                          // return handle
344         pop     {r4-r6}                         //
345         ret1                                    //
346         
347 .no_available_channels: 
348         mov     r0, #0                          // return bad
349         pop     {r4-r6}                         //
350         ret1                                    //
351
352
353
354 /***********************************************************************
355  * mme_get_channel_index
356  *
357  * Test handle and return mixing channel index
358  ***********************************************************************/
359                                                 .thumb_func
360 mme_get_channel_index:
361
362         lsl     r1, r0, #24                     // mask and test channel#
363         lsr     r1, #24-1                       //
364         cmp     r1, #0                          //
365         beq     .invalid_handle                 //
366         cmp     r1, #channelCount*2             //
367         bgt     .invalid_handle                 //
368         
369         ldr     r2,=mm_sfx_channels-2           // check if instances match
370         ldrh    r3, [r2, r1]                    //
371         lsr     r1, r3, #8                      //
372         lsr     r2, r0, #8                      //
373         cmp     r1, r2                          //
374         bne     .invalid_handle                 //
375         
376         lsl     r3, #24                         // mask channel#
377         lsr     r3, #24                         //
378         sub     r3, #1                          //
379         bx      lr                              //
380         
381 .invalid_handle:                                // return invalid handle
382         mov     r3, #0                          //
383         mvn     r3, r3                          //
384         bx      lr                              //
385
386 /***********************************************************************
387  * mmEffectActive( handle )
388  *
389  * Indicates if a sound effect is active or not
390  ***********************************************************************/
391                                                 .global mmEffectActive
392                                                 .thumb_func
393 mmEffectActive:
394         push    {lr}                            //
395         bl      mme_get_channel_index   //
396         cmp     r3, #0                                  // if r3 >= 0, it is active
397         bge     .active                                 //
398         mov     r0, #0                                  // return false
399         pop     {r3}                                    //
400         bx      r3                                              //
401         
402 .active:                                                // 
403         mov     r0, #1                                  // return true
404         pop     {r3}                                    //
405         bx      r3                                              //
406         
407 /***********************************************************************
408  * mme_clear_channel(ch)
409  *
410  * Clear channel entry and bitmask
411  * r3 preserved
412  ***********************************************************************/
413                                                 .thumb_func
414 mme_clear_channel:
415         mov     r1, #0
416         ldr     r2,=mm_sfx_channels             // clear effect channel
417         lsl     r0, #1                          //
418         strh    r1, [r2, r0]                    //
419         
420         mov     r1, #1                          // clear effect bitmask
421         lsr     r0, #1                          //
422         lsl     r1, r0                          //
423         ldr     r2,=mm_sfx_bitmask              //
424         ldr     r0, [r2, #4]
425         orr     r0, r1
426         str     r0, [r2, #4]
427         ldr     r0, [r2]                        //
428         bic     r0, r1                          //
429         str     r0, [r2]                        //
430         bx      lr
431
432 /***********************************************************************
433  * mmEffectVolume( handle, volume )
434  *
435  * Set effect volume
436  *
437  * volume 0..255
438  ***********************************************************************/
439                                                 .global mmEffectVolume
440                                                 .thumb_func
441 mmEffectVolume:
442
443         push    {r1, lr}
444         
445         bl      mme_get_channel_index
446         pop     {r1}
447         bmi     1f
448         
449         ldr     r0,=mm_sfx_mastervolume
450         ldr     r0, [r0]
451         mul     r1, r0
452         
453         #ifdef SYS_NDS
454         
455         lsr     r1, #2
456         
457         #endif
458         
459         #ifdef SYS_GBA
460         
461         lsr     r1, #10
462         
463         #endif
464         
465         mov     r0, r3
466         
467         bl      mmMixerSetVolume
468
469 1:      ret0
470
471 /***********************************************************************
472  * mmEffectPanning( handle, panning )
473  *
474  * Set effect panning
475  *
476  * panning 0..255
477  ***********************************************************************/ 
478                                                 .global mmEffectPanning
479                                                 .thumb_func
480 mmEffectPanning:
481
482         push    {r1, lr}
483         bl      mme_get_channel_index
484         pop     {r1}
485         bmi     1f
486         
487         mov     r0, r3
488         bl      mmMixerSetPan
489         
490 1:      ret0
491
492 /***********************************************************************
493  * mmEffectRate( handle, rate )
494  *
495  * Set effect playback rate
496  ***********************************************************************/
497                                                 .global mmEffectRate
498                                                 .thumb_func
499 mmEffectRate:
500         
501         push    {r1, lr}
502         bl      mme_get_channel_index
503         pop     {r1}
504         bmi     1f
505         
506         mov     r0, r3
507         bl      mmMixerSetFreq
508         
509 1:      ret0
510
511 /***********************************************************************
512  * mmEffectCancel( handle )
513  *
514  * Stop sound effect
515  ***********************************************************************/
516                                                 .global mmEffectCancel
517                                                 .thumb_func
518 mmEffectCancel:
519         
520         push    {r0, lr}
521         
522         bl      mme_get_channel_index
523         
524         pop     {r0}
525         
526         bmi     1f
527         
528         mov     r1, #MCA_SIZE                   // free achannel
529         mul     r1, r3                          //
530         ldr     r2,=mm_achannels                //
531         ldr     r2, [r2]                        //
532         add     r2, r1                          //
533         mov     r1, #ACHN_BACKGROUND            //
534         strb    r1, [r2, #MCA_TYPE]             //
535         mov     r1, #0                          //
536         strb    r1, [r2, #MCA_FVOL]             // clear volume for channel allocator
537         
538         lsl     r0, #24
539         lsr     r0, #24
540         sub     r0, #1
541         bl      mme_clear_channel
542         
543         mov     r1, #0                          // zero voice volume
544         mov     r0, r3                          //
545         bl      mmMixerSetVolume                //
546         
547         mov     r0, #1
548         ret1
549 1:      
550         mov     r0, #0
551         ret1
552
553 /***********************************************************************
554  * mmEffectRelease( channel )
555  *
556  * Release sound effect (allow interruption)
557  ***********************************************************************/
558                                                 .global mmEffectRelease
559                                                 .thumb_func
560 mmEffectRelease:
561
562         push    {r0, lr}
563         
564         bl      mme_get_channel_index
565         pop     {r0}
566         
567         bmi     1f
568         
569         mov     r1, #MCA_SIZE                   // release achannel
570         mul     r1, r3                          //
571         ldr     r2,=mm_achannels                //
572         ldr     r2, [r2]                        //
573         add     r2, r1                          //
574         mov     r1, #ACHN_BACKGROUND            //
575         strb    r1, [r2, #MCA_TYPE]             //
576         
577         lsl     r0, #24
578         lsr     r0, #24
579         sub     r0, #1
580         bl      mme_clear_channel
581         
582 1:      ret0
583
584 /***********************************************************************
585  * mmEffectScaleRate( channel, factor )
586  *
587  * Scale sampling rate by 6.10 factor
588  ***********************************************************************/
589                                                 .global mmEffectScaleRate
590                                                 .thumb_func
591 mmEffectScaleRate:
592         
593         push    {r1,lr}
594         
595         bl      mme_get_channel_index
596         pop     {r1}
597         
598         bmi     1f
599         
600         mov     r0, r3
601         bl      mmMixerMulFreq
602         
603 1:      ret0
604
605 /***********************************************************************
606  * mmSetEffectsVolume( volume )
607  *
608  * set master volume scale, 0->1024
609  ***********************************************************************/
610                                                 .global mmSetEffectsVolume
611                                                 .thumb_func
612 mmSetEffectsVolume:
613
614         lsr     r1, r0, #10
615         beq     1f
616         mov     r0, #1
617         lsl     r0, #10
618         
619 1:      ldr     r1,=mm_sfx_mastervolume
620         str     r0, [r1]
621         bx      lr
622         
623 /***********************************************************************
624  * mmEffectCancelAll()
625  *
626  * Stop all sound effects
627  ***********************************************************************/
628                                                 .global mmEffectCancelAll
629                                                 .thumb_func
630 mmEffectCancelAll:
631
632         push    {r4-r7,lr}
633         
634         ldr     r4,=mm_sfx_bitmask
635         ldr     r4, [r4]
636         ldr     r6,=mm_sfx_channels
637         mov     r5, #0
638         
639         
640         lsr     r4, #1
641         bcc     .mmeca_next
642 .mmeca_process:
643
644         ldrb    r7, [r6, r5]
645         sub     r7, #1
646         bmi     .mmeca_next
647         
648         mov     r0, r7
649         mov     r1, #0
650         bl      mmMixerSetVolume
651         
652         ldr     r0,=mm_achannels                // free achannel
653         ldr     r0, [r0]                        //
654         mov     r1, #MCA_SIZE                   //
655         mul     r1, r7                          //
656         add     r0, r1                          //
657         mov     r1, #ACHN_BACKGROUND            //
658         strb    r1, [r0, #MCA_TYPE]             //
659         mov     r1, #0
660         strb    r1, [r0, #MCA_FVOL]             //
661         
662 .mmeca_next:
663         add     r5, #2
664         lsr     r4, #1
665         bcs     .mmeca_process
666         bne     .mmeca_next
667         
668         bl      mmResetEffects
669         
670         POP     {r4-r7}
671         pop     {r3}
672         bx      r3
673
674 /***********************************************************************
675  * mmUpdateEffects()
676  *
677  * Update sound effects
678  ***********************************************************************/
679                                                 .global mmUpdateEffects
680                                                 .thumb_func
681 mmUpdateEffects:
682         
683         push    {r4-r6,lr}
684         
685         ldr     r4,=mm_sfx_bitmask
686         ldr     r4, [r4]
687         ldr     r6,=mm_sfx_channels
688         mov     r5, #0
689         
690         lsr     r4, #1
691         bcc     .next_channel
692 .process_channel:
693
694         ldrb    r0, [r6, r5]                    // get channel index
695         sub     r0, #1                          //
696         bmi     .next_channel                   //
697         
698         GET_MIXCH r1
699         
700         mov     r2, #MIXER_CHN_SIZE             // get mixing channel pointer
701         mul     r2, r0                          //
702         add     r1, r2                          //
703         
704         #ifdef SYS_NDS                          // test if channel is still active
705                                                 //
706         ldr     r2, [r1, #MIXER_CHN_SAMP]       //
707         lsl     r2, #8                          //
708         bne     .next_channel                   //
709                                                 //
710         #else                                   //
711                                                 //
712         ldr     r2, [r1, #MIXER_CHN_SRC]        //
713         asr     r2, #31                         //
714         beq     .next_channel                   //
715                                                 //
716         #endif                                  //
717         
718         ldr     r1,=mm_achannels                // free achannel
719         ldr     r1, [r1]                        //
720         mov     r2, #MCA_SIZE                   //
721         mul     r2, r0                          //
722         add     r1, r2                          //
723         mov     r0, #0                          //
724         strb    r0, [r1, #MCA_TYPE]             //
725         strb    r0, [r1, #MCA_FLAGS]            //
726         strb    r0, [r6, r5]
727         
728 .next_channel:
729         add     r5, #2                          // look for another set bit
730         lsr     r4, #1                          //
731         bcs     .process_channel                //
732         add     r5, #2                          //
733         lsr     r4, #1                          //
734         bcs     .process_channel                //
735         bne     .next_channel                   //
736         
737         mov     r4, #0
738         mov     r5, #1
739         lsl     r5, #32-channelCount
740         ldr     r6,=mm_sfx_channels
741          
742 .build_new_bitmask:
743         ldrb    r0, [r6]
744         add     r6, #2
745         cmp     r0, #0
746         beq     1f
747         orr     r4, r5
748 1:      lsl     r5, #1
749         bne     .build_new_bitmask
750         
751         lsr     r4, #32-channelCount
752         ldr     r0,=mm_sfx_bitmask
753         ldr     r1, [r0]                        // r1 = bits that change from 1->0
754         mov     r2, r1
755         eor     r1, r4
756         and     r1, r2
757
758         str     r4, [r0]
759         ldr     r4, [r0, #4]
760         orr     r4, r1
761         str     r4, [r0, #4]                    // write 1->0 mask
762         
763         pop     {r4-r6,pc}
764
765 .pool
766
767 .end