home *** CD-ROM | disk | FTP | other *** search
/ Enigma Amiga Life 109 / EnigmaAmiga109CD.iso / software / testi / corsoasm / sorgenti_darkcoder / plasma / copper.txt < prev    next >
Encoding:
Text File  |  1996-08-17  |  18.4 KB  |  309 lines

  1.                            æ2±3VERY ADVANCED COPPER±1æ0
  2.  
  3.  
  4. In  questo  articolo  si parlera` di tecniche avanzate di programmazione del
  5. copper.  Lo spunto per questo articolo l'ho avuto quando mi sono accorto che
  6. nella  lezione  11  del Corso di Asm di ±2Randy±1, si dice che l'istruzione ±3SKIP±1
  7. del  copper  non  serve a nulla.  Io non sono di questo parere, e durante la
  8. scrittura  del  Corso (al quale io ho collaborato scrivendo gran parte delle
  9. lezioni  7,9, e 10), lo feci presente a Randy mediante alcuni esempi che gli
  10. inviai affinche` li includesse nella lezione 11.  Purtroppo Randy, non lo ha
  11. fatto,  forse  per  mancanza  di  tempo  o forse perche` ha perso nell'HD il
  12. materiale  che  gli  avevo  inviato.   A  me  pero` non va giu` l'idea che i
  13. giovani  coders che hanno letto il Corso (quanti sono ?) credano che la ±3SKIP±1
  14. sia  inutile,  perche`  cio`  contrasta  con  uno degli Oscuri Assiomi della
  15. filosofia ±2Morbid Visions±1:
  16.  
  17.  ±2Amiga RULEZ => Hardware Amiga perfetto => HW privo di cose inutili.±1
  18.  
  19. L'±3AGA±1  ha  anche  dei  difetti,  ma l'±3OCS±1 no.  Quindi ho pensato di scrivere
  20. questo  articolo  per  parlare  della  ±3SKIP±1  e anche del mascheramento delle
  21. coordinate  del  copper,  argomento  che  nel  corso  e` trattato in maniera
  22. piuttosto  sommaria.   Vedremo, infatti che la SKIP si rivela molto utile in
  23. alcuni  casi,  e  che  le possibilita` di mascheramanto delle coordinate del
  24. copper  sono  molto piu` ampie di quanto spiegato, ad esempio vedremo che e`
  25. possibile mascherare anche le posizioni orizzontali.
  26. Le  tecniche che tratteremo in questo articolo, non vi permetteranno di fare
  27. Ray  Tracing  in tempo reale, (altrimenti se ne sarebbe accorto anche Randy)
  28. pero`  in  qualche  circostanza  possono  farvi risparmiare qualche preziosa
  29. linea di raster.  E siccome nei ±2Testi Oscuri Del Coding Mortale±1 si asserisce
  30. che  uno  degli  obiettivi  di  un coder e` sfruttare al ±2massimo±1 l'hardware,
  31. bisogna saper sfruttare anche queste tecniche.
  32. Iniziamo dando una descrizione piu` dettagliata del formato usato dal copper
  33. per  le  istruzioni WAIT e SKIP.  Queste 2 istruzioni hanno un formato molto
  34. simile, per questo faremo un'unica descrizione per entrambe.  La descrizione
  35. del  funzionamento  della  SKIP la vedremo successivamente.  Come ben sapete
  36. ogni istruzione copper si compone di 2 WORD.  Il formato della prima word e`
  37. descritto nella tabella seguente:
  38.  
  39.    ±3PRIMA WORD ISTRUZIONI WAIT E SKIP±1
  40.      ---------------------------------
  41.    ±3Bit 0           ±2Sempre settato a 1.
  42.  
  43.    ±3Bits 7 - 1      ±2Posizione orizzontale pennello elettronico (HP).
  44.  
  45.    ±3Bits 15 - 8     ±2Posizione verticale pennello elettronico (VP).
  46. ±1
  47. Entrambe  queste  due istruzioni basano il loro comportamento sulla verifica
  48. di  una  certa  condizione,  che normalmente e` il superamento, da parte del
  49. pennello  elettronico della posizione specificata dai campi di bits VP e HP.
  50. Come  vedremo tra un attimo, e` possibile modificare tale condizione tenendo
  51. conto   anche   dello   stato  del  blitter.   In  seguito  descriveremo  il
  52. comportamento della SKIP.  Vediamo la seconda WORD:
  53.  
  54.  
  55.    ±3SECONDA WORD ISTRUZIONI WAIT E SKIP±1
  56.      -----------------------------------
  57.    ±3Bit 0           ±2Settato a 0 per la WAIT, settato a 1 per la SKIP.
  58.  
  59.    ±3Bits 7 - 1      ±2Bit maschera posizione orizzontale (HE).
  60.  
  61.    ±3Bits 14 - 8     ±2Bit maschera posizione verticale (VE).
  62.  
  63.    ±3Bit 15          ±2Blitter-finished-disable bit. Normalmente settato a 1.
  64. ±1
  65. Il bit 0 serve al copper per capire se l'istruzione in questione e` una WAIT
  66. o  una SKIP.  I campi di bits HE e VE servono per mascherare rispettivamente
  67. le  posizioni  orizzonate e verticale.  Il funzionamento e` il seguente:  il
  68. copper  effettua  il  confronto tra la posizione specificata da HP e VP e la
  69. posizione  del  pennello  elettronico  utilizzando  solo quei bit tali che i
  70. corrispondenti  bit  di HE e VE sono settati a 1.  Se ad esempio in una WAIT
  71. poniamo i bit di HE tutti a 1, mentre i bit 8-12 di VE a 0 e i bit 13 e 14 a
  72. 1,  il  copper  rimarra`  in attesa che il pennello elettronico raggiunga la
  73. posizione  orizzontale  HP  (perche`  tutti  i  bit  di  HE  sono a 1) e una
  74. posizione verticale tale che i bit 13 e 14 siano uguali ai bit 13 e 14 di VP
  75. (perche`  essi  sono  i  soli  bit  di VE settati a 1).  Vediamo alcuni casi
  76. notevoli.  Se vogliamo utilizzare TUTTI i bit di HP e VP (cio` non usiamo il
  77. mascheramento),  dobbiamo  settare  a  1  tutti i bit di HE e VE.  In questo
  78. caso,  se  abbiamo  una WAIT, otteniamo che la seconda WORD assume il valore
  79. che   ben   conoscete  $FFFE.   Se  invece  vogliamo  una  WAIT  che  ignori
  80. completamente  la  posizione  verticale  ma  consideri  tutti  i  bit  della
  81. orizzontale   otteniamo   che  la  seconda  WORD  vale  $80FE,  come  accade
  82. nell'esempio  presente  nella  lezione  11  del  corso  di Randy.  Il bit 15
  83. permette  di  modificare  la  condizione che le 2 istruzioni verificano:  se
  84. tale  bit  e`  settato a 1, si comporteranno normalmente, in caso contrario,
  85. dovranno  anche  verificare  che  il  blitter  abbia terminato una eventuale
  86. blittata  (cioe` BLTBUSY, il bit 14 di DMACONR deve valere 0).  Per esempio,
  87. nel caso della WAIT, con il bit in questione a 0, essa oltre ad attendere il
  88. raggiungimento  della  posizione video indicata dai bits VP e HP, attendera`
  89. anche  la fine di un'eventuale blittata.  Cio` puo` essere utile nel caso si
  90. vogliano  effettuare  blittate  sincronizzate  con la posizione del pennello
  91. elettronico.
  92.  
  93. Ai  piu`  attenti non sara` sfuggito il fatto che a causa della presenza del
  94. Blitter  Finished  Disable  bit, i bit di VE sono uno in meno dei bit di VP.
  95. Piu`  precisamente  non  esiste  in  VE  un  bit  corrispondente al bit piu`
  96. significativo  di  VP.   Questo  vuol  dire  che  tale  bit  (il bit 8 della
  97. posizione verticale dello schermo) NON puo` essere mascherato.  Questo fatto
  98. ha   delle   importanti   conseguenze.    Nelle   applicazioni,  infatti  il
  99. mascheramento  viene utilizzato per avere delle istruzioni che si comportano
  100. allo  stesso  modo  in  diverse posizioni di schermo.  Il fatto di non poter
  101. mascherare  il  bit  8 della posizione verticale impedisce pertanto di avere
  102. istruzioni  che  si comportano allo stesso modo in zone dello schermo aventi
  103. il  bit  8  della  posizione  verticale diverso.  L'esempio tipico, mostrato
  104. anche  da  Randy,  e`  quello  della  WAIT  che  attende una certa posizione
  105. orizzontale indipendentemente dalla riga in cui si trova.  Se noi tentassimo
  106. di  realizzare  una tale WAIT ponendo DC.W $00xx,$80FE otterremmo in realta`
  107. una  WAIT  che  attende  una posizione dello schermo tale che il bit 8 della
  108. posizione  verticale  vale  0  e  la posizione orizzontale vale xx.  Se tale
  109. istruzione viene eseguita quando la posizione del pennello elettronico ha il
  110. bit  8  uguale  a  0,  essa attende che il pennello elettronico raggiunga la
  111. posizione  orizzontale  xx, come voluto.  In caso contrario, invece, poiche`
  112. il  bit 8 della posizione verticale del pennello elettronico vale 1 e il bit
  113. 8  di  VP vale 0, la condizione della WAIT e` subito verificata, quindi tale
  114. istruzione  NON  blocca il copper.  A causa di questo fenomeno Randy afferma
  115. nel suo corso che il mascheramento non funziona nelle righe comprese tra $80
  116. e  $FF.   Si tratta di una conclusione decisamente frettolosa.  Infatti, per
  117. ottenere  l`effetto  voluto,  basta  utilizzare  una WAIT che mascheri i bit
  118. bassi  di VP, come nel caso precedente, ma che abbia il bit non mascherabile
  119. di  VP  posto  a  1,  ovvero  una  DC.W  $80xx,$80FE.  Tale WAIT nelle righe
  120. comprese  tra  $80  e  $FF,  avra` il bit non mascherabile di VP allo stesso
  121. valore  del  bit  8  della  posizione  verticale del pennello elettronico, e
  122. pertanto  attendera` ad ogni riga la posizione orizzontale xx.  Come esempio
  123. di  applicazione vi proponiamo nel sorgente MV_Code/Copper/mask1.s l'effetto
  124. usato  da  Randy  per illustrare il mascheramento delle posizioni verticali,
  125. realizzato  (diversamente  da  Randy)  nelle  righe  comprese tra $80 e $FF.
  126. Consentiteci  a  questo  punto, rispettabili lettori di parafrasare l'inizio
  127. dello  scrolltext  della  celeberrima INTRO ±2KickReset±1 di ±3Razor 1911±1:  ±2"Randy
  128. told us that this couldn't be done...nevertheless here it is!!"±1 :))
  129.  
  130. Bisogna  comunque  sottlineare  che  una  WAIT mascherata con il bit 8 di VP
  131. settato  ad  1,  se  eseguita  in  una  riga avente il bit 8 della posizione
  132. verticale  pari  a 0, blocchera` SEMPRE il copper, in quanto il numero della
  133. riga  e`  considerato  sempre minore della posizione specificata nella WAIT.
  134. Cio`  significa  che  la  WAIT  del  nostro esempio ottiene l'effetto voluto
  135. (aspettare  la  posizione orizzontale xx SOLO nelle righe comprese tra $80 e
  136. $FF.   E se noi volessimo utilizzare le WAIT mascherate in tutto lo schermo?
  137. Beh`,  con  un po` di lavoro in piu` e` possibile anche questo.  Il sorgente
  138. ±2MV_Code/Copper/mask2.s±1  e` appunto un implementazione dell'effetto visto nel
  139. sorgente  precedente  che  funziona  in  tutto lo schermo.  Vi rimandiamo al
  140. commento del sorgente per una descrizione delle tecniche adottate.
  141.  
  142. Veniamo  dunque  all'istruzione  ±3SKIP±1.  Come detto, essa ha un formato molto
  143. simile  a  quello  della  WAIT.  Il comportamento della SKIP e` il seguente:
  144. essa  fa  saltare al copper l'istruzione seguente se il pennello elettronico
  145. ha  superato la posizione specificata.  Per esempio consideriamo le seguenti
  146. istruzioni:
  147.  
  148.     dc.w    $4037,$ffff    ; SKIP (salta) se si supera la linea $40
  149. ISTR1:    dc.w    $182,$0456    ; istruzione move del copper
  150. ISTR2:    dc.w    $182,$0fff    ; istruzione move del copper
  151.  
  152. Quando  il  copper  esegue  l'istruzione  SKIP  controlla  dove  si trova il
  153. pennello  elettronico.  Se esso ha superato la posizione specificata dai bit
  154. VP  e  HP  della  SKIP  (nell'esempio  HP=$36  e  VP=$40),  il  copper salta
  155. l'istruzione   seguente   (all'indirizzo   ISTR1)   ed  esegue  l'istruzione
  156. successiva  ad  essa (cioe` l'istruzione all'indirizzo ISTR2).  Se invece il
  157. pennello  elettronico  non  ha  ancora raggiunto la posizione indicata viene
  158. eseguita  nomalmente  l'istruzione  successiva come se la SKIP non ci fosse.
  159. Come   abbiamo   gia`  detto  anche  alla  SKIP  puo`  essere  applicato  il
  160. mascheramento  delle  posizioni mediante i bit VE e HE della seconda WORD in
  161. maniera analoga a quanto avviene per la WAIT; inoltre anche per la SKIP puo`
  162. essere  azzerato  il  Blitter  Finished Disable bit, facendo si che il salto
  163. venga  effettuato  o  meno  tenendo  conto  ANCHE  dello  stato del Blitter.
  164. Mediante  la  ±3SKIP±1 si possono realizzare dei loop nella copperlist.  Un loop
  165. nella  copperlist e` un insieme di istruzioni copper che viene ripetuto fino
  166. a  che il pennello elettronico non raggiunge una determinata posizione.  Per
  167. realizzare  il  loop  si  usa  anche  il  registro COP2LC.  Il meccanismo e`
  168. illustrato dal seguente esempio:
  169.  
  170. nel programma principale si esegue una
  171.  
  172.     move.l    #Copperloop,COP2LC(A5)    ; scrive l'indirizzo del loop
  173.                     ; nel registro COP2LC
  174.  
  175. e nella copperlist si mettono le seguenti istruzioni:
  176.  
  177.     dc.w    $2007,$FFFE    ; WAIT linea $20
  178. Copperloop:
  179.     dc.w    $180,$F00    ; istruzioni copper del loop
  180.     dc.w    $180,$0F0
  181.     dc.w    $180,$00F
  182.  
  183.     .
  184.     .
  185.  
  186.     dc.w    $180,$F0F    ; ultima istruzione del loop
  187.     dc.w    $4007,$ffff    ; SKIP (salta) se si supera la linea $40
  188.     dc.w    $8a,0        ; COPJMP2 salta all'inizio del loop
  189.  
  190.     dc.w    $182,$00F    ; istruzione fuori dal loop
  191.  
  192. Il  funzionamento  e` molto semplice.  Dopo la linea $20,il copper entra nel
  193. loop Dopo aver eseguito tutte le istruzioni del loop arrivera` alla SKIP.  A
  194. questo  punto se il pennello elettronico NON ha ancora superato la linea $40
  195. (cioe`   si  trova  piu`  in  alto  sullo  schermo)il  copper  NON  saltera`
  196. l'istruzione  seguente.   L'istruzione  seguente,  pero`  scrive  in COPJMP2
  197. provocando  un  salto  del  copper  all'indirizzo  scritto in COP2LC, ovvero
  198. all'indirizzo della prima istruzione del loop.  In questo modo il loop viene
  199. ripetuto.   Dopo  un  certo  numero  di ripetizioni, il pennello elettronico
  200. raggiungera` la linea $40.  A questo punto quando viene eseguita di nuovo la
  201. SKIP,  essa  fara`  saltare al copper l'istruzione che scrive in COPJMP2; in
  202. questo  modo  esso  non  fa  piu`  il  salto all'inizio del loop ma passa ad
  203. eseguire la prima istruzione esterna al loop.
  204.  
  205. A  cosa  servono  i  loop  nella copperlist?  E` chiaro, che possiamo sempre
  206. farne  a  meno:   invece  di  fare  il  loop scriviamo tante volte quante ci
  207. servono  la  parte di copperlist da ripetere.  In questo modo ci risparmiamo
  208. la  SKIP  e l'istruzione che scrive in COPJMP2, che rallentano un pochettino
  209. il  copper.   L'uso  dei  loop  presenta pero` dei vantaggi:  in primo luogo
  210. risparmiamo   memoria,   perche`  scriviamo  una  volta  sola  il  pezzo  di
  211. copperlist.   In  secondo  luogo,  se  il  pezzo di copperlist ripetuto deve
  212. essere   modificato   dal   processore   per   realizzare  qualche  effetto,
  213. naturalmente facendo il loop il pezzo di copperlist dovra` essere modificato
  214. una sola volta, velocizzando moltissimo il lavoro del processore.
  215.  
  216. L'utilizzo di istruzioni WAIT all'interno dei loop presenta alcuni problemi.
  217. Supponiamo  di  avere  un loop che si ripete dalla riga $20 alla riga $70, e
  218. che  all'interno  del  loop ci sia una WAIT alla riga $38.  Che succede?  La
  219. prima  volta  che il loop viene eseguito, la WAIT blocca il copper.  Dopo la
  220. linea  $38  il  copper si sblocca, arriva alla fine del loop e lo ripete.  A
  221. questo  punto,  siccome  il pennello elettronico ha superato la riga $38, la
  222. WAIT  non  blocca  piu` il copper.  Come risultato, l'esecuzione della prima
  223. iterazione  del  loop  produrra`  risultati  molto  diversi dalle iterazioni
  224. successive.   Di  solito  questo  non e` cio` che si vuole.  Nei loop con il
  225. copper sarebbe desiderabile poter aspettare una determinata riga del loop ad
  226. ogni iterazione.  Per esempio si potrebbe volere qualcosa del genere:
  227.  
  228. CopperLoop:
  229.         ; istruzioni varie
  230.  
  231.         aspetta 4 righe dall'inizio dell'iterazione
  232.  
  233.         ; istruzioni varie
  234.  
  235.         ripeti il loop fino ad una certa riga.
  236.  
  237. Come  si puo` realizzare un meccanismo del genere?  E` necessario utilizzare
  238. delle WAIT con mascherati alcuni bit della posizione verticale.
  239.  
  240. Per  esempio  supponiamo  di  avere  un  loop che si estende per 16 righe di
  241. raster  e  che vogliamo ripetere dalla riga $10 alla riga $70, ovvero per 96
  242. righe.   Poiche` 96/16=6 il copper eseguira` 6 iterazioni.  Notate che 96 e`
  243. divisibile  per  16  (non  c'e`  resto),  il  che  vuol dire che il pennello
  244. elettronico raggiungera` la riga 96 esattamente nel momento in cui il copper
  245. finisce  la  sesta  iterazione.  Vogliamo che in ogni iterazione del loop il
  246. copper  si  blocchi  alla quarta riga a partire dall'inizio dell'iterazione.
  247. Per   ottenere   cio`  usiamo  una  WAIT  in  cui  mascheriamo  i  bit  piu`
  248. significativi  della posizione verticale.  In questo caso poiche` il loop si
  249. ripete  ogni  16  righe, la WAIT si deve comportare allo stesso modo ogni 16
  250. righe, e non deve considerare le differenze di posizione tra un gruppo di 16
  251. righe  e  l'altro.   Quindi  e`  necessario  considerare  solo  i 4 bit meno
  252. significativi (che formano appunto un gruppo di 16 linee).  Per mascherare i
  253. bit  della  posizione verticale, come e` spiegato nel corso, si utilizzano i
  254. bits  da  8  a  14  della seconda word della WAIT.  Se uno di tali bit viene
  255. settato  a  1  (come accade di solito) il bit corrispondente della posizione
  256. verticale  viene utilizzato; se invece uno di tali bit viene azzerato il bit
  257. ad  esso  corrispondente e` mascherato.  Consideriamo ad esempio la seguente
  258. istruzione WAIT:
  259.  
  260.     dc.w    $0301,$8FFE
  261.  
  262. questa  istruzione aspetta la quarta riga di un gruppo di 16 linee.  Vediamo
  263. cosa  accade  nel  nostro esempio.  Il loop inizia alla riga $20.  Il copper
  264. esegue  le prime istruzioni e incontra la WAIT.  Essa considera solo i 4 bit
  265. meno  significativi  della  posizione per cui si mette ad aspettare una riga
  266. che abbia tali 4 bit al valore $3 (infatti nella seconda WORD i bit 12,13,14
  267. che  corrispondono ai bit 5,6 e 7 della posizione verticale sono a 0).  Cio`
  268. accade  alla  riga  $23.   A  questo punto il copper si sblocca.  La seconda
  269. iterazione  del  loop inizia alla riga $30.  Anche qui il copper arriva alla
  270. WAIT  e  aspetta una riga che abbia i 4 bit meno significativi al valore $3,
  271. cosa  che  accade  alla  riga  $33, ovvero ancora alla quarta riga del loop.
  272. Questo  comportamento si ripete ad ogni successiva iterazione.  Se volessimo
  273. delle  iterazioni  lunghe  8  righe  con  delle WAIT di questo tipo dovremmo
  274. lasciare  abilitati solo i 3 bit meno significativi della posizione.  Notate
  275. che  questa  tecnica  si  implementa  facilmente  solo se la lunghezza di un
  276. iterazione  e`  una  potenza di 2.  Un esempio di copper loop e` il ±2sorgente
  277. MV_Code/Copper/skip1.s±1.
  278.  
  279. Una  limitazione  all'uso  delle  WAIT nella maniera che abbiamo mostrato e`
  280. dovuta  al fatto che il bit piu` significativo della posizione verticale non
  281. e`  mascherabile.   Cio`  ci  impedisce di realizzare loop che si comportino
  282. nella  stessa  maniera  sia  al  di  sopra  della riga $80, dove il bit piu`
  283. significativo  vale  0, sia al di sotto, dove il bit piu` significativo vale
  284. 1,  proprio  perche`  non possiamo ignorare questa differenza mascherando il
  285. bit.  L'unica soluzione e` di utilizzare 2 loop, uno da eseguire al di sopra
  286. di $80 e uno al di sotto, come mostrato in ±2MV_Code/Copper/skip2.s±1.
  287.  
  288. Un  esempio  un po' piu` sofisticato e` nel sorgente ±2MV_Code/Copper/skip3.s±1.
  289. In  tale  sorgente le copperlist sono scritte utilizzando delle ±3MACRO±1 invece
  290. che  mediante  delle ±2DC.W±1.  Le ±3MACRO±1 permettono ad esempio di scrivere ±2CMOVE
  291. $0f0,COLOR00±1  invece che ±2DC.W $180,$0f0±1 e, nel caso delle ±2WAIT±1, ±2WAIT $07,$60±1
  292. invece  che  ±2DC.W $6007,$FFFE±1.  Si tratta di una scelta stilistica che a mio
  293. avviso  rende  molto  piu`  puliti e ordinati i sorgenti.  Inoltre in questo
  294. modo   si   evitano  molti  errori  di  "distrazione",  nella  scrittura  di
  295. copperlist,  come  ad  esempio dimenticarsi che il bit 0 della prima word di
  296. una ±2WAIT±1 DEVE essere settato a 1.  Nella prima parte dell'articolo non le ho
  297. usate perche` volevo confondere ulteriormente le idee durante la spiegazione
  298. della  SKIP.   Consiglio  pertanto  a tutti di utilizzarle.  Nel sorgente ci
  299. sono delle versioni ridotte delle mie macro, che potete prendere, migliorare
  300. e includere nei vostri sorgenti.
  301.  
  302. Naturalmente  mediante  le  SKIP  si  possono costruire loop che si ripetono
  303. anche  all'interno  della stessa riga dello schermo.  A causa della lentezza
  304. del  copper,  (1  istruzione=8  Pixel)  il  numero di iterazioni per riga di
  305. solito  e`  piuttoto  ridotto.   Potete  vederne  un  esempio  nel  sorgente
  306. ±2MV_Code/Copper/skip4.s±1.
  307.  
  308.                                        ±2The Dark Coder±1 / ±3Morbid Visions±1
  309.