		  __________________________________________
         ______  |::.                                    .::|  ______
        //_____\ |:::: CORSO DI ASSEMBLER - LEZIONE 11 .::::| /_____\\
       _\\ O o / |::::::::::::::::::::::::::::::::::::::::::| \ o O //_
       \_/\_-_/_  ------------------------------------------' _\_-_/\_/
        / __|.  \/ /                                      \ \/  .|__ \
       /____|. \__/                                        \__/ .|____\
    .---/   |.  \--------------------------------------------/  .|   \---.
   /   /____|____\      INTERRUPT, CIAA/CIAB, DOSLIB        /____|____\   \
  /                                                                        \
  `-------------------------------------------------------------------------'

Autore: Fabio Ciucci

(Directory Sorgenti7) - quindi scrivere "V Assembler3:sorgenti7"

Ora che avete approfondito il funzionamento del blitter, potete affermare con
sicurezza di conoscere l'hardware di Amiga, dato che 68000, blitter e copper
li sapete programmare. Per non si finisce mai di imparare, e in questa
lezione vedremo delle informazioni avanzate sul 68000, come gli interrupt,
nonch dei listati che mostrano utlizzi particolari di Blitter e Copper, e
infine l'utilizzo dei chip CIAA e CIAB, che per ora abbiamo usato solo per
testare la pressione del mouse.
Direi di cominciare con le nuove informazioni sul 68000, in modo da fare una
startup migliore di quella che abbiamo fatto nel corso della LEZIONE8.
Le informazioni su vettori di eccezione, interrupt ecc. che saranno spiegate
non saranno tutte utili e indispensabili per la programmazione di giochi e
demo, ma solo una parte di esse ci servir. Non lasciatevi dunque intimorire
dalla quantit di cose accennate: in pratica  poco ci che useremo!
Innanzitutto occorre parlare dei due modi perativi del 680x0, ossia del modo
utente e del modo supervisore. Gi nel 68000-2.txt si accennava, senza
spiegare, che ci sono delle istruzioni "privilegiate", che devono essere
eseguite in modo supervisore, ossia durante una eccezione o un interrupt.
Per ora abbiamo fatto eseguire le nostre routines sempre in modo USER, ossia
nel modo utente, perch non era necessario eseguire istruzioni privilegiate,
e perch non abbiamo sfruttato gli Interrupt del processore.
Devo premettere che la velocit di esecuzione non cambia tra modo utente e
modo supervisore, per cui far eseguire il proprio programma come una exception
o un interrupt non turbizzer di certo l'esecuzione.
D'altronde una "eccezione"  chiamata cos proprio perch si tratta di una
cosa che deve avvenire solo in casi "eccezionali", per esempio un software
failure, noto come GURU. Da notare che ci sono dei guru che sono causati dal
sistema operativo Amiga, (come errori di exec) e altri che sono programmati
direttamente dalla Motorola, autrice dei 680x0. Per esempio, gli errori di
BUS, di DIVISIONE PER ZERO, ecc, ossia i vettori, sono di questo tipo.
Avrete notato che il sistema operativo riesce a far aggiornare la posizione
della freccia puntatore del mouse anche se stiamo eseguendo una nostra
routine, a patto che non si disabiliti il multitasking con una chiamata al
Disable(). Ebbene, se il processore sta eseguendo un nostro loop, come fa
ogni fotogramma ad aggiornare altre cose? Ricorderete che se si disabilitano
gli interrupt di sistema si blocca tutto. Infatti si serve degli interrupt,
che "interrompono" l'esecuzione del nostro programmino ogni fotogramma,
eseguono una loro routine, e riprendono l'esecuzione del nostro programma
dove l'avevano lasciata, il tutto senza che ce ne accorgiamo!
Oltre agli interrupt, ci sono altri modi per eseguire delle routines in
supervisore, ad esempio le istruzioni TRAP, o un qualsiasi errore in un
programma. Ad esempio quando avviene una divisione per zero, o il processore
trova dei dati che non corrispondono a nessuna istruzione, viengono eseguite
in modo supervisore le routines dei guru meditation e software failure.
Gli emulatori di altri processori, per esempio, fanno in modo che ad ogni
valore binario di istruzione del processore emulato, ad esempio un 80286 o
il 6502 del commodore 64, corrisponda una routine che faccia le operazioni che
avrebbe fatto quell'istruzione per quel processore (grossomodo!).
Ora, non  negli scopi di questo corso imparare a programmare sistemi
operativi o emulatori, dato che il sistema operativo dell'Amiga  gia
il milgiore al mondo, e gli emulatori su Amiga sono insuperabili, tanto che
chi ha un Amiga pu far eseguire i programmi di un MacIntosh e di un MSDOS,
e giocare ai giochi dei vecchi C64 e Spectrum.
Ma anche se si vuole programmare un demo o un gioco, o una utility come il
protracker/octamed,  UTILISSIMO gestire qualche interrupt.
Molte demo e giochi di vecchio tipo cominciavano mandando in modo supervisore
il 68000, scrivendo subito sullo SR e facendo giochi strani con lo Stack.
Ebbene, molti di questi giochi non funzionano su computer con 68020, perch
lo Status Register nei processori pi evoluti ha delle funzioni in pi, che
tali programmatori ignoravano, e settando o azzerando bit alla rinfusa
commisero un enorme sbaglio. Inoltre in modo supervisore lo stack (SP)  uno
stack "privato" del modo supervisore, mentre si accede allo stack di utente
con il registro USP (User Stack Pointer). Insomma in modo supervisore 
meglio andarci coi piedi di piombo, a meno che non si conoscano perfettamente
tutte le nuove caratteristiche del 68020/30/40 e anche del 68060!
Infatti il modo utente serve proprio a evitare di eseguire istruzioni che
potrebbero provocare effetti diversi su processori diversi, che solo i sistemi
operativi devono eseguire. Per i coder si sono sempre sentiti pi tosti a
mettere sottosopra i registri in modo supervisore, col risultato di farsi la
fama di "programmatori incapaci di codice non compatibile".
Ma vediamo di eseguire qualche istruzione in modo supervisore, prima di
continuare con la teoria. Tra i tanti modi che ci sono per far eseguire una
routine in exception, il pi "sicuro"  quello di usare l'apposita funzione
del sistema operativo "exec.library/Supervisor", che richiede in entrata di
mettere in A5 l'indirizzo della routine da eseguire:

	move.l	4.w,a6			; ExecBase in a6
	lea	SuperCode(PC),a5	; Routine da eseguire in supervisor
	jsr	-$1e(a6)		; LvoSupervisor - esegui la routine
					; (non salva i registri! attenzione!)
	rts				; esci, dopo aver eseguito la routine
					; "SuperCode" in supervisor.
			
SuperCode:
	movem.l	d0-d7/a0-a6,-(SP)	; Salva i registri nello stack
	...				; istruzioni da eseguire
	...				; come fosse una subroutine....
	movem.l	(SP),d0-d7/a0-a6	; Riprende i registri dallo stack
	RTE	; Return From Exception: come l'RTS, ma per le eccezioni.



Come si pu notare, non c' niente di pi facile. La routine da eseguire
la potete considerare come una subroutine da chiamare con JSR o BSR, solo che
si chiama con "JSR -$1e(a6)" dopo aver messo il suo indirizzo in A5, e
naturalmente l'ExecBase in A6. Al termine della breve "subroutine supervisor",
anzich mettere un RTS per ritornare, occorrer mettere un RTE, apposito per
tornare da eccezioni e interrupt. A questo punto il processore torner ad
eseguire in modo utente l'istruzione sotto il "JSR -$1e(a6)", proprio come
fosse stato un "BSR" o un "JSR". La funzione Supervosor non salva i registri
n li ripristina col movem, per cui se se ne modifica qualcuno durante la
routine in supervisor tali valori rimarranno al ritorno da quella routine.
A questo proposito vi consiglio di salvare e ripristinare i registri
manualmente all'inizio e alla fine della routine supervisor.
Da notare che accedendo a SP o A7 in modo supervisor si salva nello stack
di supervisore, e non in quello utente, richiamabile come USP. Questo non
 un problema, perch lo stack di supervisore salva e ripristina come quello
utente, ma in caso di programmazione pericolosa dello stack potreste prendere
dei granchi con impiantamento generale.
Eseguiamo delle istruzioni privilegiate nell'esempio lezione11a.s.
Ecco le istruzioni privilegiate eseguibili solo in modo supervisore:

	ANDI.W	#xxxx,SR
	ORI.W	#xxxx,SR
	EORI.W	#xxxx,SR
	MOVE.W	xxxxx,SR
	MOVE.W	SR,xxxxx
	MOVEC	registro,registro	; 68010+ - registri speciali per
					; controllo cache, MMU, vettori come:
					; CAAR,CACR,DFC,ISP,MSP,SFC,USP,VBR.
	RTE

Ci sarebbero anche MOVES, RESET, STOP, ma non ci interessano.
Interessa poco la possibilit di agire sullo Status Register, perch 
molto pericoloso (data la differenza dei bit dello SR tra 68000 e altri 680x0),
e non  indispensabile disturbarlo. Tra l'altro quando si salta ad una
eccezione viene salvato nello stack, oltre al Program Counter, anche lo Status
Register, e al momento dell'RTE viene ripristinato il vecchio valore del
Program Counter, per tornare sotto il "JSR -$1e(a6)", e il vecchio SR,
cosicch non ci sono cambiamenti, se non durante l'esecuzione dell'exception.
Invece l'istruzione MOVEC ci interesser molto di pi, perch per poter
usare un interrupt in modo che funzioni anche su processori 68020+ 
necessario sapere dove si trova il VBR (Vector Base Register).
Per quanto riguarda il controllo delle CACHE, credo sia meglio non interferire,
in modo che l'utente possa attivarle o disattivarle con delle utility prima
di eseguire il nostro demo o gioco, in modo da valutare le differenze di
settanggio. Se invece decidiamo per lui quali cache devono essere attivate e
quali disattivate, rischiamo anche di fare codice che non funziona sul 68060
e eventuali nuovi processori RISC che potrebbero emularlo.
L'istruzione MOVEC, disponibile solo dal 68010 in avanti, serve per copiare
il contenuto di un registro An o Dx in un registro speciale, o viceversa.
Vediamo i registri speciali presenti nel 68020:

	CAAR	- CAche Address Register
	CACR	- CAche Control Register
	VBR	- Vector Base register

Ci sono anche DFC (Destination Function Code), SFC (Source Function Code),
ISP (Interrupt Stack Pointer), MSP (Master Stack Pointer), USP (User Stack
Pointer), ma non ci interessano, perch servono solo a chi programma sistemi
operativi, emulatori eccetera.
A noi in questo momento serve sapere il valore del registro speciale VBR,
vedremo poi il perch. Per ottenerlo basta un "MOVEC VBR,d0", ad esempio.
Ma cosa  il Vector Base Register? Innanzitutto occorre spiegare cosa  un
vettore (da non confondere con i vettori in matematica, o i vectors 3d!).
Prima vediamo la tabella dei vettori, poi spieghiamoli:

NUM. VETTORE   OFFSET	Assegnazione e significato

	0	$0	Serve solo al momento del reset (SSP start)
	1	$4	Serve solo al reset, infatti c' ExecBase (PC start)
	2	$8	GURU/soft. failure: Errore di BUS
	3	$c	GURU/soft. failure: Errore di Indirizzo
	4	$10	GURU/soft. failure: Istruzione illegale
	5	$14	GURU/soft. failure: Divisione per zero
	6	$18	Eccezioni generate da istruzioni CHK,CHK2 (68020+)
	7	$1c	Eccezioni gen. da istruzioni TRAPV (68020+ TRAPCC)
	8	$20	GURU/soft. failure: Violazione di privilegio
	9	$24	Traccia (trace exception)
	$A	$28	GURU/soft. failure: Emulatore di linea %1010 (LINE-A)
	$B	$2c	GURU/soft. failure: Emulatore di linea %1111 (LINE-F)
	$C	$30	non usato
	$D	$34	Violazione di protocollo coprocessore (68020+)
	$E	$38	Errore di formato (solo 68020, dopo CALLM,RTM)
	$F	$3c	Interruzione non inizializzata
	...	...

	$18	$60	Interruzione spuria

; Ecco gli autovettori di interruzione: sono questi quelli che ci interessano!

	$19	$64	INTERRUPT livello 1 (softint,dskblk,tbe)
	$1a	$68	INTERRUPT livello 2 (ports: I/O,ciaa,int2)
	$1b	$6c	INTERRUPT livello 3 (coper,vblanc,blit)
	$1c	$70	INTERRUPT livello 4 (canali audio aud0/aud1/aud2/aud3)
	$1d	$74	INTERRUPT livello 5 (rbf,dsksync)
	$1e	$78	INTERRUPT livello 6 (exter: ciab,int6 + inten)
	$1f	$7c	INTERRUPT livello 7 (schede hardware esterne: NMI)

	$20	$80	Vettore richiamabile con TRAP #0
	$21	$84	Vettore richiamabile con TRAP #1
	$22	$88	Vettore richiamabile con TRAP #2
	$23	$8c	Vettore richiamabile con TRAP #3
	$24	$90	Vettore richiamabile con TRAP #4
	$25	$84	Vettore di TRAP #5, eccetera, fino TRAP #15
	...

	seguono vettori per errori dell'eventuale coprocessore matematico
	e dell'MMU, che non ci interessano.


                             -|             |- 
         -|                  [-_-_-_-_-_-_-_-]                  |- 
         [-_-_-_-_-]          |             |          [-_-_-_-_-] 
          | o   o |           [  0   0   0  ]           | o   o | 
           |     |    -|       |           |       |-    |     | 
           |     |_-___-___-___-|         |-___-___-___-_|     | 
           |  o  ]              [    0    ]              [  o  | 
           |     ]   o   o   o  [ _______ ]  o   o   o   [     | ----__________
_____----- |     ]              [ ||||||| ]              [     | 
           |     ]              [ ||||||| ]              [     | 
       _-_-|_____]--------------[_|||||||_]--------------[_____|-_-_ 
      ( (__________------------_____________-------------_________) ) 


Immaginiamo la situazione di un processore 68000, per il quale non occorre
cercare il valore del registro VBR, (non esiste nemmeno!).
In questo caso l'offset  proprio la locazione di memoria! $0 = $00000000
All'indirizzo $4 troviamo l'Execbase, mentre la prima long a $0 di solito 
azzerata. Ma ecco che "dentro" la locazione $8 troviamo l'indirizzo della
routine che fa apparire la scritta "GURU MEDITATION/SOFTWARE FAILURE" in caso
di BUS ERROR. Infatti tale guru, come visto nel 68000-2.TXT, ha come numero
di identificazione #00000002, ossia il suo numero di vettore.
Il terzo vettore contiene l'indirizzo della routine che fa apparire il guru
di ADDRESS ERROR (#00000003), e cos via.
In pratica, quando il processore trova uno di questi errori, salta al vettore
corrispondente, che contiene l'indirizzo della routine da eseguire in modo
supervisore (data la gravit della situazione!).
Al momento del reset vengono scritti dalla ROM tutti gli indirizzi nei vettori
dal primo all'ultimo. Se avete dei programmini che modificano i messaggi dei
software failure o dei guru, sappiate che fanno "puntare" i vettori alle sue
routines, anzich alle routines normali di sistema.
Naturalmente ci sono modi "legali" per cambiare i vettori, ossia passando da
strutture e routines del sistema operativo. Scrivere brutalmente l'indirizzo
della propria routine nel vettore pu risultare inefficace o incompatibile.
Ad esempio:

	MOVE.L	#MiaDivisionePerZero,$14.w	; sostituisco il vettore
						; del guru da div. per zero.
	rts

MiaDivisionePerZero:
	...
	RTE

NON FATE MAI COME IN QUESTO ESEMPIO, PER UN PAIO DI RAGIONI. LA PRIMA  CHE
NEL 68020+ NON  DETTO CHE TALE VETTORE SI TROVI ALL'INDIRIZZO $14, LA SECONDA
 CHE  UN METODO INCOMPATIBILE CON MMU E STRUTTURE DEL SISTEMA OPERATIVO
AMIGA.
Comunque, in teoria questo sistema dovrebbe funzionare, e su Amiga500 funziona
quasi sempre, a patto che la routine supervisor sia scritta bene.
La modifica dei vettori di errore/guru per ci interessa minimamente, perch
il nostro programma non dovrebbe contenere errori da correggere al momento del
salto al guru/software failure!
Anche i vettori delle istruzioni "TRAP #xx" ci interessano poco. Tali vettori
erano usati in passato per andare in eccezione, ma abbiamo gi visto un modo
pi sicuro, quello tramite sistema operativo.
Comunque, per vostra curiosit, il modo "antico" era:

	move.l	$80.w,OldVector		; Salva il vecchio vettore TRAP #0
	move.l	#SuperCode,$80.w	; Routine da eseguire in supervisor
					; posta nel vettore TRAP #0
	TRAP	#0			; Esegui Supercode come eccezione
	move.l	OldVector(PC),$80.w	; ripristina il vecchio vettore TRAP #0
	rts				; esci, dopo aver eseguito la routine
					; "SuperCode" in supervisor.
OldVector:
	dc.l	0
			
SuperCode:
	movem.l	d0-d7/a0-a6,-(SP)	; Salva i registri nello stack
	...				; istruzioni da eseguire
	...				; come fosse una subroutine....
	movem.l	(SP),d0-d7/a0-a6	; Riprende i registri dallo stack
	RTE	; Return From Exception: come l'RTS, ma per le eccezioni.

Come vedete si pu facilmente capire la funzione dell'istruzione TRAP: in
pratica se si esegue un "TRAP #0" viene eseguita come eccezione la routine
il cui indirizzo  contenuto nella .l all'indirizzo $80, mentre con
l'istruzione "TRAP #1" si esegue quella in $84, e cos via.
Allo stesso modo, gli interrupt contengono l'indirizzo della routine da
eseguire in caso di interrupt. Il modo antico per settare un interrupt era:

	move.l	$6c.w,OldInt6c		; Salva il vecchio int livello 3
	move.l	#MioInt6c,$6c.w		; Mia routine per int livello 3

Alla fine del programma veniva ripristinato il vecchio interrupt in $6c.
In questo interrupt di solito viene messo il "BSR.w MT_MUSIC", dato che
tale interrupt (VERTB) viene eseguito una volta per fotogramma.

	                       _----|         _ _ _ _ _ 
	                        ----|_----|   ]-I-I-I-[ 
	    _ _ _ _ _ _ _----|      | ----|   \ `  ' / 
	    ]-I-I-I-I-[  ----|      |     |    |. ` | 
	     \ `   '_/       |     / \    |    | /^\| 
	      []  `__|       ^    / ^ \   ^    | |*|| 
	      |__   ,|      / \  / ^ ^`\ / \   | ===| 
	   ___| ___ ,|__   / ^  /=_=_=_=\ ^ \  |, `_| 
	   I_I__I_I__I_I  (====(_________)_^___|____|____ 
	   \-\--|-|--/-/  |     I  [ ]__I I_I__|____I_I_| 
	    |[] `    '|_  |_   _|`__  ._[  _-\--|-|--/-/ 
	   / \  [] ` .| |-| |-| |_| |_| |_| | []   [] | 
	  <===>      .|-=-=-=-=-=-=-=-=-=-=-|        / \ 
	  ] []|` ` [] | .   _________   .   |-      <===> 
	  <===>  `  ' ||||  |       |  |||  |  []   <===> 
	   \_/     -- ||||  |       |  |||  | .  '   \_/ 
	  ./|' . . . .|||||/|_______|\|||| /|. . . . .|\_ 
	- --------------------------------------------------- 

*******************************************************************************
*		IL REGISTRO VBR NEI PROCESSORI 68010 E SUPERIORI	      *
*******************************************************************************

Vi starete domandando cosa c'entra il VBR con i vettori. Ebbene, il Vector
Base Register  l'indirizzo di BASE a cui aggiungere gli offset per trovare
l'indirizzo dei vettori. Se il VBR=0, allora l'interrupt livello 3 si trover
all'indirizzo $6c, come nel 68000, e allo stesso modo il TRAP #0 si trover
sempre in $80, e gli esempi visti sopra funzionerebbero.
Ma se il VBR fosse a $10000, l'interrupt di livello 3 si troverebbe non pi
a $6c, ma a VBR+$6c, ossia a $1006c! Lo stesso per tutti gli altri vettori.
Dunque, in linea di massima:

	indirizzo del vettore = VBR + OFFSET

Sul processore 68000 la base  sempre $0000, tanto che non esiste il registro
VBR n l'istruzione privilegiata MOVEC. Ma su 68010 e superiori il VBR pu
essere spostato in altri luoghi, anche in FAST RAM.
Dopo un reset, il VBR  comunque sempre azzerato, sia su A3000 che su A1200
o A4000.  eseguendo il SETPATCH o altre utility che il VBR viene spostato.
Infatti, molte demo/giochi su file che funzionano se fatte partire da sole
da un disco senza caricare prima il setpatch, non funzionano se caricate
dallo shell del workbench. Alcune funzionano ma sono "mute", proprio perch
scrivono il loro interrupt, che suona solo la musica, in $6c, quando invece
il VBR punta pi avanti di $0000.
Dato che sappiamo questo, basta controllare se il processore  un 68000 o
un 68010+, e nel caso sia un 68010 o superiore prelevare il valore del VBR
e sommarlo al vettore che si vuole cambiare.
Ecco come fare in pratica:

	move.l	4.w,a6		; ExecBase in a6
	btst.b	#0,$129(a6)	; Testa se siamo su un 68010 o superiore
	beq.s	IntOK		;  un 68000! Allora la base  sempre zero.
	lea	SuperCode(PC),a5 ; Routine da eseguire in supervisor
	jsr	-$1e(a6)	; LvoSupervisor - esegui la routine
				; (non salva i registri! attenzione!)
	bra.s	IntOK		; Abbiamo il valore del VBR, continuiamo...

;**********************CODICE IN SUPERVISORE per 68010+ **********************
SuperCode:
	movem.l	a0-a1,-(SP)	; Salvo a0 ed a1 nello stack
	dc.l  	$4e7a9801	; Movec Vbr,A1 (istruzione 68010+).
				;  in esadecimale perch non tutti gli
				; assemblatori assemblano il movec.
	lea	BaseVBR(PC),a0	; Label dove salvare il valore del VBR
	move.l	a1,(a0)		; Salvo il valore.
	movem.l	(SP)+,a0-a1	; Ripristino i vecchi valori di a0 ed a1
	RTE			; Ritorna dalla eccezione
;*****************************************************************************

BaseVBR:
	dc.l	0

IntOK:
	move.l	BaseVBR(PC),a0	 ; In a0 il valore del VBR
	move.l	$64(a0),OldInt64 ; Sys int liv 1 salvato (softint,dskblk)
	move.l	$68(a0),OldInt68 ; Sys int liv 2 salvato (I/O,ciaa,int2)
	move.l	$6c(a0),OldInt6c ; Sys int liv 3 salvato (coper,vblanc,blit)
	move.l	$70(a0),OldInt70 ; Sys int liv 4 salvato (audio)
	move.l	$74(a0),OldInt74 ; Sys int liv 5 salvato (rbf,dsksync)
	move.l	$78(a0),OldInt78 ; Sys int liv 6 salvato (exter,ciab,inten)

	movem.l	d0-d7/a0-a6,-(Sp)	; Salva i registri nello stack
	bsr.s	START			; Esegui la routine principale
	movem.l	(sp)+,d0-d7/a0-a6	; Riprendi i registri dallo stack

	move.l	BaseVBR(PC),a0	     ; In a0 il valore del VBR
	move.l	OldInt64(PC),$64(a0) ; Sys int liv1 salvato (softint,dskblk)
	move.l	OldInt68(PC),$68(a0) ; Sys int liv2 salvato (I/O,ciaa,int2)
	move.l	OldInt6c(PC),$6c(a0) ; Sys int liv3 salvato (coper,vblanc,blit)
	move.l	OldInt70(PC),$70(a0) ; Sys int liv4 salvato (audio)
	move.l	OldInt74(PC),$74(a0) ; Sys int liv5 salvato (rbf,dsksync)
	move.l	OldInt78(PC),$78(a0) ; Sys int liv6 salvato (exter,ciab,inten)
	rts

START:
	move.l	BaseVBR(PC),a0	     ; In a0 il valore del VBR
	move.l	#MioInt6c,$6c(a0)	; metto la mia rout. int. livello 3.
	...
	eseguo il programma
	...
	rts

Da notare che anche se si volesse usare l'istruzione TRAP occorrerebbe mettere
in a0 il BaseVbr e fare l'offset $80(a0). Lo stesso dicasi per tutti i vettori.
 presente da questa lezione in avanti una startup nuova, startup2.s, da
includere al posto della startup1.s. L'unica differenza  che contiene le
istruzioni viste sopra ed  diponibile la label BaseVbr per modificare gli
interrupt propriamente su tutti i microprocessori. Il salvataggio dei vecchi
interrupt ed il ripristino alla fine sono fatti dalla startup, assieme al
salvataggio e al ripristino dei canali DMA e di INTENA.
Un'altra modifica  l'aggiunta di una routine che blocca l'input del mouse
e della tastiera al sistema operativo, vedremo che caricando i file servir.
Ora che sappiamo come sostituire un interrupt di sistema con uno nostro,
dobbiamo vedere come fare il nostro interrupt.
Per anticipare le pagine seguenti, suoniamo la musica in interrupt nel listato
esempio Lezione11b.s. Capirete meglio come funziona continuando a leggere!

                                |||||
.__________________________.oOo_/o_O\_oOo.____________________________________.
*******************************************************************************
*		COME SI "COSTRUISCE" UNA ROUTINE DI INTERRUPT		      *
*******************************************************************************

Il sistema delle interruzioni (interrupt) consente ad un dispositivo esterno o
ad un chip custom di interrompere l'esecuzione del processore, per farlo
saltare in modo user alla routine il cui indirizzo si deve trovare in uno
degli autovettori di interruzione (ad esempio $6c).
Questi interrupt hanno dei livelli diversi di PRIORIT, che vanno da un
livello minimo (1) al livello massimo (7). Queste priorit servono nel caso
che si presenti l'occorrenza di una o pi interruzioni durante l'esecuzione
di un interrupt. Se per esempio il programma normale in modo user viene
interrotto da un interrupt di livello basso, diciamo 2, e mentre viene eseguita
questa routine si presenta la richiesta di un interrupt di livello pi alto,
ad esempio 5, l'interrupt 2 viene interrotto a sua volta dall'interrupt 5, con
maggiore priorit, e una volta terminato quest'ultimo interrupt il controllo
torna all'interrupt di livello 2, il quale poi lo ripassa al programma normale
in modo user. In questo modo pi routines interrupt possono rimanere in
attesa di terminare l'esecuzione, e a seconda del loro livello saranno finite
di eseguire prima. La necessit di introdurre gli interrupt fin dai primi
microprocessori  legata al fatto che spesso si utilizza male la potenza
della CPU, a causa di loop di attesa molto lunghi. Se per esempio si dovesse
attendere l'inizio del vertical blank, si dovrebbe fare un loop che controlla
la linea raggiunta, e fino a che tale linea non  raggiunta il processore non
fa altro che bloccarsi in quel ridicolo loop. Se l'attesa per un certo
segnale fosse di qualche secondo, immaginatevi come sarebbe sprecata la potenza
del processore! Per questo esistono gli interrupt, che permettono al processore
di eseguire dei programmi senza preoccuparsi di attendere gli "eventi".
Se si genera un interrupt di livello 3 ogni vertical blank, si potr far
calcolare al processore un frattale o una immagine 3d, e quando si presenter
l'interrupt, al momento del vertical blanking, sar interrotto il calcolo del
solido 3d, verr eseguita la routine da eseguire all'inizio del Vblank, poi
si torner a continuare la routine 3d dove si era lasciata.
Il multitasking stesso  possibile grazie a questo: poter stampare o leggere
dal disk drive mentre si fa qualcos'altro  possibile perch, a differenza
del PC MSDOS, il processore pu svolgere un compito che sar interrotto
al momento giusto dall'interrupt del disco o della porta seriale/parallela,
e che sar ripreso appena eseguiti tali interrupt.
L'Amiga assegna 6 dei 7 livelli di interrupt disponibili, a segnali prodotti
dai chip custom (blitter, copper, cia) in determinate situazioni.
Il settimo livello  usato da schede esterne come la Action Replay, dato che
le linee IPL2-IPL0 che lo generano sono portate alla porta di espansione.
I primi 6 livelli di interrupt sono generati dai chip custom, ad esempio in
occasione del completamento di una blittata o di un vertical blank video.

                                 ||||
                            _A_  /oO\
.__________________________(iIi)_\\//_oOo.____________________________________.
*******************************************************************************
*			COME SI USANO INTENA ED INTENAR			      *
*******************************************************************************

 possibile, tramite il registro INTENA ($dff09a), mascherare alcuni di
questi interrupt, ossia evitare che siano generati.
Esiste anche un registro per richiedere interrupt, l'INTREQ ($dff09c).
Questi registri funzionano come il DMACON ($dff09a), infatti il bit 15 decide
se gli altri bit specificati devono essere settati o azzerati.
Come abbiamo fatto per il DMACON/DMACONR nella lezione 8, vediamo la "mappa"
dei registri INTENA($dff09a) a sola scrittura e INTENAR($dff01c) a sola
lettura:

INTENA/INTENAR ($dff09a/$dff01c)

BIT	NOME	 LIV.	DESCRIZIONE

15	SET/CLR		Bit di controllo "Set/clear". Determina se i bit ad 1
			devono essere azzerati o settati, come in DMACON.
			I bit=0 non saranno n settati nazzerati
14	INTEN		Master interrupt (interrutt. generale di abilitazione)
13	EXTER	6 ($78)	Interrupt esterno, connesso alla linea INT6
12	DSKSYN	5 ($74)	Generato se il registro DSKSYNC corrisponde ai dati
			letti dal disco nel drive.Serve per i loader hardware.
11	RBF	5 ($74)	Buffer UART di ricezione della porta seriale PIENO.
10	AUD3	4 ($70)	Lettura di un blocco di dati del can. audio 3 finita.
09	AUD2	4 ($70)	Lettura di un blocco di dati del can. audio 2 finita.
08	AUD1	4 ($70)	Lettura di un blocco di dati del can. audio 1 finita.
07	AUD0	4 ($70)	Lettura di un blocco di dati del can. audio 0 finita.
06	BLIT	3 ($6c)	Se il blitter ha finito una blittata si setta ad 1
05	VERTB	3 ($6c)	Generato ogni volta che il pennello elettronico 
			alla linea 00, ossia ad ogni inizio di vertical blank.
04	COPER	3 ($6c)	Si pu settare col copper per generarlo ad una certa
			linea video. Basta richiederlo dopo un certo WAIT.
03	PORTS	2 ($68)	Input/Output Porte e timers, connesso alla linea INT2
02	SOFT	1 ($64)	Riservato agli interrupt inizializzati via software.
01	DSKBLK	1 ($64)	Fine del trasferimento di un blocco dati dal disco.
00	TBE	1 ($64)	Buffer UART di trasmissione della porta seriale VUOTO.

Come si pu notare, l'analogia con DMACON/DMACONR  evidente:
-Il bit 15  importantissimo: se esso  acceso allora i bit settati a 1 in
 scrittura nel $dff09A servono ad abilitare i relativi interrupt, se il bit 15
  a 0, allora gli altri bit a 1 nel registro servono a disabilitare, ossia
 a mascherare, i relativi interrupt.
 Per abilitare o disabilitare uno o pi interrupt, come in DMACON,  comunque
 necessario impostare ad 1 i relativi bit; quello che determina se quegli
 interrupt devono venir abilitati o disabilitati  il bit 15: se  ad 1 si
 abilitano, mentre se  a 0 si spengono (sempre indipendetemente dal loro
 precedente stato).
 Diciamo che si sceglie su quali bit OPERARE, poi si decide se attivarli(0) o
 disattivarli(1) in base al bit 15. I bit 0 non sono n settati n azzerati.

 Facciamo un esempio:
		;5432109876543210
	move.w #%1000000111000000,$dff09A ; sono ABILITATI i bits 6,7 e 8
		;5432109876543210
	move.w #%0000000100100000,$dff09A ; sono DISABILITATI i bits 5 e 8.

-Il bit 14 funge da interruttore generale (come fa il bit 9 nel DMACON).
 Lo si pu azzerare ad esempio per disabilitare momentaneamente tutti i
 livelli di interrupt, senza ricorrere all' azzeramento dell'intero registro.

Vi ricorderete della vecchia Lezione3a.s che con un:

	MOVE.W	#$4000,$dff09a	; INTENA - Ferma gli interrupt

Si bloccavano tutti gli interrupt, metre con:

	MOVE.W	#$C000,$dff09a	; INTENA - Riabilita gli interrupt

					;5432109876543210
Si riabilitavano tutti. Ebbene, $4000 = %0100000000000000, ossia si azzera il
bit MASTER, il 14. Invece $c000 = %1100000000000000, cio si riabilita il bit
MASTER, e con esso tutti gli interrupt. In Lezione11b.s per abilitare VERTB:

	move.w	#$c020,$9a(a5)	; INTENA - abilito interrupt "VERTB" del
				; livello 3 ($6c), quello che viene generato
				; una volta al fotogramma (alla linea $00).

		; 5432109876543210
Infatti, $c020 = %1100000000100000 - bit 5, VERTB, settato assieme al MASTER.

Come avrete notato, gli interrupt spaziano dalla gestione della porta seriale
alla lettura della sincronia delle tracce del disk drive, senza risparmiare
n blitter n CIA, n COPPER. Ridefinirsi tutti i livelli di interrupt 
molto pericoloso dal punto di vista della compatibilit, ed  anche molto
difficile e specifico di chi voglia farsi un proprio sistema operativo.
Ai programmatori di demo e giochi interessa soltanto l'interrupt $6c, ossia
quello di livello 3, che riguarda la generazione di interrupt sincronizzati
col pennello elettronico (VERTB - bit 5), o generabili a particolari linee
video con il copper (COPER - bit 4). Pi raramente pu succedere di dover
avere a che fare con interrupt per la gestione della tastiera o altro.
In particolare il caricamento da disco tramite loader hardware  fuori moda,
perch occorre fare giochi e demo installabili su hard disk, per cui gli
interrupt del disk drive non ci interesseranno. Inoltre, anche se voleste fare
un gioco che sfrutti la porta seriale per giocare in doppio su 2 computer
collegati via cavo o via modem, sarebbe meglio usare le chiamate legali da
sistema operativo del SERIAL.DEVICE, anzich farsi degli interrupt poco
compatibili con eventuali schede multiseriali o nuovo hardware.
				 .
				      
				 :    :
				     
				_|    l_
				\      /
				 \    /
				  \ _!_
				   \/

*******************************************************************************
*			COME SI USANO INTREQ ED INTREQR			      *
*******************************************************************************

In Lezione11b.s abbiamo visto anche INTREQ/INTREQR. Di cosa si tratta?
Avrete notato come  strutturato l'interrupt $6c:

MioInt6c:
	btst.b	#5,$dff01f	; INTREQR - il bit 5, VERTB,  azzerato?
	beq.s	NointVERTB		; Se si, non  un "vero" int VERTB!
	movem.l	d0-d7/a0-a6,-(SP)	; salvo i registri nello stack
	bsr.w	mt_music		; suono la musica
	movem.l	(SP)+,d0-d7/a0-a6	; riprendo i reg. dallo stack
nointVERTB:	 ;6543210
	move.w	#%1110000,$dff09c ; INTREQ - cancello rich, BLIT,COPER,VERTB
				; dato che il 680x0 non la cancella da solo!!!
	rte	; uscita dall'int COPER/BLIT/VERTB


NOTA: INTREQR  la word $dff01e/1f. In questo caso agiamo sul suo byte $dff01f
      anzich su $dff01e, ma si tratta sempre del byte basso di INTREQR.

La mappa di INTREQ/INTREQR  uguale a quella di INTENA/INTENAR:

INTREQ/INTREQR ($dff09c/$dff01e)

BIT	NOME	 LIV.	DESCRIZIONE

15	SET/CLR		Bit di controllo "Set/clear". Determina se i bit ad 1
			devono essere azzerati o settati, come in DMACON.
			I bit=0 non saranno n settati nazzerati
14	INTEN	6 ($78)	interrupt livello 6 CIAB
13	EXTER	6 ($78)	Interrupt esterno, connesso alla linea INT6
12	DSKSYN	5 ($74)	Generato se il registro DSKSYNC corrisponde ai dati
			letti dal disco nel drive.Serve per i loader hardware.
11	RBF	5 ($74)	Buffer UART di ricezione della porta seriale PIENO.
10	AUD3	4 ($70)	Lettura di un blocco di dati del can. audio 3 finita.
09	AUD2	4 ($70)	Lettura di un blocco di dati del can. audio 2 finita.
08	AUD1	4 ($70)	Lettura di un blocco di dati del can. audio 1 finita.
07	AUD0	4 ($70)	Lettura di un blocco di dati del can. audio 0 finita.
06	BLIT	3 ($6c)	Se il blitter ha finito una blittata si setta ad 1
05	VERTB	3 ($6c)	Generato ogni volta che il pennello elettronico 
			alla linea 00, ossia ad ogni inizio di vertical blank.
04	COPER	3 ($6c)	Si pu settare col copper per generarlo ad una certa
			linea video. Basta richiederlo dopo un certo WAIT.
03	PORTS	2 ($68)	Input/Output Porte e timers, connesso alla linea INT2
02	SOFT	1 ($64)	Riservato agli interrupt inizializzati via software.
01	DSKBLK	1 ($64)	Fine del trasferimento di un blocco dati dal disco.
00	TBE	1 ($64)	Buffer UART di trasmissione della porta seriale VUOTO.


A cosa serve un registro per la richiesta di interrupt?
A richiedere interrupt, naturalmente. E anche a DISDIRLI, dato che una volta
che viene richiesto un interrupt, automaticamente (dai chip custom) o
manualmente (dal nostro programma), viene eseguito tale interrupt, ma non
viene cancellata la "richiesta", per cui alla fine di ogni interrupt occorre
cancellare gli interrupt gi svolti dalla lista degli interrupt da fare.

L'INTREQ ($dff09c)  usato dal 680x0 per forzare l'esecuzione di un interrupt,
di solito l'interrupt software, oppure dal COPPER per eseguire l'interrupt
COPER ad una certa linea video. Naturalmente una volta settata una richiesta
di interrupt, se tale interrupt non  abilitato in INTENA si pu attendere
anche una vita.
Quando un bit settato in INTREQ  contemporaneamente settato anche in INTENA
si verifica l'interrupt corrispondente a quel bit.
Attenzione alla particolarit che se il bit 14 di INTREQ  settato si
verifica un interrupt di livello 6 (a patto che il corrispondente bit in
INTENA, Master Enable, sia anch'esso settato)
Altrimenti  usato per cancellare i bit di richiesta degli interrupt gi
eseguiti, dato che le richieste di interrupt non sono azzerate automaticamente.
Occorre stare attenti a questo fatto, perch se si dimentica di cancellare i
bit di richiesta alla fine di ogni interrupt eseguito, il processore lo
eseguir nuovamente!! Ora dovreste capire la parte finale dell'interrupt:

		 ;6543210
	move.w	#%1110000,$dff09c ; INTREQ - cancello rich. BLIT,COPER,VERTB
				; dato che il 680x0 non la cancella da solo!!!
	rte	; uscita dall'int COPER/BLIT/VERTB


l'INTREQR ($dff01e)  a sola lettura, al contrario di INTREQ che  a sola
scrittura. Serve per sapere quale chip ha richiesto l'interrupt. Infatti, se
viene eseguito l'interrupt di livello 3 ($6c), pu essere "colpa" del blitter,
del vertical blank o del copper. Testando i bit di INTREQR capiamo quale di
queste 3  la causa, e determiniamo quale routine eseguire, o se eseguire la
routine, nel caso ci interessi solo una di queste 3 eventualit. Il bit 15
non ha significati nell'INTREQR, dato che  il Set/Clr.
Rivediamo ora l'utilizzo fatto in Lezione11b.s:

	btst.b	#5,$dff01f	; INTREQR - il bit 5, VERTB,  azzerato?
	beq.s	NointVERTB	; Se si, non  un "vero" int VERTB!

In questo caso, dato che il BTST su un indirizzo pu essere solo .BYTE, 
testato il $dff01f, ossia il byte basso della word, anzich il $dff01e.
Nel caso che si salti a Noint,  evidente che l'interrupt  stato generato
dal copper o dal blitter, e sarebbero settati i bit 4 o 6. A tal proposito
occorre disdire anche queste richieste di interrupt, per evitare che ogni
microsecondo si ritorni ad eseguire l'interrupt per nulla:

		 ;6543210
	move.w	#%1110000,$dff09c ; INTREQ - cancello rich. BLIT,COPER,VERTB
				; dato che il 680x0 non la cancella da solo!!!
	rte	; uscita dall'int COPER/BLIT/VERTB

Vi sembrer strano che, nonostante sia abilitato con INTENA soltanto VERTB,
possa succedere che vengano richiesti ed ESEGUITI interrupt di COPER o BLIT.
Effettivamente non dovrebbero essere richiesti, n eseguiti...
Ma per motivi legati probabilmente alla MMU o alla velocit del processore,
su computer pi veloci del 1200 base, come l'A4000, pu accadere benissimo,
e ci infatti causa dei problemi a delle demo, anche alcune recenti per AGA.
Infatti su A1200 base pu succedere che l'interrupt funzioni anche senza il
btst del bit VERTB, ma su A4000 o A1200 turbizzato non  cos. Ammetto che
"in teoria" dovrebbe funzionare, ma il fatto  che molte demo per A1200, se
eseguite su A4000 suonano la musica 2 volte per fotogramma, e sono ridicole.
Dunque siate categorici nel testare sempre i bit di intreq prima di eseguire
l'interrupt, anche se sul vostro computer funziona tutto, o vi troverete con
un gioco/demo che fa le bizze su a4000 e company.

Per riassumere, ecco le cose da fare per settare un nostro interrupt:

- Ottenere l'indirizzo del VBR, salvare il vecchio interrupt e ripristinarlo
  prima di uscire. Questo compito  svolto bene da startup2.s, non c'
  problema: l'indirizzo del VBR  nella label BaseVBR.
- Azzerare tutti gli interrupt con INTENA. Anche questo compito  svolto
  dalla startup2.s, con un MOVE.W #$7fff,$9a(a5).
- Mettere l'indirizzo del nostro interrupt nell'autovettore giusto.
- Abilitare solo l'interrupt, o gli interrupt che ci servono

Ed ecco cosa ricordarsi di mettere nella nostra routine interrupt:

- Salvare e ripristinare tutti i registri con un bel MOVEM, dato che se si
  "sporcasse" qualche registro, immaginatevi alla fine dell'interrupt cosa
  succederebbe, quando si torna ad eseguire un programma interrotto in
  chiss quale situazione e con chiss quali valori nei registri!
- Testare subito il $dff01e/1f (INTREQR), per sapere chi o che cosa ha
  generato un interrupt di quel livello. Per esempio, un interrupt di livello
  3 pu essere generato da COPER, VERTB o BLITTER; un interrupt di livello 4
  da AUD0, AUD1, AUD2 o AUD3, eccetera. Attenzione al fatto che anche se alle
  volte sembra funzionare senza questo test, su A4000 o simili andr tutto
  sfasato come se la CPU fosse ubriaca (pu essere un effeto speciale, pero!)
- Cancellare i bit di INTREQ ($dff09c) che hanno causato l'interrupt eseguito,
  dato che non sono cancellati automaticamente. Se ci si dimentica di fare
  questo il processore avr la richiesta fissa di interrupt e sar eseguito
  continuamente.
- Terminare l'interrupt con un RTE, come si termina una subroutine con RTS.

Alla luce di queste considerazioni, vi ripropongo il primo interrupt:

MioInt6c:
	btst.b	#5,$dff01f	; INTREQR - il bit 5, VERTB,  azzerato?
	beq.s	NointVERTB		; Se si, non  un "vero" int VERTB!
	movem.l	d0-d7/a0-a6,-(SP)	; salvo i registri nello stack
	bsr.w	mt_music		; suono la musica
	movem.l	(SP)+,d0-d7/a0-a6	; riprendo i reg. dallo stack
nointVERTB:	 ;6543210
	move.w	#%1110000,$dff09c ; INTREQ - cancello rich, BLIT,COPER,VERTB
				; dato che il 680x0 non la cancella da solo!!!
	rte	; uscita dall'int COPER/BLIT/VERTB

                                  ||||   
                              <---/oO\-- 
._________________________________\--/________________________________________.
*******************************************************************************
*		GLI INTERRUPT E IL SISTEMA OPERATIVO			      *
*******************************************************************************

Il 680x0 ha solo 7 livelli di INTERRUPT, ma allora come  possibile che gli
interrupt in pratica siano 15? Ebbene, il chip Paula si occupa di dividere
in pseudointerrupt i 7 livelli "reali" di interrupt. Per esempio fa saltare
all'interrupt di livello 3 in tre casi: COPER, VERTB e BLIT, e l'unico modo
di sapere quale di queste tre eventualit ha generato l'interrupt  di
consultare un registro collegato al chip Paula stesso, ossia INTREQR!
D'altronde, essendo solo 7 i livelli di interrupt "reali" del 680x0, non 
possibile che interrupt "sdoppiati" da Paula dello stesso livello possano
interrompersi tra loro. Mentre un interrupt di livello 5, come il DSKSYNC,
pu interrompere l'esecuzione di un'interrupt di livello 3, come COPER, non
 possibile per BLIT interrompere COPER, anche se ha "priorit Paulesca"
maggiore, dato che si trovano nello stesso livello fisico del 680x0.
Per questo se durante l'esecuzione di un interrupt si verifica la richiesta
di interrupt di un'altro psoudolivello di Paula nello stesso livello 680x0,
come ad esempio uno BLIT mentre si esegue un COPER, al termine dell'int
che esegue il COPER verr eseguito subito un'altra volta l'interrupt di
livello 3, questa volta eseguendo la routine per COPER (secondo il btst fatto
sull'INTREQR verr identificato quale tipo di "sottoint" eseguire).
Ecco le priorit dei livelli di interrupt nel sistema operativo, ossia
nell' Exec.library, che come vedete segue la priorit hardware:


	livello 1: ($64)	MINIMA PRIORIT

	1	buffer di trasmissione vuoto	TBE
	2	blocco del disco trasferito	DSKBLK
	3	interrupt software		SOFTINT

	livello2: ($68)

	4	porte esterne INT2 & CIAA	PORTS

	livello3: ($6c)

	5	copper				COPER
	6	intervallo di vertical blank	VERTB
	7	blittata finita			BLIT

	livello4: ($70)

	8	canale audio 2			AUD2
	9	canale audio 0			AUD0
	10	canale audio 3			AUD3
	11	canale audio 1			AUD1

	livello5: ($74)

	12	buffer di ricezione pieno	RBF
	13	sync del disco trovata		DSKSYNC

	livello6: ($78)		MASSIMA PRIORIT

	14	external INT6 & CIAB		EXTER
	15	speciale (abilitazione master)	INTEN

	livello7: ($7c) (schede esterne come la Action Replay)

	-	interrupt non mascherabile	NMI

Il fatto che il sistema operativo gestisca sue routines interrupt rende pi
pericolosa la sostituzione di alcuni di essi da parte nostra.
Per quanto riguarda la priorit 6, la graphics.library usa l'interrupt del
timer CIAB Time Of Day (TOD) per controllare lo schermo.
Nella priorit 5, DSKSYNC  usato dal TrackDisk e RBF dal serial.device.
Nel livello 4, ci sono i canali audio, usati dall'audio.device.
Nel livello 3, l'interrupt BLIT, che avviene quando il blitter ha finito una
sua operazione, spesso vengono messe routines che si occupano di riutilizzare
i dati appena scritti dal blitter, per evitare di perdere tempo.
Nel livello 2, del chip CIAA il Timer.device usa l'interrupt TimerA per
l'handshake di tastiera, il TimerB per il timer a microsecondi, e l'interrupt
TOD alarm a 50/60Hz. Esiste anche l'INT2 per eventuali schede hardware
esterne.
Nel livello 1, il pi basso, l'interrupt TBE  usato dal Serial.device,
l'interrupt DSKBLK  usato dal TrackDisk.device. Gli interrupt SOFTINT, ossia
software, sono definibili via sistema operativo, ad esempio con la funzione
Cause dell'Exec o facendo una message port di tipo SOFT_INT.

*******************************************************************************
*		GLI INTERRUPT COPER CHIAMATI DA COPPERLIST		      *
*******************************************************************************

Se si vuole chiamare l'interrupt COPER del livello 3 ($6c) ad una certa linea
video, basta scrivere $8010 nell'intreq ($dff09c), dopo un wait che aspetti
quella linea video:

COPPERLIST:
	dc.w	$100,$200	; BPLCON0 - no bitplanes
	dc.w	$180,$00e	; color0 BLU
	dc.w	$a007,$fffe	; WAIT - attendi la linea $a0
	dc.w	$9c,$8010	; INTREQ - Richiedo un interrupt COPER, il
				; quale fa agire sul color0 con un "MOVE.W".
	dc.w	$FFFF,$FFFE	; Fine della copperlist

Infatti il valore $8010 = $8000 + %10000, ossia si setta il bit 4, COPER.

Vediamo in Lezione11c.s un esempio pratico.

Naturalmente l'interrupt si pu chiamare anche a diverse linee, ogni volta
cambiando "effetto". Verifichiamolo in Lezione11d.s

Data la particolare complessit degli interrupt, per ora non faremo esempi
ulteriori, riguardo ad interrupt dei dischi, della porta seriale ecc.
Alle applicazioni che ci interessano, ossia DEMO e GIOCHI, bastano spesso solo
i due tipi di interrupt di livello 3 ($6c) che abbiamo visto, ossia il VERTB,
che viene eseguito ogni fotogramma, e il COPER, richiamabile dal copper a una
qualsiasi linea video.
Le applicazioni degli altri interrupt saranno commentate man mano che saranno
trovate nei listati esempio, dato che spaziano in ogni campo!
Per ora, possiamo anticipare l'uso di tutti i livelli di interrupt:
nei listati Lezione11e.s e Lezione11f.s sono ridefiniti TUTTI gli interrupt,
e sono ablitati TUTTI i livelli, ma naturalmente ci sono routines solo nel
livello 3. Questo esempio pu essere utile come "partenza" per definirsi un
qualsiasi livello interrupt: potete "ritagliare" il livello che vi interessa
e metterci le routines "dentro".

             .   .                   .         .               .    .
        .              .             .       .           .               .
   .             .            .  .       .        .
       .           .    ,----|     .   _ _ _ _ _           .      .
            .         . `----|,----|   ]-I-I-I-[       .     .   _ _ _  _ _ _
     _ _ _ _ _ _ ,----|      |`----| . \_`_ '__/      .          ]-I-I--I-I-[
     ]-I-I-I-I-[ `----|  .   |     |    |. `  |.                  \_`__  '_/
      \ `   '_/       |     /^\  . |    | /\ |           .        |~_ [],|
       [] `__|       |    /  ^\   |    | |_| |     _ _ _  _ _ _  _|______|_
       |__   ,|      /^\  /  ^  \ /^\   | === |     I-I-I--I-I-I <=-=-==-=-=>
    ___| ___ ,|__   /-=-\/=_=_=_=\-=-\  |, `_ |     \ ` `  ' ' /  \__   _'_/
   (__I__I_I__I_ ) (====(_________)___)_| ___ |__    | $te |    |.   _ |
    \-\--|-|--/-/  |     I  [ ]  I  (  I|_I I |I )  _|____ ___|_   |   _  |
     |[] `    '|_  |_   _|`__  ._[  _\ \  | | / /  <=-=- F -=-=>  |`    '|
    / \  [] ` .| |-| |-| |_| |_| |_| | []   [] |  /\\__ ___ ___/   | '    |
   <===>      .|-=-=-=-=-=-=-=-=-=-=-|  , ,   / \/  \|.    .  | /\ |[]    |
   |[ ]|` ` [] | .   _________   .   |-    , <=======|u$N|<==>|'   __|
   <===>  `  ' ||||  |       |  |||  |  []   <=======|        ||  || '  I-|
    \T/     -- ||||  | Oi!  |  |||  | .  '   \T/--T-T-T-T-T-T|T-T||__.   |
  __/|\   .   .||||| |       | ||||  |. . . /|\__|_|_|_|_|_|||_|/ O!`4\_
   : \       ||||! ! _o ,  |  ||!  |       / | \ ! ! ! | !.!|  /         
       \      !||!   //\/   |  |!   |       \ | /       !   !| /      :
     :          `!   '/\     !  !    !        \!/             !      ____
___ _|_______________/ /_  /\________________________________________\  //_ ___
 | \/  \// 
     :                                                                 

*******************************************************************************
* INFORMAZIONI AVANZATE SUL COPPER - USO DEL SOLO COLOR0 ($180)-NO BITPLANES  *
*******************************************************************************

Come recita il titolo, ora vedremo le applicazioni possibili usando solo una
copperlist senza bitplanes. Ossia creare con soli WAIT e MOVE dei disegni o
delle animazioni.
Naturalmente, se aggiungete bitplanes potete "incrociare" e "sovrapporre"
questi effetti, modificando molte volte per copperlist il color2 o il color3,
oltre al color0.
Per iniziare, per, occorre spiegare delle cose che non sono ancora state
trattate. Si tratta del "tempo" impiegato dal copper per eseguire un suo
comando MOVE. Abbiamo gi visto come cambiare l'intera palette, anche di 32
colori, ad una certa linea video, per far comparire su schermo qualche
centinaio di colori, col solo "32" o "16" colori ufficialmente settati nel
BPLCON0. Ebbene, in una linea siamo riusciti a cambiare 32 colori, ossia ad
eseguire 32 MOVE del copper:

	dc.w	$180,xxx	; 1 move nel color0
	dc.w	$182,xxx	; 1 move nel color1
	...			; ecc.

Ebbene, in realt se cominciamo a cambiare i colori dalla posizione
orizzontale $07, o anche $01, tutti i 32 colori saranno effettivamente
cambiati solamente verso la met dello schermo, perch ogni move richiede
8 pixel lowres per essere eseguito. Per questo conviene sempre cambiare i
colori 1 linea prima che inizi veramente il disegno. Se mettessimo una
conquantina di MOVE di seguito, arriveremmo con l'ultima alla linea sotto!
D'altronde, sarebbe materialmente impossibile eseguire decine di move in meno
di 1 cinquantesimo di cinquantesimo di secondo!
Comunque possiamo usare questa apparente limitazione per i nostri scopi, ad
esempio per cambiare colore in senso orizzontale ogni 8 pixel, senza usare
i WAIT, ma semplicemente affiancando una cinquantina di COLOR0 per linea.
Vediamo un esempio pratico di questo in Lezione11g1.s
Un utilizzo di questo listato potrebbe essere quello di cambiare il $182, e
non il $180, in uno schermo a 1 bitplane: in questo modo le eventuali "scritte"
in sovraimpressione sarebbero sfumate da snistra verso destra anzich
dall'alto verso il basso, come facciamo di solito con la copperlist.

In Lezione11g2.s e Lezione11g3.s ci sono versioni pi colorate di questo
effetto, tanto che possono essere una base per degli effetti "PLASMA".

A proposito, se "roteassimo" o "ciclassimo" i colori di una linea di questo
tipo cosa avremmo? Un effetto molto noto, usato dalle intro gi negli albori
dell'Amiga: vediamo l'effetto "supercar" in Lezione11g4.s

Forse ciclare solo 2 linee non esalta. Vediamo di ciclarne di pi, magari
facendo un effetto di tipo "annodato", in Lezione11g5.s

Un'altra "fantasia" che sfrutta l'"affollamento" di colorXX messi di seguito,
nell'esempio Lezione11g6.s

Ora vediamo un modo un p "economico" per fare un simil-plasma: anzich
cambiare i contenuti dei molti color0, mettiamo un wait all'inizio di ogni
linea, ognuna delle quali conterr 52 color0: possiamo spostare la linea a
destra e a sinistra semplicemente cambiando la posizione orizzontale dei
vari wait! In pratica questo  in Lezione11g7.s

* USO DELLA COPPER2 (COP2LC/COPJMP2):

Avrete notato che oltre al $dff080 e il $dff088 per puntare e far partire la
copper 1, esistono il $dff084 e il $dff08a per puntare e far partire la
copper2. Ma come funziona la copper2? E in che cosa pu servirci?
Ogni inizio frame il copper fa partire la copper 1, il cui indirizzo viene
letto dal $dff080.
Noi a volte lo facciamo partire "al volo", senza attendere nemmeno la fine del
frame, scrivendo nel COPJMP1, ossia $dff088.
Se mettessimo una copperlist nel $dff084, (COP2LC), dovremmo anche farla
partire scrivendo nel COPJMP2 ($dff08a).
Ma alla fine del frame ripartirebbe la copper1.
Ora, questa caratteristica pu servire per fare diverse copperlist a cui
saltare, come facciamo per le istruzioni 680x0 con i "JMP".
Per esempio, se volessimo eseguire fino a met schermo la copper1, dopodich
saltare ad eseguire l'altra met dalla copper 2, basterebbe puntare la
copper 2 all'inizio, e farla partire dalla copperlist tramite copjmp2:


	move.l	#copper1,$dff080	; COP1LC
	move.l	#copper2,$dff084	; COP2LC
	...
	
	section	copperissime,data_C

copper1:
	...	; istruzioni varie...
	dc.w	$a007,$fffe	; Aspetta linea $a0
	dc.w	$8a,0		; COPJMP2 - fai partire la copper 2


copper2:
	...	; istruzioni varie
	dc.w	$ffff,$fffe	; Fine della copperlist, si ripartir con
				; la copper1!


Il copper, arrivato al "dc.w $8a,0" salta (come BRA o JMP) a copper2, a patto
che questa fosse messa in $dff08a preventivamente. Da notare che SALTA, e non
fa come un "bsr", per cui non torna mai sotto il "dc.w $8a,0" della copper1.
Vediamo ora un paio di utilizzi pratici della copper2. Uno  quello di fare
le cosiddette copper dinamiche, ossia composte da 2 copperlist scambiate
ogni fotogramma, come un "double buffering" dei bitplane. Questo serve per
rendere pi "lisce" le sfumature, infatti se scambiate 2 colori ogni
fotogramma, avrete un'effetto tipo l'interlace, che far "vedere" il colore
intermedio.
Basta prepararsi 2 copperlist con la stessa sfumatura, ma un poco "sfasata", e
scambiarle continuamente.

Vediamo in pratica una Dynamc Cop in Lezione11h1.s
Avete notato la differenza? Si pu spacciare per una sfumatura AGA!
E dire che le copper dinamiche sono state usate in pochi giochi, nonostante
non siano poi cos difficili da fare. Tra i giochi che hanno copper dinamiche
devo ricordare AGONY, e il recente SHADOW FIGHTER, il picchiaduro italiano
dei NAPS TEAM.

Ora vediamo un'altra applicazione della copper2. Anzich scambiare un paio
di copperlist, possiamo ciclarne qualche decina, per cui possiamo dire che
si pu "precalcolare" un effetto copper calcolando 1 copperlist per ogni
"fase" dell'effetto: dato che poi l'effetto sar ciclico, baster puntare
ogni volta alla copperlist "dopo".
In questo modo otteniamo l'effetto copper, ma in termini di tempo risparmiamo
TOTALMENTE quello che sarebbe stato usato dalle routines 68000! Per cui si pu
dire che l'effetto copper in questione  "GRATIS", e possiamo farci girare
sopra una routine che mangia tutto il resto del tempo.

Vediamo una routine "normale", in Lezione11h2.s, e la versione che precalcola
i fotogrammi "copper", ossia Lezione11h3.s. L'unico inconveniente della
versione "turbizzata/precalcolata"  che serve della memoria aggiuntiva
per memorizzare tutte le copperlist-frames.

Visto che volete superare l'esame di copper livello 2, dovete sapere anche
che si pu "mascherare" la coordinata Y dei WAIT. In pratica, un wait con
la Y mascherata  cos:

	dc.w	$0007,$80FE	; Wait ad Y "mascherata"

E significa: non controllare la linea Y, ma aspetta la posizione $07 X della
linea attuale.  un WAIT "handicappato", che non sa leggere la posizione Y.
In realt non sa leggere i 7 bit bassi della posizione Y, per cui funziona
dopo la linea $80. Ma allora, a cosa ci serve un wait mascherato che non
controlla la posizione Y prima della posizione Y $80?
Se avessimo da muovere una mitica barretta, di quelle della lezione 3,
dovremmo cambiare tutti i wait che la compongono. Se invece mettiamo un wait
normale all'inizio, e sotto tutti wait mascherati, baster cambiare il primo
wait e gli altri "seguiranno". Il risparmio di istruzioni 680x0  evidente:
con un solo add/sub si sposta un'intera barretta.

Vediamo un'implementazione in Lezione11h4.s (mitica barretta di Lezione3!)

Da notare che funziona anche sotto la linea verticale $FF, dato che riparte
la numerazione da $00. Ora che lo sapete, se vi capita di spostare qualche
cosa in quella zona potete sfruttare questo trucchetto.

Ora si potrebbe parlare dell'istruzione SKIP, ma dato che non la ho mai vista
usare da nessuno, e io stesso non vedo a cosa possa servire (si possono fare
tranquillamente tutte le cose usando la copper2 per i salti...), tralascio
questo argomento, spero crediate nella totale inutilit di tale comando.

Ora, per finire l'argomento "SOLO COPPER SENZA BITPLANES", vi propongo 6
listati che riassumono i pi comuni effetti di questo tipo.

Lezione11i1.s  uno scroll di colori a tutto schermo

Lezione11i2.s  una pseudo parallasse a 3 livelli di barre. Pu servire come
sfondo per un gioco platform durante una "salita", ad esempio.

Lezione11i3  una fantasia in COP minore...

Lezione11i4.s  una sfumatura pseudocasuale, che mixa i valori della posizione
orizzontale del pennello elettronico (di solito valori diversi), per farne
i colori della sfumatura copper.

Lezione11i5.s  una copperlist che cicla i colori in un modo che sembri 3d.

		                 .......
		              .::::::::::.
		       _______j::'__ __:::,__
		     _/     /. .-^-.-^-.\ \_
		    /        \_ |  |  |_/    \
		   /      __  T `---'\--'!      \
		  /     __/`  |   _ \ \       \
		 /     _/     | _/\ _/ _,\       \
		(      '\     | `   Y   \       \
		 \       \    l____________/        \
		  \      7________________\_        /
		   \     l       ____      (       /
		    \_____\       T       /______/
		     /    \        |       /    /
		    C _ _ ( __     |   __ ( _ _(
		      T         |      T  
		      /           \      \ xCz
		  ___/_______________\_______\__
		 (________________)_____________)


*******************************************************************************
*	INFORMAZIONI AVANZATE SUL COPPER - ANCHE I BITPLANES ABILITATI	      *
*******************************************************************************

Avete visto che con i soli move&wait del copper possiamo fare un bel p di
cose? Ma cosa succede se facciamo copperlist complicate con i bitplanes
abilitati? Possiamo cambiare il bplmod ad ogni linea per allungare le figure,
o usare il bplcon1 ($dff102) per ondeggiarle, o addirittura cambiare ad ogni
linea i puntatori ai bitplanes!!!

Nei listati seguenti, tra l'altro,  usato un sistema particolare per
calcolare il diwstart/diwstop, e ddfstart/ddfstop, ossia tramite alcuni
EQUATE, che sono usati per calcolare i valori grazie agli operatori di
shift "<<" e ">>", nonch "&" (and), e i comuni "*","/","+","-".
Se volete fare uno schermo in 320*256 normale, si fa prima a mettere i
valori normali, o cambiarli a mano. Se invece voleste fare uno schermo di
grandezza particolare, ad esempio 256*256, pu risparmiare tempo.


scr_bytes	= 40	; Numero di bytes per ogni linea orizzontale.
			; Da questa si calcola la larghezza dello schermo,
			; moltiplicando i bytes per 8: schermo norm. 320/8=40
			; Es. per uno schermo largo 336 pixel, 336/8=42
			; larghezze esempio:
			; 264 pixel = 33 / 272 pixel = 34 / 280 pixel = 35
			; 360 pixel = 45 / 368 pixel = 46 / 376 pixel = 47
			; ... 640 pixel = 80 / 648 pixel = 81 ...

scr_h		= 256	; Altezza dello schermo in linee
scr_x		= $81	; Inizio schermo, posizione XX (normale $xx81) (129)
scr_y		= $2c	; Inizio schermo, posizione YY (normale $2cxx) (44)
scr_res		= 1	; 2 = HighRes (640*xxx) / 1 = LowRes (320*xxx)
scr_lace	= 0	; 0 = non interlace (xxx*256) / 1 = interlace (xxx*512)
ham		= 0	; 0 = non ham / 1 = ham
scr_bpl		= 1	; Numero Bitplanes

; parametri calcolati automaticamente

scr_w		= scr_bytes*8		; larghezza dello schermo
scr_size	= scr_bytes*scr_h	; dimensione in bytes dello schermo
BPLC0	= ((scr_res&2)<<14)+(scr_bpl<<12)+$200+(scr_lace<<2)+(ham<<11)
DIWS	= (scr_y<<8)+scr_x
DIWSt	= ((scr_y+scr_h/(scr_lace+1))&255)<<8+(scr_x+scr_w/scr_res)&255
DDFS	= (scr_x-(16/scr_res+1))/2
DDFSt	= DDFS+(8/scr_res)*(scr_bytes/2-scr_res)

Poi, in copperlist metteremo:

	dc.w	$8e,DIWS	; DiwStrt
	dc.w	$90,DIWSt	; DiwStop
	dc.w	$92,DDFS	; DdfStart
	dc.w	$94,DDFSt	; DdfStop
	dc.w	$100,BPLC0	; BplCon0

Comunque, non  "infallibile", se volete fare schermi di grandezze strane
potrebbe non funzionare, e sar meglio andare "a mano".
Si pu anche usare per calcolare il valore, controllandolo dopo
l'assemblaggio con "? DIWS" o "? xxxx", poi scrivere il valore a mano.
Ecco i listati di questa sezione:

Lezione11l1.s - cambia ad ogni linea sia il color0 che il bplcon1 ($dff102),
causando l'ondulazione dei bitplanes.

Lezione11l2.s - Si cambiano per ogni linea ben 3 colori su 4 (2 bitplanes).

Lezione11l3a.s, Lezione11l3b.s e Lezione113c.s sono 3 passaggi per arrivare
a fare l'effetto di ondeggiamento del logo AMIGA ET della piccola demo presente
nel disco 1. A forza di pezzi la abbiamo descritta tutta!
La figura ondeggia grazie ai moduli negativi alternati a quelli azzerati.

Lezione11l4.s - Questo  un'altro modo per ondeggiare: sono ridefiniti i
bplpointers ad ogni linea!

Lezione11l5.s - Se aveste una piccola immagine larga 40*29 pixel, e voleste
riempirci lo schermo intero cosa potreste fare? Provare a fare uno zoom di
8 volte, trasformandola in 320*232.  quello che fa questo listato, usando
per l'allungamento vertivale i modulo, per quello orizzontale una routine
che testa ogni bit e lo "trasforma" in un byte (8 bit).

Lezione11l5b.s -  una versione ottimizzata del listato precedente, che usa
una tabella contenente le 256 possibili combinazioni di un byte "espanso" a
8 byte. Richiede meno della met di tempo per eseguire! Imparate a farvi
questo tipo di ottimizzazioni: routines che sembrerebbero impossibili da
rendere pi veloci, alle volte possono subire una turbizzazione cos!

* COME FARE UNO SCHERMO IN INTERLACE (lungo 512 linee)

Il modo interlacciato permette di visualizzare il doppio di dati video.
Questo  possibile raddoppiando il numero di linee visualizzate.
Normalmente sono posibili 256 linee verticali, mentre con l'interlace si pu
arrivare a 512, sia in lowres che in hires.
Ci sono per delle particolarit, infatti non basta puntare i bitplanes
e settare il bit di interlace (il bit 2 del bplcon0).
Per quanto riguarda la figura RAW, basta convertire un normale disegno
interlacciato con l'iffconverter e salvarlo, ossia una figura in 320x512 o
in 640x512. Anche un brush pi piccolo va bene, ma considerate sempre che
l'immagine non deve risultare "schiacciata" per la doppia risoluzione
verticale. Come  noto l'interlacciato "sfarfalla", "traballa".
Questo  un male, ma anche un bene, infatti su normali TV o monitor non
sarebbe possibile una risoluzione verticale superiore a 256 linee, occorrerebbe
un monitor "VGA", ossia multisync o multiscan.
Il "trucchetto"  fatto visualizzando una volta solo le 256 linee dispari, e
l'altra le altre 256 linee pari.
Lo scambio avviene ogni frame per cui inganna decentemente l'occhio, a parte
lo sfarfallio, (che, se sono scelti bene i colori, diminuisce molto).
Questo scambio per non  del tutto "automatico", occorre fare qualcosina
noi "a mano".

  +-----------------------------------+----------------------------------+
  |    QUADRO 1 (linee dispari)       |        QUADRO 2 (linee pari)     |
  +-----------------------------------+----------------------------------+
  | LINEA 1: ---> xxxxxxxxxxxx        |                                  |
  |                                   |       LINEA 2: ---> xxxxxxxxxxxx |
  | LINEA 3: ---> xxxxxxxxxxxx        |                                  |
  |                                   |       LINEA 4: ---> xxxxxxxxxxxx |
  | LINEA 5: ---> xxxxxxxxxxxx        |                                  |
  |                                   |       LINEA 6: ---> xxxxxxxxxxxx |
  | LINEA 7: ---> xxxxxxxxxxxx        |                                  |
  |                                   |       LINEA 8: ---> xxxxxxxxxxxx |
  | LINEA 9: ---> xxxxxxxxxxxx        |                                  |
  |                                   |       LINEA 10: --> xxxxxxxxxxxx |
  |          [...]                    |                                  |
  |                                   |                 [...]            |
  | LINEA 311: -> xxxxxxxxxxxx        |                                  |
  |                                   |       LINEA 312: -> xxxxxxxxxxxx |
  | LINEA 313: -> xxxxxxxxxxxx        |                                  |
  +-----------------------------------+----------------------------------+

Per il modo interlacciato occorre ridefinire il modulo mettendolo a 40 se
siamo in lowres, o a 80 se siamo in hires. In pratica occorre mettere nel
modulo la lunghezza di una linea, per saltarla: essendo il modulo un valore
che viene aggiunto alla fine di ogni linea video, se saltiamo la lunghezza
di una intera linea ecco cosa succede: viene letta e visualizzata la prima
linea, alla fine viene saltata la seconda, e sotto viene visualizzata la
terza; alla fine di questa viene saltata una linea e viene visualizzata la
quinta, eccetera. In pratica abbiamo fatto in modo che siano visualizzate
solo le linee dispari.
Lo schermo per motivi hardware non pu visualizzare pi di 256 linee, ma
nessuno ci impone quali 256, n da quando farle iniziare a visualizzare.
Se una volta saltiamo le linee pari, e quella dopo saltiamo le linee dispari,
possiamo visualizzare una schermata di 512 linee in una da 256 "alternata"!

Recapitoliamo: abbiamo una pic, per esempio, in 640x512 interlacciata, che
abbiamo convertito in RAW e vogliamo visualizzare. Abbiamo puntato la pic
opportunamente e messo il modulo a -80, nonch settato il bit di interlace
(oltre a quello di hires) nel bplcon0 ($dff100).
Cosa otteniamo? LA FIGURA COME FOSSE IN LOWRES! ALTA 256 LINEE, COME SE
L'AVESSIMO ABBASSATA DI RISOLUZIONE!

Allora, qua  il momento di fare quella piccola parte "a mano" per permettere
l'interlacciamento. Esiste un apposito bit, che va controllato, che ci indica
se visualizzare le linee dispari o quelle pari a ogni fotogramma.
Questo  il bit 15 del VPOSR ($dff004), detto LOF, o Long Frame, che indica
se siamo nel "frame lungo" o no. Ecco un esempio di routine:

LACEINT:
	MOVE.L	#BITPLANE,D0	; Indirizzo bitplane
	btst.b	#15-8,$dff004	; VPOSR LOF bit?
	Beq.S	Faidispari	; Se si, tocca alle linee dispari
	ADD.L	#80,D0		; Oppure aggiungi la lunghezza di una linea,
				; facendo partire la visualizzazione dalla
				; seconda: visualizzate linee pari!
FaiDispari:
	LEA	BPLPOINTERS,A1	; PLANE POINTERS IN COPLIST
	MOVE.W	D0,6(A1)	; Punta la figura
	SWAP	D0
	MOVE.W	D0,2(A1)
	RTS

Come vedete, se il bit LOF  azzerato si parte a visualizzare dalla prima
linea, quindi per effetto del modulo saranno visualizzate le linee 1,3,5,7...
eccetera, ossia quelle dispari. Altrimenti, si salta una linea, facendo
partire la visualizzazione dalla seconda, quindi 2,4,6,8... eccetera: PARI!

C' chi fa 2 copperlist, una che punta in un modo e una che punta nell'altro,
e poi a seconda del bit LOF punta una o l'altra ogni fotogramma. Credo per
sia pi "furbo" puntare solo i bitplanes... comunque potete fare come volete,
basta aver capito il metodo.

Lezione11l6.s -  un esempio in 640x512 ad 1 bitplane

Lezione11l6b.s -  un esempio in 320x512 a 4 bitplanes

Per terminare questo corso di laurea in "copper", livello 2, non ci rimane
che fare un esempio pi complesso anche per gli sprite.
Vi ricordate come li "riutilizzammo" nella lezione7 per fare le stelline?
Ebbene, cosa succede se riutilizziamo gli sprite ogni 2 linee?

Lezione11l7.s - Per vedere un megariutilizzo degli sprite (128 volte ognuno)

		        ____::::____
		      _/::__  __  \_
		     ,|:( o _, o )  |,
		     (|    (_       |)
		     `l.   _        .j'
		       \   \____/   / Zs!
		  ______\   :l/    /____
		 /::: \________/   ::\
		/::                   :\


******************************************************************************
*		I 2 CHIP 8520, DETTI CIAA E CIAB                             *
******************************************************************************

Se smontate l'Amiga troverete, oltre ad Agnus, Paula, Denise, il 680x0 ecc.,
anche un paio di chippetti 8520, detti CIA. Questi chip hanno 16 pin di
Input/Output ognuno, un registro di shift seriale, tre timer, un pin di solo
output e uno di solo input. Di conseguenza entrambi hanno 16 registri che
si possono raggiungere accedendo ai loro relativi indirizzi:

Mappa di indirizzi del CIAA
---------------------------------------------------------------------------
 Byte    Register                  Data bits
Address    Name     7     6     5     4     3     2     1    0
---------------------------------------------------------------------------
$BFE001    pra     /FIR1 /FIR0  /RDY /TK0  /WPRO /CHNG /LED  OVL
$BFE101    prb     Porta parallela
$BFE201    ddra    Direzione per port A (BFE001);1=output (normalmente $03)
$BFE301    ddrb    Direzione per port B (BFE101);1=output (pu essere in/out)
$BFE401    talo    CIAA timer A byte basso (.715909 Mhz NTSC; .709379 Mhz PAL)
$BFE501    tahi    CIAA timer A byte alto
$BFE601    tblo    CIAA timer B byte basso (.715909 Mhz NTSC; .709379 Mhz PAL)
$BFE701    tbhi    CIAA timer B byte alto
$BFE801    todlo   Timer a 50/60 Hz - bits 7-0 (VSync or line tick)
$BFE901    todmid  Timer a 50/60 Hz - bits 15-8
$BFEA01    todhi   Timer a 50/60 Hz - bits 23-16
$BFEB01            Non usato
$BFEC01    sdr     CIAA serial data register (connesso alla tastiera)
$BFED01    icr     CIAA interrupt control register
$BFEE01    cra     CIAA control register A
$BFEF01    crb     CIAA control register B

Nota:  il CIAA pu generare un interrupt INT2, ossia liv.2, $68.


Mappa di indirizzi del CIAB
---------------------------------------------------------------------------
 Byte     Register                   Data bits
Address     Name     7     6     5     4     3     2     1     0
---------------------------------------------------------------------------
$BFD000    pra     /DTR  /RTS  /CD   /CTS  /DSR   SEL   POUT  BUSY
$BFD100    prb     /MTR  /SEL3 /SEL2 /SEL1 /SEL0 /SIDE  DIR  /STEP
$BFD200    ddra    Direction for Port A (BFD000);1 = output (normalm. a $FF)
$BFD300    ddrb    Direction for Port B (BFD100);1 = output (normalm. a $FF)
$BFD400    talo    CIAB timer A byte basso (.715909 Mhz NTSC; .709379 Mhz PAL)
$BFD500    tahi    CIAB timer A byte alto
$BFD600    tblo    CIAB timer B byte basso (.715909 Mhz NTSC; .709379 Mhz PAL)
$BFD700    tbhi    CIAB timer B byte alto
$BFD800    todlo   Timer di sync orizzontale -  bits 7-0
$BFD900    todmid  Timer di sync orizzontale -  bits 15-8
$BFDA00    todhi   Timer di sync orizzontale -  bits 23-16
$BFDB00            Non usato
$BFDC00    sdr     CIAB serial data register (non usato)
$BFDD00    icr     CIAB interrupt control register
$BFDE00    cra     CIAB Control register A
$BFDF00    crb     CIAB Control register B

Nota:  CIAB pu generare un INT6, ossia liv 6: $78.

Da questa "mappa" si pu vedere come i compiti dei 2 CIA vadano dalla lettura
della tastiera, alla gestione della porta seriale, (per scambio dati tra 2
computer o tra computer e modem), alla gestione della porta parallela (per la
stampante, ad esempio), al controllo delle testine del disk drive, inoltre
ha degli "orologi" che possono contare microsecondi o ore.
In realt per non ci interesseranno tutte queste caratteristiche, per vari
motivi. Innanzitutto, la parte riguardante l'hardware dei disk drive pu
essere omessa, dato che ogni buon gioco/demo che si rispetti deve essere
installabile su Hard Disk (o CD-ROM!), come ad esempio Brian The Lion.
Per quanto riguarda la gestione della porta parallela e della porta seriale,
gli utilizzi potrebbero essere quello di stampare qualche istruzione per il
gioco o qualche frase detta da un personaggio, oppure per la seriale la
possibilit di giocare in due con i computer in rete, ossia connessi con un
cavo. Per occorre dire che la gestione della stampante  bene farla fare
al sistema operativo, usando il "parallel.device". Lo stesso dicasi della
porta seriale: il serial.device  certamente pi sicuro di routines scritte
via hardware, specialmente per gli Amiga futuri o per le schede multiseriali.
Per quanto riguarda i timers, dato che il sistema operativo ne utilizza
diversi per le proprie mansioni, vedremo se e quali utilizzare.
Ma allora, ci interessa quasi esclusivamente la lettura da tastiera?
Ebbene si, infatti se sbirciaste nel codice di un videogioco, notereste che
vengono ridefiniti solo gli interrupt $6c (coper/vertb/vblank) e $68 (int2 del
ciaa di tastiera): il livello 3 ($6c) serve per mettere la musica o altre
routines in sincronia col pennello elettronico, mentre il livello 2 ($68) per
leggere la tastiera. Naturalmente con la lettura del tasto sinistro del mouse o
di altre cose si accede a registri cia, ma si tratta di semplici controlli o
settaggi di BIT, non occorre fare lunghe dissertazioni su "btst.b #6,$bfe001".
Nelle demo, addirittura,  facile che non sia ridefinito alcun interrupt,
oppure che sia usato solo il $6c per metterci la musica.
Quindi, cominciamo la spiegazione del CIAA dalla gestione della tastiera, in
cui sono coinvolti i registri $bfec01 (sdr), $bfed01 (icr), $bfee01 (cra) e
l'interrupt di livello 2 ($68). Prima vediamo i 3 registri separatamente, poi
facciamo degli esempi sul loro corretto utilizzo. Premetto che quando un tasto
viene premuto o rilasciato viene inviato dalla tastiera un codice a 8 bit
attraverso $bfec01, e viene generato un interrupt di livello 2 ($68) nel
quale occorre "dire" alla tastiera che si  ricevuto il codice di quel tasto.
Da notare che tale codice NON  il codice ascii del carattere premuto, ma
un codice con la posizione del bottone premuto nella tastiera.

****************************************************************************
;* BFEC01    sdr     CIAA sdr (serial data register - connesso alla tastiera)
****************************************************************************

 un registro di shift a 8 bit sincrono, connesso alla tastiera.
Esso pu funzionare in 2 modi: INPUT o OUTPUT, e la selezione tra questi
due modi si pu fare agendo sul bit 6 di $bfee01 (cra).
Nel modo INPUT i dati ricevuti dalla tastiera sono inseriti nel registro,
un bit alla volta e, quando sono "arrivati" tutti gli 8 bit che formano il
carattere premuto, si genera un'interrupt INT2 ($68), dal quale occorre
vedere di quale tasto si tratta e annotarlo in qualche variabile.
In questo caso si legge il byte corrispondente al codice del carattere:
Es:
	move.b $bfec01,d0

Nel modo OUTPUT invece si scrive sul registro, ad esempio "clr.b $bfec01".

****************************************************************************
;* BFED01    icr     CIAA interrupt control register
****************************************************************************

Questo registro controlla gli interrupt generabili dal CIAA.
I Cia infatti generano degli interrupt in varie occasioni, per esempio quando
 finito un conto alla rovescia di un timer, o quando la porta seriale ha
finito un trasferimento.
A noi in particolare interessa l'interrupt INT2, di livello2, ossia il vettore
di offset $68, che viene generato quando  premuto un tasto.
Il funzionamento degli icr ($bfed01 per CIAA e $bfdd00 per CIAB)  molto
particolare, infatti consistono in una "maschera" a sola scrittura di un
registro dati a sola lettura. Ma cosa significa questo? Innanzitutto che 
molto facile sbagliarsi e far impazzire gli interrupt del CIA, il che  poco
augurabile. Ogni interrupt risulta abilitato se il corrispondente bit della
maschera  settato ad 1, infatti ogni interrupt CIAA, come farebbe con un
INTREQ ($dff09c), setta il suo bit di richiesta in questo registro.
A questo punto, se tale interrupt  abilitato, si setta il bit 7 (IR), che 
una specie di set/clr bit, come in dmacon, ossia quando tale bit  azzerato
gli altri 6 bit settati sono azzerati, quando il bit 7  settato, invece, gli
altri bit settati sono settati, mentre quelli a zero non vengono modificati.
La cosa che pu confondere  che quando si legge il registro il suo contenuto
viene azzerato, sia che si faccia un "tst.b $bfed01" che qualsiasi azione di
lettura; azzerando il registro si elimina anche la richiesta di interrupt, in
modo analogo all'azzeramento dei bit di INTREQ ($dff09c).
Ora ci interessa solo la sua funzione per l'interrupt di tastiera, per cui
vediamo in breve i suoi bit in modo lettura, con commenti solo dove interessa:

CIAA ICR ($bfed01)

BIT	NOME	DESCRIZIONE

07	IR	Bit che indica, se settato, che c' un interrupt in corso
06	0
05	0
04	FLG
03	SP	Se settato, siamo in un interrupt generato dalla tastiera
02	ALRM
01	TB
00	TA

Ricordarsi che se si legge il registro questo si azzera, per cui se volete
sapere quali bit fossero settati, dovete copiarlo in un registro Dx e fare
dei controlli su tale registro: rileggendo $bfed01 i bit sono azzerati.

****************************************************************************
;* BFEE01    cra     CIAA cra (control register A)
****************************************************************************

Questo registro  detto "di controllo" proprio perch i suoi bit controllano
la funzione di altri registri. Ecco una sua "mappa", con i commenti solo ai
bit che ci interessano per la lettura da tastiera:

CIA Control Register A

  BIT  NOME	FUNZIONE
  ---  ----	--------
   0  START	Timer A
   1  PBON	Timer A
   2  OUTMODE	Timer A
   3  RUNMODE	Timer A
   4  LOAD	Timer A
   5  INMODE	Timer A
   6  SPMODE	Se  a 1 = registro ($bfec01) output (per scriverci)
		Se  a 0 = registro ($bfec01) input (per leggerlo)
   7   Non usato


Come si vede, l'unico bit che ci interessa  il 6, che "decide" la funzione
del $bfec01, ossia se la sua direzione sia "verso la tastiera" (output), per
cui possiamo scriverci, o "dalla tastiera verso l'Amiga" (input), per cui
possiamo leggere il carattere relativo al tasto che  stato premuto.
Per cambiare modalit basta fare questo:

	bset.b	#6,$bfee01	; CIAA cra - sp ($bfec01) output
	....
	bclr.b	#6,$bfee01	; CIAA cra - sp (bfec01) input

Oppure, se vi pare pi elegante, potete usare AND e OR per falo:

	or.b	#$40,$bfee01	; SP OUTPUT (%0100000, settiamo il bit 6!)
	...
	and.b	#$bf,$bfee01"	; SP INPUT  (%10111111, azzeriamo il bit 6!)

Potreste anche muovere "0000" in un registro, moltiplicarlo per 5, dividerlo
per 5, aggiungere 20, sottrarre 10, aggiungere 1, sottrarre 11, settare o
azzerare il bit 6, e fare un and o un or con il $bfee01, l'assembler permette
di usare infinite strade per fare la stessa cosa. Ma il bset/clr pu bastare!
Comunque occorre precisare che tra modo input e modo output occorre attendere
una novantina di microsecondi, dato che l'hardware del CIAA e del chip della
tastiera non pu temporizzarsi da solo in modo input.
Gli 8 bit del carattere corrispondente al tasto premuto sono trasferiti
serialmente un bit alla volta dal chip della tastiera al CIAA. Quando tutti
e 8 i bit sono stati trasferiti, DOBBIAMO ABBASSARE LA LINEA KDAT PER ALMENO
UNA NOVANTINA DI MICROSECONDI (o 3/4 linee raster) PER CONFERMARE ALLA TASTIERA
CHE ABBIAMO RICEVUTO I DATI. Il "filo" KDAT si controlla dal bit SP/SPMODE,
e in pratica dobbiamo fare questo:

------------------------------------------------------------------------------
	move.b	$bfec01,d0	; CIAA sdr - Leggiamo il carattere attuale
	bset.b	#6,$bfee01	; CIAA cra - sp ($bfec01) output, in modo da
				; abbassare la linea KDAT per confermare che
				; abbiamo ricevuto il carattere.

	st.b	$bfec01		; $FF in $bfec01 - u! ho ricevuto il dato!

; Qua dobbiamo mettere una routine che aspetti 90 millisecondi perch la
; linea KDAT deve stare bassa abbastanza tempo per essere "capita" da tutti
; i tipi di tastiere. Si possono, per esempio, aspettare 3 o 4 linee raster.

	bclr.b	#6,$bfee01	; CIAA cra - sp (bfec01) input nuovamente.
------------------------------------------------------------------------------

Leggendo la tastiera via hardware, occorre stare molto attenti alla
temporizzazione che attende 90 millisecondi, per 2 motivi:
1) La routine di temporizzazione deve aspettare lo stesso tempo su tutti
   i processori, dal 68000 al 68060. Per questo potete usare il pennello
   elettronico, o anche un timer del CIA, ma MAI fare un semplice loop
   dbra eseguito molte volte, o una serie di NOP, perch su 68020+ a causa
   della cache sar eseguito in un battibaleno.
2) Una volta che la nostra routine "aspetta" bene su tutti i 680x0, occorre
   anche considerare il fatto che non tutte le tastiere sono uguali!
   Per esempio per una tastiera potrebbero bastare 2 linee raster mentre
   per un'altra ne potrebbero occorrere 4! Infatti le tastiere contengono
   un chip che le controlla, e questo pu essere diverso nei diversi
   modelli di Amiga. Per esempio nell'A1200 la tastiera  "economica",
   infatti si differenzia dalle tastiere Amiga normali (Mitsumi in genere)
   per il fatto che non si pu registrare pi di una pressione alla
   volta... se si tiene premuto un tasto e nel contempo se ne preme un'altro,
   rilasciando il primo non compare il secondo.
   La routine di wait che deve aspetare tra:
   "or.b #$40" o "bset.b #6",$bfee01 e "and.b #$bf" o "bclr.b #6",$bfee01
   determina se il vostro programma legger la tastiera correttamente o si
   inchioder alla pressione di un tasto su alcuni computer.

A tal proposito vediamo in che modo aspettare correttamente, usando il vblank:

; Se non volete "sporcare" registri indirizzi:

------------------------------------------------------------------------------
	moveq	#4-1,d0		; we wait 4 rasterlines (3+random...!)
waitlines:
	move.b	$DFF006,d1
stepline:
	cmp.b	$DFF006,d1
	beq.s	stepline
	dbra	d0,waitlines
------------------------------------------------------------------------------

; Se invece volete "sporcare" anche un registro indirizzi:

------------------------------------------------------------------------------
	lea	$dff006,a0	; VHPOSR
	moveq	#4-1,d0	; Numero di linee da aspettare = 4 (in pratica 3 pi
			; la frazione in cui siamo nel momento di inizio)
waitlines:
	move.b	(a0),d1	; $dff006 - linea verticale attuale in d1
stepline:
	cmp.b	(a0),d1		; siamo sempre alla stessa linea?
	beq.s	stepline	; se si aspetta
	dbra	d0,waitlines	; linea "aspettata", aspetta d0-1 linee
------------------------------------------------------------------------------

		                    _  ___
		         _,_  椳4,
		       ,ذ ___       V
		    __V  ذ4, '  ___     0
		  _#  #_,J  "4,   I
		 ""_____L  #__,ع   
		JF  س0_  ~~    J#
		1  _  ,ذ______,
		#1   ##   دQ
		N     `&`  W
		 b       س   J
		  `         `"    _d@
		   _               __
		     0       ___,س
		     VL_   _ز  xCz
		     ؤ
		      ذ
		        ^

- CARATTERISTICHE DEL CODICE CARATTERE TRASMESSO IN $bfec01

Abbiamo gi detto che il codice trasmesso non  un codice ascii, ma una
informazione sul tasto che  stato premuto. Questo anche perch a seconda
delle diverse tastiere, in lingua inglese, italiana o altro, molti tasti hanno
una lettera diversa stampata sopra. Ma se dico: il terzo tasto della seconda
fila, non ci si pu sbagliare. Comunque gli 8 bit (1 byte) che prendiamo dal
$bfec01 contengono 7 bit relativi all'identificatore del tasto, pi un bit
che stabilisce se il tasto  stato premuto o rilasciato.
Infatti, l'identificatore del tasto viene inviato sia quando si preme che
quando si rilascia, con la differenza che il bit pi alto, l'ottavo, una
volta  azzerato (rilasciato) o settato (premuto).
Come non bastasse, tutti i codici trasmessi sono ruotati di un bit a sinistra
prima della loro trasmissione.
L'ordine della trasmissione quindi  6-5-4-3-2-1-0-7. Comunque basta usare
l'istruzione "ROR.B #1,xxx" per riportare l'ordine 7-6-5-4-3-2-1-0.
La trasmissione di un bit impiega 60 microsecondi, dunque l'intero byte che
costituisce il carattere viene trasferito in 480 microsecondi, per cui si
possono trasferire 17000 bit al secondo. Ma che ci importa? Nulla!
Vediamo invece come si fa a riconoscere se il tasto premuto  la A, la B o
qualche altro. Nell'hardware manual c' una lista con un codice per tasto,
dove la particolarit  che al codice $01 corrisponde il tasto "1".
Per ottenere questi codici occorre eseguire un NOT del byte, otre che fare
una rotazione dei bit con un "ROR" per riportare l'ordine 76543210.
In pratica, quello che dobbiamo fare :

	move.b	$bfec01,d0	; CIAA sdr (serial data register - connesso
				; alla tastiera - contiene il byte inviato dal
				; chip della tastiera) LEGGIAMO IL CHAR!
	NOT.B	D0		; aggiustiamo il valore invertendo i bit
	ROR.B	#1,D0		; e riportando la sequenza a 76543210.

Ora in d0 abbiamo il byte con la sequenza di bit 76543210 anzich 65432107,
e in pi tutti i bit sono invertiti, in modo da far partire il "conto" dal
primo tasto in alto a sinistra (non ESC, ma quello accanto all'1).
Ecco la sequenza dei codici con il relativo carattere (normale e shiftato, ma
considerate che qua la tastiera descritta  quella USA). (TASTI PREMUTI)

	cod.	$00	 ;` - ~
	cod.	$01      ;1 - ! 
	cod.	$02      ;2 - @
	cod.	$03      ;3 - #
	cod.	$04      ;4 - $
	cod.	$05      ;5 - %
	cod.	$06      ;6 - ^
	cod.	$07      ;7 - &
	cod.	$08      ;8 - *
	cod.	$09      ;9 - (
	cod.	$0A      ;0 - )
	cod.	$0B      ;- - _
	cod.	$0C      ;= - +
	cod.	$0D      ;\ - |
	cod.	$0e	 ;  << vuoto
	cod.	$0F      ;0  tastierino numerico
	cod.	$10      ;q - Q
	cod.	$11      ;w - W
	cod.	$12      ;e - E
	cod.	$13      ;r - R
	cod.	$14      ;t - T
	cod.	$15      ;y - Y
	cod.	$16      ;u - U
	cod.	$17      ;i - I
	cod.	$18      ;o - O
	cod.	$19      ;p - P
	cod.	$1A      ;[ - {
	cod.	$1B      ;] - }
	cod.	$1c	 ; << non usato
	cod.	$1D      ;1  tastierino numerico
	cod.	$1E      ;2  tastierino numerico
	cod.	$1F      ;3  tastierino numerico
	cod.	$20      ;a - A
	cod.	$21      ;s - S
	cod.	$22      ;d - D
	cod.	$23      ;f - F
	cod.	$24      ;g - G
	cod.	$25      ;h - H
	cod.	$26      ;j - J
	cod.	$27      ;k - K
	cod.	$28      ;l - L
	cod.	$29      ;; - :
	cod.	$2A      ;' - "
	cod.	$2B      ;(solo in tast. internazionali) - vicino al return
	cod.	$2c	 ; << non usato
	cod.	$2D      ;4  tastierino numerico
	cod.	$2E      ;5  tastierino numerico
	cod.	$2F      ;6  tastierino numerico
	cod.	$30      ;< (shift sin. solo in tastiere internazionali)
	cod.	$31      ;z - Z
	cod.	$32      ;x - X
	cod.	$33      ;c - C
	cod.	$34      ;v - V
	cod.	$35      ;b - B
	cod.	$36      ;n - N
	cod.	$37      ;m - M
	cod.	$38      ;, - <
	cod.	$39      ;. - >
	cod.	$3A      ;/ - ?
	cod.	$3b	 ; << non utilizzato
	cod.	$3C      ;.  tastierino numerico
	cod.	$3D      ;7  tastierino numerico
	cod.	$3E      ;8  tastierino numerico
	cod.	$3F      ;9  tastierino numerico
	cod.	$40      ;space
	cod.	$41      ;back space <-
	cod.	$42      ;tab ->|
	cod.	$43      ;return tastierino numerico (enter)
	cod.	$44      ;return <-'
	cod.	$45      ;esc
	cod.	$46      ;del
	cod.	$47	 ; << non usato
	cod.	$48	 ; << non usato
	cod.	$49	 ; << non usato
	cod.	$4A      ;-  tastierino numerico
	cod.	$4b	 ; <<
	cod.	$4C      ;cursore alto  ^
	cod.	$4D      ;cursore basso v
	cod.	$4E      ;cursore destra   
	cod.	$4F      ;cursore sinistra 
	cod.	$50      ;f1
	cod.	$51      ;f2
	cod.	$52      ;f3
	cod.	$53      ;f4
	cod.	$54      ;f5
	cod.	$55      ;f6
	cod.	$56      ;f7
	cod.	$57      ;f8
	cod.	$58      ;f9
	cod.	$59      ;f10
	cod.	$5A      ;(  tastierino numerico
	cod.	$5B      ;)  tastierino numerico
	cod.	$5C      ;/  tastierino numerico
	cod.	$5D      ;*  tastierino numerico
	cod.	$5E      ;+  tastierino numerico
	cod.	$5F      ;help
	cod.	$60      ;lshift (sinistro)
	cod.	$61      ;rshift (destro)
	cod.	$62	 ;caps lock
	cod.	$63      ;ctrl
	cod.	$64      ;lalt (sinistro)
	cod.	$65      ;ralt (destro)
	cod.	$66      ;lamiga (sinistro)
	cod.	$67      ;ramiga (destro)


Come vedete, la sequenza rispetta grossomodo l'ordine dei tasti, partendo
dalla fila con 1,2,3,4,5... poi la fila sotto con q,w,e,r,t,y... eccetera.
Questi codici si riferiscono ai tasti PREMUTI, dove il bit 7, quello che
determina se un tasto  stato premuto o rilasciato,  a zero. Infatti
abbiamo eseguito un NOT sul byte, che ha invertito pure il bit 8: se un
tasto  PREMUTO, ora il bit 8=0, se un tasto  rilasciato, il bit 8=1.
Se il tasto  rilasciato, il bit 7 (l'ottavo) va considerato come settato,
per cui la tabella di prima diventerebbe:

	cod.	$80	 ;` - ~
	cod.	$81      ;1 - ! 
	cod.	$82      ;2 - @
	...

Attenzione al fatto che gli Amiga600 non hanno il tastierino numerico, per
cui se usate i tasti del tastierino su tali computer non c' modo di
premerli! Vi consiglio quindi di evitare i tasti del tastierino.
Alla luce di queste considerazioni, possiamo vedere come sar l'interrupt
di livello 2 ($68), che ci permetter di salvare nella variabile "ActualKey"
il codice dei tasti premuti:

		         ___________
		        /~~/~~|~~\~~\
		        \  \  |  /  /______
		       __\_________/__oOOo_Z________
		      |::888_~_888 oT::::Y~~~~~|
		 _    |:::\  '  /  __ ||::::|     |
		 \\/Z |::::\ `-' /]||T|::::|     |
		(\\  )|::::/\`='/\    |::::l_____j
		 \/ ~Z   \  /~~~~~~~/~~~~~~~~~~~
		 /\_/     \ _ /  _    /
		 \   /   /T  (Y)   |\__/
		  \_____/ |       |
		          |   :    |
		          |        |
		          |   .    | ppX

*****************************************************************************
*	ROUTINE IN INTERRUPT $68 (livello 2) - gestione TASTIERA
*****************************************************************************

;03	PORTS	2 ($68)	Input/Output Porte e timers, connesso alla linea INT2

MioInt68KeyB:	; $68
	movem.l d0-d1/a0,-(sp)	; salva i registri usati nello stack
	lea	$dff000,a0	; reg. custom per offset

	MOVE.B	$BFED01,D0	; Ciaa icr - in d0 (leggendo l'icr causiamo
				; anche il suo azzeramento, per cui l'int 
				; "disdetto" come in intreq).
	BTST.l	#7,D0	; bit IR, (interrupt cia autorizzato), azzerato?
	BEQ.s	NonKey	; se si, esci
	BTST.l	#3,D0	; bit SP, (interrupt della tastiera), azzerato?
	BEQ.s	NonKey	; se si, esci

	MOVE.W	$1C(A0),D0	; INTENAR in d0
	BTST.l	#14,D0		; Bit Master di abilitazione azzerato?
	BEQ.s	NonKey		; Se si, interrupt non attivi!
	AND.W	$1E(A0),D0	; INREQR - in d1 rimangono settati solo i bit
				; che sono settati sia in INTENA che in INTREQ
				; in modo da essere sicuri che l'interrupt
				; avvenuto fosse abilitato.
	btst.l	#3,d0		; INTREQR - PORTS?
	beq.w	NonKey		; Se no, allora esci!

; Dopo i controlli, se siamo qua significa che dobbiamo prendere il carattere!

	moveq	#0,d0
	move.b	$bfec01,d0	; CIAA sdr (serial data register - connesso
				; alla tastiera - contiene il byte inviato dal
				; chip della tastiera) LEGGIAMO IL CHAR!

; abbiamo il char in d0, lo "lavoriamo"...

	NOT.B	D0		; aggiustiamo il valore invertendo i bit
	ROR.B	#1,D0		; e riportando la sequenza a 76543210.
	move.b	d0,ActualKey	; salviamo il carattere

; Ora dobbiamo comunicare alla tastiera che abbiamo preso il dato!

	bset.b	#6,$bfee01	; CIAA cra - sp ($bfec01) output, in modo da
				; abbassare la linea KDAT per confermare che
				; abbiamo ricevuto il carattere.

	st.b	$bfec01		; $FF in $bfec01 - u! ho ricevuto il dato!

; Qua dobbiamo mettere una routine che aspetti 90 microsecondi perch la
; linea KDAT deve stare bassa abbastanza tempo per essere "capita" da tutti
; i tipi di tastiere. Si possono, per esempio, aspettare 3 o 4 linee raster.

	moveq	#4-1,d0	; Numero di linee da aspettare = 4 (in pratica 3 pi
			; la frazione in cui siamo nel momento di inizio)
waitlines:
	move.b	6(a0),d1	; $dff006 - linea verticale attuale in d1
stepline:
	cmp.b	6(a0),d1	; siamo sempre alla stessa linea?
	beq.s	stepline	; se si aspetta
	dbra	d0,waitlines	; linea "aspettata", aspetta d0-1 linee

; Ora che abbiamo atteso, possiamo riportare $bfec01 in modo input...

	bclr.b	#6,$bfee01	; CIAA cra - sp (bfec01) input nuovamente.

NonKey:		; 3210
	move.w	#%1000,$9c(a0)	; INTREQ togli richiesta, int eseguito!
	movem.l (sp)+,d0-d1/a0	; ripristina i registri dallo stack
	rte

-----------------------------------------------------------------------------

Non dovreste aver notato niente di nuovo,  solo una "sintesi" delle cose che
abbiamo gi spiegato. Poi in definitiva sono solo poche linee e usiamo solo
i registri d0 ed a0, non  mica una routine COMPLICATA!.
L'unica cosa che dovete ricordarvi  di mettere questo interrupt al vettore
$68+VBR, e di abilitarlo, settando il bit 3 di INTENA ($dff09a).
Per esempio, se state usando un'interrupt di livello 3 ($6c) per suonare la
musica, usando il solo VERTB (bit 5), potete scrivere:

		; 5432109876543210
	move.w	#%1100000000101000,$9a(a5)    ; INTENA - abilito solo VERTB
					      ; del livello 3 e il livello2

O in altri termini "move.w #$c028,$dff09a".

Possiamo vedere il corretto uso di questo interrupt in Lezione11m1.s

In questo listato il codice della tastiera viene immesso nel color0 per far
"vedere" l'effettivo funzionamento della routine stessa.
Per uscire naturalmente occorre premere un tasto: la barra di spazio.

Per comodit, in Lezione11m2.s  inclusa una routine rudimentale di
conversione dei codici di tastiera in ASCII, che pu servire se volete fare
in modo che si possa stampare quello che viene scritto con la tastiera, per
esempio se volete farvi una utility, o semplicemente scrivere il vostro nome
nell'high score del vostro gioco.

		             ___________
		            (          )
		             \_       _/
		              L       L
		         ____/___   ___\____
		        //\\_//\\
		       ((     ______     ))
		        \\ _/\___\X/___/\_ //
		         \ T     \ /     T /
		           |      T      |
		         __|   - o|O -   |__
		     tS / |      |      | \ tS
		\      / / \_____T_____/ \ \      /
		->----/ /\      /   \      /\ \----<-
		/     \   \    (_____)    /   /     \
		       \   \_/\_______/\_/   /
		        \_________A_________/

*****************************************************************************
;		I TIMERS DEL CIAA E DEL CIAB
*****************************************************************************

Questi timers in realt sono usati pochissimo nei giochi, e nelle demo quasi
mai, giusto in certe (complicate) routines che suonano la musica, che
comunque basta includere e suonano per i fatti loro.
Tra l'altro questi timers sono usati anche dal sistema operativo, per cui
usandoli si pu rischiare di far impazzire tutto all'uscita del nostro
programma.
Inoltre, aspettando le linee raster con il $dff006 si possono fare tutte
le attese che servono, senza rischiare queste eventualit.
Per questo, nella lezione sono presenti solo un paio di listati che usano i
timer, come esempio. In listati di lezioni pi avanzate avremo modo di
trovare applicazioni di questi timer, e li vedremo caso per caso.

Lezione11n1.s	- Uso del timer A del CIAA o CIAB

Lezione11n1b.s	- Uso del timer B del CIAA o CIAB

Lezione11n2.s	- Uso del timer TOD (Time of day)

Nell'usare i timer del CIA considerate che il sistema operativo usa questi
per i seguenti scopi: (meglio usare il CIAB!)

   CIAA, timer A	Utilizzato per l'interfacciamento con la tastiera

!  CIAA, timer B	Utilizzato dall'exec per lo scambio dei task ecc.

   CIAA, TOD		timer a 50/60 Hz utilizzato dal Timer.device

   CIAB, timer A	Non utilizzato, disponibile per i programmi

   CIAB, timer B	Non utilizzato, disponibile per i programmi

   CIAB, TOD		Utilizzato dalla graphics.library per seguire le
   			posizioni del pennello elettronico.

Se dovete usare timer che servono anche al sistema operativo, fatelo solo
se avete disabilitato multitasking e interrupt di sistema, se avete
cio preso il controllo completo del sistema. Mai il CIAA, timer B!

                                                              |||||
              |||||                                       _A_ /o O\
_   _ ___.oOo _o_O_ oOo. __ ____ ___ _ _ _____ _ _ _ _   (_^_)_\_/ _oOo. _  
*****************************************************************************
;		IL CARICAMENTO DI FILES CON LA DOS.LIBRARY
*****************************************************************************

Per concludere questa lezione piena di perfezionamenti e argomenti vari,
non c' miglior argomento del CARICAMENTO DEI DATI.
Se volete programmare qualcosa di "grosso", dal punto di vista della mole di
disegni, musiche e dati vari, non si pu semplicemente includere tutto con
l'incbin e salvare il megaeseguibile con "WO", perch il file verrebbe
troppo grande per poter essere caricato in memoria.
Supponiamo, per esempio, di voler fare uno slideshow, ossia un programma che
mostri una serie di figure una dopo l'altra, e che queste figure siano
ben 30, lunghe 100Kb l'una. Vengono 3MB di figure. Non potendo fare una
serie di 30 INCBIN per salvare un file di 3MB e passa, non ci resta che
trovare un modo per "caricarne" una alla volta.
Ma quale modo usare? Ce ne sono 2 principalmente:

1) CARICAMENTO AUTOBOOT DALLE TRACCE DEL DISCHETTO, ossia una modalit non
   compatibile col DOS, infatti potrete notare che molti dischi di giochi,
   se inseriti nel drive una volta caricato il workbench, non sono leggibili
   con comandi come "DIR", e risultano NON DOS, o ERRATI... insomma sembrano
   dischi marci! Se sottoposti a copia, tramite copiatori come XCOPY o DCOPY,
   alcuni di questoi giochi non dos appaiono a tracce "ROSSE", ossia non
   riconoscibili nemmeno dal copiatore, mentre altri nonostante risultino
   illeggibili dal dos appaiono come "SANI", ossia a tracce verdi.
   Devo precisare che i giochi CRACKATI (sprotetti e distribuiti dai pirati)
   sono tutti del secondo tipo, ossia a tracce VERDI, infatti la sprotezione
   spesso sta nel trasformare le tracce incopiabili in tracce copiabili, ma
   spesso rimangono illeggibili dal dos. Le TRACKMO sono la gran parte delle
   demo, e sono a tracce "copiabili", ma non leggibili via dos.
   Una caratteristica  quella che occorre scrivere codice per indirizzi
   assoluti, non rilocabile, per cui solitamente viene usato solo il primo
   mega di CHIP RAM, o per a1200 i primi 2 mega, ed eventuali espansioni di
   FAST RAM non sono utilizzate, a parte quelli che usano COMPLESSI LOADER
   CON RILOCATORI che somigliano a mini sistemi operativi, che per spesso
   fanno cilecca sul 68040 per le eccessive "esuberanze" di programmazione.
   Questo sistema ha il "pregio" di essere leggermente pi veloce, sui
   floppy disk, del dos normale, ma lo svantaggio di non poter installare su
   Hard-Disk il programma, n poterlo convertire per CD32 eccetera.

2) CARICAMENTO "LEGALE" USANDO LA DOS.LIBRARY, ossia un modo molto simile a
   quello usato da un qualsiasi programma che usa il sistema operativo,
   compilato con un qualsiasi linguaggio come il C, l'AMOS eccetera.
   In realt, mantenendo una nostra copperlist e operando sui registri
   hardware facciamo un sistema "ibrido", ossia usamo la dos.library in uno
   stato "particolare" con la nostra copperlist e i nostri interrupt.
   Una caratteristica dei programmi che usano questo sistema  che il
   sistema operativo deve essere lasciato "intatto" e il codice deve
   essere totalmente rilocabile, (potendo accedere all'eventuale FAST RAM).
   Questo sistema ha il pregio di poter essere usato su Hard Disk, CD-ROM
   e qualsiasi drive supportato dal sistema, anche future periferiche.

Bench il primo sistema possa sembrare pi accattivante per uno che vuole
programmare a livello hardware, in realt si tratta di un modo VECCHIO,
SPESSO INCOMPATIBILE e limitante per l'impossibilit di installare su HD
il programma (o demo che sia). Finch parliamo di una demo o di un gioco
per Amiga500 che sia ad 1 solo disco, forse  accettabile l'opzione del
trackloader, ma da 2 dischi in s il sistema porta solo a far arrabbiare i
possessori di HardDisk, che saranno sempre di pi.
Un gioco installato su HD caricher sempre pi velocemente di uno da disco
con il loader pi turbo possibile.
Poi c' il discorso della FAST RAM: per poterla utilizzare con un trackloader
occorrerebbe fare un mini sistema operativo che trovi dove si trova e
rilochi il codice al giusto indirizzo. Non ho intenzione di proporvi il
listato di uno di questi loader+rilocatori, per non indurvi sulla cattiva
strada. Pensate alla soddisfazione di poter convertire il vostro gioco per
il CD32, o di vederlo funzionare sul 68060 e su qualsiasi HardDisk, e
invece alla delusione di veder fallire il rilocatore "a mano" o di notare che
il programma non sfrutta la fast ram.... vi ho convinto?

C' anche un'altra cosa: sarebbe bene fare uso del comando "assign" per
le nostre produzioni che devono caricare files. Ad esempio, se facciamo un
gioco ad un disco, dando il nome "Cane" al disco, potremo caricare i
file con "Cane:file1", "Cane:file2", "Cane2/oggetti/ogg1", e cos via.
Nel caso si voglia installare su Hard Disk, baster fare una directory,
copiarci il contenuto del disco, e aggiungere alla startup-sequence:

	assign	Cane: dh0:giococane	; ad esempio...

Se il gioco  a pi dischi, baster copiare tutti i dischi nella directory
e fare l'assign di ogni disco:

	assign	Cane1: dh0:giococane
	assign	Cane2: dh0:giococane
	assign	Cane3: dh0:giococane

Per aggiungere "automaticamente" alla startup-sequence o all'user-startup
gli assign necessari, durante l'installazione del gioco, si possono usare le
opzioni dell'installer commodore o altri sistemi, ma questo esula dal corso.

Bene, vediamo allora come caricare un file "path:xx" in una destinazione in
memoria. Ci sono vari modi. Il pi semplice  questo:

CaricaFile:
	move.l	#filename,d1	; indirizzo con stringa "file name + path"
	MOVE.L	#$3ED,D2	; AccessMode: MODE_OLDFILE - File che esiste
				; gi, e che quindi potremo leggere.
	MOVE.L	DosBase(PC),A6
	JSR	-$1E(A6)	; LVOOpen - "Apri" il file
	MOVE.L	D0,FileHandle	; Salva il suo handle
	BEQ.S	ErrorOpen	; Se d0 = 0 allora c' un errore!

	MOVE.L	D0,D1		; FileHandle in d1 per il Read
	MOVE.L	#buffer,D2	; Indirizzo Destinazione in d2
	MOVE.L	#42240,D3	; Lunghezza del file (ESATTA!)
	MOVE.L	DosBase(PC),A6
	JSR	-$2A(A6)	; LVORead - leggi il file e copialo nel buffer

	MOVE.L	FileHandle(pc),D1 ; FileHandle in d1
	MOVE.L	DosBase(PC),A6
	JSR	-$24(A6)	; LVOClose - chiudi il file.
ErrorOpen:
	rts


FileHandle:
	dc.l	0

; Stringa di testo, da terminare con uno 0, a cui dovr puntare d1 prima di
; fare l'OPEN della dos.lib. Conviene mettere l'intero path.

Filename:
	dc.b	"Assembler3:sorgenti7/amiet.raw",0	; path+nomefile
	even

Questo  perfetto se si conosce la lunghezza esatta del file da caricare.
Trattandosi del nostro programma, si suppone che si sappia quando sono lunghi
i nostri file dati!

Vediamo un esempio in Lezione11o1.s

Per a noi interessa maggiormente caricare un file mentre stiamo
visualizzando una nostra copperlist, e magari suonando una musica in
interrupt. Come si concilia un caricamento "legale" con un sistema operativo
del tutto disabilitato? Intanto consideriamo il fatto che gli interrupt
di sistema devono essere tutti riattivati, mentre la copperlist di sistema
non serve, e possiamo tenere la nostra. Allora come continuare a suonare
la musica, o fare qualcos'altro, mentre sta avvenendo un caricamento?
I sistemi sono molteplici. Potremmo aggiungere delle nostre routines in
modo "legale" all'interrupt di sistema, con un AddIntServer().
Oppure potremmo eseguire un nostro interrupt, che poi SALTI ad eseguire
quello di sistema. Un modo un p meno rispettoso, che per funziona e
preferisco usare, anche perch l'ho visto usare nei giochi per CD32.
In pratica ecco cosa dobbiamo fare: ripristinare i vecchi interrupt e il
vecchio stato DMA/INTENA, riabilitare il multitasking eccetera, come
facciamo all'uscita, ma lasciare la nostra copperlist e "infilare" il
nostro interrupt $6c in pi a quello di sistema. Poi caricare il file, e
ATTENDERE QUALCHE SECONDO PER ESSERE SICURI CHE LA SPIA DEL DRIVE O
DELL'HARD DISK O DEL CD-ROM SI SIA SPENTO, poi richiudere tutto e tornare
a battere nel metallo senza piet.
Insomma, prima e dopo il caricamento occorre riabilitare e ridisabilitare il
sistema operativo, lasciando la nostra copperlist.
L'unico particolare  l'interrupt: come facciamo ad eseguire il nostro, poi
saltare a quello vecchio? Voglio proporvi un sistema da veri contrabbandieri,
che per funziona, a patto che si chiami la routine "ClearMyCache", che
azzera l'instruction cache del processore (68020+).
Infatti useremo per la prima (e ultima) volta, codice AUTOMODIFICANTE!
Non andrebbe mai usato, ma voglio farvi vedere uno dei pochi casi in cui
funziona ed  utile, giusto per informazione.
Avete presente che ogni istruzione quando viene assemblata diventa una
serie di valori esadecimali? ad esempio RTS diventa $4e75, e cos via.
Noi dobbiamo JUMPARE al vecchio interrupt, dopo aver eseguito il nostro.
Dunque, un "JMP $12345", ad esempio, diventa "$49f900012345", ossia $4ef9,
seguito dall'indirizzo a cui saltare, che  una long:

	dc.w	$4ef9		; val esadecimale di JMP
Crappyint:
	dc.l	0		; Indirizzo dove Jumpare, da AUTOMODIFICARE...

Ora, se mettessimo in CrappyInt l'indirizzo dell'interrupt di sistema con:

	move.l	oldint6c(PC),crappyint	; Per DOS LOAD - salteremo all'oldint

Avremmo il "JMP oldint6c" che cercavamo... allora l'interrupt finale :

          :                                                     ||| |
          .  ||||                                            .  oO\ .
          : ([oO])                                          (^) \O/<:
          |__\--/__                                          |\__>  |
 -  - - --+------ - ---- ----- - ---------- ------ ------- - - -----+-  -   -
*****************************************************************************
; Routine di interrupt da mettere durante il caricamento. Le routines che
; saranno messe in questo interrupt saranno eseguite anche durante il
; caricamento, sia che avvenga da floppy disk, da Hard Disk, o CD ROM.
; DA NOTARE CHE STIAMO USANDO L'INTERRUPT COPER, E NON QUELLO VBLANK,
; QUESTO PERCH DURANTE IL CARICAMENTO DA DISCO, SPECIALMENTE SOTTO KICK 1.3,
; L'INTERRUPT VERTB NON  STABILE, tanto che la musica avrebbe dei sobbalzi.
; Invece, se mettiamo un "$9c,$8010" nella nostra copperlist, siamo sicuri
; che questa routine sar eseguita una volta sola per fotogramma.
*****************************************************************************

myint6cLoad:
	btst.b	#4,$dff01f	; INTREQR - il bit 4, COPER,  azzerato?
	beq.s	nointL		; Se si, non  un "vero" int COPER!
	move.w	#%10000,$dff09c	; Se no,  la volta buona, togliamo il req!
	movem.l	d0-d7/a0-a6,-(SP)
	bsr.w	mt_music	; Suona la musica
	movem.l	(SP)+,d0-d7/a0-a6
nointL:
	dc.w	$4ef9		; val esadecimale di JMP
Crappyint:
	dc.l	0		; Indirizzo dove Jumpare, da AUTOMODIFICARE...
				; ATTENZIONE: il codice automodificante non
				; andrebbe usato. Comunque se si chiama un
				; ClearMyCache prima e dopo, funziona!

Come vedete, basta puntare in $6c+VBR questo interrupt per eseguire mt_music
e il vecchio interrupt di sistema, ottenendo la musica+il caricamento in
contemporanea.

Vediamo un esempio in Lezione11o2.s

A questo punto vi potete immaginare a cosa pu servire la routine che blocca
l'input di intuition: quando carichiamo un file, riabilitiamo multitasking
e interrupts di sistema quindi, anche se viene visualizzata la nostra
copperlist, il workbench funziona perfettamente, tanto che se durante un
caricamento si muove "alla cieca" il mouse, si pu anche azionare qualche
men o clickare qualche icona, o dare dei comandi da tastiera per il cli.
Pensate ad un videogiocatore che ha il vizio di muovere e pigiare il mouse
durante i caricamenti per non innervosirsi: all'uscita del gioco potrebbe
accorgersi di aver clickato l'icona dell'Hard Disk e aver scelto per caso
l'opzione format dal men del WB che non vedeva, e magari premendo
accidentalmente la tastiera potrebbe avergli dato un nome osceno, anche.
Quindi, anche se una volta disabilitato il sistema operativo chiamare la
routine InputOff non  indispensabile, nel caso si carichino files o si
facciano altre operazioni  bene che non sia possibile fare danni!

			-	-	-

Per terminare la lezione, vediamo come fare a caricare un file di cui non
sappiamo a priori la lunghezza, prendendo anche l'occasione per spiegare
le routines di AllocMem e FreeMem.
Per sapere la lunghezza di un file, basta eseguire una apposita funzione,
detta Examine, a patto che si abbia lockato il file. Ci non  molto
difficile, basta fare qualche JSR in pi.
Da notare che Examine non fa altro che riempire un buffer lungo $104 bytes
con i vari dati del file, ecco un esempio:


	cnop	0,4	; Attenzione! Il FileInfoBlock deve essere allineato
			; a longword, non basta che sia ad un indirizzo pari!

fib:
	dcb.b	$104,0	; Struttura FileInfoBlock: offsets.
			; 0 = fib_DiskKey
			; 4 = fib_DirEntyType (<0 = file, >0 = directory)
			; 8 = FileName (max 30 caratteri, terminato con 0)
			; $74 = fib_Protection, $78 = fib_EntryType
			; $7c = fib_Size, $80 = fib_NumBlocks
			; $84 = fib_Date (3 longs: Days, Minute, Tick)
			; $90 = comment (termina con uno 0)

Come vedete, all'offset $7c troviamo la lunghezza. Le altre cose non ci
interessano... che ce ne facciamo della data o del commento?
Comunque, dato che dobbiamo allocare la memoria per il file, la allocheremo
anche per il FileInfoBlock, in modo da risparmiarci questo "dcb.b $104,0".
Una volta saputa la lunghezza del file, dovremo creare un buffer in memoria
lungo quanto il file, per csaricarcelo dentro. Questo si fa con AllocMem,
che richiede in entrata il numero di bytes da allocare e il tipo di memoria,
se chip o no, in modo analogo alle section con "_C" o no.
A differenza delle sections, per, alla fine del programma dobbiamo liberare
manualmente tutti i blocchi allocati tramite la funzione FreeMem.

 AllocMem
 --------

Questa routine di Exec serve per richiedere un blocco di memoria da usare per
i nostri scopi. Basta indicare il tipo di memoria richiesto (in pratica se
deve essere CHIP ram o no), e la lunghezza in bytes di tale blocco.
La routine ALLOCA il pezzo di ram libera per il nostro uso esclusivo, in
quanto ne prendiamo possesso, dato che il sistema operativo non scriver pi
in quel pezzo di memoria, fino a che non glielo "rendiamo", con freemem.
Infatti il sistema multitasking Amiga funziona con questo sistema: ogni
programma richiede quanta memoria gli serve tramite AllocMem, il sistema
operativo gli riserva dei pezzi di ram libera, poi ad un'altro programma che
carica in multitasking saranno allocate altre parti di ram libera.
Per ora abbiamo usato le "SECTION BSS" per gli spazi di memoria azzerata che
ci servivano, in quanto sapevamo la loro grandezza in partenza.
Ed  meglio usare le BSS per i bitplanes o i buffer di grandezza certa, per
varie ragioni, come il non dover chiamare routines e il poter mettere le
label qu e l nel buffer, a differenza della mem allocata a cui dovremmo
accedere per forza tramite offsets dall'inizio del blocco.
Nel nostro listato, carichiamo in memoria un file di cui non sappiamo la
lunghezza, per cui qua  obbligatorio usare l'AllocMem, dopo che abbiamo
saputo quanto spazio occuper il file.
Vediamo in dettaglio la funzione:

	move.l	Grandezza(PC),d0 ; Grandezza del blocco in bytes
	move.l	TypeOfMem(PC),d1 ; Tipo di Memoria (chip,public...)
	move.l	4.w,a6
	jsr	-$c6(a6)	; Allocmem
	move.l	d0,FileBuffer	; Indirizzo inizio del blocco di mem. allocata
	beq.s	FineMem		; d0=0? Allora errore!
	...

Se non  indispensabile allocare chip mem (ossia se nel buffer allocato non
ci andr n grafica n suono), allocate sempre "MEMF_PUBLIC", che significa:
"memoria fast se c', o se proprio non c' allora chip".
Ricordo per l'ennesima volta che  bene risparmiare chip mem, e che la FAST
memory  pi veloce della chip.
All'uscita, in d0 ci sar l'indirizzo del blocco di memoria richiesto, che
tra l'altro sar allineato a long word (ossia allineato a 32 bit).
Se invece d0=0, non  stato possibile allocare un blocco di questo tipo!
Testate sempre questa cosa, o in caso di fine mem copiereste tutto in $0!!!

Possiamo anche richiedere che la memoria richiesta sia azzerata, basta
settare il bit MEMF_CLEAR, il 16 ($10000). Ecco i parametri pi utili da
mettere in d1, per richiedere i vari tipi di memoria:

MEMF_CHIP	=	2	; Richiesta Chip Ram
MEMF_FAST	=	4	; Richiesta Fast Ram (non usatelo)
MEMF_PUBLIC	=	1	; Richiesta Fast, ma se non c' va bene chip!

E, naturalmente, se volete che i blocchi siano azzerati:

CHIP		=	$10002
FAST		=	$10004	; non usatelo...
PUBLIC		=	$10001

Vi sconsiglio di richiedere MEMF_FAST, perch la fast non  presente su
tutte le macchine. Usate sempre MEMF_PUBLIC, a parte quando la memoria
allocata deve essere usata come bitplane, copperlist o audio, ossia MEMF_CHIP.
Da notare che la lunghezza del blocco che immettiamo sar arrotondata dal
sistema operativo ad un multiplo dei blocchi (chunk) del sistema. Questo non
 un problema per noi, infatti se immettiano 39, probabilmente alloca 40,
ma i 39 richiesti ci sono tutti, quindi a noi non interessa.
All'uscita dal programma ricordatevi di liberare il blocco di memoria!

 FreeMem
 -------

Questa  la routine da chiamare per liberare i blocchi di memoria allocata.
 richiesto l'indirizzo del blocco in a1, e la lunghezza in bytes in d0.
ATTENZIONE: Se si tenta di liberare un blocco che non era stato allocato
veramente, causerete un casino pazzesco con Guru Meditation/soft Failure!
Ecco come liberare il blocco di memoria di prima:

	move.l	Grandezza(PC),d0  ; Grandezza del blocco in bytes
	move.l	FileBuffer(PC),a1 ; Indirizzo del blocco di mem. allocata
	move.l	4.w,a6
	jsr	-$d2(a6)	; FreeMem

	                               /T /I
	                              / |/ | .-~/
	                          T\ Y  I  |/  /  _
	         /T               | \I  |  I  Y.-~/
	        I l   /I       T\ |  |  l  |  T  /
	     T\ |  \ Y l  /T   | \I  l   \ `  l Y
	 __  | \l   \l  \I l __l  l   \   `  _. |
	 \ ~-l  `\   `\  \  \\ ~\  \   `. .-~   |
	  \   ~-. "-.  `  \  ^._ ^. "-.  /  \   |
	.--~-._  ~-  `  _  ~-_.-"-." ._ /._ ." ./
	 >--.  ~-.   ._  ~>-"    "\\   7   7   ]
	^.___~"--._    ~-{  .-~ .  `\ Y . /    |
	 <__ ~"-.  ~       /_/   \   \I  Y   : |
	   ^-.__           ~(_/   \   >._:   | l______
	       ^--.,___.-~"  /_/   !  `-.~"--l_ /     ~"-.
	              (_/ .  ~(   /'     "~"--,Y   -=b-. _)
	               (_/ .  \  :           / l      c"~o \
	                \ /    `.    .     .^   \_.-~"~--.  )
	                 (_/ .   `  /     /       !       )/
	                  / / _.   '.   .':      /        '
	                  ~(_/ .   /    _  `  .-<_
	                    /_/ . ' .-~" `.  / \  \          ,z=.
	                     ~( /   '  :   | K   "-.~-.______//
	                       "-,.    l   I/ \_    __{--->._(==.
	                       //(     \  <    ~"~"     //
	                      /' /\     \  \     ,v=.  ((
	                    .^. / /\     "  }__ //===-  `
	                   / / ' '  "-.,__ {---(==-
	                 .^ '       :  T  ~"   ll
	                / .  .  . : | :!        \\
	               (_/  /   | | j-"          ~^ 
	                 ~-<_(_.^-~"

A questo punto possiamo anche vedere il programma: Lezione11o3.s

