home *** CD-ROM | disk | FTP | other *** search
/ No Fragments Archive 10: Diskmags / nf_archive_10.iso / MAGS / CHOSNECK / CHOS2.ZIP / CHOSNECK.2ND / STUFF / DATAS.ZIP / ART48.SCR < prev    next >
Encoding:
Text File  |  1989-06-08  |  17.6 KB  |  445 lines

  1. <head>
  2. <title="...forever...">
  3. <font=monaco10.fnt>
  4. <font=newy36.fnt>
  5. <font=time24.fnt>
  6. <image=back.raw w=256 h=256 t=-1>
  7. <buf=6841>
  8. <bgcolor=-1>
  9. <background=0> 
  10. <link_color=000>
  11. <module=console.mod>
  12. <pal=back.pal>
  13. colors:
  14. 251 - black
  15. </head>
  16. <body>
  17. <frame x=0 y=0 w=640 h=6841 b=-1 c=-1>
  18.  
  19.     
  20. <f1><c000>  DSP routines and optimisation <f0>
  21. <f1><c000>        various bits and pieces <f0>
  22.     
  23.                                  by earx 2003
  24.  
  25.  
  26. - history ---------------------------------------------------------------------
  27.  
  28. a falcon is fitted with a  cute  little  dsp  chip.  this piece of silicon is a
  29. wonder of efficiency especially for  it's  time.  it  runs most instructions in
  30. just 2 cycles and is capable of  running  at 50MHz without breaking a sweat (or
  31. higher with new srams).
  32.  
  33. now when the falcon  was  released  there  were  alot  of  discussions going on
  34. whether it could actually beat a  top  class  pc  (costing twice as much). most
  35. people agreed that a 16MHz 68030 wasn't  much compared to a 50MHz 80486. others
  36. argued that the 56001 added so much  power  to  the falcon that it could easily
  37. take on such a pc.
  38.  
  39. i was and still am one  of  the  supporters  of  the latter statement. the only
  40. problem was that a 030 might be  relatively  easy to code. easier even than the
  41. 68000 we loved from the st days. but the dsp was a whole different story.  it's
  42. architecture differs alot from the usual cpu's (risc/cisc).
  43.  
  44. it wasn't easy to code  dsp.  especially  not  in  the  early days. people were
  45. forced to program  with  motorola's  assembler,  which  might  be  complete and
  46. flexible, but also bloated and slower than snails.
  47.  
  48. i have enormous respect for the poineers  on the dsp. eko, aura, bitmaster, dnt
  49. crew, aggression and  others.  from  just  doing  what  the  dsp  was initially
  50. intented to do (audio filtering) they  let  it  play mods, make it paint shaded
  51. vectors, do 3d transformations, fractals and so on.
  52.  
  53. - motivation ------------------------------------------------------------------
  54.  
  55. the point here is it really  took  a  few  years before the dsp's potential was
  56. released. however, i think we haven't seen  the  best  routs yet. this is why i
  57. write a doc to possible get  some  people  interested in dsp hacking. also, dsp
  58. coding is alot of fun. there are really  funny ways of doing things on it, that
  59. don't seem like cpu coding at all. but we'll get to that later on..
  60.  
  61. first, i want to mention  the  reasons  for  coding  dsp.  a good reason may be
  62. educational purposes. the dsp, cpu  system  is basicly a multiprocessor scheme.
  63. complete with seperate busses, synchronous  and asynchronous transfer. it might
  64. tell a deal about how other multiprocessor systems to do work (consoles!).
  65.  
  66. another reason can be  an  interest  in  filtering,  fft's,  dct's and all that
  67. stuff. the dsp is very suited for these and implementation is relatively easy.
  68.  
  69. the third is reason is just  to  show  what  a  basic  machine can do! to fully
  70. exploit the potential of the falcon you have to use dsp, there is no way around
  71. it! using only a 030 might  seem  like  working  with one hand tied behind your
  72. back, but this analogy is  somewhat  flawed.  people use both hands intuitively
  73. and without much effort. but  programming  multiprocessor systems is definetely
  74. less intuitive than coding singleprocessor  systems.  yep,  this  is a bit of a
  75. warning. but don't get scared yet ;)
  76.  
  77. - getting started -------------------------------------------------------------
  78.  
  79. i won't explain everything you need to know  here. i'm sorry, but i have little
  80. time. and besides. tat  wrote  a  good  doc  on  getting  started  with the dsp
  81. (loading p56s/lods, transferring with hostport).
  82.  
  83. all i will say here is that a running  dsp  program is often a synergy of a cpu
  84. program and a dsp program, not just a  dsp  program and it's loader. and to get
  85. this running you need to make some protocols for transmission.
  86.  
  87. furthermore you need a good dsp  assembler.  as i said, motorola's is complete,
  88. but not very practical to use.  the  conversion  of  cld->p56 is tedious. or at
  89. least i tend to think so. there are alternatives. dspdit for lovers of turboasm
  90. and qdsp for others (you can install it  as  a  tool in devpac or just run with
  91. commandline). there is also a56, but i have never tried.
  92.  
  93. a debugger is also a must. on  cpu  it's  often possible to just hack something
  94. and if it goes wrong look  at  the  code  a  bit  to  set  it right. on the dsp
  95. however, especially when starting it's not so easy. we'll get to that later.
  96.  
  97. okay, now it's time  to  give  some  pointers.  dsp  coding  has  quite alot of
  98. pitfalls and in order to have a  decent  coding session it's advisable to avoid
  99. these.
  100.  
  101. - split buses -----------------------------------------------------------------
  102.  
  103. the 56k dsp's use a  split  bus  architecture  known  as modified harvard. this
  104. means you have actually 3 buses. the  first  is p(rogram) memory, guess what it
  105. is for ;) the second and third are  x,y data memory. to maximise throughput you
  106. can load and store in parallel or load  twice or store twice whatever.. this is
  107. quite a change of philosphy from the normal 680x0 we're used to.
  108.  
  109. the 680x0 can ofcourse load and store in one instruction, which is damn easy to
  110. code. however, it can definetely  not  do  it  in  parallel.  also next to this
  111. parallel load/store the dsp has an arithmetic instruction. all three can run
  112. in parallel.
  113.  
  114. ofcourse we can't do this or need this  all  of the time, but it is possible to
  115. reach these peak throughputs with some good  thinking. a good advice however is
  116. to first write your code in  pure  'sequential'  form  and  get it to work. and
  117. later on parallelize it step by step.
  118.  
  119. for instance:
  120.  
  121. ; x0=scalar
  122.     do  #13,_loop
  123.     move                y:(r4)+,y0  ; get input.
  124.     mpy x0,y0,a                     ; a=result
  125.     move                a,x:(r0)+   ; store res.
  126. _loop:
  127.  
  128. can be done as:
  129.  
  130. ; x0=scalar
  131.     move                y:(r4)+,y0  ; y0=input[0]
  132.     mpy x0,y0,a                     ; a=result
  133.     do  #13,_loop
  134.     mpy x0,y0,a         a,x:(r0)+   ; store res.
  135.     move                y:(r4)+,y0  ; get input.
  136. _loop:
  137.  
  138. and we can go further
  139.  
  140. ; x0=scalar
  141.     move                y:(r4)+,y0  ; y0=input[0]
  142.     mpy x0,y0,a                     ; a=result[0]
  143.     do  #13,_loop
  144.     mpy x0,y0,a         a,x:(r0)+   y:(r4)+,y0  ; a=result[n]
  145. _loop:
  146.  
  147. yes, about 3 times as fast! but notice that we have to make a special 'head' to
  148. initialize correctly. this is often the case.  also notice the r0/r4. these are
  149. chosen for a reason. in a  parallel  x/y  move  one  reg must be r0..r3 and the
  150. other must be r4..r7. all things we have  to  keep  in mind. but as we can see,
  151. this does pay off. :)
  152.  
  153. about the extra header code. it's often also  the  case that you need a tail to
  154. complete the  last  iteration.  so,  indeed,  this  optimisation  isn't without
  155. consequences, sometimes even increasing the code-size a bit.
  156.  
  157. - overlaps --------------------------------------------------------------------
  158.  
  159. a thing correlated to the split buses is  the overlap of memory. you would like
  160. the think of your memory as 3  banks  (p,x,y) which have nothing todo with each
  161. other. this is not true. infact you  have both internal and external memory (oh
  162. boy!). the internal memory is small:
  163.  
  164.  internal               | external
  165. ------------------------+-------------------------
  166. p: 512 words p:0..p:$200|p:$0200..p:$7FFF
  167. x: 256 words p:0..p:$100|x:$0100..x:$3FFF
  168. y: 256 words p:0..p:$100|y:$0100..y:$3FFF
  169.  
  170. but where's the overlap then? right here:
  171.  
  172. p:$0200..p:$3FFF = y:$0200..y:$3FFFF
  173. p:$4000..p:$7FFF = x:$0000..x:$3FFFF
  174.  
  175. note that p:$4000..p:$40FF is _external_  x  ram.  no internal ram is mirrored,
  176. only external.
  177.  
  178. as you can see, that's quite nasty.  it's  possible to be unaware of this since
  179. it's all very transparent. and then all of a sudden your code gets overwritten
  180. ;)
  181.  
  182. a situation that occurs alot is that you  start coding your program at p:$40 as
  183. usual and then you code a  few  hours  till  you  hit p:$200. you now overwrite
  184. y:$200. that's quite cute, as long  as  there's  nothing useful there in y mem.
  185. *g* okay, i'll not leave you standing in the  cold and give a good hint. use an
  186. endlabel to get the end address of your code..
  187.  
  188. ....
  189.  
  190.     org p:$40
  191.  
  192.     ...
  193.     ..
  194.  
  195.     mpy ..
  196.     mac ..
  197.     rep ..
  198.     div ..
  199.     rts
  200. end_p_mem:
  201.  
  202. ; internal y memory 0..$100..
  203.     org y:0
  204.  
  205. ; external y memory end_p_mem..$3FFF
  206.     org y:end_p_mem
  207.  
  208. yep, this way you avoid fuckups.
  209.  
  210. now some things to take  into  consideration  for speed optimisation. the first
  211. thing is internal memory. the dsp can do  3 accesses on the 3 internal memories
  212. at once. also it can  do  1  access  on  an  external  memory and 2 on internal
  213. memories in the same period. however, it can't do 2 accesses on the external
  214. ram at once. it needs an extra cycle!
  215.  
  216. for example:
  217.  
  218. ; r0<$100, r4>=$100 (r0 internal, r4 external)
  219.     org p:$40
  220.     mpy x0,y0,a     a,x:(r0)+   y:(r4)+,y0
  221.  
  222. will run in 2 cycles. but:
  223.  
  224. ; r0>=$100, r4>=$100 (both external!)
  225.     org p:$40
  226.     mpy x0,y0,a     a,x:(r0)+   y:(r4)+,y0
  227.  
  228. will take an extra cycle! another bad situation:
  229.  
  230. ; r0>=$100, r4>=$100 (both external!)
  231.     org p:$200
  232.     mpy x0,y0,a     a,x:(r0)+   y:(r4)+,y0
  233.  
  234. now 3 accesses on the external memories are done.
  235.  
  236. please note i'm not exactly sure about  the  timings,  but i do know the latter
  237. situations actually are slower than the  first.  we may conclude from this that
  238. keeping at least the time-critical loops in  internal  p memory is a good idea.
  239. also it's healthy to keep one address register pointing to internal mem
  240. (either x or y).
  241.  
  242. - bootstrapping ---------------------------------------------------------------
  243.  
  244. if i'm not mistaking, at p:$7800 (or  x:$3800)  a nice small piece of bootstrap
  245. code is found. if you are friendly enough  to  need all dsp mem you can get you
  246. will most likely take it for yourself. no  problem, you say. next time a p56 is
  247. loaded, the os will just reset the dsp and reinstall this bootstrap code.
  248. *buzz* wrong.
  249.  
  250. atari didn't implement this, so killing the bootstrap on dsp is effectively the
  251. same as killing low memory (tos) on the cpu side. in order to do this right you
  252. need to re-install a bootstrapper yourself. the  code involved here is too much
  253. to mention and i recommend you look at the bootstrapper by nocrew.
  254.  
  255. - hostport --------------------------------------------------------------------
  256.  
  257. the hostport is an essential communication  channel  between cpu and dsp. there
  258. has been alot of fuss about it. first  of  all atari decided to make it a cheap
  259. 8bit implementation. 24bit words  are  split  up  into  3 bytes and transferred
  260. sequentially. Also in  some  cases  you  are  forced  to  use handshaking which
  261. further decreases performance. These are  really  the  Achilles heel of the dsp
  262. system. It's like driving a racecar and being restricted to 100KMH.
  263.  
  264. but enough about this. i am here to tell how to get around these limitations as
  265. much as that's possible.
  266.  
  267. handshaking is an interesting issue. first  of all, the fastest processor needs
  268. to be handshaked. note  the  subtlety.  if  the  complexity of the calculations
  269. fluctuate, this is hard to know...  so,  in  those  cases you have to handshake
  270. both cpu and dsp. anyway, that's safest. it will always run on any falcon.
  271.  
  272. often  however  coders  have  kicked  the  cpu  side  handshaking  to  increase
  273. throughput. this is possible. i  have  seen  routs  function well even when the
  274. when the cpu had a  higher  clock  than  the  dsp.  however, remember to always
  275. handshake before transmitting the first word. this way, you start synchronized.
  276. also, the dsp loop must be  damn  tight  and  must  typicly  be a maximum of 10
  277. cycles (excluding the actual transmit instructions!).
  278.  
  279. for instance:
  280.  
  281. ; wrapped texturemapping (for 64x64 textures)
  282. ; y:(r4): texture
  283. ; high accuracy.. 10bit subtexel. on 68K 8bit is common..
  284.     movec   #$FFFF,m0
  285.     movec   #$FFFF,m5
  286.     move            #<$08,y0
  287.     ; y0=downscalar (%UUUUUUuuuuuuuuuu -> %0000UUUUUUuuuuuu)
  288.     move            #$000FC0,y1
  289.     ; y1=U.0 mask
  290.     move            #$002000,x1
  291.     ; x1=downscalar (V<<10+v -> V)
  292.  
  293. ; r0: %UUUUUUuuuuuuuuuu[0] (start)
  294. ; n0: u_step (%UUUUUUuuuuuuuuuu)
  295. ; r5: %VVVVVVvvvvvvvvvv[0] (start)
  296. ; n5: v_step (%VVVVVVvvvvvvvvvv)
  297. ; n2=#pixels in scan
  298.  
  299.     do  n2,_xloop
  300.     move            r0,x0
  301.     ; x0=%UUUUUUuuuuuuuuuu
  302.     mpyr    y0,x0,a     (r0)+n0
  303.     ; a=%0000UUUUUUuuuuuu, r0=%UUUUUUuuuuuuuuuu[n+1]
  304.     and y1,a        r5,x0
  305.     ; a=%0000UUUUUU000000, x0=%VVVVVVvvvvvvvvvv
  306.     mac x1,x0,a     (r5)+n5
  307.     ; a=%0000UUUUUUVVVVVV, r5=%VVVVVVvvvvvvvvvv[n+1]
  308.     move            a,n4
  309.     ; n4=%0000UUUUUUVVVVVV
  310.     jclr    #1,x:<<HSR,*            ; Wait until host is ready.
  311.     movep   y:(r4+n4),x:<<HTX       ; Transmit texel.
  312. _xloop:
  313.  
  314. this is my new texturing loop.  it  is  able  to wrap (for seamless 'wallpaper'
  315. textures) and fast enough to keep up with accelerated cpu's.
  316.  
  317. now optimising the dsp part  is  important,  but  like  i said before. many dsp
  318. applications are a combined process. so the cpu could do with some optimisation
  319. as well. on a standard  falcon  (16MHz  030,  32MHz  dsp). the dsp runs circles
  320. around the cpu so to say, so the cpu becomes the bottleneck.
  321.  
  322. a normal texturing loop would look like this:
  323.  
  324.     move.w  #NUM_PIXELS-1,d7
  325.  
  326.     lea $FFFFA206.w,a1          ; dsp xmit (16 bit)
  327. loop:   move.w  (a1),(a0)+      ; copy dsp->screen
  328.     dbf d7,loop                 ; and another..
  329.  
  330. we already see that we only use the  lower 16bit part of the transmit register.
  331. this causes only two reads  on  the  bus  instead  of three. remember the story
  332. about the 8bit hostport? well, that applies here. because we fetch a 16bit word
  333. we cause two (8bit) accesses.
  334.  
  335. this loop is reasonable, however it  can  be  much  better for wider spans. and
  336. with that i mean spans with more than 2  pixels ;) you can try to unroll as far
  337. as i-cache can take you, to kill the dbf time (10 cycles mostly!).
  338.  
  339.     moveq   #NUM_PIXELS,d7
  340.     moveq   #16-1,d1
  341.  
  342.     move.l  d7,d0
  343.     and.l   d1,d0                   ; count mod 16
  344.     neg.l   d0
  345.     lsr.w   #4,d7                   ; count/16
  346.     jmp jump(pc,d0.l*2)             ; skip unneeded pix.
  347.  
  348. loop:
  349.     rept    16                      ; unroll 16 times
  350.     move.w  (a1),(a0)+              ; copy dsp->screen
  351.     endr
  352. jump:   dbf d7,loop                 ; next 16 pixels..
  353.  
  354. a more costly initialisation, but as you can see also a much cheaper loop. this
  355. can result in 1.1 mln pix/s on  a  standard  falcon (rgb 320x200) and this is a
  356. _net_ measurement. not just measurements  of  the innerloop, but also including
  357. the yloop and scanconversion.
  358.  
  359. yes, this is all very nice. but what about the cases _with_ handshaking? right,
  360. if you really need handshaking it's best to use it on larger words. handshaking
  361. for each byte is madness, it's a  good  idea  to  use it on 16bit or even 24bit
  362. portions. sadly, 24bit portions aren't very  common.  the 030 doesn't use 12 or
  363. 24bit memory words (would be nice in  this  case wouldn't it? ;)) so we're more
  364. interested in 16 and 8 bit modes.
  365.  
  366. if we need to transmit bytes it's best  to pack them toghether in a 16bit word.
  367. a good example is using bytes  to  index  a palette. often, when rgb operations
  368. like saturated are too costly, you'd want to use this.
  369.  
  370. now we can look up as follows:
  371.  
  372. ; init
  373.     clr.l   d0
  374.     lea $FFFFA207.w,a1              ; dsp xmit (lsb)
  375.     lea palette,a2
  376.     lea $FFFFA202.w,a3              ; dsp control
  377. ; ..loop..
  378. wait:   btst    #0,(a3)             ; handshake.....
  379.     beq.s   wait                    ; ..
  380.     move.b  (a1),d0                 ; get index from dsp.
  381.     move.w  (a2,d0.l*2),(a0)+       ; lookup and store.
  382.  
  383. ofcourse a complete handshake for only 1 silly byte lookup is crazy:
  384.  
  385. ; init
  386.     clr.l   d0
  387.     lea $FFFFA206.w,a1              ; dsp xmit (lsw)
  388.     lea palette,a2
  389.     lea $FFFFA202.w,a3              ; dsp control
  390. ; ..loop..
  391. wait:   btst    #0,(a3)             ; handshake.....
  392.     beq.s   wait                    ; ..
  393.     move.w  (a1),d0                 ; get indices from dsp.
  394.     move.l  (a2,d0.l*4),(a0)+       ; lookup and store.
  395.  
  396. here we see two pixels are done at once  with only 3 bus accesses extra (only 1
  397. or 2 with good use of ttram!). please  note that you also need a special double
  398. palette here, like so:
  399.  
  400.     dc.w    col0,col0
  401.     dc.w    col0,col1
  402.     dc.w    col0,col2
  403.     dc.w    ...
  404.     ...
  405.     ...
  406.     dc.w    col0,col254
  407.     dc.w    col0,col255
  408.     dc.w    col1,col0
  409.     dc.w    col1,col1
  410.     ...
  411.     ...
  412.     dc.w    col1,col255
  413.     dc.w    col2,col0
  414.     ...
  415.     ...
  416.  
  417.  
  418. ofcourse on the dsp this requires  a  packing  operation.  but as we shall see,
  419. this is nothing:
  420.  
  421.     move            #>128,y0        ; y0=scalar
  422. ; x0: first index ($00FF), a0: second index ($00SS)
  423.     mac x0,y0,a                     ; a0=$00FFSS
  424.  
  425. okay, one last note about handshaking: btst  sucks! yes folks, there is another
  426. and better way todo it.
  427.  
  428.     btst    #0,(a0)
  429. =
  430.     moveq   #1,d0
  431.     and.b   d0,(a0)
  432.  
  433. this does not clear the other bits  in  the  status  reg. why? it's a read only
  434. register! the good part of this is also that  no write is issued on the bus, so
  435. it's basicly not more than a btst  but  faster.  ofcourse you have to reserve a
  436. register, but who cares. i  don't  know  why  atari or other programmers didn't
  437. notice this one. it can give a healthy speedup... okay, not factor 2, but still
  438. enough for some cases where you need to have  an  fx run in 1 or 2 vbl and need
  439. to give it a little 'push'.
  440.  
  441. <link=art48b.scr>Go to NEXT PART</l>
  442. </frame>
  443. </body>
  444.  
  445.