#define __SFR_OFFSET 0
#include <avr/io.h>
nop
/* MFM-Kodierung (wird bei ED gar nicht verwendet??):
! = Verletzung der Kodierungsvorschrift zur Synchronworterkennung 0xA1
| | |
0 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 Datenbits
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
_____ _____ ! _____ _______ ___ _____ ___
/ \_______/ \_______/ \___/ \_____/ \___/ \___/ \___/ Magnetfluss
___ _____ ___ _____ ___ _ _____ ___ _ _ ___ _ _ _ __ __
\_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \ Signal RD/WD
3 4 3 4 3 2 4 3 2 2 3 2 2 Zeiten
01 01 0 00! 01 1 01 0 0 0 01 1 1 Generierte Bits
0 1 1 0 0 1 1 0 0 0 0 1 1 1 Merkbits
*/
// Als Makro, Unterprogrammaufrufe sind beim AVR lahm und fressen 9 Takte
.macro timed_toggle
sts OCR1AL,r0 ;in Zukunft toggeln lassen
9: sbic TIFR1,OCF1A
rjmp 9b ;warten bis es getoggelt hat
sbi TIFR1,OCF1A ;Interrupt löschen
.endm
// Vorbereiteten Datenblock mit Datenadressmarke (vorn) und CRC (hinten) ausgeben
// R22 = Datenrate 0=ED, 1=HD, 2=DD, 3=SD
// R24 = Anzahl der Taktsynchronbits (Nullen), typisch 12×8 = 48
// R27:R26 = Länge (typisch 515) = Anzahl Datenbytes
// R31:R30 = Adresse im RAM (Zeiger Z)
// 3 A1-Synchronbytes werden automatisch ausgegeben
// Verwendete Register: R0,R1=0,R22,R24,Parameter-Register
outBlockMFM:
sts OCR1AH,r1 ;High-Schattenregister sicherheitshalber löschen
// Addierwert aus Datenrate errechnen
ldi r25,4
inc r22
0: lsl r25
dec r22
brne 0b
mov r1,r25
// Zähler initialisieren und starten
; ldi r25,0b00000001
; sts TCCR1B,r25 ;Zähler starten (falls erforderlich)
lds r0,TCNT1L
sts OCR1AL,r0 ;„ferne Zukunft“ initialisieren
ldi r25,0b01000000
sts TCCR1A,r25 ;„Toggle OC0A“ aktivieren
// hier sicherheitshalber Pin auf High stellen - am besten weglassen
// und in der Mikrocontroller-Initialisierungroutine 1× erledigen
ldi r25,0b10000001
sbis PINB,5
sts TCCR1B,r25 ;Force Output Compare = Flipflopzustand kippen
// Hier Schreibfreigabe aktivieren
cbi PORTE,6
// Taktsynchronbits
0:
rcall 6f
dec r25
brne 0b
// 3 Rahmensynchronbytes (A1)
ldi r25,3
add r0,r1
0: rcall 6f ;1 fertig
add r0,r1 ;0 fertig
add r0,r1
rcall 6f ;1 fertig
add r0,r1 ;0 fertig
rcall 6f ;0 fertig
add r0,r1
add r0,r1 ;spezielle 0 fertig
rcall 6f ;0 fertig
add r0,r1
rcall 6f ;1 fertig
dec r24
brne 0b
clt ;„vorheriges Bit“ löschen
2:
ld r24,Z+ ;Byte aus RAM laden
ldi r25,8 ;Bitzähler
3:
sbrc r24,7
rjmp 7f
brts 1f
// 0-Bit: L-H ausgeben
timed_toggle ;L ausgeben lassen
add r0,r1
timed_toggle ;H ausgeben lassen
rjmp 0f
// 0-Bit: H-H ausgeben (= nichts tun als 2 Schritte warten)
1:
add r0,r1
0: add r0,r1
clt
rjmp 8f
// 1-Bit: H-L ausgeben: Wie L-H ausgeben aber vorher addieren
7:
set ;„Vorheriges Bit“ setzen
add r0,r1 ;später
timed_toggle ;L ausgeben lassen
add r0,r1
timed_toggle ;H ausgeben lassen
8: lsl r24
dec r25
brne 3b
sbiw r26,1
brne 2b
// Hier Schreibfreigabe wegnehmen
sbi PORTE,6
clr r1
sts TCCR1A,r1 ;nicht mehr toggeln
; sts TCCR1B,r1 ;Zähler stoppen (falls erforderlich)
ret
// Als Unterprogramm, wenn Zeit vorhanden ist
// (sollte sogar für HD, nicht aber für ED gehen; ist aber schwer einzuschätzen)
6:
timed_toggle
add r0,r1
timed_toggle
add r0,r1
ret
// Daten (Sektor oder Vorblock) einlesen
// Die Synchronisation erfolgt nicht an den Nullen, sondern nur am Synchronwort 0xA1.
// Das letzte davon genügt sogar, die Mehrfachheit wird an der Verletzung der MFM-Kodierung erkannt.
// Unerwarteter Abbruch wenn R27:R26 am Ende nicht Null ist:
// Zu kleiner oder zu großer Impulsabstand.
// Kein Abbruch bei nicht auffindbarem Synchronwort: Watchdog benutzen!
// Kein Abbruch bei fehlenden Impulsen: Watchdog benutzen!
// Kein Abbruch bei nur (also umlaufendem) Synchronwort: Watchdog benutzen!
// Keine CRC-Prüfung, das muss der Aufrufer erledigen
// R22 = Datenrate: 0 = 1 MBit/s (ED), 1 = 500 kBit/s (HD), 2 = 250 kBit/s (DD), 3 = 125 kBit/s (SD)
// R27:R26 = Datensatzlänge (typ. 515 für DOS-Datensektoren)
// R31:R30 = Pufferbereich (RAM)
// Veränderte Register: Parameter-Register (außer R22) sowie
// R0: Letzter Timer-Wert für Differenzbildung
// R1 = 0 (wird für Interrupt-Acknowledge benötigt)
// R23: Datenbyte
// R24: Capture-Ereignis-Zeitdifferenz in CPU-Takten
// R25: Datenbitzähler (8..1) / im Suchmodus (negativ)
// Timer1 muss initialisiert sein (Zählerlänge auf Vielfaches von 256, also 8..16 Bit)
// Input-Capture muss auf fallende Flanke konfiguriert sein
// Interrupts gesperrt, außer Watchdog (falls via Interrupt)
// Für ED (R22=0) ist eine gesonderte Programmlogik unten, mit einigen Vereinfachungen
inBlockMFM:
ldi r25,1<<ICF1
mov r1,r25 ;1 Takt für Interrupt-ACK sparen (out vs. sbi)
out TIFR1,r1 ;Interrupt löschen
clt ;Im Synchronfeld Nullen detektieren lassen
0: sbic TIFR1,ICF1
rjmp 0b
lds r0,ICR1L ;Ersten Impuls hernehmen
out TIFR1,r1 ;Interrupt quittieren
ldi r25,-1 ;Bitzähler im Synchronisiermodus
tst r23 ;Sonderfall ED?
brne 6f
rjmp 12f ;Zur Sonderroutine springen
// Unterprogramme
0: clt ;0 merken
rjmp 1f ;"0" ausgeben
// Mittlere Schleife für mittleren Impulsabstand (48 CPU-Takte)
3: brts 0b
set ;1 merken, "01" ausgeben
// Vorlaufendes Null-Bit ausgeben,
// nicht auf Synchronwort 0xA1 gucken (kann gar nicht passieren!)
4: lsl r23 ;Null-Bit
dec r25 ;Bitzähler
brne 1f
st Z+,r23 ;Byte abspeichern wenn Bitzähler Null
sbiw r26,1
breq 9f
ldi r25,8
// Innere Schleife für kürzesten Impulsabstand (32 CPU-Takte)
// R25 hält die gesammelten Bits des aktuellen Bytes
// 10 Takte im Abspeichermodus (+ 4 Takte alle 8 Bits)
// Max. 12 Takte im Synchronwortsuchmodus
1: lsl r23 ;Datenbyte
bld r23,0 ;T nach Bit 0 kopieren
2: dec r25 ;Bitzähler
brpl 9f ;Im Synchronmodus wenn negativ
ori r25,0xC0 ;Nicht positiv werden lassen
cpi r23,0xA1 ;Synchronwort 0xA1 vorhanden?
brne 6f
; cpi r25,-4 ;Mit der speziellen Null, vier Bits vorher detektiert?
; brne 6f
rjmp 8f ;Mit Datenbytes starten
9: brne 6f
st Z+,r23
sbiw r26,1
breq 9f ;geschafft
8: ldi r25,8
// Schleife zum Abholen des Input-Capture-Wertes
// Problem: Blockiert ohne Signal ewig. Watchdog verwenden!
// 7 Takte
6: sbic TIFR1,ICF1
rjmp 6b
lds r24,ICR1L
out TIFR1,r1
sub r24,r0 ;Differenz zum vorherigen Wert
add r0,r24 ;nächster vorheriger Wert
// Ergebnis zurechtschieben
// 3 Takte (bei HD)
cpi r22,2
brcs 7f ;500 kBit/s (HD) — Wert belasssen
breq 8f ;250 kBit/s (DD) — Wert halbieren
lsr r24 ;125 kBit/s (SD) — Wert vierteln
8: lsr r24
// Input-Capture-Differenz bewerten
// MFM ist RLL(1,3), es gibt daher nur Lauflängen von 2, 3 oder 4.
7: cpi r24,24 ;Diese Vergleichswerte sind CPU-taktfrequenzabhängig!
brcs 8f ;Fehler: Abstand zu klein
cpi r24,40
brcs 1b ;5 {Σ 25 Takte} 2 Zeiten: 1 Bit: Sende T Merke T
// Ende der inneren Schleife
cpi r24,56
brcs 3b ;3 Zeiten: T==1 ? Sende 0 Merke 0 : Sende 01 Merke 1
// Ende der mittleren Schleife
cpi r24,72
brcc 8f ;Fehler: Abstand zu groß
// Fall Zeiten = 4
brts 4b ;4 Zeiten: Sende 0T Merke T
ldi r25,0 ;Der Fall T==0 darf nur beim A1-Syncbyte passieren
rjmp 4b
// Ende der äußeren Schleife
// Im Fehlerfall abbrechen oder Synchronbyte weitersuchen
8: clt
tst r25
brmi 6b
9: clr r1
ret
/*=== ED ===============================================*
* Hier ist das getrennte Verarbeiten *
* von Synchronisierung und Datenlesen erforderlich *
*=== ED ===============================================*/
/*=== Synchronisierung ===*/
0: clt
lsl r23
rjmp 12f ;5-6; nie 0xA1
// Äußere Schleife für maximalen Impulsabstand (32 CPU-Takte)
// Mittlere Schleife für mittleren Impulsabstand (24 CPU-Takte)
3: brts 0b
set
4: lsl r23 ;3; nie 0xA1
// Innere Schleife für kürzesten Impulsabstand (16 CPU-Takte)
1: lsl r23
bld r23,0
cpi r23,0xA1
breq 6f ;4 -- es geht mit T=1 ins Datenlesen
12: sbic TIFR1,ICF1
rjmp 12b
lds r24,ICR1L
out TIFR1,r1
sub r24,r0
add r0,r24 ;7
cpi r24,20
brcs 1b ;3 → Σ 14 Takte: Okay! Keine Zeit zu kurze Abstände zu detektieren
// Ende der inneren Schleife
cpi r24,28
brcs 3b ;3-1 → Σ max. 19 Takte
// Ende der mittleren Schleife
cpi r24,36
brcs 4b ;generiere 0T
// Ende der äußeren Schleife
clt
rjmp 12b
/*=== Datenlesen ===*/
// „Unterprogramme“
0: clt
lsl r23
rjmp 7f ;Takte: Auf jeden Fall weniger
1:
st Z+,r23
sbiw r26,1
breq 9f
ldi r25,8
rjmp 1f ;(9) - alle 8 Bits
2:
st Z+,r23
sbiw r26,1
breq 9f
6: ldi r25,8
rjmp 2f ;(9) - alle 8 Bits
// Äußere Schleife für maximalen Impulsabstand (32 CPU-Takte)
// Mittlere Schleife für mittleren Impulsabstand (24 CPU-Takte)
3: brts 0b
set
4: lsl r23
dec r25
breq 1b ;5
// Innere Schleife für kürzesten Impulsabstand (16 CPU-Takte)
1: lsl r23
bld r23,0
7: dec r25
breq 2b ;4
2: sbic TIFR1,ICF1
rjmp 2b
lds r24,ICR1L
out TIFR1,r1
sub r24,r0
add r0,r24 ;7
cpi r24,20
brcs 1b ;3 → Σ 14 Takte: Okay
// Ende der inneren Schleife
cpi r24,28
brcs 3b ;3-1 → Σ max. 21 Takte
// Ende der mittleren Schleife
cpi r24,36
brcc 9f ;Abstand zu groß
brtc 12b ;Zurück zur Synchronisierung (mit T=0)!
rjmp 4b ;6-1 → Σ 28 Takte: Genug Platz (32 Takte)
// Ende der äußeren Schleife
9: clr r1
ret
Vorgefundene Kodierung: UTF-8 | 0
|