
ASSEMBLERKURS - LEKTION 21: Diskette (Version 2: Stand: 09/2024)

Autor: Göran Strack


1. Demo (Programm) direkt von Diskette starten
===============================================================================

In Listing21a.s wird eine einfache Methode gezeigt, wie wir eine Demo oder ein
ausführbares Programm auf eine Diskette bekommen um es dann direkt von Diskette
starten zu können.


2. Allgemeine Infos zu Disketten
===============================================================================

Die normale Amiga 3,5"-Diskette (2DD), 2 steht für "double-sided" und DD für
"double-density" hat zwei Speicherseiten, eine obere und eine untere.

Jede Seite hat 80 Tracks (Spuren). Wobei sich Track 0 auf dem äußersten
konzentrischen Ring und Track 79 auf dem innersten befindet. Die Tracks
mit der gleichen Tracknummer auf beiden Seiten der Diskette werden Zylinder
genannt. Jeder Track teilt sich in 11 Sektoren. Jeder Sektor hat eine
Speichergröße von 512 Bytes.

Zusammengefasst:
Jede Diskette hat 2 Seiten.
Jede Diskette hat 80 Tracks auf beiden Seiten. (0-79)
Jede Diskette hat 80 Zylinder.
Jeder Track hat 11 Sektoren. 
Jeder Sektor speichert 512 Bytes (Nutzdaten).
Jede Diskette hat somit 2*80*11=1760 Sektoren.
Damit ermittelt sich die Speichergröße einer Diskette zu 
1760 * 512 Bytes= 901.120 Bytes, oder 901.120 Bytes/1024=880 kBytes

Einfachheithalber wird später von Blöcken bzw. Blocknummern gesprochen und zwar
sind alle 1760 Sektoren die 1760 Blöcke. 

Die Formel zur Berechnung der Blocknummer ist dabei wie folgt:
Block = 2*ll*Zylinder + ll*Seite + Sektor 
(obere Seite = 0, untere Seite =1)

Blocknummer:	Adresse:
0	; Bootblock (bzw. Bootsektor) 2*11*0+11*0+0 = 0
1	; Bootblock (bzw. Bootsektor) 2*11*0+11*0+1 = 1
2
...
879	;							  2*11*39+11*1+10= 879
880	; root-block				  2*11*40+11*0+0 = 880
...


Adresse			Bytes		Sektor		Track	Zylinder	Seite	Block		
[$00-$DBFFF]    [0-901.120]	0-1759		0-79	0-79		0/1		0-1759	
$00				00			0			0		0			0		0	
$200			512			1			0		0			0		1
$400			1024		2			0		0			0		2
...
6DE00			450048		879			39		39			1		879				
6E000			450560		880			40		40			0		880


Das Diskettenlaufwerk hat zwei Leseköpfe auf beiden Seiten die immer 
parallel den gleichen Sektor lesen oder schreiben. 

; Listing21b.s 		RT - Read Track von Diskette mit ASMone
;					WT - Write Track auf Diskette mit ASMone

; Listing21b2.s 	RS - Read Sector von Diskette mit ASMone
;					WS - Write Sector

Action Replay3 >RT-Befehl
	
08400		33792		66	3	3	0	6	66	; RT 6 1 30000 (head 0)	
09A00		39424		77	3	3	1	7	77	; RT 7 1 30000 (head 1)
0B000		45056		88	4	4	0	8	88	; RT 8 1 30000 (head 0)


3. Bootblock
===============================================================================

Der Bootblock ist zwei Sektoren lang (1024 Bytes) und startet bei Sektor 0.

; Listing21b3.s		Bootloader-Programm mit Betriebssystem-Funktionen


4. DoIO() 
===============================================================================

Für den Anfang ist es wichtig zu wissen, dass die Exec-Betriebssystemroutine
DoIO() mit A6=ExecBase und A1=IoStdReq aufgerufen wird. 

jsr	DoIo(a6) mit DoIO = -456 bzw. jsr -456(a6) oder jsr -$1c8(a6)

Dieser IoStdReq ist praktischerweise genau der mit dem das OS zuvor mittels
DoIO() den Bootblock geladen hat. Daher paßt er zum aktuellen Trackdisk-
Laufwerk. Wir brauchen also den Zeiger in a1 nicht laden, es sei denn wir
definieren eine eigene IoStdReq-Struktur.

Die Parameterübergabe, um mittels DoIO() eine I/O-Operation auszuführen,
geschieht innerhalb der IoStdReq Struktur. Diese ist in exec/io.i
definiert:

Um etwas von der Diskette zu lesen muss man also eingeben:
- die Anzahl der zu übertragenen Blöcke * 512 in IO_LENGTH
- die Zieladresse in IO_DATA
- den Startblock in IO_OFFSET (d.h. Blocknummer*512)

oder:

LENGTH - Wieviele Bytes sollen eingelesen werden?
		 (Eingabe: Anzahl Sektoren * 512)
DATA -   Wohin sollen die Daten geschrieben werden?  
		 (Eingabe: Zieladresse im Speicher)
OFFSET - Wo soll mit dem Lesevorgang begonnen werden.
		 (Eingabe: Sektorstartadresse*512)

es gelten folgende Offsets:
DoIO		= -456	-$1c8	; exec.library LVOs
IO_LENGTH	= 36	$24	
IO_DATA		= 40	$28
IO_OFFSET	= 44	$2c	

IO_COMMAND  = 28
CMD_READ	= 2
TD_MOTOR	= 9

- IO_COMMAND ist noch vom vorherigen Lesen des Bootblocks auf CMD_READ
  und kann so bleiben.
Dann kann man DoIO() aufrufen, d.h. "jsr -456(a6)".

  Bsp.
   ;lea  IoStdReq,a1
   ;move.w	#2,28(a1)			; command: Read
  	move.l	#2*512,36(a1)		; Length 2 Sektoren
	move.l	#Puffer,40(a1)      ; Zieladresse
	move.l	#1024,44(a1)        ; ab Sektor 3
	jsr	DoIo(a6) 

Nachdem Übertragen der Daten sollte der Motor ausgeschaltet werden.

	move.l	#0,36(a1)			; motor off
	move.w	#9,28(a1)			; command: MOTOR
	jsr	DoIo(a6)

In der Struktur werden also Informationen über das Kommando (Lesen von Tracks),
der Einleselänge, der Zieladresse und dem Anfangssektor abgelegt. 
Eine Exec-Funktion, namens DoIO() sendet diesen Block ab, und bewegt die
Hardware zum Einlesen der Diskettendaten. Nach dem Absenden sind die Daten im
Speicherbereich. Ist der Returnwert von DoIO() ungleich null, so ist ein
Fehler aufgetreten:

Hinweis 1: Den IoStdReq-Pointer in A1 solltest man vorher retten, falls man
ihn noch mal braucht. Denn A1 ist wie D0,D1 und A0 ein non-volatile Register
und wird bei einem Funktionsaufruf zerstört.

Hinweis 2: IO_DATA sollte bei Kickstart 1.x auf Chip-RAM zeigen. Erst ab
OS2.0 kann das trackdisk.device Daten ins Fast-RAM laden.

Bei Nutzung von OS-Strukturen und Funktionen ist es natürlich immer
hilfreich die entsprechenden Include-Files zu benutzen, wie hier z.B.
        include "exec/io.i"

		;------ Required portion of IO request:

 STRUCTURE  IO,MN_SIZE
    APTR    IO_DEVICE                   ; device node pointer
    APTR    IO_UNIT                     ; unit (driver private)
    UWORD   IO_COMMAND                  ; device command
    UBYTE   IO_FLAGS                    ; special flags
    BYTE    IO_ERROR                    ; error or warning code
    LABEL   IO_SIZE
	
;------ Standard IO request extension:

    ULONG   IO_ACTUAL                   ; actual # of bytes transfered
    ULONG   IO_LENGTH                   ; requested # of bytes transfered
    APTR    IO_DATA                     ; pointer to data area
    ULONG   IO_OFFSET                   ; offset for seeking devices
    LABEL   IOSTD_SIZE


In Listing21c.s sehen wir wie man mittel der Systemfunktion DoIO() Daten oder
Programme an eine bestimmte Speicherstelle laden und dann das Programm von dort
starten kann.


5. MFM-Kodierung
===============================================================================

Magnetismus ist ein physikalisches Phänomen, bei dem sich zwischen zwei 
entgegengesetzten Polen (Nord- und Südpol) ein Magnetfeld ausbreitet. Die
Feldlinien sind dabei in sich geschlossen und ausgerichtet. Ein stärkeres
Magnetfeld wird durch dichtere Magnetfeldlinien gekennzeichnet und als
magnetischer Fluss wird die Gesamtheit der Magnetfeldlinien verstanden. 
Wird der magnetische Fluss auf eine bestimmte Fläche betrachtet, wird dies als
magnetische Flussdichte bezeichnet. Als Magnete gibt es Permanentmagnete und
magnetisierbare Materialien. Auch stromdurchflossene Leiter erzeugen ein
Magnetfeld. Soweit zu den Grundlagen.

Jetzt wieder zurück zur Diskette.
Die Daten werden auf der Diskette auf einer runden Magnetscheibe gespeichert.
Dabei entpspricht das 1-Signal einem Wechsel der Polarität der Magnetisierung
und keine Änderung der Polarität entspricht dem 0-Signal. Durch den 
Polaritätswechsel wird im Lesekopf eine Spannung induziert.

Das führt zu folgenden Problemen. Ein 0-Bit ist eine Nicht-Flussumkehr.
Folgen zu viele Nullen führt das beim Lesen zu Fehlern bei der Synchronisation.
Das Problem hierbei ist der mögliche Verlust des Gleichlaufs, wenn es für eine
gewisse Zeit keine Flussumkehrungen gibt, weil physikalische Faktoren die 
Rotationsgeschwindigkeit verändern können.

Und als 1-Bit wird eine Flussumkehr angesehen. Aus diesem Grund ist keine
11... Sequenz möglich und es kann aufgrund physikalischer Beschränkungen für
DD-Scheiben und Oberflächenmaterial nur alle 4ns eine Umkehrung geben.
MFM hat 2ns-Zellen.

Aus diesem Grund werden Daten auf der Diskette codiert gespeichert und die
MFM-Kodierung ist dabei nur eine Möglichkeit. Eine weitere wäre z.B. GCR.
Ein zweiter Grund für die Codierung ist eine "Markierung" im Syncwort. 

Die MFM-Kodierung (Modified Frequenz Modulation) wird durch folgendem Schema
realisiert. Jedem Datenbit (d) folgt ein Taktbit (t). Dadurch erfolgt eine
Verdopplung der Daten. (aus 8 Bit werden 16 Bits usw.)

- neben einer 1 folgt immer eine 0
- maximal drei Nullen dürfen nebeneinader stehen

1-0		 - 0	Wenn mindestens eins der beiden benachbarten Datenbits gesetzt
0-1		 - 0	ist, wird ein Reset-Taktbit eingefügt.
1-1		 - 0
0-0		 - 1

Beispiel:
Byte:	$A1		%10100001				; Bit 9=0 gedacht
Datenbits		% 1 0 1 0 0 0 0 1		; Lücke zwischen jedem Bit lassen 
Taktbits	    %0 0 0 0 1 1 1 0		; clockbits
Ergebnis		%0100010010101001		; $44A9

Durch dieses Codierung können maximal 3 Nullen nebeneinander stehen. Eine 1
steht immer allein. 

===============================================================================