Obligement - L'Amiga au maximum

Vendredi 19 avril 2019 - 00:46  

Translate

En De Nl Nl
Es Pt It Nl


Rubriques

 · Accueil
 · A Propos
 · Articles
 · Galeries
 · Glossaire
 · Liens
 · Liste jeux Amiga
 · Quizz
 · Téléchargements
 · Trucs et astuces


Articles

 · Actualité (récente)
 · Actualité (archive)
 · Comparatifs
 · Dossiers
 · Entrevues
 · Matériel (tests)
 · Matériel (bidouilles)
 · Points de vue
 · En pratique
 · Programmation
 · Reportages
 · Tests de jeux
 · Tests de logiciels
 · Tests de compilations
 · Articles divers

 · Articles in english
 · Articles en d'autres langues


Twitter

Suivez-nous sur Twitter




Liens

 · Sites de téléchargements
 · Associations
 · Pages Personnelles
 · Matériel
 · Réparateurs
 · Revendeurs
 · Presse et médias
 · Programmation
 · Logiciels
 · Jeux
 · Scène démo
 · Divers


Jeux Amiga

0, A, B, C, D, E, F,
G, H, I, J, K, L, M,
N, O, P, Q, R, S, T,
U, V, W, X, Y, Z


Trucs et astuces

0, A, B, C, D, E, F,
G, H, I, J, K, L, M,
N, O, P, Q, R, S, T,
U, V, W, X, Y, Z


Glossaire

0, A, B, C, D, E, F,
G, H, I, J, K, L, M,
N, O, P, Q, R, S, T,
U, V, W, X, Y, Z


Partenaires

Annuaire Amiga

Amedia Computer

Relec

Hit Parade


Contact

David Brunet

Courriel

 


Programmation : Assembleur - Routine de lecture d'une disquette piste par piste
(Article écrit par Loïc Far et extrait d'Amiga News Tech - septembre 1991)


Nous allons aujourd'hui mettre en pratique nos connaissances acquises la dernière fois, en mettant au point une routine de lecture d'une disquette entière piste par piste.

Nous avons déjà effectué les trois-quarts du travail, avec les routines pour déplacer la tête de lecture, sélectionner un lecteur de disquette, etc. passées en revue le mois dernier. Il nous reste tout de même un gros morceau à traiter, à savoir le décodage des données MFM brutes, telles qu'elles sont lues par le DMA disque. Si la réalisation d'une telle routine n'est pas réellement compliquée, elle nécessite toutefois une bonne compréhension du format de codage.

Vous avez dit MFM ?

Mais au fait, pourquoi donc coder les données avant de les écrire sur disque ? Simplement pour des raisons de sécurité. Divers codages existent, que nous n'aborderons pas ici (GCR, etc.). Le principe du MFM est d'insérer un bit de parité entre chaque bit de donnée réelle. Ce qui implique logiquement que le tampon mémoire MFM doit être deux fois plus grand que celui des données réelles.

Le tordu qui a inventé le codage MFM a décidé de séparer les bits de données en deux zones distinctes. On code d'abord les bits de numéros impairs, puis les bits de numéros pairs, en accord avec le tableau suivant :

Assembleur

Le décodage est assez simple (en tout cas, beaucoup plus simple que le codage). Reportez-vous à la routine "MFMUncode" du programme pour voir comment l'on s'y prend. A noter que le Blitter, paraît-il, pourrait être utilisé pour coder et décoder le MFM... Je n'ai jamais vu l'intérêt d'une telle pratique : le temps machine gagné serait infime, et de plus, cela oblige à utiliser un tampon mémoire de décodage situé en mémoire Chip, alors qu'en utilisant le 68000, ce tampon mémoire peut indifféremment se trouver en mémoire Fast.

Format des secteurs sur disque

En plus du codage, chaque secteur possède un en-tête permettant, entre autres, de vérifier que la lecture s'est bien passée. Le format de cet en-tête est le suivant :
  • 2 octets à $00 ($AAAA chacun en MFM).
  • 2 octets à $A1 ($4489 chacun en MFM).
  • 1 mot long de description du secteur (8 octets MFM).
  • 16 octets réservés à AmigaDOS (32 octets MFM).
  • 1 mot long de somme de contrôle pour l'en-tête (8 octets MFM).
  • 1 mot long de somme de contrôle pour les données (8 octets MFM).
  • 512 octets de données (1024 octets MFM).
Les deux octets nuls marquent le début du secteur et ne sont pas lus par le DMA. Les deux octets suivants servent à indiquer au DMA quand il doit commencer à écrire les données lues en mémoire. Cette valeur $4489 est particulière en MFM : elle correspond à la valeur $A1 codée sans prendre en compte le bit numéro 7. De cette manière, on est sûr que cette valeur ne se retrouvera jamais à l'intérieur des données du secteur, et par la même, qu'elle en indique bien le début. Il est évidemment possible, pour des raisons de protection par exemple, d'utiliser une autre valeur que $4489.

Le mot long de description du secteur est formé de quatre octets, qui contiennent, dans l'ordre : $FF (format Amiga), le numéro de la piste, le numéro du secteur, et le nombre de secteurs restant à lire avant d'atteindre la fin de la piste. Le DMA disque lisant une piste entière depuis une position aléatoire, on connaît ainsi la position du secteur dans la piste.

Les 16 octets suivants sont réservés à AmigaDOS, qui s'en sert pour décider de la manière dont il va assigner les secteurs aux fichiers.

Les deux sommes de contrôle (checksum) permettent de vérifier, pour chaque secteur, que la lecture s'est bien déroulée. Il s'agit d'un "ou exclusif" (EOR) de chaque mot long de l'en-tête et des données, respectivement.

Encore une fois, je ne peux que vous conseiller d'étudier de plus près cette routine. Notez simplement qu'elle ne peut lire que des disquettes au format AmigaDOS standard (OFS ou FFS), et foire complètement avec les disquettes spéciales (protection de jeu, etc.).

Voilà. Un dernier point avant de vous laisser étudier ce programme : je vous avais promis de bannir cet inacceptable délai logiciel entre deux impulsions STEP du lecteur de disquette, au profit d'un délai matériel utilisant l'un des chronomètres offerts par les CIA. Je n'en n'ai malheureusement pas eu le temps. Aussi, c'est avec confusion et humilité que je vous prie de bien vouloir pardonner mes excuses.

	incdir	"include:"
	include	"hardware/custom.i"
	include	"hardware/cia.i"
	include	"exec/exec_lib.i"

	opt	o+,ow-

; ************************************
rCIAA	EQUR	A4
rCIAB	EQUR	A3

; ************************************
CIAA	EQU	$bfe001
CIAB	EQU	$bfd000

DSKRDY	EQU	5		; bits utiles du CIAA-PRA
DSKTRK0	EQU	4
DSKPROT	EQU	3
DSKCHNG	EQU	2

DSKMTR	EQU	7		; bits utiles du CIAB-PRB
DSKSEL3	EQU	6
DSKSEL2	EQU	5
DSKSEL1	EQU	4
DSKSEL0	EQU	3
DSKSIDE	EQU	2
DSKDIR	EQU	1
DSKSTEP	EQU	0

MFMSIZE	EQU	512*12+256	; taille du buffer MFM

Custom	EQU	$dff000

SysCop1	EQU	$26
SysCop2	EQU	$32

WIDTH	EQU	40		; Largeur de l'écran (octets)
HEIGHT	EQU	40		; Hauteur de l'écran (lignes)
BPLSIZE	EQU	WIDTH*HEIGHT	; Taille du bitplan

; ************************************
	rsreset
olddma	rs.w	1
oldint	rs.w	1
oldcop1	rs.l	1
oldcop2	rs.l	1
VARSIZE	rs.w	0

	rsreset
head	rs.w	1		; tête de lecture (0-1)
track	rs.w	1		; piste (0-79)
essais	rs.w	1		; essais en cas d'erreur (max 4)
DVARSIZE rs.w	0

; ************************************
	section	READTRACK,CODE

Start	movea.l	(_SysBase).w,a6
	lea	VARS(pc),a5

	lea	gfxname(pc),a1	; Ouvre la graphics.library
	moveq	#0,d0		; pour y puiser les adresses
	jsr	_LVOOpenLibrary(a6)	; des 2 CopperLists système
	movea.l	d0,a1
	move.l	SysCop1(a1),oldcop1(a5)
	move.l	SysCop2(a1),oldcop2(a5)
	jsr	_LVOCloseLibrary(a6)	; Il faut la refermer !

	lea	Custom,a6	; c'est
	lea	CIAA,rCIAA	; son
	lea	CIAB,rCIAB	; destain !

	move.w	dmaconr(a6),d0	; sauve DMACON
	ori.w	#$8200,d0
	move.w	d0,olddma(a5)

	move.w	intenar(a6),d0	; sauve INTENA
	ori.w	#$c000,d0
	move.w	d0,oldint(a5)

	move.w	#$7fff,d0	; et les efface tous les deux
	move.w	d0,dmacon(a6)
	move.w	d0,intena(a6)
	move.w	d0,intreq(a6)	; ainsi que INTREQ

	lea	NewCop,a0	; Construit notre CopperList
	move.l	#Screen,d0	; (adresse du bitplan)
	move.w	d0,CLPlan-NewCop+6(a0)
	swap	d0
	move.w	d0,CLPlan-NewCop+2(a0)
	move.l	a0,cop1lc(a6)	; et l'active
	move.w	d0,copjmp1(a6)

	move.w	#$8390,dmacon(a6)
	move.w	#$1002,intena(a6)

	bsr.s	StartDrive	; démarre le moteur de DF0:

	moveq	#0,d1
	move.w	#$0F00,d2	; couleur des 2 lignes si erreur
ReadAllDisk:
	btst	#6,ciapra(rCIAA)
	beq.s	Exit
	move.w	d1,d0		; numéro de piste dans d0
	lea	TRACK(pc),a0	; adresse de lecture dans a0
	bsr	ReadThisTrack	; lecture !
	bne.s	Error		; ça n'a pas marché...
	addq.w	#1,d1
	cmpi.w	#159,d1
	bls.s	ReadAllDisk	; boucle pour toutes les pistes

Exit	moveq	#$000F,d2	; couleur des 2 lignes si OK
	btst	#6,ciapra(rCIAA)
	beq.s	Exit

Error	bsr.s	StopDrive	; arrête le moteur de DF0:

	lea	NewCop,a0	; change la couleur des 2 lignes
	move.w	d2,CLCol1-NewCop+6(a0)
	move.w	d2,CLCol2-NewCop+6(a0)

WaitLMB	btst	#6,ciapra(rCIAA)
	bne.s	WaitLMB		; attend Mickey

	move.w	#$7fff,d0	; remet le système dans
	move.w	d0,dmacon(a6)	; son état normal
	move.w	d0,intena(a6)
	move.l	oldcop1(a5),cop1lc(a6)
	move.l	oldcop2(a5),cop2lc(a6)
	move.w	d0,copjmp1(a6)
	move.w	olddma(a5),dmacon(a6)
	move.w	oldint(a5),intena(a6)

	moveq	#0,d0		; Retour au CLI sans code d'erreur
	rts

; ************************************
; Démarre le drive DF0: et place les têtes
; au dessus de la piste 0, face 0
StartDrive:
	bset	#DSKSEL0,ciaprb(rCIAB)	; unselect drive 0
	bclr	#DSKMTR,ciaprb(rCIAB)	; motor on
	bclr	#DSKSEL0,ciaprb(rCIAB)	; select drive 0

.Wait	btst	#DSKRDY,ciapra(rCIAA)	; teste DSKRDY
	bne.s	.Wait			; faut encore attendre

	bset	#DSKDIR,ciaprb(rCIAB)	; dir = -> piste 0
	bset	#DSKSIDE,ciaprb(rCIAB)	; face = lower (0)
.Seek0	btst	#DSKTRK0,ciapra(rCIAA)	; piste 0 atteinte ?
	beq.s	.Track0			; oui
	bsr	MoveHeads		; sinon déplace les têtes
	bra.s	.Seek0			; et boucle
.Track0	rts

; ************************************
; Arrêtre le drive DF0: (la LED s'éteint)
StopDrive:
	bset	#DSKSEL0,ciaprb(rCIAB)	; unselect drive 0
	bset	#DSKMTR,ciaprb(rCIAB)	; motor off
	bset	#DSKMTR,ciaprb(rCIAB)	; motor off
	bclr	#DSKSEL0,ciaprb(rCIAB)	; select drive 0
	rts

; ************************************
; Lecture de la piste D0 dans le buffer pointé par A0.
; Cette routine recherche la bonne piste (Seek), la lit
; et décode les données MFM.
ReadThisTrack:
	movem.l	a0-a6/d1-d5,-(sp)
	lea	DskVars(pc),a5		; a5 = variables "locales"
	clr.w	essais(a5)

	bsr	SeekThisTrack		; recherche la piste à lire

.Retry	bsr	PrintStatus		; affiche les infos
	move.w	#$4000,dsklen(a6)	; efface DSKLEN
	move.l	#MFMBUF,dskpt(a6)	; Adresse de lecture
	move.w	#$4489,dsksync(a6)	; Synchro MFM standard
	move.w	#$7f00,adkcon(a6)	; Efface ADKCON
	move.w	#$9500,adkcon(a6)	; Valeur correcte dans ADKCON
	move.w	#$8000|MFMSIZE,dsklen(a6)	; Longueur de lecture (mots)
	move.w	#$8000|MFMSIZE,dsklen(a6)	; écrite 2 fois
.Wait	move.w	intreqr(a6),d0		; Lecture terminée ?
	andi.w	#$2,d0
	beq.s	.Wait			; Pas encore
	move.w	#$2,intreq(a6)
	move.w	#$4000,dsklen(a6)	; Efface DSKLEN

	bsr.s	MFMUncode		; Décodage des données MFM
	beq.s	.ReadOk

.Error	addq.w	#1,essais(a5)
	andi.w	#3,essais(a5)
	bne.s	.Retry
	moveq	#-1,d0			; erreur de lecture !

.ReadOk	movem.l	(sp)+,a0-a6/d1-d5
	rts

DskVars	dcb.b	DVARSIZE

; ************************************
; Cette routine décode les données MFM de MFMBUF (1 piste)
; dans le buffer pointé par A0.
; Retourne 0 si OK, -1 si erreur
MFMUncode:
	lea	MFMBUF,a1	; données MFM
	move.l	#$55555555,d2	; masque bits impairs
	moveq	#10,d5		; 11 secteurs à décoder
.GAP	cmpi.w	#$4489,(a1)+	; cherche le début du secteur
	bne.s	.GAP
	cmpi.w	#$4489,(a1)
	beq.s	.GAP

	move.l	(a1)+,d0
	and.l	d2,d0
	lsl.l	#1,d0
	move.l	(a1)+,d1
	and.l	d2,d1
	or.l	d1,d0		; d0=format,track,sector,count

	add.w	d0,d0
	andi.w	#$1E00,d0
	lea	0(a0,d0.w),a2	; a2=secteur dans le track-buffer

	lea	36(a1),a1	; saute les infos DOS et le header checksum
	move.l	(a1)+,d0	; d0=data checksum. a1=données
	moveq	#9,d3		; 10 mots longs à vérifier
	lea	-48(a1),a3	; a3 pointe le header (OS recovery info)
.Check	move.l	(a3)+,d1
	eor.l	d1,d0
	dbra	d3,.Check
	and.l	d2,d0
	bne.s	.ReadError

	addq.l	#4,a1
	move.l	(a1)+,d3	; d3=data area checksum
	lea	512(a1),a4	; a1=oddbits, a4=evenbits
	moveq	#127,d4		; 128 mots de données à décoder
.UncodeSector:
	move.l	(a1)+,d0
	eor.l	d0,d3
	and.l	d2,d0
	lsl.l	#1,d0

	move.l	(a4)+,d1
	eor.l	d1,d3
	and.l	d2,d1

	or.l	d1,d0
	move.l	d0,(a2)+
	dbra	d4,.UncodeSector
	and.l	d2,d3
	bne.s	.ReadError
	dbra	d5,.GAP
	moveq	#0,d0
	rts

.ReadError:
	moveq	#-1,d0
	rts

; ************************************
; Positionne les têtes de lecture/écriture au dessus
; de la piste désignée par D0.
SeekThisTrack:
	moveq	#1,d2
	bset	#DSKSIDE,ciaprb(rCIAB)	; face 0
	clr.w	head(a5)
	lsr.w	#1,d0
	bcc.s	.LowerSide
	bclr	#DSKSIDE,ciaprb(rCIAB)	; face 1
	addq.w	#1,head(a5)

.LowerSide:
	move.w	d0,d1
	sub.w	track(a5),d0	; Dans quelle direction aller ?
	beq.s	.SeekOk		; Ben.. Nulle part, on y est déjà !
	bpl.s	.SeekForward	; Vers le sillon 79 (centre)

.SeekBackward:
	bset	#DSKDIR,ciaprb(rCIAB)	; Vers le sillon 0 (extérieur)
	neg.w	d2
	bra.s	.SeekIt

.SeekForward:
	bclr	#DSKDIR,ciaprb(rCIAB)

.SeekIt	bsr.s	MoveHeads	; Déplace les têtes d'1 piste
	add.w	d2,track(a5)	; Inc/Dec le compteur de pistes
	cmp.w	track(a5),d1	; Piste demandée atteinte ?
	bne.s	.SeekIt		; pas encore...

.SeekOk	rts

; ************************************
; Fournit l'impulsion STEP au drive
; suivie du nécessaire délai (soft... argh !)
MoveHeads:
	bset	#DSKSTEP,ciaprb(rCIAB)	; step = high
	nop
	nop
	nop
	bclr	#DSKSTEP,ciaprb(rCIAB)	; step = low
	nop
	nop
	nop
	bset	#DSKSTEP,ciaprb(rCIAB)	; step = high

; ************************************
; Délai logiciel... !!!! ABSOLUMENT INTERDIT !!!!
SoftDelay:
	move.w	#4000,d0
	dbra	d0,*
	rts

; ************************************
; Affiche le numéro de la piste
; en cours de lecture.
PrintStatus:
	movem.l	a0-a3/a6/d1,-(sp)
	lea	.fmt(pc),a0
	lea	DskVars(pc),a1
	lea	.putch(pc),a2
	lea	.buf(pc),a3
	movea.l	(_SysBase).w,a6
	jsr	_LVORawDoFmt(a6)	; Merci Exec...
	moveq	#0,d1
	lea	Screen,a1	; pointeur sur le bitplan
.loop	lea	Numbers(pc),a0	; données des chiffres
	move.b	(a3)+,d1
	beq.s	.ret
	subi.b	#" ",d1
	beq.s	.space
	subi.b	#"0"-" ",d1
	lsl.b	#3,d1
	lea	8(a0,d1.w),a0
.space	move.b	(a0)+,HEIGHT*0(a1)
	move.b	(a0)+,HEIGHT*1(a1)
	move.b	(a0)+,HEIGHT*2(a1)
	move.b	(a0)+,HEIGHT*3(a1)
	move.b	(a0)+,HEIGHT*4(a1)
	move.b	(a0)+,HEIGHT*5(a1)
	move.b	(a0)+,HEIGHT*6(a1)
	move.b	(a0)+,HEIGHT*7(a1)
	addq.l	#1,a1
	bra.s	.loop
.ret	movem.l	(sp)+,a0-a3/a6/d1
	rts

.putch	move.b	d0,(a3)+
	rts

.fmt	dc.b	"%d:%d ;%d<  ",0
.buf	dc.l	0,0,0,0
.var	dc.w	0

; ************************************
Numbers	DC.B	$00,$00,$00,$00,$00,$00,$00,$00	; espace
	DC.B	$7C,$C6,$CE,$D6,$E6,$C6,$7C,$00	; 0
	DC.B	$18,$38,$18,$18,$18,$18,$7E,$00	; 1
	DC.B	$3C,$66,$06,$3C,$60,$66,$7E,$00	; 2
	DC.B	$3C,$66,$06,$1C,$06,$66,$3C,$00	; 3
	DC.B	$1C,$3C,$6C,$CC,$FE,$0C,$1E,$00	; 4
	DC.B	$7E,$62,$60,$7C,$06,$66,$3C,$00	; 5
	DC.B	$3C,$66,$60,$7C,$66,$66,$3C,$00	; 6
	DC.B	$7E,$66,$06,$0C,$18,$18,$18,$00	; 7
	DC.B	$3C,$66,$66,$3C,$66,$66,$3C,$00	; 8
	DC.B	$3C,$66,$66,$3E,$06,$66,$3C,$00	; 9
	DC.B	$06,$0C,$18,$30,$60,$C0,$80,$00	; /
	DC.B	$0C,$18,$30,$30,$30,$18,$0C,$00	; (
	DC.B	$30,$18,$0C,$0C,$0C,$18,$30,$00	; )

; ************************************
VARS	dcb.b	VARSIZE,0
gfxname	dc.b	"graphics.library",0
	even

; ************************************
TRACK	dcb.b	11*512

; ************************************
	section	CHIP,DATA_C

NewCop	dc.w	diwstrt,$2c81,diwstop,$2cc1	; ecran standard 320x200
	dc.w	ddfstrt,$0038,ddfstop,$00d0
	dc.w	bplcon0,$1200			; 1 bitplan
	dc.w	bplcon1,$0000,bplcon2,$0000
	dc.w	bpl1mod,$0000,bpl2mod,$0000
CLPlan	dc.w	bplpt,$0000,bplpt+2,$0000
CLCol1	dc.w	$2a0f,$fffe,color,$00F0
	dc.w	$2b0f,$fffe,color,$0000
CLCol2	dc.w	$530f,$fffe,color,$00F0,bplcon0,$0000
	dc.w	$540f,$fffe,color,$0000
	dc.l	-2

; ************************************
Screen	dcb.b	BPLSIZE
MFMBUF	dcb.w	MFMSIZE

; ************************************
	END



[Retour en haut] / [Retour aux articles] [Article précédent] / [Article suivant]