home *** CD-ROM | disk | FTP | other *** search
/ The Datafile PD-CD 5 / DATAFILE_PDCD5.iso / demos / baah / Articles / GfxWar / GraphicWar < prev   
Encoding:
Text File  |  1996-05-14  |  22.2 KB  |  448 lines

  1. Alain BROBECKER                                    Dracula / Positivity (STe)
  2. rte de Dardagny                                    Baah / Arm's Tech (Archie)
  3.  01630 CHALLEX                                                      Baah (PC)
  4.     FRANCE
  5. ----------------------------------------------------------------- 12 april 96
  6.  
  7.                                - GRAPHIC WAR -
  8.                                 =============
  9.                              (aka memory war ][)
  10.                              
  11.  
  12.  
  13. FOREWORD
  14.  
  15.     This text is aimed to assembly programmer, I doubt people programming
  16.   in high level languages will appreciate it much. All algorithms, ideas
  17.   and assembly code in here were made by me. (else I mention the author)
  18.   If you use it in one of your programs, please send me a free copy of it
  19.   and credit me. If you appreciated this text, I would be glad to know it. 
  20.  
  21.  
  22.  
  23. INTRODUCTION
  24.  
  25.     Again an article dedicated to the owners of low end computer, and which
  26.   deals about reduction of program size on disk. Last time we saw how to
  27.   create 'mathematical' tables, but what is the most expensive part of datas
  28.   used in programs? Yup, graphixx. The common way to reduce their size is to
  29.   use a packer, but as usual the best performances won' t be get by using
  30.   a performant packer or what, but by combinating our grey cells and subtle
  31.   formulaes.
  32.   
  33.  
  34.  
  35. GETTING STARTED
  36.  
  37.     The first background we' ll create is not an idea of mine, but I found
  38.   it in an issue of the excellent "Coder' s Revenge" magazine by the German
  39.   crew Archiologics. (Though some issues were mostly in German, I recommend
  40.   you get copies of them. Must mention I had troubles making it work on my
  41.   1Meg A3000,800Kb floppy too.) The trick is really simple, and efficient.
  42.   The pixel at (x;y) quite simply gets the xANDy color, and the nice result
  43.   can be seen in the appendix A. You can also try xEORy as basic formula, or
  44.   whatever combination such as (x+y)OR(x-y)...
  45.     As usual, the program is very short (80 bytes) compared to the size
  46.   the created 256*256 image would take on disk. The "packing" ratio is then
  47.   1:819, which proves to beat everyhing, even a fractal packer I think.
  48.   (Since the philosophy of fractal packers is, as far as I know, to find a
  49.   formula which constructs the image, and that is exactly what we do)
  50.     By the way, when speaking about fractals, don' t you think the given
  51.   image is quite similar to the Sierpinsky Gasket? Well, not very surprising
  52.   since one way of obtaining this fractal involves eor operations from one
  53.   line to one another. (Well, there is much to say about this, but it' s not
  54.   our subject for now)
  55.  
  56.  
  57.  
  58. AMIGA LIKE
  59.  
  60.     For some years now, amiga demomakers are putting "messy" 1 bitplane
  61.   (ie 1 bit per pixel) image as a background, and I must admit that it looks
  62.   quite nice. The way they are creating those backgrounds is very simple:
  63.   they take a brush in deluxe paint, choose a color and make large random
  64.   movements with the mouse, sometimes clearing, sometimes drawing.
  65.     Don' t sound very hard to do huh? Well it isn' t, and we' ll prove it.
  66.   The basic proggy which simulates an amiga graphist (hey, don' t take it
  67.   too serious! ;) would look like the following...
  68.             10 : MODE 9:OFF
  69.             20 : COLOUR 0,0,4*17,0 : COLOUR 1,0,5*17,0
  70.             30 : FOR a%=1 TO 1<<13
  71.             40 :   IF RND(2)=1 THEN GCOL(1) ELSE GCOL(0)
  72.             50 :   CIRCLE FILL RND(1279),RND(1023),8
  73.             60 : NEXT a%
  74.  
  75.     So what are the problems when converting this to pure ASM. (Well, some
  76.   basic+ASM programmers wouldn' t care, they would simply save it on disk,
  77.   as they do for huge precalc tables! ;) The first one is to have a decent
  78.   random generator. My generator is based upon no algorithm, I just tried
  79.   operations combination till I got a decent one, which seemed to give
  80.   equal probabilities for each number. As far as I know, the problem when
  81.   creating a rnd number generator is in fact to have a "formula" which does
  82.   not look like one. (Else, you may see patterns arising in your programs)
  83.   My routine is quite small and fast (3 instructions, 5 cycles. If you have
  84.   better, I would be glad to hear about it), and the really handy thing I
  85.   learned out of my tries (and out of the vlc86c010 book, I must admit) is
  86.   that shifts, rotations and carry mixed with classical operations gives
  87.   good result for rnd generators.
  88.     Once this problem has been set up, the other problems where quite easy
  89.   to cope with. I only needed to draw (or clear) sprites. In order to
  90.   simplify thing, I decided not to cope with maximum speed sprites and
  91.   clipping (hey, I create a background only once in a demo, so my aim is
  92.   not maximum speed, but minimum size!), making things much easier.
  93.     So, the result is in appendix B, and provides a 440 bytes proggy which
  94.   creates a 10240 bytes texture. For once I will let you calculate the
  95.   "packing" ratio. (It become dull...) The main difference between a piccy
  96.   generated by our proggy and a hand made background, is that sometimes a
  97.   graphist try to have a logo or else drawn with the messy shit. We can do it
  98.   too, but the equations will certainly be more complex. (And so will be the
  99.   code. Don' t be afraid, complex code is the most interesting one) As an
  100.   introduction to more sophisticated backgrounds, here is a proggy which
  101.   draws a spiral.
  102.             10 : MODE 9:OFF:ORIGIN 640,512
  103.             20 : COLOUR 0,0,4*17,0 : COLOUR 1,0,5*17,0
  104.             30 : FOR r%=1 TO 1024
  105.             40 :   x%=r%*COS(2*PI*r%/256)
  106.             50 :   y%=r%*SIN(2*PI*r%/256)
  107.             60 :   FOR a%=1 TO 4
  108.             70 :     IF RND(2)=1 THEN GCOL(1) ELSE GCOL(0)
  109.             80 :     CIRCLE FILL x%+RND(r%/3),y%+RND(r%/3),4+RND(r%/24)
  110.             90 :   NEXT a%
  111.            100 : NEXT r%
  112.  
  113.  
  114.  
  115. GREY EDIT LIKE
  116.  
  117.     After an excursion to alien worlds (amiga) we are back on the Archimedes
  118.   world with a prestigious guest-star... (drum roll, applauses...) It is,
  119.   it is... John KORTINK, the author of the GreyEdit program. If you can get
  120.   a copy of it, don' t hesitate a second, it' s really worth its disk space.
  121.   (Though I don' t use it much, I' m pleased to know it' s somewhere in my
  122.   dusty disk box) Amongst other features, this proggy allows you to create a
  123.   random pattern, and then apply "filters" on it, like smoothing, embossing
  124.   and much more. After a few iterations of those operations, you will have
  125.   (sometimes) a nice pattern appearing.
  126.     A typical combinations which always give good results is creating the
  127.   random texture, embossing it and then smoothing it. Now you' re normally
  128.   familiar with random generation, so we have to fix the embossing and the
  129.   smoothing.
  130.     Embossing is easy to make, you only need to calculate the difference
  131.   between two pixels, and this adds the relief. Of course, the difference
  132.   will sometimes be negative or higher than the authorised maximum, then you
  133.   set it to 0 or to the maximum value. If we don' t do this, there won' t
  134.   be much changes between the initial texture and the embossed one, since
  135.   they would both be random patterns only.
  136.     Smoothing, though a bit harder, has quite a similar philosophy. The dest
  137.   (x;y) pixel get the average of all the pixels surrounding source (x;y),
  138.   with given coefficients. I choosed to take in account only the 8 pixels
  139.   surrounding (x;y), and for the one who are used to mathematics, making the
  140.   averaging is a stupid 3*3 matrix operation. As far as I know, the Gauss
  141.   smooth means the coefs of matrix are given by a  "Gaussian" equation, which
  142.   varies exponentially with the distance. The coefficients I personnaly used
  143.   have nothing to do with mathematics, they only give good results.
  144.     One thing I wanted and which is not in GreyEdit is the "wrapping" of the
  145.   created pattern. This kind of problem is easily set up by using x mod(N)
  146.   instead of x, where N is the width of the texture. (Same for y) Another
  147.   handy thing you shall know is that if you choose N a power of 2, the mod(N)
  148.   operation is the same as ANDing the value with (N-1).
  149.     The resulting program is again quite good in terms of size (392 bytes)
  150.   and I very much like the resulting background. One problem is that the
  151.   texture looks a bit tiny if looked at on a huge (640*512) screen. If you
  152.   want to eliminate this problem, then you simply have to create a less
  153.   random texture, add noise (=random perturbation) on it and go on as usual.
  154.   I have not tested it right now, but I can' t imagine why it would not work.
  155.   (Since I can' t have a 640*512 screen, I have no use of this) 
  156.     As an example, let' s say you create a 128*128 random pattern, then you
  157.   double it, giving a 256*256 texture with square pixels. Then, add a small
  158.   random perturbation to all pixels individually, and go on. (Emboss+Smooth) 
  159.  
  160.  
  161.                                  - THE END -
  162.  
  163.  
  164.  
  165. ;****************************************************************************
  166. ;*****                                                                  *****
  167. ;*****                            APPENDIX A                            *****
  168. ;*****                                                                  *****
  169. ;****************************************************************************
  170. ; Not much to say, except that this small proggy certainly suits well for
  171. ; apprentice ASM coders. (Good lucks, guys. Getting started is the hardest)
  172. .make_background_1
  173.   mov       r13,r14                 ; Save return adress.
  174.   swi       256+22                  ; Switch to mode13.
  175.   swi       256+13
  176.   swi       OS_RemoveCursors        ; Who needs them?
  177.   adr       r0,videoram_adress      ; Use system routs to get videoram adress.
  178.   mov       r1,r0
  179.   swi       OS_ReadVduVariables
  180.   ldr       r0,videoram_adress      ; And put videoram adress in r0.
  181.   mov       r1,#255                 ; r1=y counter.
  182. .y_loop
  183.   mov       r2,#255                 ; r2=x counter.
  184. .x_loop
  185.   and       r3,r2,r1                ; Here' s the magical operation.
  186.   strB      r3,[r0],#1              ; Save xANDy in memory.
  187.   subS      r2,r2,#1                ; All 256 pixels drawn?
  188.   bGE       x_loop                  ; No, then loop.
  189.   add       r0,r0,#320-256          ; Next line.
  190.   subS      r1,r1,#1                ; All 256 lines drawn?
  191.   bGE       y_loop                  ; No, then loop.
  192.   mov       pc,r13                  ; That' s all folks. 
  193.    
  194. .videoram_adress                    ; Here are the magical values used
  195.   dcd       148,-1                  ;   by swi OS_ReadVduVariables. 
  196.  
  197.  
  198.  
  199. ;****************************************************************************
  200. ;*****                                                                  *****
  201. ;*****                            APPENDIX B                            *****
  202. ;*****                                                                  *****
  203. ;****************************************************************************
  204. ; The really interesting thing is the random32 macro. Others things that
  205. ; should be noticed are that I create the 1 bpp background just after the
  206. ; proggy, (bss) and normally I should watch if I have enough Wimp_Slot.
  207. ; Another thing is that I know that the sprite is a 5*5 "circle", and so
  208. ; I was able to use tricks, so that it goes faster. I don' t perform
  209. ; clipping. Now go through the code if you want to know more.
  210.  
  211. ; This macro takes two random numbers, and by using subtile (hum) operations
  212. ; it changes them in two new random numbers.
  213. macro random32 m0,m1
  214. { add       m0,m1,m0,ror #3
  215.   mov       m0,m0,ror m1
  216.   eor       m1,m0,m1,ror #7
  217. }
  218.  
  219. #set        nb_sprites = 3<<12      ; Nb of sprites to put on screen.
  220.  
  221. .make_background_2
  222.   mov       r13,r14                 ; Save return adress.
  223.   swi       256+22                  ; Switch to mode9.
  224.   swi       256+9
  225.   swi       OS_RemoveCursors        ; Who needs them?
  226.   adr       r0,videoram_adress      ; Use system routs to get videoram adress.
  227.   mov       r1,r0
  228.   swi       OS_ReadVduVariables
  229.   adr       r0,colors               ; Change colors using a swi.
  230.   mov       r1,#12                  ; "Write" 12 bytes. (2 colors)
  231.   swi       OS_WriteN
  232. ; First we clear the place where we' ll put the background.
  233.   adr       r0,bss                  ; Create 1 bpp background here.
  234.   mov       r1,#512*(256+6)/8       ; Number of bytes to clear.
  235.   mov       r2,#0                   ; Fill with zeroes.
  236. .clear_one
  237.   str       r2,[r0,r1]              ; Clear one long.
  238.   subS      r1,r1,#4                ; All longs cleared?
  239.   bGE       clear_one
  240. ; Here really starts the creation of the background.
  241.   mov       r1,#nb_sprites          ; Nb of sprites to 'draw'.
  242.   adr       r2,random_germs         ; Load the random germs.
  243.   ldmia     r2!,{r2,r3}
  244.   mov       r4,#%01110              ; Mask for the sprites.
  245.   mov       r5,#%11111
  246. .make_one_sprite
  247.   mov       r6,r2,lsr #32-8         ; r6=y=rnd(256).
  248.   add       r6,r0,r6,lsl #6         ; r6=tmp_buffer+y*512/64.
  249.   mov       r7,r3,lsr #32-4         ; r7=int(x/32)=rnd(16).
  250.   add       r6,r6,r7,lsl #2         ; r6 points on first longword.
  251.   and       r7,r2,#%11111           ; r7=x mod32=rnd(32).
  252.   rsb       r8,r7,#32               ; r8=32-(x mod32).
  253.   mov       r9,r5,lsl r7            ; Shift the two masks of sprite.
  254.   mov       r10,r5,lsr r8
  255.   mov       r7,r4,lsl r7
  256.   mov       r8,r4,lsr r8
  257.   tst       r3,#%1                  ; Clear or set the sprite?
  258. ; Here we draw all five lines of sprite. The drawing method depend upon CC,
  259. ; if set to NE, we 'orr' the shifted sprite and bground, else 'bic' them.
  260.   ldmia r6,{r11,r12} : orrNE r11,r11,r7 : bicEQ r11,r11,r7 : orrNE r12,r12,r8
  261.   bicEQ r12,r12,r8 : stmia r6,{r11,r12} : add r6,r6,#64
  262.   ldmia r6,{r11,r12} : orrNE r11,r11,r9 : bicEQ r11,r11,r9 : orrNE r12,r12,r10
  263.   bicEQ r12,r12,r10 : stmia r6,{r11,r12} : add r6,r6,#64
  264.   ldmia r6,{r11,r12} : orrNE r11,r11,r9 : bicEQ r11,r11,r9 : orrNE r12,r12,r10
  265.   bicEQ r12,r12,r10 : stmia r6,{r11,r12} : add r6,r6,#64
  266.   ldmia r6,{r11,r12} : orrNE r11,r11,r9 : bicEQ r11,r11,r9 : orrNE r12,r12,r10
  267.   bicEQ r12,r12,r10 : stmia r6,{r11,r12} : add r6,r6,#64
  268.   ldmia r6,{r11,r12} : orrNE r11,r11,r7 : bicEQ r11,r11,r7 : orrNE r12,r12,r8
  269.   bicEQ r12,r12,r8 : stmia r6,{r11,r12}
  270.   random32 r3,r2                    ; Next random number.
  271.   subS      r1,r1,#1                ; One sprite drawn.
  272.   bNE       make_one_sprite
  273. ; Now draw a part of the 512*256, 1 bpp background to mode9 screen.
  274.   add       r0,r0,#3*4+3*64         ; Take middle of created bground.
  275.   ldr       r1,videoram_adress      ; Adress of videoram.
  276.   mov       r2,#256                 ; Nb of hlines to convert.
  277. .draw_one_hline
  278.   mov       r3,#10                  ; Nb of 1 bpp longs to convert per hline.
  279. .draw_32_pixels
  280.   mov       r4,#4                   ; 1 src long -> 4 dest longs.
  281.   ldr       r5,[r0],#4              ; Load source pixels, in 1 bpp.
  282. .draw_8_pixels
  283.   movS      r6,#0                   ; r6 will contain the dest long.
  284. #set N=0
  285. #rept 8
  286.   movS      r5,r5,lsr #1            ; Put pixel in carry bit.
  287.   addCS     r6,r6,#1<<N             ; If pixel is set, put it in r6.
  288. #set N=N+4
  289. #endr
  290.   str       r6,[r1],#4              ; Save dest long.
  291.   subS      r4,r4,#1                ; All sets of 8 pixels done?
  292.   bNE       draw_8_pixels           ; No, then loop.
  293.   subS      r3,r3,#1                ; All longs done?
  294.   bNE       draw_32_pixels          ; No, then loop.
  295.   add       r0,r0,#6*4              ; Offset to next hline in source.
  296.   subS      r2,r2,#1                ; All hline done?
  297.   bNE       draw_one_hline          ; No, then loop.
  298.   mov       pc,r13                  ; That' s all folks.
  299.  
  300. .videoram_adress                    ; Here are the magical values used
  301.   dcd       148,-1                  ;   by swi OS_ReadVduVariables.
  302. .colors                             ; Values used by the WriteN swi to
  303.   dcb       19,&0,16,&00,&44,&00    ;   change colors.
  304.   dcb       19,&1,16,&00,&55,&00
  305. .random_germs                       ; The magical random numbers.
  306.   dcd       &eb1a2c37,&3fd2e145
  307. .bss                                ; MUST BE AT THE VERY END.
  308.  
  309.  
  310.  
  311. ;****************************************************************************
  312. ;*****                                                                  *****
  313. ;*****                            APPENDIX C                            *****
  314. ;*****                                                                  *****
  315. ;****************************************************************************
  316. ;   This routine creates a N*N wrapping texture. The principle is to
  317. ; generate a pattern filled with random numbers (noise), and then to apply
  318. ; miscellaneous filters on this texture.
  319. ;   The first filter I decided to invoke is an 'emboss' one, which takes
  320. ; the differences between pixels to give a kind of relief, and then I
  321. ; apply a 'smooth' filter on the texture. (it is a 3*3 matrix operation,
  322. ; see below)
  323.  
  324. ; This macro takes two random numbers, and by using subtile (hum) operations
  325. ; it changes them in two new random numbers.
  326. macro random32 m0,m1
  327. { add       m0,m1,m0,ror #3
  328.   mov       m0,m0,ror m1
  329.   eor       m1,m0,m1,ror #7
  330. }
  331.  
  332. #set        M = 8                   ; Well, texture must be a power of 2,
  333. #set        N = 1<<M                ;    so N=2^M is the size of texture.
  334. #set        middle = 11             ; Intensity for null relief*2.
  335.  
  336. .make_background3
  337.   mov       r13,r14                 ; Save return adress.
  338.   swi       256+22                  ; Switch to mode13.
  339.   swi       256+13
  340.   swi       OS_RemoveCursors        ; Who needs them?
  341.   adr       r0,videoram_adress      ; Use system routs to get videoram adress.
  342.   mov       r1,r0
  343.   swi       OS_ReadVduVariables
  344.   adr       r0,color_table          ; Intensity -> mode13 color table.
  345.   adr       r1,bss                  ; Create texture here.
  346.   ldr       r2,videoram_adress      ; Use videoram as temporary buffer.
  347. ; First, we fill first minibuffer with the random 'noise'.
  348.   adr       r3,random_germs         ; Load germs for the random generator.
  349.   ldmia     r3,{r3-r4}
  350.   mov       r5,#N*N                 ; r5=nb of pixies to generate.
  351.   mov       r6,#&1f1f1f1f           ; Mask for pixels' intensities.
  352. .random_fill
  353.   and       r7,r6,r3                ; Pixels' intensities between 0-31.
  354.   str       r7,[r1],#4              ; Save 4 pixels.
  355.   random32  r3,r4                   ; Next random number.
  356.   subS      r5,r5,#4                ; 4 pixels processed.
  357.   bNE       random_fill
  358.   sub       r1,r1,#N*N              ; r1 back on its position.
  359. ; Then we add relief to the texture by taking the difference (delta) between
  360. ; the pixels up and down of the current position.
  361.   mov       r3,#0                   ; r3=y counter<<(32-7).
  362. .emboss_one_line
  363.   sub       r4,r3,#1<<(32-M)        ; r4=(y-1) mod N <<(32-M). (Wrapping)
  364.   add       r5,r3,#1<<(32-M)        ; r5=(y+1) mod N <<(32-M). (Wrapping)
  365.   add       r4,r1,r4,lsr #(32-2*M)  ; r4 points on src_line up.
  366.   add       r5,r1,r5,lsr #(32-2*M)  ; r5 points on src_line down.
  367.   mov       r6,#N                   ; r6=nb of pixels per line.
  368. .emboss_one
  369.   ldrB      r8,[r4],#1              ; r8=pixie up.
  370.   ldrB      r7,[r5],#1              ; r7=pixie down.
  371.   sub       r7,r7,r8                ; r7=delta.
  372.   addS      r7,r7,#middle           ; Add the middle constant.
  373.   movMI     r7,#0                   ; Make sure intensity is between 0-31.
  374.   cmp       r7,#31
  375.   movGE     r7,#31
  376.   strB      r7,[r2],#1              ; Save it.
  377.   subS      r6,r6,#1                ; One pixel done
  378.   bNE       emboss_one
  379.   addS      r3,r3,#1<<(32-M)        ; Line done.
  380.   bNE       emboss_one_line
  381.   sub       r2,r2,#N*N              ; r2 back.
  382. ; We smooth the texture by applying the following 3*3 matrix on pixels...
  383. ;         ( 1 2 1 )   ( pix0 pix1 pix2 )
  384. ;  1/16 * ( 2 4 2 ) * ( pix3 pix4 pix5 ) = new pix.
  385. ;         ( 1 2 1 )   ( pix6 pix7 pix8 )
  386. ; At the same time we convert the intensities into pixels by using the
  387. ; given color table.
  388.   mov       r3,#0                   ; r3=y counter.
  389. .smooth_line
  390.   mov       r4,#0                   ; r4=x counter.
  391.   sub       r5,r3,#1<<(32-M)        ; r5=(y-1) mod N <<(32-M). (Wrapping)
  392.   add       r7,r3,#1<<(32-M)        ; r7=(y+1) mod N <<(32-M). (Wrapping)
  393.   add       r5,r2,r5,lsr #(32-2*M)  ; r5 points on src_line up.
  394.   add       r6,r2,r3,lsr #(32-2*M)  ; r6 points on src_line.
  395.   add       r7,r2,r7,lsr #(32-2*M)  ; r7 points on src_line down.
  396. .smooth_one
  397.   sub       r8,r4,#1<<(32-M)        ; r8=(x-1) mod N <<(32-M). (Wrapping)
  398.   add       r9,r4,#1<<(32-M)        ; r9=(x+1) mod N <<(32-M). (Wrapping)
  399.   ldrB      r10,[r6,r4,lsr #(32-M)] ; Load all the pixels, and add them
  400.   ldrB      r14,[r5,r4,lsr #(32-M)] ; with the good coefficients in r10.
  401.   add       r10,r14,r10,lsl #1
  402.   ldrB      r14,[r7,r4,lsr #(32-M)]
  403.   add       r10,r10,r14
  404.   ldrB      r14,[r6,r8,lsr #(32-M)]
  405.   add       r10,r10,r14
  406.   ldrB      r14,[r6,r9,lsr #(32-M)]
  407.   add       r10,r10,r14
  408.   ldrB      r14,[r5,r8,lsr #(32-M)]
  409.   add       r10,r14,r10,lsl #1
  410.   ldrB      r14,[r5,r9,lsr #(32-M)]
  411.   add       r10,r10,r14
  412.   ldrB      r14,[r7,r8,lsr #(32-M)]
  413.   add       r10,r10,r14
  414.   ldrB      r14,[r7,r9,lsr #(32-M)]
  415.   add       r10,r10,r14
  416.   mov       r10,r10,lsr #5          ; r10=intensity.
  417.   cmp       r10,#11                 ; No more than 12 colors.
  418.   movGE     r10,#11
  419.   ldrB      r10,[r0,r10]            ; Convert it with table lookup.
  420.   strB      r10,[r1],#1             ; And save new pixel value.
  421.   addS      r4,r4,#1<<(32-M)        ; Next pixel.
  422.   bNE       smooth_one
  423.   addS      r3,r3,#1<<(32-M)        ; Next line.
  424.   bNE       smooth_line
  425. ; Now we copy the created texture on screen.
  426.   adr       r0,bss                  ; Adress of created texture.
  427.   ldr       r1,videoram_adress      ; r1=videoram adress.
  428.   mov       r2,#N                   ; r2=y counter.
  429. .y_loop
  430.   mov       r3,#N                   ; r3=x counter.
  431. .x_loop
  432.   ldrB      r4,[r0],#1              ; Load pixel.
  433.   strB      r4,[r1],#1              ; And copy it in videoram.
  434.   subS      r3,r3,#1                ; Whole hline drawn?
  435.   bNE       x_loop                  ; No, then loop.
  436.   add       r1,r1,#320-256          ; Next dest line.
  437.   subS      r2,r2,#1                ; All hlines drawn?
  438.   bNE       y_loop                  ; No, then loop.
  439.   mov       pc,r13                  ; That' s all folks.
  440.  
  441. .videoram_adress                    ; Here are the magical values used
  442.   dcd       148,-1                  ;   by swi OS_ReadVduVariables.
  443. .color_table
  444.   dcb &08,&09,&0a,&0b,&a4,&a5,&a6,&a7,&d8,&d9,&da,&db ; Blue.
  445. .random_germs                       ; The magical random numbers.
  446.   dcd       &eb1a2c37,&3fd2a145
  447. .bss                                ; MUST BE AT THE VERY END.
  448.