;Codé par Denis Duplan pour Stash of Code (http://www.stashofcode.fr, stashofcode@gmail.com) en 2017.

;Ce(tte) oeuvre est mise à disposition selon les termes de la Licence (http://creativecommons.org/licenses/by-nc/4.0/) Creative Commons Attribution - Pas d’Utilisation Commerciale 4.0 International.

;Test du détournement de $6C pour mettre un code sous interruption VERTB. Le Copper attend le milieu de l'écran pour passer COLOR00 à $0F00 (rouge). Le CPU lit dans VPOSR et VHPOSR pour attendre le quart supérieur de l'écran, stocke $00F0 (vert) dans color, déclenche une interruption niveau 3 en positionnant VERTB dans INTREQ. Le gestionnaire de l'interruption passe COLOR00 à color (vert), stocke $000F (bleu) dans color, et acquitte l'événement en effaçant VERTB dans INTREQ. Pour finir, le hardware attend la fin de la trame (dépassement de la 313ème ligne en PAL), puis déclenche une interruption de niveau 3 en positionnant VERTB dans INTREQ. Le gestionnaire de l'interruption passe COLOR00 à color (bleu), stocke $000F (bleu) dans color - ça ne sert à rien, mais on ne l'inhibe pas en compliquant le code du gestionnaire par un test -, et acquitte l'événement en effaçant VERTB dans INTREQ.

;Le hardware positionne TOUJOURS le bit correspondant à un événement dans INTREQ. Toutefois, il ne génère une interruption CPU associée (par exemple, une interruption de niveau 3 pour l'événement VERTB) que si le bit correspondant est positionné dans INTENA. Par ailleurs, le hardware n'efface JAMAIS le bit dans INTREQ : c'est la tâche du gestionnaire de l'événement. En conséquence, si VERTB entraîne une interruption CPU mais que le gestionnaire d'interruption (vecteur 27 => adresse $6C) n'acquitte pas l'événement en effaçant VERTB dans INTREQ, ce gestionnaire est appelé en permanence car le hardware maintient l'interruption niveau 3 - le CPU rend la main, mais à peine. Noter qu'il est possible de générer un événement à la place du CPU en positionnant le bit correspondant dans INTREQ. Un test montre que lorsque VERTB est positionné manuellement (ie : move.w #$8020,INTREQ(a5)) durant la trame, le hardware n'en continue pas moins de le positionner à la fin de la trame.

;---------- Directives ----------

	SECTION yragael,CODE_C

;---------- Constantes ----------

	;Registres

VPOSR=$004
INTENA=$09A
INTENAR=$01C
INTREQ=$09C
INTREQR=$01E
DMACON=$096
DMACONR=$002
COLOR00=$180
COP1LCH=$080
COPJMP1=$088

	;Programme

DISPLAY_Y=$2C
DISPLAY_DY=256
COPSIZE=2*4+4

;---------- Initialisations ----------

	;Empiler les registres

	movem.l d0-d7/a0-a6,-(sp)

	;StingRay's stuff

	lea graphicsLibrary,a1
	movea.l $4,a6
	jsr -408(a6)		;OpenLibrary ()
	move.l d0,graphicsBase
	move.l graphicsBase,a6
	move.l $22(a6),view
	movea.l #0,a1
	jsr -222(a6)		;LoadView ()
	jsr -270(a6)		;WaitTOF ()
	jsr -270(a6)		;WaitTOF ()
	jsr -228(a6)		;WaitBlit ()
	jsr -456(a6)		;OwnBlitter ()
	move.l graphicsBase,a1
	movea.l $4,a6
	jsr -414(a6)		;CloseLibrary ()

	moveq #0,d0			;Default VBR is $0
	movea.l $4,a6
	btst #0,296+1(a6)	;68010+?
	beq _is68000
	lea _getVBR,a5
	jsr -30(a6)			;SuperVisor ()
	move.l d0,VBRPointer
	bra _is68000
_getVBR:
	movec vbr,d0
	rte
_is68000:

	;Couper le système

	jsr -132(a6)

	;Allouer de la mémoire en CHIP mise à 0 pour la Copper list

	move.l #COPSIZE,d0
	move.l #$10002,d1
	jsr -198(a6)
	move.l d0,copperlist

	;Couper les interruptions hardware et les DMA

	lea $DFF000,a5
	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)

	;Détourner les vecteurs d'interruption

	movea.l VBRPointer,a0
	lea $64(a0),a0
	lea vectors,a1
	REPT 6		;Les interruptions hardware génèrent des interruptions de niveau 1 à 6 correspondant aux vecteurs 25 à 30 pointant sur les adresses $64 à $78
	move.l (a0),(a1)+
	move.l #_rte,(a0)+
	ENDR

	;Ecrire la Copper list

	move.l copperlist,a0
	move.w #((DISPLAY_Y+(DISPLAY_DY>>1))<<8)!$0001,(a0)+
	move.w #$FF00,(a0)+
	move.w #COLOR00,(a0)+
	move.w #$0F00,(a0)+
	move.l #$FFFFFFFE,(a0)+

;---------- Programme principal ----------

	;Activer la Copper list

	move.l copperlist,COP1LCH(a5)
	clr.w COPJMP1(a5)
	move.w #$8280,DMACON(a5)	;DMAEN=1, COPEN=1
	
	;Activer l'interruption VERTB

	movea.l VBRPointer,a0
	move.l #_VERTB,$6C(a0)
	move.w #$C020,INTENA(a5)	;INTEN=1, VERTB=1

	;Boucle principale
	
_loop:

	;Attendre le premier quart de la hauteur de l'écran

_wait0:
	move.l VPOSR(a5),d0
	lsr.l #8,d0
	and.w #$01FF,d0
	cmpi.w #DISPLAY_Y+(DISPLAY_DY>>2),d0
	blt _wait0

	;Déclencher une interruption VERTB

	move.w #$00F0,color
	move.w #$8020,INTREQ(a5)

	;Attendre une ligne suivante

_wait1:
	move.l VPOSR(a5),d0
	lsr.l #8,d0
	and.w #$01FF,d0
	bne _wait1		;Attendre la ligne 0 car le code prend moins de temps qu'il n'en faut au raster pour balayer les lignes DISPLAY_Y+DISPLAY_DY à 312 et donc reboucler sur la ligne 0

	;Tester la pression du bouton gauche de la souris	

	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 vecteurs	d'interruption

	movea.l VBRPointer,a0
	lea $64(a0),a0
	lea vectors,a1
	REPT 6
	move.l (a1)+,(a0)+
	ENDR

	;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)		;OpenLibrary ()
	move.l d0,graphicsBase

	movea.l d0,a0
	move.l 38(a0),COP1LCH(a5)
	clr.w COPJMP1(a5)

	;StingRay's stuff

	movea.l view,a1
	move.l graphicsBase,a6
	jsr -222(a6)		;LoadView ()
	jsr -462(a6)		;DisownBlitter ()
	move.l graphicsBase,a1
	movea.l $4,a6
	jsr -414(a6)		;CloseLibrary ()

	;Rétablir le système

	jsr -138(a6)

	;Libérer la mémoire

	movea.l copperlist,a1
	move.l #COPSIZE,d0
	jsr -210(a6)

	;Dépiler les registres

	movem.l (sp)+,d0-d7/a0-a6
	rts

;---------- Interruptions ----------

	;VERTB

_VERTB:
	;	movem.l d0-d7/a0-a6,-(sp)	;Indispensable, mais à limiter aux registres modifiés
	move.w color,COLOR00(a5)
	move.w #$000F,color

	;Acquitter l'événement VERTB

	move.w #$0020,INTREQ(a5)
	;	movem.l (sp)+,d0-d7/a0-a6	;Indispensable, mais à limiter aux registres modifiés
	rte

	;Inhibition

_rte:
	rte

;---------- Données ----------

graphicsLibrary:	DC.B "graphics.library",0
					EVEN
graphicsBase:		DC.L 0
view:				DC.L 0
intena:				DC.W 0
intreq:				DC.W 0
dmacon:				DC.W 0
VBRPointer:			DC.L 0
vectors:			BLK.L 6
copperlist:			DC.L 0
color:				DC.W 0
