;Un usage avancé du Copper : MOVE, WAIT, SKIP et JUMP pour afficher une zone dessinée par un nombre quelconque de MOVE (enfin, entre 1 et 40) dont l'ordonnée et la hauteur sont quelconques. Sans doute le truc le plus chiant que j'ai jamais codé...

;ATTENTION ! ASM-One 1.20 interprète IF X<100 comme <= 100, mais il interprète IF X>100 comme > 100 ! Mais c'est sans doute que IF ne s'utilise jamais sans condition (syntaxe IF(cc), comme indiqué dans le guide...). A ce propos, noter qu'un IFNE [condition] est validé si jamais la condition est vraie, car l'évaluation de la condition donne VRAI qui vaut 1 et non FAUX qui vaut 0... Pas des plus intuitif.

;Par Denis Duplan (denis.duplan@gmail.com) pour Stash of Code (http:://www.stashofcode.fr) en août 2017

;Cette oeuvre est mise à disposition selon les termes de la Licence Creative Commons Attribution – Pas d’Utilisation Commerciale – Partage dans les Mêmes Conditions 4.0 France.

;---------- Directives ----------

	SECTION yragael,CODE_C

;---------- Constantes ----------

;Registres

FMODE=$1FC
INTENA=$09A
INTENAR=$01C
INTREQ=$09C
INTREQR=$01E
DMACON=$096
DMACONR=$002
COLOR00=$180
COLOR01=$182
COP1LCH=$080
COP1LCL=$082
COPJMP1=$088
COP2LCH=$084
COP2LCL=$086
COPJMP2=$08A
DIWSTRT=$08E
DIWSTOP=$090
BPLCON0=$100
BPLCON1=$102
BPLCON2=$104
DDFSTRT=$092
DDFSTOP=$094
BPL1MOD=$108
BPL2MOD=$10A
BPL1PTH=$0E0
BPL1PTL=$0E2

;Programme

DISPLAY_DX=320
DISPLAY_DY=256
DISPLAY_X=$81
DISPLAY_Y=$2C
DISPLAY_DEPTH=1
COPPERLIST=2000
START=100		;Correspond à la ligne START-DISPLAY_Y dans le bitplane (START=0 => ligne 44, START=127 => ligne 83, START=255 => ligne 211)
END=280			;Correspond à la ligne END-DISPLAY_Y dans le bitplane (idem)
NB_MOVES=40

;---------- Initialisations ----------

;Empiler les registres

	movem.l d0-d7/a0-a6,-(sp)
	lea $DFF000,a5

;Allouer de la mémoire en CHIP mise à 0 pour la Copper list

	move.l #COPPERLIST,d0
	move.l #$10002,d1
	movea.l $4,a6
	jsr -198(a6)
	move.l d0,copperList

;Allouer de la mémoire en CHIP mise à 0 pour le bitplane (un bitplane 320x256)

	move.l #DISPLAY_DY*(DISPLAY_DX>>3),d0
	move.l #$10002,d1
	movea.l $4,a6
	jsr -198(a6)
	move.l d0,bitplane

;Couper le système

	movea.l $4,a6
	jsr -132(a6)

;Couper le matériel comme un gros sale

	move.w INTENAR(a5),intena
	move.w #$7FFF,INTENA(a5)
	move.w INTREQR(a5),intreq
	move.w #$7FFF,INTREQ(a5)
	move.w DMACONR(a5),dmacon
	move.w #$07FF,DMACON(a5)

;---------- Copper list ----------

	movea.l copperList,a0

;Configuration de l'écran

	move.w #DIWSTRT,(a0)+
	move.w #(DISPLAY_Y<<8)!DISPLAY_X,(a0)+
	move.w #DIWSTOP,(a0)+
	move.w #((DISPLAY_Y+DISPLAY_DY-256)<<8)!(DISPLAY_X+DISPLAY_DX-256),(a0)+
	move.w #BPLCON0,(a0)+
	move.w #(DISPLAY_DEPTH<<12)!$0200,(a0)+
	move.w #BPLCON1,(a0)+
	move.w #$0000,(a0)+
	move.w #BPLCON2,(a0)+
	move.w #$0000,(a0)+
	move.w #DDFSTRT,(a0)+
	move.w #((DISPLAY_X-17)>>1)&$00FC,(a0)+
	move.w #DDFSTOP,(a0)+
	move.w #((DISPLAY_X-17+(((DISPLAY_DX>>4)-1)<<4))>>1)&$00FC,(a0)+
	move.w #BPL1MOD,(a0)+
	move.w #(DISPLAY_DEPTH-1)*(DISPLAY_DX>>3),(a0)+
	move.w #BPL2MOD,(a0)+
	move.w #(DISPLAY_DEPTH-1)*(DISPLAY_DX>>3),(a0)+

;Comptabilité OCS avec AGA

	move.w #FMODE,(a0)+
	move.w #$0000,(a0)+

;Adresse du bitplane

	move.w #BPL1PTL,(a0)+
	move.l bitplane,d0
	move.w d0,(a0)+
	move.w #BPL1PTH,(a0)+
	swap d0
	move.w d0,(a0)+

;Palette

	move.w #COLOR01,(a0)+
	move.w #$0FFF,(a0)+

;Couleur de fond à noir

	move.w #COLOR00,(a0)+
	move.w #$0000,(a0)+

	;Test d'une boucle : répéter une série de NB_MOVES MOVE à partir du début du bitplane (position horizontale $3E) entre les lignes START et END incluses

;Il est impossible de demander au Copper d'attendre le faisceau d'électrons sur une position horizontale uniquement, du moins sur l'intégralité des positions verticales possibles. En effet, s'il est bien possible de spécifier un masque à appliquer à la position verticale pour limiter sa comparaison aux bits non masqués de la valeur Y sur 8 bits spécifiée dans l'octet de poids fort du second mot, il est impossible de masquer le bit 7. Or à partir de la position verticale $80 (ie : 128), le bit 7 de la position verticale passe à 1. Ce bit est comparé à Y, où il est à 0. La position verticale courante étant donc supérieure à Y, le WAIT est validé, quelle que soit le résultat de la comparaison sur la position horizontale. La série de MOVE est donc exécutée avant la position horizontale où le faisceau d'électrons était attendu. C'est le problème décrit dans l'Amiga Hardware Reference Manual pour l'instruction SKIP, rencontré ici pour l'instruction WAIT dans une boucle où la numéro de la ligne est conduit à passer $80 (à quoi s'ajoute un autre problème, celui de passer $100).

;SYNTHESE GENERALE
;
;// hp est une valeur paire dans [$00, $E0] en PAL
;// vp est une valeur quelconque dans [$00, $FF]
;// he est une valeur quelconque dans [$00, $7F] (son bit 7 sera toujours mis à 1)
;// ve est une valeur paire dans [$00, $FE]
;
;typedef unsigned char BYTE;
;
;function WAIT (BYTE hp, BYTE he, BYTE he, BYTE ve) {
;	ve = $80 | ve;				// Le bit 7 de y et vp ne peut être masqué
;	if ((y & ve) >= (vp & ve))
;		return;
;	while ((y & ve) < (vp & ve));
;	while ((x & he) < (hp & he));
;}
;
;function SKIP (BYTE hp, BYTE vp, BYTE he, BYTE ve) {
;	ve = $80 | ve;				// Le bit 7 de y et vp ne peut être masqué
;	if ((y & ve) >= (vp & ve)) {
;		if ((x & he) >= (hp & he))
;			return (true);
;	}
;	return (false);
;}
;
;L'algorithme est le suivant, x et y correspondant aux coordonnées du faisceau d'électrons qui évoluent parallèlement sur 8 bits (donc rebouclant par overflow à chaque trame) comme dans un autre thread, et SKIP () et WAIT () comparant x et y aux valeurs fournies après application des masques fournis à x et y ainsi qu'à ces valeurs :
;
;Il faut donc noter que SKIP (x, y, mx, my) est équivalent à SKIP (x, y, mx, my ! $80), et de même pour WAIT ().
;
;if (START > 255)
;	WAIT ($E0, $FF, $FE, $7F)									// Attendre d'être en ($E0, $FF) ou au-delà
;if (START < 255) {
;	WAIT ($00, START, $FE, $7F)									// Attendre d'être en ($00, START) ou au-delà
;	while (true) {
;		if (SKIP ($00, $80, $00, $00)) {						// Tester si on est en (%xxxxxxxx, $80) ou au-delà
;			WAIT ($3E, $80, $FE, $00)							// Attendre d'être en ($3E, %1xxxxxxx) ou au-delà
;			for (i = 0 i != NB_MOVES i ++)
;				move (value, COLOR00)
;			if (NB_MOVES <= 38)
;				WAIT ($E0, $80, $FE, $00)						// Attendre d'être en ($E0, %1xxxxxxx) ou au-delà
;		}
;		else {
;			WAIT ($3E, $00, $FE, $00)							// Attendre d'être en ($3E, %0xxxxxxx) ou au-delà
;			for (i = 0 i != NB_MOVES i ++)
;				move (value, COLOR00)
;			if (NB_MOVES <= 38)
;				WAIT ($E0, $00, $FE, $00)						// Attendre d'être en ($E0, %0xxxxxxx) ou au-delà
;		}
;		if ((END >= 255) && (START <= 255)) {
;			if (SKIP ($00, $FF, $00, $7F) { 					// Tester si on est en (%xxxxxxxx, $FF) ou au-delà
;				WAIT ($3E, $FF, $FE, $7F)						// Attendre d'être en ($3E, $FF) ou au-delà
;				for (i = 0 i != NB_MOVES i ++)
;					move (value, COLOR00)
;				if (END == 255)
;					return
;				if (NB_MOVES <= 38)
;					WAIT ($E0, $FF, $FE, $7F)					// Attendre d'être en ($E0, $FF) ou au-delà
;				while (true) {
;					WAIT ($3E, $00, $FE, $00)					// Attendre d'être en ($3E, %0xxxxxxx) ou au-delà
;					for (i = 0 i != NB_MOVES i ++)
;						move (value, COLOR00)
;					if (NB_MOVES <= 38)
;						WAIT ($E0, $00, $FE, $00)				// Attendre d'être en ($E0, %0xxxxxxx) ou au-delà
;					if (SKIP ($00, (END + 1) & $FF, $00, $7F))	// Tester si on est en (%xxxxxxxx, (END + 1) & $FF) ou au-delà
;						return
;				}
;			}
;		}
;		if (SKIP ($00, (END + 1) & $FF, $00, $7F))				// Tester si on est en (%xxxxxxxx, (END + 1) & $FF) ou au-delà
;			return
;	}
;}
;else
;	WAIT ($00, $FF, $FE, $7F)									// Attendre d'être en ($00, $FF) ou au-delà
;if ((END >= 255) && (START >= 255)) {
;	if (SKIP ($00, $FF, $00, $7F) { 							// Tester si on est en (%xxxxxxxx, $FF) ou au-delà
;		WAIT ($3E, $FF, $FE, $7F)								// Attendre d'être en ($3E, $FF) ou au-delà
;		for (i = 0 i != NB_MOVES i ++)
;			move (value, COLOR00)
;		if (END == 255)
;			return
;		if (NB_MOVES <= 38)
;			WAIT ($E0, $FF, $FE, $7F)							// Attendre d'être en ($E0, $FF) ou au-delà
;		while (true) {
;			WAIT ($3E, $00, $FE, $00)							// Attendre d'être en ($3E, %0xxxxxxx) ou au-delà
;			for (i = 0 i != NB_MOVES i ++)
;				move (value, COLOR00)
;			if (NB_MOVES <= 38)
;				WAIT ($E0, $00, $FE, $00)						// Attendre d'être en ($E0, %0xxxxxxx) ou au-delà
;			if (SKIP ($00, (END + 1) & $FF, $00, $7F))			// Tester si on est en (%xxxxxxxx, (END + 1) & $FF) ou au-delà
;				return
;		}
;	}
;}

	IF START>255

	;---------- Début de la partie conditionnelle pour le cas START > 255 : Pas de franchissement de $FF ----------

	;Si la position verticale START est > 255, attendre la fin de la ligne 255...

	move.w #($FF<<8)!$E0!$0001,(a0)+					;WAIT 1st word (y ! x ! 1)
	move.w #$8000!($7F<<8)!$00FE!$0000,(a0)+			;WAIT 2nd word (BFD ! mask y ! mask x ! 0)

	;---------- Fin de la partie conditionnelle pour le cas START > 255 : Pas de franchissement de $FF ----------

	ENDC

	IF 255>START
	
	;---------- Début de la partie conditionnelle pour le cas START < 255 : Franchissement potentiel de $FF ----------

	;Préparer l'adresse pour sauter en (A)

	move.l a0,d0
	addi.l #5*4,d0
	move.w #COP1LCL,(a0)+
	move.w d0,(a0)+										;MOVE (COP1LCL)
	swap d0
	move.w #COP1LCH,(a0)+
	move.w d0,(a0)+										;MOVE (COP1LCH)

	;Attendre la position verticale START et la position horizontale $00 (pour être bien certain de pouvoir attendre la position horizontale à laquelle exécuter les MOVE)

	move.w #(START<<8)!$00!$0001,(a0)+					;WAIT 1st word (y ! x ! 1)
	move.w #$8000!($7F<<8)!$FE!$0000,(a0)+				;WAIT 2nd word (BFD ! mask y ! mask x ! 0)

	;Préparer l'adresse pour sauter en (B)

	move.l a0,d0
	IF 38>NB_MOVES										;Car bogue dans ASM-One 1.20 : < est interprété comme <= !
	addi.l #(5+NB_MOVES+4)*4,d0
	ELSE
	addi.l #(5+NB_MOVES+3)*4,d0
	ENDC
	move.w #COP2LCL,(a0)+
	move.w d0,(a0)+										;MOVE (COP2LCL)
	swap d0
	move.w #COP2LCH,(a0)+
	move.w d0,(a0)+										;MOVE (COP2LCH)

	;(A) Si la position verticale est < $80, sauter en (B)

	move.w #($80<<8)!$00!$0001,(a0)+					;SKIP 1st word (y ! x ! 1)
	move.w #$8000!($00<<8)!$00!$0001,(a0)+				;SKIP 2nd word (BFD ! mask y ! mask x ! 1)
	move.l a0,d0
	move.w #COPJMP2,(a0)+
	move.w #$0000,(a0)+									;MOVE (COPJMP2)

	;La position verticale est >= $80. Attendre la position horizontale $3E à une position verticale quelconque mais >= $80 (ie : 1xxxxxxx)

	move.w #($80<<8)!$3E!$0001,(a0)+					;WAIT 1st word (y ! x ! 1)
	move.w #$8000!($00<<8)!$FE!$0000,(a0)+				;WAIT 2nd word (BFD ! mask y ! mask x ! 0)

	;Exécuter les MOVE

	lea colors,a1
	REPT NB_MOVES
	move.w #COLOR00,(a0)+
	move.w (a1)+,(a0)+									;#NB_MOVES MOVE (COLOR00)
	ENDR

	;Attendre la fin de la ligne à la position horizontale $E0 à une position verticale quelconque mais >= 80 (ie : 1xxxxxxx), donc la ligne suivante...

	IF 38>NB_MOVES										;Car bogue dans ASM-One 1.20 : < est interprété comme <= !
	move.w #($80<<8)!$E0!$0001,(a0)+					;WAIT 1st word (y ! x ! 1)
	move.w #$8000!($00<<8)!$FE!$0000,(a0)+				;WAIT 2nd word (BFD ! mask y ! mask x ! 0)
	ENDC

	;Sauter en (C)

	move.l a0,d0
	IF 38>NB_MOVES										;Car bogue dans ASM-One 1.20 : < est interprété comme <= !
	addi.l #(4+NB_MOVES+1)*4,d0
	ELSE
	addi.l #(4+NB_MOVES)*4,d0
	ENDC
	move.w #COP2LCL,(a0)+
	move.w d0,(a0)+										;MOVE (COP2LCL)
	swap d0
	move.w #COP2LCH,(a0)+
	move.w d0,(a0)+										;MOVE (COP2LCH)
	move.w #COPJMP2,(a0)+
	move.w #$0000,(a0)+									;MOVE (COPJMP2)

	;(B) La position verticale est < $80. Attendre la position horizontale $3E à une position verticale quelconque mais < $80 (ie : 0xxxxxxx)

	move.w #($00<<8)!$3E!$0001,(a0)+					;WAIT 1st word (y ! x ! 1)
	move.w #$8000!($00<<8)!$FE!$0000,(a0)+				;WAIT 2nd word (BFD ! mask y ! mask x ! 0)

	;Exécuter les MOVE

	lea colors,a1
	lea 40*2(a1),a1
	REPT NB_MOVES
	move.w #COLOR00,(a0)+
	move.w -(a1),(a0)+									;#NB_MOVES MOVE (COLOR00)
	ENDR

	;Attendre la fin de la ligne à la position horizontale $E0 à une position verticale quelconque mais < $80 (ie : 0xxxxxxx), donc la ligne suivante

	IF 38>NB_MOVES										;Car bogue dans ASM-One 1.20 : < est interprété comme <= !
	move.w #($00<<8)!$E0!$0001,(a0)+					;WAIT 1st word (y ! x ! 1)
	move.w #$8000!($00<<8)!$FE!$0000,(a0)+				;WAIT 2nd word (BFD ! mask y ! mask x ! 0)
	ENDC

	;---------- Début de la partie conditionnelle pour le cas START < 255 : Franchissement potentiel de $FF ----------

	ELSE

	;---------- Début de la partie conditionnelle pour le cas START = 255 : Franchissement potentiel de $FF ----------

	;Attendre la position horizontale $00 à la position verticale $FF

	move.w #($FF<<8)!$00!$0001,(a0)+					;WAIT 1st word (y ! x ! 1)
	move.w #$8000!($7F<<8)!$FE!$0000,(a0)+				;WAIT 2nd word (BFD ! mask y ! mask x ! 0)

	;---------- Fin de la partie conditionnelle pour le cas START = 255 : Franchissement potentiel de $FF ----------

	ENDC

	IF (END>=255)&(255>=START)							;Car bogue dans ASM-One 1.20 : < est interprété comme <= !

	;---------- Début de la partie conditionnelle pour le cas (END >= 255) & (START <= 255) : Franchissement de $FF ----------
	
;Pourquoi (END>=255)&(255>=START) ? Car le cas où START>255 implique que END>255 et donc qu'il n'y a pas franchissement de $FF : la partie du code précédent valable pour les lignes < $80, nécessairement précédé d'un $FFE1FFFE pour franchir $FF (ce WAIT est conditionné par START>255), permet de gérer sans problème la situation.
;On arrive donc ici quand on vient d'achever de tracer $FE et qu'après avoir attendu en $E0, le Copper considère donc que la ligne est $FF
;C'est la raison pour laquelle on a introduit la partie conditionnnelle quand START=255. Car sinon, on aurait déjà tracé $FF en arrivant ici

	;(C) Sauter en (A) si la position verticale est < $FF

	move.w #($FF<<8)!$00!$0001,(a0)+					;SKIP 1st word (y ! x ! 1)
	move.w #$8000!($7F<<8)!$00!$0001,(a0)+				;SKIP 2nd word (BFD ! mask y ! mask x ! 1)
	move.w #COPJMP1,(a0)+
	move.w #$0000,(a0)+									;MOVE (COPJMP2)

	;La position verticale est $FF. Attendre la position horizontale $3E à la position verticale $FF

	move.w #($FF<<8)!$3E!$0001,(a0)+					;WAIT 1st word (y ! x ! 1)
	move.w #$8000!($7F<<8)!$FE!$0000,(a0)+				;WAIT 2nd word (BFD ! mask y ! mask x ! 0)

	;Exécuter les MOVE

	lea colors,a1
	lea 40*2(a1),a1
	REPT NB_MOVES
	move.w #COLOR00,(a0)+
	move.w -(a1),(a0)+									;#NB_MOVES MOVE (COLOR00)
	ENDR

	;Si la dernière ligne est $FF, sauter en (E)

	IF END=255

	;---------- Début de la partie conditionnelle pour le cas END = 255 : Fin ----------
	
;Pourquoi ne pas tester au Copper si $FF est la dernière ligne à tracer ? Parce qu'il est impossible d'élaborer un test qui fonctionne. En effet, au sortir des MOVE précédents, soit les MOVE étaient assez nombreux et $E0 a été dépassée et le Copper considère que la ligne est $00, soit ils ne le sont pas assez et il considère que la ligne est toujours $FF. On peut aligner les deux cas en rajoutant un WAIT en $E0 après le second (d'ailleurs c'est ce qu'on fait plus loin). Bref, on se trouve alors dans la situation où le Copper considère que la ligne est $00 après avoir tracé $FF. Mais alors quel test pour savoir si $FF est la dernière ligne ? Un SKIP en (END&$FF) permettant d'enjamaber un COPJMP en (D) va fonctionner ainsi :
;si END=255, on teste si $00>=$FF	=> échoue donc COPJMP : ok
;si END=256, on teste si $00>=$00	=> réussit donc COPJMP évité : ok
;si END=257, on teste si $00>=$01	=> échoue donc COPJMP : nok
;on voit qu'il est impossible de concilier échec/réussite du SKIP et le saut/pas saut attendu. Pour cette raison, on décide de faire un code conditionnel pour sortir de la boucle sur END=255

	move.l a0,d0
	IF 38>NB_MOVES
	addi.l #(7+NB_MOVES+3)*4,d0
	ELSE
	addi.l #(6+NB_MOVES+2)*4,d0
	ENDC
	move.w #COP1LCL,(a0)+
	move.w d0,(a0)+										;MOVE (COP1LCL)
	swap d0
	move.w #COP1LCH,(a0)+
	move.w d0,(a0)+										;MOVE (COP1LCH)	
	move.w #COPJMP1,(a0)+
	move.w #$0000,(a0)+									;MOVE (COPJMP1)

	;---------- Fin de la partie conditionnelle pour le cas END = 255 : Fin ----------

	ENDC

	;Attendre la fin de la ligne $FF à la position horizontale $E0, donc la ligne suivante $00
;EN FAIT c'est même après 39 MOVE et c'est pour ça que j'ai transformé les tests en 38>NB_MOVES ! VOIR TEST.S
;ce test n'est pas nécessaire pour NB_MOVES>=40, et il est même nuisible. En effet, après 40 MOVE à partir de $3E sur la ligne $FF, le raster n'est qu'en $DE mais le Copper n'a pas le temps d'exécuter ce WAIT avant qu'il ne franchisse $E0. Or quand le raster franchi $E0, le Copper considère que la ligne courante est devenue la ligne suivante, donc que le raster n'est plus sur la ligne $FF mais la ligne $00 (le tracé de la ligne $FF n'est pas terminé, mais le Copper ne se fonde pas sur cela dans son mécanisme de comparaison ; tout se passe comme s'il avait son propre compteur de ligne courante, incrémenté à chaque franchissement de $E0). Par conséquent, le Copper va interpréter ce WAIT comme un WAIT en $E0 sur la ligne $FF alors que la ligne courante serait $00, ce qui va le faire attendre tout une trame ! C'est pour cela qu'il faut conditionner ce WAIT

	IF 38>NB_MOVES
	move.w #($FF<<8)!$E0!$0001,(a0)+					;WAIT 1st word (y ! x ! 1)
	move.w #$8000!($7F<<8)!$FE!$0000,(a0)+				;WAIT 2nd word (BFD ! mask y ! mask x ! 0)
	ENDC

	;Préparer l'adresse pour sauter en (A)

	move.l a0,d0
	addi.l #2*4,d0
	move.w #COP1LCL,(a0)+
	move.w d0,(a0)+										;MOVE (COP1LCL)
	swap d0
	move.w #COP1LCH,(a0)+
	move.w d0,(a0)+										;MOVE (COP1LCH)

	;(A) Attendre la position horizontale $3E à une position verticale quelconque mais < $80 (ie : 0xxxxxxx)

	move.w #($00<<8)!$3E!$0001,(a0)+					;WAIT 1st word (y ! x ! 1)
	move.w #$8000!($00<<8)!$FE!$0000,(a0)+				;WAIT 2nd word (BFD ! mask y ! mask x ! 0)

	;Exécuter les MOVE

	lea colors,a1
	lea 40*2(a1),a1
	REPT NB_MOVES
	move.w #COLOR00,(a0)+
	move.w -(a1),(a0)+									;#NB_MOVES MOVE (COLOR00)
	ENDR

	;Attendre la fin de la ligne à la position horizontale $E0 à une position verticale quelconque mais < $80 (ie : 0xxxxxxx), donc la ligne suivante

	IF 38>NB_MOVES										;Car bogue dans ASM-One 1.20 : < est interprété comme <= !
	move.w #($00<<8)!$E0!$0001,(a0)+					;WAIT 1st word (y ! x ! 1)
	move.w #$8000!($00<<8)!$FE!$0000,(a0)+				;WAIT 2nd word (BFD ! mask y ! mask x ! 0)
	ENDC

	;---------- Fin de la partie conditionnelle pour le cas (END >= 255) & (START <= 255) : Franchissement de $FF ----------

	ENDC

	;(D) Sauter en (A) si la position verticale est < ((END + 1) & $FF)

	move.w #(((END+1)&$FF)<<8)!$00!$0001,(a0)+			;SKIP 1st word (y ! x ! 1)
	move.w #$8000!($7F<<8)!$00!$0001,(a0)+				;SKIP 2nd word (BFD ! mask y ! mask x ! 1)
	move.w #COPJMP1,(a0)+
	move.w #$0000,(a0)+									;MOVE (COPJMP1)

	;(E) Restaurer l'adresse de la Copper list pour la prochaine trame

	move.l copperList,d0
	move.w #COP1LCL,(a0)+
	move.w d0,(a0)+										;MOVE (COP1LCL)
	swap d0
	move.w #COP1LCH,(a0)+
	move.w d0,(a0)+										;MOVE (COP1LCH)

;Couleur de fond à noir

	move.w #COLOR00,(a0)+
	move.w #$0000,(a0)+

;Fin

	move.l #$FFFFFFFE,(a0)

;---------- Programme principal ----------

;Rétablir les DMA

	move.w #$8380,DMACON(a5)	;DMAEN=1, BPLEN=1, COPEN=1

;Activer la Copper list

	move.l copperList,COP1LCH(a5)
	clr.w COPJMP1(a5)

;Boucle principale

	move.l bitplane,a0
	movea.l a0,a1
	REPT 10
	move.l #$FFFFFFFF,(a1)+
	ENDR
	lea (128-DISPLAY_Y)*(DISPLAY_DX>>3)(a0),a1
	REPT 10
	move.l #$AAAAAAAA,(a1)+
	ENDR
	lea (255-DISPLAY_Y)*(DISPLAY_DX>>3)(a0),a1
	REPT 10
	move.l #$AAAAAAAA,(a1)+
	ENDR
	lea (DISPLAY_DY-1)*(DISPLAY_DX>>3)(a0),a1
	REPT 10
	move.l #$FFFFFFFF,(a1)+
	ENDR

	lea (START-DISPLAY_Y)*(DISPLAY_DX>>3)(a0),a1
	REPT 10
	move.l #$F0F0F0F0,(a1)+
	ENDR
	lea (END-DISPLAY_Y)*(DISPLAY_DX>>3)(a0),a1
	REPT 10
	move.l #$F0F0F0F0,(a1)+
	ENDR


_loop:
	btst #6,$BFE001
	bne _loop

;---------- Finalisations ----------

;Couper les interruptions hardware et les DMA

	move.w #$7FFF,INTENA(a5)
	move.w #$7FFF,INTREQ(a5)
	move.w #$07FF,DMACON(a5)

;Rétablir les interruptions hardware et les DMA

	move.w dmacon,d0
	bset #15,d0
	move.w d0,DMACON(a5)
	move.w intreq,d0
	bset #15,d0
	move.w d0,INTREQ(a5)
	move.w intena,d0
	bset #15,d0
	move.w d0,INTENA(a5)

;Rétablir la Copper list

	lea graphicslibrary,a1
	movea.l $4,a6
	jsr -408(a6)
	move.l d0,a1
	move.l 38(a1),COP1LCH(a5)
	move.l 50(a1),COP2LCH(a5)
	clr.w COPJMP1(a5)
	jsr -414(a6)

;Libérer la mémoire

	movea.l bitplane,a1
	move.l #DISPLAY_DY*(DISPLAY_DX>>3),d0
	movea.l $4,a6
	jsr -210(a6)
	movea.l copperList,a1
	move.l #COPPERLIST,d0
	movea.l $4,a6
	jsr -210(a6)

;Rétablir le système

	movea.l $4,a6
	jsr -138(a6)

;Dépiler les registres

	movem.l (sp)+,d0-d7/a0-a6
	rts

;---------- Données ----------

graphicslibrary:	dc.b "graphics.library",0
	even
copperList:		dc.l 0
bitplane:			dc.l 0
dmacon:			dc.w 0
intena:			dc.w 0
intreq:			dc.w 0
colors:
	DC.W $0F00,$00F0,$000F,$0FFF,$0000,$0FF0,$0F0F,$00FF
	DC.W $0C00,$00C0,$000C,$0CCC,$0000,$0CC0,$0C0C,$00CC
	DC.W $0900,$0090,$0009,$0999,$0000,$0990,$0909,$0099
	DC.W $0600,$0060,$0006,$0666,$0000,$0660,$0606,$0066
	DC.W $0300,$0030,$0003,$0333,$0000,$0330,$0303,$0033
