home *** CD-ROM | disk | FTP | other *** search
/ The Datafile PD-CD 5 / DATAFILE_PDCD5.iso / demos / baah / TinyDemos / IntroCR2 / CRintroSRC < prev    next >
Encoding:
Text File  |  1996-12-30  |  37.7 KB  |  809 lines

  1. ;                                     \|/
  2. ;                                     O O
  3. ; --------------------------------oOO--U--OOo--------------------------------
  4. ; -                                                                         -
  5. ; -                    - 2nd Intro for Coder's Revenge -                    -
  6. ; -                   © Alain BROBECKER (baah/Arm'sTeack)                   -
  7. ; -                                                                  Oct 96 -
  8. ; ---------------------------------------------------------------------------
  9. ;
  10. ;   Again a small demo for the fantastic "Coder's Revenge" disc magazine,
  11. ; by the Archiologics. ThanX to them for providing us this masterpiece,
  12. ; and don' t forget to contribute articles!
  13. ;
  14. ;   This source is given for free and is widely commented, so I hope some
  15. ; people will look at it and (maybe) improve their own code. Re-use of my
  16. ; routines is allowed (though not recommended, cos you' ll understand more
  17. ; if you write your owns...) as long as it is not for commercial purposes,
  18. ; as long as you credit me and send me a free copy of your proggy. Oh, btw
  19. ; the assembler I used is ExtASM 0.50b. You' ll have to make changes in
  20. ; macros if you use a newer version of ExtASM.
  21. ;
  22. ;           Alain BROBECKER         Dracula / Positivity (STe)
  23. ;           rte de Dardagny         baah / Arm's Tech (Archie)
  24. ;            01630 CHALLEX                           baah (PC)
  25. ;               FRANCE
  26.  
  27. #name       2ndIntroCR              ; tHa iNcReDibLe kEwl diScMaG...
  28.  
  29. ;------ Constants -----------------------------------------------------------
  30. #set        screennb = 3            ; Nb of mode13 screens.
  31. #set        sin_N = 14              ; Shift for fixed point sinuses.
  32. #set        sin_shift = 10          ; 2^sin_shift is the nb of angles.
  33. #set        sin_nb = 1<<(sin_shift-3) ; Nb of angles between [0;pi/4[.
  34. #set        tx_N = 7                ; Well, texture must be a power of 2, so
  35. #set        tx_S = 1<<tx_N          ;   tx_S=2^tx_N is the size of texture.
  36. #set        tx_Middle = 17          ; Intensity for null relief*2.
  37. #set        fractal = 30            ; Value for shift of rnd nb in fracland.
  38.  
  39. #set        quad_N = 8              ; Shift for fixed point quadsplines.
  40. #set        zoom_N = 7              ; Zoom is a 2<<zoom_N.
  41.  
  42. ;------ BSS Offsets ---------------------------------------------------------
  43. #set        stack = 4*128           ; Top of stack.
  44. #set        sinus = stack           ; Sinus table.
  45. #set        edges = sinus+10*sin_nb*4 ; Source coords for scanline edges.
  46. #set        end = edges+256*4*4+4096
  47.  
  48.  
  49. ;****************************************************************************
  50. ;****************************************************************************
  51. ;*****                                                                  *****
  52. ;*****                              MACROS                              *****
  53. ;*****                                                                  *****
  54. ;****************************************************************************
  55. ;****************************************************************************
  56.  
  57. ;====> Umul64 <=====================
  58. ; This macro performs an unsigned 32*32 bits multiply.
  59. ;   [m0|m1]=m2*m3. You can choose [m2|m3]=[m0|m1]
  60. ;   It destroys m0,m1,m4,m5,m6 and the flags. (C is the carry flag)
  61. macro umul64 m0,m1,m2,m3,m4,m5,m6
  62. { mov       m4,m2,lsr #16           ; m4=up(m2).
  63.   sub       m5,m2,m4,lsl #16        ; m5=down(m2).
  64.   mov       m0,m3,lsr #16           ; m0=up(m3).
  65.   sub       m1,m3,m0,lsl #16        ; m1=down(m3).
  66.   mul       m6,m5,m0                ; m6=down(m2)*up(m3).
  67.   mul       m0,m4,m0                ; m0=up(m2)*up(m3).
  68.   mlaS      m6,m1,m4,m6             ; C+m6=up(m2)*down(m3)+down(m2)*up(m3).
  69.   adc       m0,m0,m6,lsr #16
  70.   mul       m1,m5,m1                ; m1=down(m2)*down(m3).
  71.   addS      m1,m1,m6,lsl #16
  72.   adc       m0,m0,#0                ; [m0|m1]=m2*m3.
  73. }
  74. ;====> Adjust64 <===================
  75. ; This macro adjusts the 64 bits result to 32 bits, according to the fixed
  76. ; point shift factor. (c0)
  77. ;   m0=[m1|m2]>>c0. You can choose m1=m0.
  78. ;   It destroys m0.
  79. macro adjust64 m0,m1,m2,c0
  80. { mov       m0,m1,lsl #32-c0
  81.   add       m0,m0,m2,lsr #c0
  82. }
  83. ;====> Add64 <======================
  84. ; This macro performs a 64 bits addition.
  85. ;   [m0|m1]=[m2|m3]+[m4|m5]. You can choose [m2|m3] or [m4|m5]=[m0|m1].
  86. ;   It destroys [m0|m1] and the flags.
  87. macro add64 m0,m1,m2,m3,m4,m5
  88. { addS      m1,m3,m5
  89.   adc       m0,m2,m4
  90. }
  91. ;====> Sub64 <======================
  92. ; This macro performs a 64 bits substract.
  93. ;   [m0|m1]=[m2|m3]-[m4|m5]. You can choose [m2|m3] or [m4|m5]=[m0|m1].
  94. ;   It destroys [m0|m1] and the flags.
  95. macro sub64 m0,m1,m2,m3,m4,m5
  96. { subS      m1,m3,m5
  97.   sbc       m0,m2,m4
  98. }
  99. ;====> Random32 <==================
  100. ; This macro takes a random number, and makes a new one.
  101. macro random32 m0
  102. { eor       m0,m0,m0,rrx
  103.   adc       m0,m0,m0,ror #7
  104. }
  105. ;====> Swap32 <====================
  106. ; This macro exchanges two registers.
  107. macro swap32 m0,m1
  108. { add       m0,m0,m1                ; m0=a+b.
  109.   sub       m1,m0,m1                ; m1=a+b-b=a.
  110.   sub       m0,m0,m1                ; m0=a+b-a=b.
  111. }
  112.  
  113. ;****************************************************************************
  114. ;****************************************************************************
  115. ;*****                                                                  *****
  116. ;*****                               CODE                               *****
  117. ;*****                                                                  *****
  118. ;****************************************************************************
  119. ;****************************************************************************
  120.  
  121. ;----------------------------------------------------------------------------
  122. ; Initialise stack, ask for slot+screen memory, switch to good video mode...
  123. .proggy_start
  124.   adr       r13,bss+stack           ; Initialise stack pointer.
  125.   mov       r0,#bss+end-&8000       ; Ask for needed slot memory.
  126.   mov       r1,#-1                  ; Don' t change next slot size.
  127.   swi       Wimp_SlotSize
  128.   mov       r0,#2                   ; Ask for ScreenMem size.
  129.   swi       OS_ReadDynamicArea      ; r1=current ScreenMem size.
  130.   rsbS      r1,r1,#(screennb+1)*81920 ; r1=needed-curent screenmem.
  131.   movGT     r0,#2                   ; Ask for it if we don' t have enough.
  132.   swiGT     OS_ChangeDynamicArea
  133.   swi       256+22                  ; Vdu 22, set screenmode.
  134.   swi       256+13                  ; Switch to mode 13.
  135.   swi       OS_RemoveCursors        ; Who needs them?
  136.   adr       r0,videoram_adress      ; Get videoram adress.
  137.   mov       r1,r0
  138.   swi       OS_ReadVduVariables
  139. ;----------------------------------------------------------------------------
  140. ; Clear the bss section, since some routines need it. I assume that bss+end
  141. ; is longword aligned. (So you must take care when defining bss offsets)
  142. ; And then do the same with videoram.
  143.   adr       r0,bss+stack            ; Begin to clear here.
  144.   mov       r1,#end-stack           ; Nb of bytes to clear.
  145.   bl        clear_mempart
  146.   ldr       r0,videoram_adress      ; Begin to clear here.
  147.   mov       r1,#(screennb+1)*81920  ; Nb of bytes to clear.
  148.   bl        clear_mempart
  149. ;----------------------------------------------------------------------------
  150. ; Creates the sinus table. As for the inverses creation, the routine has
  151. ; already been released through "Memory War".
  152. .make_sinus
  153.   adr       r0,bss+sinus
  154.   ldr       r1,sinA                 ; r1=sinA*2^28.
  155.   ldr       r2,cosA                 ; r2=cosA*2^28.
  156.   mov       r3,#0                   ; r3=sin0*2^28.
  157.   mov       r4,#1<<28               ; r4=cos0*2^28.
  158.   mov       r5,#sin_nb+1
  159. .make_one_sinus
  160.   mov       r6,r4,lsr #28-sin_N     ; r6=cosN*2^sin_N.
  161.   str       r6,[r0,#sin_nb*2*4]     ; Save sin(N+pi/2)=cosN.
  162.   mov       r6,r3,lsr #28-sin_N     ; r6=sinN*2^sin_N.
  163.   str       r6,[r0],#4              ; Save sinN.
  164.   umul64 r6,r7,r1,r3,r8,r9,r10      ; [r6|r7]=sinN*sinA.
  165.   umul64 r8,r9,r2,r4,r10,r11,r12    ; [r8|r9]=cosN*cosA.
  166.   sub64 r6,r7,r8,r9,r6,r7           ; [r6|r7]=cos(N+1)=cosN*sin1-sinN*sin1.
  167.   umul64 r3,r8,r3,r2,r9,r10,r11     ; [r3|r8]=sinN*cosA.
  168.   umul64 r4,r9,r4,r1,r10,r11,r12    ; [r4|r9]=cosN*sinA.
  169.   add64 r3,r8,r3,r8,r4,r9           ; [r3|r8]=sin(N+1)=sinN*cos1+cosN*sin1.
  170.   adjust64 r3,r3,r8,28              ; r1=sin(N+1)=sinN*cos1+cosN*sin1.
  171.   adjust64 r4,r6,r7,28              ; r2=cos(N+1)=cosN*sin1-sinN*sin1.
  172.   subS      r5,r5,#1                ; One sinus processed.
  173.   bNE       make_one_sinus
  174.   sub       r0,r0,#4                ; Complete the table by stupid copy.
  175.   mov       r1,r0                   ; Point on the position which are like
  176.   add       r2,r0,#sin_nb*8         ;  (pi/4+k*(pi/2))   0<=k<=4
  177.   mov       r3,r2
  178.   add       r4,r2,#sin_nb*8
  179.   mov       r5,r4
  180.   add       r6,r4,#sin_nb*8
  181.   mov       r7,r6
  182.   add       r8,r6,#sin_nb*8
  183.   mov       r9,r8
  184.   mov       r10,#sin_nb+1           ; Copy sin_nb+1 values.
  185. ._make_sinus_copy
  186.   ldr       r11,[r0],#-4
  187.   str       r11,[r3],#4             ; sin(pi-X)=sinX.
  188.   str       r11,[r8],#-4            ; sin(2*pi+X)=sinX.
  189.   rsb       r11,r11,#0
  190.   str       r11,[r4],#-4            ; sin(pi+X)=-sinX.
  191.   str       r11,[r7],#4             ; sin(2*pi-X)=-sinX.
  192.   ldr       r11,[r2],#-4
  193.   str       r11,[r1],#4             ; cos(-X)=cosX.
  194.   subS      r10,r10,#1              ; One value copied.
  195.   strNE     r11,[r9],#4             ; cos(2*pi+X)=cosX. No copy if r10=0.
  196.   rsb       r11,r11,#0
  197.   str       r11,[r5],#4             ; cos(pi-X)=-cosX.
  198.   str       r11,[r6],#-4            ; cos(pi+X)=-cosX.
  199.   bNE       _make_sinus_copy
  200. ;----------------------------------------------------------------------------
  201. ; Creates the texture with the nice CR logo in it. Main idea is (as usual) to
  202. ; make a N*N fractal landscape, take the modulo of it, expand pixels which
  203. ; are in the logo, emboss and re-smooth.
  204. .make_fracland
  205.   ldr       r0,videoram_adress      ; We create texture in videoram,
  206.   add       r0,r0,#screennb*81920   ;   just after screenbanks.
  207.   ldr       r1,random_germ          ; Load the random germ.
  208.   mov       r2,#1                   ; This will be used by routine.
  209.   strB      r2,[r0]                 ; Also save 1 as upper left pixel.
  210.   mov       r3,#tx_N                ; Iteration=tx_N.
  211.   mov       r4,#0                   ; Position of upper left pixel.
  212.   mov       r5,#0
  213.   bl        recursive_landscape
  214.   ldr       r1,videoram_adress      ; Adress of a second buffer.
  215.   add       r1,r1,#81920
  216.   bl        smooth_texture          ; Smooth texture.
  217. ; Take the modulo(32) of whole image.
  218.   mov       r2,#tx_S*tx_S           ; Nb of pixels to compute.
  219. ._modulo_one
  220.   subS      r2,r2,#1                ; One pixel will be computed.
  221.   ldrB      r3,[r1,r2]              ; Load pixel.
  222.   and       r3,r3,#&1f              ; Modulo operation.
  223.   strB      r3,[r1,r2]              ; Save it.
  224.   bNE       _modulo_one
  225. ; Draw the logo on the texture by expanding the pixel in logoCR.
  226.   mov       r2,r1
  227.   adr       r3,logoCR
  228. ._logo_draw
  229.   ldrB      r4,[r3],#1              ; r4=offset.
  230.   cmp       r4,#&ff
  231.   bEQ       _end_logo
  232.   add       r2,r2,r4                ; r2 points on good offset.
  233.   ldrB      r4,[r3],#1              ; r4=nb_bytes.
  234. ._logo_draw_byte
  235.   ldrB      r5,[r2]                 ; Load value.
  236.   mov       r5,r5,lsl #5            ; Expand it.
  237.   strB      r5,[r2],#1              ; Save one pixel.
  238.   subS      r4,r4,#1
  239.   bNE       _logo_draw_byte
  240.   b         _logo_draw
  241. ._end_logo
  242. ; Emboss the texture and then smooth it again.
  243.   ldr       r1,videoram_adress      ; Adress of the second buffer.
  244.   add       r1,r1,#81920
  245. .emboss_texture
  246.   mov       r2,#0                   ; r2=y counter<<(32-tx_N).
  247. ._emboss_one_line
  248.   sub       r3,r2,#1<<(32-tx_N)     ; r3=(y-1) mod tx_S <<(32-tx_N). (Wrapping)
  249.   add       r4,r3,#1<<(32-tx_N)     ; r4=(y+1) mod tx_S <<(32-tx_N). (Wrapping)
  250.   add       r3,r1,r3,lsr #(32-2*tx_N); r3 points on src_line up.
  251.   add       r4,r1,r4,lsr #(32-2*tx_N); r4 points on src_line down.
  252.   mov       r5,#tx_S                ; r5=nb of pixels per line.
  253. ._emboss_one
  254.   ldrB      r14,[r3],#1             ; r14=pixie up.
  255.   ldrB      r6,[r4],#1              ; r6=pixie down.
  256.   sub       r6,r6,r14               ; r6=delta.
  257.   addS      r6,r6,#tx_Middle        ; Add the middle constant.
  258.   movMI     r6,#0                   ; Make sure intensity is between 0-63.
  259.   cmp       r6,#63
  260.   movGE     r6,#63
  261.   strB      r6,[r0],#1              ; Save it.
  262.   subS      r5,r5,#1                ; One pixel done
  263.   bNE       _emboss_one
  264.   addS      r2,r2,#1<<(32-tx_N)     ; Line done.
  265.   bNE       _emboss_one_line
  266.   sub       r0,r0,#tx_S*tx_S        ; r0 points back on buffer.
  267.   bl        smooth_texture          ; Smooth texture.
  268.   swap32    r0,r1
  269.   bl        smooth_texture          ; Smooth texture.
  270.   swap32    r0,r1
  271.   bl        smooth_texture          ; Smooth texture.
  272. ; Convert the texture using colormap.
  273.   adr       r2,tx_colors
  274.   mov       r3,#tx_S                ; y counter.
  275. ._tx_convert_line
  276.   mov       r4,#tx_S                ; x counter.
  277. ._tx_convert_one
  278.   ldrB      r5,[r1],#1              ; Load pixel.
  279.   ldrB      r5,[r2,r5]            ; Convert it.
  280.   strB      r5,[r0],#1              ; Draw it.
  281.   subS      r4,r4,#1                ; One pixel done.
  282.   bNE       _tx_convert_one
  283.   subS      r3,r3,#1                ; One line done.
  284.   bNE       _tx_convert_line
  285. ;----------------------------------------------------------------------------
  286. ; Clear the screenbanks.
  287.   ldr       r0,videoram_adress
  288.   mov       r1,#screennb*81920      ; Nb of bytes to clear.
  289.   bl        clear_mempart
  290. ;----------------------------------------------------------------------------
  291. ; Enables our Vertical Blanking (VBl) interrupt.
  292.   mov       r0,#&10                 ; Claim event vector. (&10)
  293.   adr       r1,vbl_routine          ; Adress of claiming routine.
  294.   adr       r2,workscr_nb           ; Value to pass in r12 when rout is called.
  295.   swi       OS_Claim
  296.   mov       r0,#&e                  ; Enable an event.
  297.   mov       r1,#4                   ; Event 4=VBl.
  298.   swi       OS_Byte
  299. ;----------------------------------------------------------------------------
  300. .one_frame
  301.   bl        get_workscr_adr         ; r0=workscreen adress.
  302.   add       r0,r0,#(320-168)/2      ; Recenter.
  303.   ldr       r1,videoram_adress      ; Adress of texture.
  304.   add       r1,r1,#screennb*81920   ; In videoram, just after screenbanks.
  305.   adr       r2,bss+sinus
  306.   str       r13,old_stack           ; Save stack.
  307.   b         rotozoom
  308. .back_from_rotozoom
  309.   ldr       r13,old_stack           ; Restore stack.
  310.   bl        vsync_routine           ; Wait until next workscr is ready.
  311.   swi       OS_ReadEscapeState      ; Escape key pressed?
  312.   bCC       one_frame               ; No, then loop.
  313. ;---------------------------------------------------------------------------
  314. ; Disables our Vertical Blanking interrupt and then quit.
  315.   mov       r0,#&d                  ; Disable an event.
  316.   mov       r1,#4                   ; Event 4=VBl.
  317.   swi       OS_Byte
  318.   mov       r0,#&10                 ; Release Event Vector. (&10)
  319.   adr       r1,vbl_routine          ; Give same values as when claiming.
  320.   adr       r2,workscr_nb
  321.   swi       OS_Release
  322.   swi       OS_Exit
  323.  
  324.  
  325. ;****************************************************************************
  326. ;****************************************************************************
  327. ;*****                                                                  *****
  328. ;*****                            MAIN DATAS                            *****
  329. ;*****                                                                  *****
  330. ;****************************************************************************
  331. ;****************************************************************************
  332.  
  333. .old_stack
  334. .sinA       dcd 1647089             ; sin((pi/4)/sin_nb)*2^28.
  335. .cosA       dcd 268430403           ; cos((pi/4)/sin_nb)*2^28.
  336.  
  337. .random_germ dcd &eb1a2c34          ; The magical random number.
  338.  
  339. .videoram_adress dcd 148,-1         ; Values for the swi.
  340.  
  341. .tx_colors
  342.   dcb &d4,&d5,&d6,&d7 ; Brown cycle.
  343.   dcb &d7,&d6,&d5,&d4,&3b,&3a,&39,&38,&07,&06,&05,&04
  344.   dcb &0c,&0d,&0e,&0f,&b0,&b1,&b2,&b3,&dc,&dd,&de,&df ; Purple cycle.
  345.   dcb &df,&de,&dd,&dc,&b3,&b2,&b1,&b0,&0f,&0e,&0d,&0c
  346.   dcb &08,&09,&0a,&0b,&a4,&a5,&a6,&a7,&d8,&d9,&da,&db ; Blue cycle.
  347.   dcb &db,&da,&d9,&d8,&a7,&a6,&a5,&a4,&0b,&0a,&09,&08
  348.  
  349. .workscr_nb dcd 2                   ; This two variables must be left together.
  350. .displayscr_nb dcd 1
  351.  
  352. .logoCR incbin "LogoCR"
  353. ALIGN
  354.  
  355. ;****************************************************************************
  356. ;****************************************************************************
  357. ;*****                                                                  *****
  358. ;*****                             ROUTINES                             *****
  359. ;*****                                                                  *****
  360. ;****************************************************************************
  361. ;****************************************************************************
  362.  
  363. ; ---------------------------------------------------------------------------
  364. ; ---              Routine for Vertical Blank interrupt.                  ---
  365. ; ---------------------------------------------------------------------------
  366. ;   We check if the next screen which will be displayed is entirely drawn
  367. ; (ie display_scr_nb-1<>workscr_nb), and in this case we use a swi to change
  368. ; the screen bank to display_scr_nb-1. When displayscr_nb-1 reachs 0 it is
  369. ; set back to ScreenNb by using self-modified code. (Op2 in vbl_screennb_mov
  370. ; was changed to ScreenNb)
  371. ;   At first I was accessing directly the MemC to change the display screen,
  372. ; but since this isn' t compatible coding, I had to spent some time with
  373. ; ArmOric and his PRMs in order to use a swi during this interrupt, and I
  374. ; recommend you get a look at PRMs.
  375. ;   When this routine is called we must have r12 pointing on a buffer which
  376. ; contains workscr_nb and displayscr_nb.
  377. .vbl_routine
  378.   cmp       r0,#4                   ; Event=VBl?
  379.   movNE     pc,r14                  ; No, then it' s none of our business.
  380.   stmfd     r13!,{r0,r1,r14}        ; Save registers.
  381.   ldmia     r12,{r0,r1}             ; r0=workscr_nb | r1=displayscr_nb.
  382.   subS      r1,r1,#1                ; r1=displayscr_nb-1.
  383.   movEQ     r1,#screennb            ; If r1=0 then back to ScreenNb.
  384.   cmp       r0,r1                   ; Flags=workscr_nb-(displayscr_nb-1).
  385.   ldmEQfd   r13!,{r0,r1,pc}         ; If equal don' t show next screen.
  386.   str       r1,[r12,#4]             ; Save new displayscr_nb.
  387.   mov        r12,pc            ; Keep current status/mode.
  388.   orr        r0,r12,#3            ; Derive supervisor version of it.
  389.   teqp        r0,#0            ; Enter supervisor mode.
  390.   mov          r0,r0
  391.   stmfd        r13!,{r14}            ; Save Supervisor R14
  392.   mov       r0,#&71                 ; Next showscreen.
  393.   swi       OS_Byte
  394.   ldmfd        r13!,{r14}            ; Restore Supervisor R14
  395.   teqp      r12,#0            ; Re-enter original processor mode.
  396.   mov        r0,r0
  397.   ldmfd     r13!,{r0,r1,pc}         ; Could have been so short and so good.
  398.  
  399. ; ---------------------------------------------------------------------------
  400. ; ---                Routine for Vertical Synchronisation.                ---
  401. ; ---------------------------------------------------------------------------
  402. ;   When this routine is called, this means we have just finished to draw the
  403. ; workscr_nb, and we notify it by setting new workscreen to old workscr_nb-1.
  404. ; (As for the vbl_routine, we loop if workscr_nb-1 reaches 0, and this is
  405. ; performed with modification of vsync_screennb_mov)
  406. ;   Once the notification has been made, we wait until the new workscr_nb is
  407. ; different from the displayscr_nb.
  408. .vsync_routine
  409.   stmdb     r13!,{r0,r14}
  410.   ldr       r0,workscr_nb           ; Load workscr_nb.
  411.   subS      r0,r0,#1                ; r0=workscr_nb-1.
  412.   movEQ     r0,#screennb            ; If r0=0 then back to ScreenNb.
  413.   str       r0,workscr_nb           ; Save new workscr_nb.
  414. ._wait_vsync
  415.   ldr       r14,displayscr_nb       ; Load displayscr_nb.
  416.   cmp       r0,r14                  ; displayscr_nb=new_workscr_nb?
  417.   bEQ       _wait_vsync             ; Then wait.
  418.   ldmia     r13!,{r0,pc}
  419.  
  420. ; ---------------------------------------------------------------------------
  421. ; ---                  Routine to get WorkScreen adress.                  ---
  422. ; ---------------------------------------------------------------------------
  423. ; This routine returns the workscreen adress in r0.
  424. .get_workscr_adr
  425.   stmdb     r13!,{r14}
  426.   ldr       r0,videoram_adress
  427.   ldr       r14,workscr_nb
  428.   sub       r14,r14,#1              ; RiscOS is an OS for coders... ;)
  429.   add       r14,r14,r14,lsl #2      ; r14=workscr_nb*5.
  430.   add       r0,r0,r14,lsl #6+8      ; r0=video+workscr_nb*320*256.
  431.   ldmia     r13!,{pc}
  432.  
  433. ; ---------------------------------------------------------------------------
  434. ; ---                   Routine to clear a memory part.                   ---
  435. ; ---------------------------------------------------------------------------
  436. ; Beware, this routine kills r2, r1 and since it works with longwords, you
  437. ; must make sure evrything is multiple of 4.
  438. ; Parameters are...
  439. ;           r0=adress to clear. (Must be longword aligned)
  440. ;           r1=nb of bytes to clear. (Must be multiple of 4)
  441. .clear_mempart
  442.   mov       r2,#0                   ; Fill with zeroes.
  443. ._clear
  444.   subS      r1,r1,#4                ; One longword will be cleared.
  445.   str       r2,[r0,r1]              ; Clear it.
  446.   bNE       _clear                  ; Continue until r1=0.
  447.   mov       pc,r14
  448.  
  449. ; ---------------------------------------------------------------------------
  450. ; ---                    Routine smoothing a texture                      ---
  451. ; ---                          © Alain BROBECKER                          ---
  452. ; ---------------------------------------------------------------------------
  453. ; This routines works by applying the following 3*3 convolution matrix...
  454. ;         ( 1 2 1 )   ( pix0 pix1 pix2 )
  455. ;  1/16 * ( 2 4 2 ) * ( pix3 pix4 pix5 ) = new pix.
  456. ;         ( 1 2 1 )   ( pix6 pix7 pix8 )
  457. ; Parameters are...
  458. ;     r0 = adress of initial N*N texture.
  459. ;     r1 = adress of N*N buffer for smoothed result.
  460. .smooth_texture
  461.   stmfd     r13!,{r0-r9,r14}
  462.   mov       r2,#0                   ; r2=y counter.
  463. ._smooth_line
  464.   mov       r3,#0                   ; r3=x counter.
  465.   sub       r4,r2,#1<<(32-tx_N)     ; r4=(y-1) mod N <<(32-M). (Wrapping)
  466.   add       r6,r2,#1<<(32-tx_N)     ; r6=(y+1) mod N <<(32-M). (Wrapping)
  467.   add       r4,r0,r4,lsr #(32-2*tx_N) ; r4 points on src_line up.
  468.   add       r5,r0,r2,lsr #(32-2*tx_N) ; r5 points on src_line.
  469.   add       r6,r0,r6,lsr #(32-2*tx_N) ; r6 points on src_line down.
  470. ._smooth_one
  471.   sub       r7,r3,#1<<(32-tx_N)     ; r7=(x-1) mod N <<(32-M). (Wrapping)
  472.   add       r8,r3,#1<<(32-tx_N)     ; r8=(x+1) mod N <<(32-M). (Wrapping)
  473.   ldrB      r9,[r5,r3,lsr #(32-tx_N)] ; Load all the pixels, and add them
  474.   ldrB      r14,[r4,r3,lsr #(32-tx_N)] ;   with the good coefficients in r9.
  475.   add       r9,r14,r9,lsl #1
  476.   ldrB      r14,[r6,r3,lsr #(32-tx_N)]
  477.   add       r9,r9,r14
  478.   ldrB      r14,[r5,r7,lsr #(32-tx_N)]
  479.   add       r9,r9,r14
  480.   ldrB      r14,[r5,r8,lsr #(32-tx_N)]
  481.   add       r9,r9,r14
  482.   ldrB      r14,[r4,r7,lsr #(32-tx_N)]
  483.   add       r9,r14,r9,lsl #1
  484.   ldrB      r14,[r4,r8,lsr #(32-tx_N)]
  485.   add       r9,r9,r14
  486.   ldrB      r14,[r6,r7,lsr #(32-tx_N)]
  487.   add       r9,r9,r14
  488.   ldrB      r14,[r6,r8,lsr #(32-tx_N)]
  489.   add       r9,r9,r14
  490.   mov       r9,r9,lsr #4            ; r9=smoothed intensity.
  491.   strB      r9,[r1],#1              ; Save new pixel value.
  492.   addS      r3,r3,#1<<(32-tx_N)     ; Next pixel.
  493.   bNE       _smooth_one
  494.   addS      r2,r2,#1<<(32-tx_N)     ; Next line.
  495.   bNE       _smooth_line
  496.   ldmfd     r13!,{r0-r9,pc}
  497.  
  498. ; ---------------------------------------------------------------------------
  499. ; ---                Routine creating a fractal landscape                 ---
  500. ; ---                          © Alain BROBECKER                          ---
  501. ; ---------------------------------------------------------------------------
  502. ; Recursive landscape creation. Considering a point in the landscape and the
  503. ; iteration (=width of square) we construct the points m4-m8 and go on
  504. ; recursively on all resulting four squares.
  505. ;    m0--m4--m1         h4=0.5*(h0+h1)+rnd
  506. ;    |   |   |          h5=0.5*(h1+h2)+rnd
  507. ;    m7--m8--m5         h6=0.5*(h2+h3)+rnd
  508. ;    |   |   |          h7=0.5*(h3+h0)+rnd
  509. ;    m3--m6--m2         h8=0.25*(h0+h1+h2+h3)+rnd
  510. ; Parameters are...
  511. ;     r0=adress of buffer for landscape.
  512. ;     r1=random number.
  513. ;     r2=1.
  514. ;     r3=iteration.
  515. ;     r4=posx.
  516. ;     r5=posy.
  517. .recursive_landscape
  518.   stmfd     r13!,{r3-r5,r14}
  519. ; At first, we calculate h4,h5,h6,h7 and h8.
  520.   add       r6,r4,r2,lsl r3
  521.   and       r6,r6,#tx_S-1           ; r6=(posx+2^iteration) mod(tx_S).
  522.   add       r7,r5,r2,lsl r3
  523.   and       r7,r7,#tx_S-1           ; r7=(posy+2^iteration) mod(tx_S).
  524.   add       r9,r4,r7,lsl #tx_N      ; r9 points on m3.
  525.   add       r8,r6,r7,lsl #tx_N      ; r8 points on m2.
  526.   add       r7,r6,r5,lsl #tx_N      ; r7 points on m1.
  527.   add       r6,r4,r5,lsl #tx_N      ; r6 points on m0.
  528.   ldrB      r6,[r0,r6]              ; r6=h0.
  529.   ldrB      r7,[r0,r7]              ; r7=h1.
  530.   ldrB      r8,[r0,r8]              ; r8=h2.
  531.   ldrB      r9,[r0,r9]              ; r9=h3.
  532.   sub       r10,r3,#1
  533.   mov       r10,r2,lsl r10          ; r10=2^(iteration-1).
  534. ; Calculation of m8.
  535.   add       r14,r6,r7
  536.   add       r14,r14,r8
  537.   add       r14,r14,r9              ; r14=h0+h1+h2+h3.
  538.   mov       r14,r14,asr #2          ; r14=0.25*(h0+h1+h2+h3).
  539.   random32  r1                      ; New random number.
  540.   rsb       r11,r3,#fractal+1       ; r11=fractal+1-iteration=shift for rnd.
  541.   addS      r14,r14,r1,asr r11      ; r14=0.25*(h0+h1+h2+h3)+rnd.
  542.   movLE     r14,#1                  ; Make sure 0<r14<256.
  543.   cmp       r14,#255
  544.   movGE     r14,#255
  545.   add       r12,r5,r10              ; Make r12 point on m8.
  546.   add       r12,r4,r12,lsl #tx_N
  547.   add       r12,r12,r10
  548.   strB      r14,[r0,r12]            ; Save h8.
  549. ; Calculation of m6.
  550.   add       r14,r8,r9               ; r14=h2+h3.
  551.   mov       r14,r14,asr #1          ; r14=0.5*(h2+h3).
  552.   random32  r1                      ; New random number.
  553.   rsb       r11,r3,#fractal         ; r11=fractal-iteration=shift for rnd.
  554.   addS      r14,r14,r1,asr r11      ; r14=0.5*(h2+h3)+rnd.
  555.   movLE     r14,#1                  ; Make sure 1<r14<256.
  556.   cmp       r14,#255
  557.   movGE     r14,#255
  558.   add       r12,r5,r2,lsl r3        ; Make r12 point on m6.
  559.   add       r12,r4,r12,lsl #tx_N
  560.   add       r12,r12,r10
  561.   strB      r14,[r0,r12]            ; Save h6.
  562. ; Calculation of m5.
  563.   add       r14,r7,r8               ; r14=h1+h2.
  564.   mov       r14,r14,asr #1          ; r14=0.5*(h1+h2).
  565.   random32  r1                      ; New random number.
  566.   addS      r14,r14,r1,asr r11      ; r14=0.5*(h1+h2)+rnd.
  567.   movLE     r14,#1                  ; Make sure 1<r14<256.
  568.   cmp       r14,#255
  569.   movGE     r14,#255
  570.   add       r12,r4,r2,lsl r3        ; Make r12 point on m5.
  571.   add       r12,r12,r5,lsl #tx_N
  572.   add       r12,r12,r10,lsl #tx_N
  573.   ldrB      r8,[r0,r12]             ; Load value at m5.
  574.   cmp       r8,#0                   ; Pixel already set?
  575.   strEQB    r14,[r0,r12]            ; Else save h5.
  576. ; Calculation of m4.
  577.   add       r14,r6,r7               ; r14=h0+h1.
  578.   mov       r14,r14,asr #1          ; r14=0.5*(h0+h1).
  579.   random32  r1                      ; New random number.
  580.   addS      r14,r14,r1,asr r11      ; r14=0.5*(h0+h1)+rnd.
  581.   movLE     r14,#1                  ; Make sure 1<r14<256.
  582.   cmp       r14,#255
  583.   movGE     r14,#255
  584.   add       r12,r4,r10              ; Make r12 point on m4.
  585.   add       r12,r12,r5,lsl #tx_N
  586.   ldrB      r8,[r0,r12]
  587.   cmp       r8,#0
  588.   strEQB    r14,[r0,r12]            ; Save h4.
  589. ; Calculation of m7.
  590.   add       r14,r6,r9               ; r14=h0+h3.
  591.   mov       r14,r14,asr #1          ; r14=0.5*(h0+h3).
  592.   random32  r1                      ; New random number.
  593.   addS      r14,r14,r1,asr r11      ; r14=0.5*(h0+h3)+rnd.
  594.   movLE     r14,#0                  ; Make sure 1<r14<256.
  595.   cmp       r14,#255
  596.   movGE     r14,#255
  597.   add       r12,r5,r10              ; Make r12 point on m7.
  598.   add       r12,r4,r12,lsl #tx_N
  599.   ldrB      r8,[r0,r12]
  600.   cmp       r8,#0
  601.   strEQB    r14,[r0,r12]            ; Save h7.
  602. ; Second part, recursive call.
  603.   subS      r3,r3,#1
  604.   ldmEQfd   r13!,{r3-r5,pc}         ; Stop recusrion when iter=0.
  605.   bl        recursive_landscape     ; Else go on with four subsquares.
  606.   add       r4,r4,r2,lsl r3         ;   start pos=m4.
  607.   bl        recursive_landscape
  608.   add       r5,r5,r2,lsl r3         ;   start pos=m8.
  609.   bl        recursive_landscape
  610.   sub       r4,r4,r2,lsl r3         ;   start pos=m7.
  611.   bl        recursive_landscape
  612.   ldmfd     r13!,{r3-r5,pc}
  613.  
  614. ; ---------------------------------------------------------------------------
  615. ; ---              Rotozoom with edges as quadratic splines.              ---
  616. ; ---                          © Alain BROBECKER                          ---
  617. ; ---------------------------------------------------------------------------
  618. ;   I won' t explain much about rotozoom in here, just let you know that for
  619. ; each destination scanline we calculate the edges' positions in src bitmap,
  620. ; thus giving us xleft_src,yleft_src,xright_src,yright_src. Then we perform
  621. ; a linear interpolation to know the source coord of each pixel in scanline.
  622. ;   The important thing for the beauty of the effect stands in the way edges
  623. ; are computed. Classical method is to caclulate the brows of the rotozoom
  624. ; box and linearly (again) interpolate to know scanlines' edges src_pos. But
  625. ; this don' t give very original (and nice) results, so I dwelt far in maths
  626. ; and used quadratic splines instead. I define 6 control points and use a
  627. ; quadratic interpolation to know edges' src_pos. Maybe reminding you some
  628. ; things about quadsplines will be good. A quadspline is an equation in the
  629. ; form m(t)=a0+a1*t+a2*t^2. Given m0,m1,m2 the control brows, consider we
  630. ; want m(0)=m0, m(1/2)=m1 and m(1)=m2, thus gives us the coefs a0,a1,a2.
  631. ; (I let you solve this system with 3 equations and 3 parameters) Once done
  632. ; we compute m(k/2^n) where 0=<k=<2^n in an incremental way.
  633. ;   The result is a realtime, fairly optimised, routine running quite fast,
  634. ; on my Arm2 at least. Scanline interpolation just fits in cache memory, but
  635. ; we' ll need to declare texture as uncachable in order to have max speed on
  636. ; cached processor. (When a ldrB is performed, a whole cache line=4 longs is
  637. ; loaded from memory and this is a disadvantage for rotozoom/mapping since
  638. ; we are not interested in consecutive source pixels) In order to have the
  639. ; texture uncacheable, quite simply I put it in videoram.
  640. ;   Ok, as usual the source is widely commented, so I even wonder why I made
  641. ; this boring foreword! =) EnjOY!
  642. ;
  643. ; Parameters are...
  644. ;     r0=videoram adress.
  645. ;     r1=adress of source bitmap.
  646. ;     r2=adress of sinus table.
  647.  
  648. ;====> Quad_Coef <=================
  649. ; This macro computes coefs for quadratic splines in incremental mode.
  650. ;   m0=a0<<2N | m1=a1<<N-a2 | m2=a2.
  651. ;   Where a0=m0, a1=-3m0+4m1-m2, and a2=2m0-4m1+2m2.
  652. ;   It destroys m0,m1,m2.
  653. macro quad_coef m0,m1,m2
  654. { rsb       m1,m0,m1,lsl #1         ; m1=-m0+2m1.
  655.   rsb       m1,m2,m1,lsl #1         ; m1=-2m0+4m1-m2.
  656.   rsb       m2,m1,m2                ; m2=a2=2m0-4m1+2m2.
  657.   sub       m1,m1,m0                ; m1=a1=-3m0+4m1-m2.
  658.   mov       m0,m0,asr #2*sin_N-2*quad_N-zoom_N ; m0=a0<<2N.
  659.   mov       m1,m1,asr #2*sin_N-quad_N-zoom_N ; m1=a1<<N.
  660.   add       m1,m1,m2,asr #2*sin_N-zoom_N ; m1=inc1=a1<<N+a2.
  661. }
  662. ;====> rotozoom4 <==================
  663. ; This macro computes a dest longword and put it in m6.
  664. ;   Needs m0=src_img, m1..m4=x,y,dx,dy and m5 as temp register.
  665. ;   Last linear interpolation is active if notlast=1.
  666. macro rotozoom4 m0,m1,m2,m3,m4,m5,m6
  667. { and       m5,m2,#&ff<<(32-tx_N)   ; m5=int(y)<<(32-tx_N).
  668.   add       m5,m5,m1,lsr #tx_N      ; m5=(int(y)*tx_S+x)<<(32-2*tx_N).
  669.   ldrB      m6,[m0,m5,lsr #32-2*tx_N] ; Load byte.
  670.   add       m1,m1,m3                ; x+=dx.
  671.   add       m2,m2,m4                ; y+=dy.
  672.   and       m5,m2,#&ff<<(32-tx_N)   ; Do the same for 2nd pixel.
  673.   add       m5,m5,m1,lsr #tx_N
  674.   ldrB      m5,[m0,m5,lsr #32-2*tx_N]
  675.   add       m6,m6,m5,lsl #8         ; Merge pixels in longword.
  676.   add       m1,m1,m3
  677.   add       m2,m2,m4
  678.   and       m5,m2,#&ff<<(32-tx_N)   ; 3rd pixel...
  679.   add       m5,m5,m1,lsr #tx_N
  680.   ldrB      m5,[m0,m5,lsr #32-2*tx_N]
  681.   add       m6,m6,m5,lsl #16
  682.   add       m1,m1,m3
  683.   add       m2,m2,m4
  684.   and       m5,m2,#&ff<<(32-tx_N)   ; 4th pixel...
  685.   add       m5,m5,m1,lsr #tx_N
  686.   ldrB      m5,[m0,m5,lsr #32-2*tx_N]
  687.   add       m6,m6,m5,lsl #24
  688. #if notlast
  689.   add       m1,m1,m3                ; Not needed in last long.
  690.   add       m2,m2,m4
  691. #endif
  692. }
  693.  
  694. ; Storage and variables for the rotozoom effect. Note that in splines_angles
  695. ; the angle is in sin_nb_shift+3 upper bits, and increment in lower bits,
  696. ; thus allowing to have the modulo operation gratuitous.
  697. .rotozoom_temp dcd 0,0              ; Temporary storage.
  698. .splines_angles
  699.   dcd &80000005,3,3,23              ; Angles and angle_inc for splines mvts.
  700.   dcd bss+edges                     ; Easier and faster than an adr.
  701.  
  702. ; Here comes the core of the rotozoom, needs r0=@video, r1=@src, r2=@sinus.
  703. ; At first we calculate the src_pos of all 6 control brows.
  704. .rotozoom
  705.   adr       r13,rotozoom_temp
  706.   stmia     r13!,{r0-r1}            ; Save workscreen+source.
  707.   ldmia     r13,{r5-r7,r9,r14}      ; Load splines_angles+@edges
  708.   add       r5,r5,r5,lsl #32-sin_shift ; Angles+=inc_angles.
  709.   add       r6,r6,r6,lsl #32-sin_shift
  710.   add       r7,r7,r7,lsl #32-sin_shift
  711.   add       r9,r9,r9,lsl #32-sin_shift
  712.   stmia     r13,{r5-r7,r9}          ; Save new angles.
  713.   ldr       r6,[r2,r6,lsr #32-sin_shift-2] ; r6=sin(a0).
  714.   add       r6,r6,#3<<(sin_N-1)     ; r6=??+sin(a0).
  715.   ldr       r7,[r2,r7,lsr #32-sin_shift-2] ; r7=sin(a1).
  716.   add       r7,r7,#3<<(sin_N-1)     ; r7=??+sin(a1).
  717.   ldr       r8,[r2,r9,lsr #32-sin_shift-2] ; r8=sin(a2).
  718.   ldr       r12,[r2,r5,lsr #32-sin_shift-2] ; r12=sin(b).
  719.   add       r2,r2,#2*sin_nb         ; r2 points on cosinus table.
  720.   ldr       r13,[r2,r5,lsr #32-sin_shift-2] ; r13=cos(b).
  721.   ldr       r9,[r2,r9,lsr #32-sin_shift-2] ; r9=cos(a2).
  722.   mul       r0,r12,r6               ; r0=x0=(??+sin(a0))*sin(b).
  723.   mul       r1,r13,r6               ; r1=y0=(??+sin(a0))*cos(b).
  724.   rsb       r10,r0,#0               ; r10=x2'=-x0.
  725.   rsb       r11,r1,#0               ; r11=y2'=-y0.
  726.   mul       r4,r13,r7               ; r4=x2=(??+sin(a1))*cos(b).
  727.   mul       r5,r12,r7               ; r5=(??+sin(a1))*sin(b).
  728.   rsb       r5,r5,#0                ; r5=y2=-(??+sin(a1))*sin(b).
  729.   rsb       r6,r4,#0                ; r6=x0'=-x2.
  730.   rsb       r7,r5,#0                ; r7=y0'=-y2.
  731.   add       r2,r0,r4                ; r2=(x0+x2).
  732.   mov       r2,r2,asr #1            ; r2=(x0+x2)/2.
  733.   add       r2,r2,r8,lsl #sin_N-2   ; r2=x1=(x0+x2)/2+k*sin(a2).
  734.   add       r3,r1,r5
  735.   mov       r3,r3,asr #1
  736.   add       r3,r3,r9,lsl #sin_N-2   ; r3=y1=(y0+y2)/2+k*cos(a2).
  737.   add       r12,r6,r10
  738.   mov       r12,r12,asr #1
  739.   add       r8,r12,r8,lsl #sin_N-2  ; r8=x1'=(x0'+x2')/2+k*sin(a2).
  740.   add       r12,r7,r11
  741.   mov       r12,r12,asr #1
  742.   sub       r9,r12,r9,lsl #sin_N-2  ; r9=y1'=(x0'+x2')/2-k*cos(a2).
  743. ; Here we have r0..r11=x0,y0,...,x2',y2'. Time now to compute the coefs
  744. ; for the incremental spline calculation, and with this we calculate the
  745. ; xl_src,yl_src,dx_src/168,dy_src/168 for all scanline. In the particular
  746. ; case when tx_N=7 and quad_N=8, the shift for "wiped" instructions is 0.
  747.   quad_coef r0,r2,r4                ; r2=inc1x | r4=a2x.
  748.   quad_coef r1,r3,r5                ; r3=inc1y | r5=a2y.
  749.   quad_coef r6,r8,r10               ; r8=inc1x' | r10=a2x'.
  750.   quad_coef r7,r9,r11               ; r9=inc1y' | r11=a2y'.
  751.   mov       r13,#256                ; Nb of lines to compute.
  752. .spline_one
  753.   mov       r12,r0,lsl #32-tx_N-2*quad_N ; r12=xleft<<(32-tx_N).
  754.   str       r12,[r14],#4
  755.   mov       r12,r1,lsl #32-tx_N-2*quad_N ; r12=yleft<<(32-tx_N).
  756.   str       r12,[r14],#4
  757.   sub       r12,r6,r0               ; r12=(mx'-mx)<<2N.
  758.   add       r12,r12,r12,lsl #1      ; r12=(mx'-mx)*3<<2N.
  759.   add       r12,r12,r12,asr #6      ; r12=(mx'-mx)/168<<(9+2N).
  760. ;  mov       r12,r12,asr #tx_N-(32-9-2*quad_N) ; r12=(mx'-mx)/168<<(32-tx_N).
  761.   str       r12,[r14],#4
  762.   sub       r12,r7,r1               ; r12=(my'-my)<<2N.
  763.   add       r12,r12,r12,lsl #1      ; r12=(my'-my)*3<<2N.
  764.   add       r12,r12,r12,asr #6      ; r12=(my'-my)/168<<(9+2N).
  765. ;  mov       r12,r12,asr #tx_N-(32-9-2*quad_N) ; r12=(my'-my)/168<<(32-tx_N).
  766.   str       r12,[r14],#4
  767.   add       r0,r0,r2                ; mx+=inc1x.
  768.   add       r2,r2,r4,asr #2*sin_N-1-zoom_N ; inc1x+=inc2x. (inc2x=2a2x)
  769.   add       r1,r1,r3                ; my+=inc1y.
  770.   add       r3,r3,r5,asr #2*sin_N-1-zoom_N ; inc1y+=inc2y.
  771.   add       r6,r6,r8                ; mx'+=inc1x'.
  772.   add       r8,r8,r10,asr #2*sin_N-1-zoom_N ; inc1x'+=inc2x'.
  773.   add       r7,r7,r9                ; my'+=inc1y'.
  774.   add       r9,r9,r11,asr #2*sin_N-1-zoom_N ; inc1y'+=inc2y'.
  775.   subS      r13,r13,#1              ; One line performed.
  776.   bNE       spline_one
  777. ; Now we go on with the rotozoom linear interpolation. Comments can be
  778. ; found in the rotozoom4 macro.
  779.   adr       r0,rotozoom_temp
  780.   ldmia     r0,{r0,r1}              ; r0=videoram adress | r1=source.
  781.   sub       r2,r14,#256*4*4         ; r2 points back on edges.
  782.   mov       r3,#256                 ; Nb of lines.
  783. .rotozoom_line
  784.   ldmia     r2!,{r4-r7}             ; Load x,y,dx,dy.
  785. #set  notlast = 1                   ; We need the final interpolation.
  786. #rept 6
  787.   rotozoom4 r1,r4,r5,r6,r7,r14,r8
  788.   rotozoom4 r1,r4,r5,r6,r7,r14,r9
  789.   rotozoom4 r1,r4,r5,r6,r7,r14,r10
  790.   rotozoom4 r1,r4,r5,r6,r7,r14,r11
  791.   rotozoom4 r1,r4,r5,r6,r7,r14,r12
  792.   rotozoom4 r1,r4,r5,r6,r7,r14,r13
  793.   stmia     r0!,{r8-r13}
  794. #endr
  795.   rotozoom4 r1,r4,r5,r6,r7,r14,r8
  796.   rotozoom4 r1,r4,r5,r6,r7,r14,r9
  797.   rotozoom4 r1,r4,r5,r6,r7,r14,r10
  798.   rotozoom4 r1,r4,r5,r6,r7,r14,r11
  799.   rotozoom4 r1,r4,r5,r6,r7,r14,r12
  800. #set notlast = 0                    ; No more final linear interpolation.
  801.   rotozoom4 r1,r4,r5,r6,r7,r14,r13
  802.   stmia     r0!,{r8-r13}
  803.   add       r0,r0,#320-168          ; Next dest line.
  804.   subS      r3,r3,#1                ; One line rotozoomED.
  805.   bNE       rotozoom_line
  806.   b         back_from_rotozoom
  807. ;----------------------->  THIS MUST BE AT VERY END  <-----------------------
  808. .bss
  809.