\ **************************************************** \ * * \ * BOOTMA10.FTH * \ * * \ * Zutaten fuer FAT-Reparatur und Bootmanager unter * \ * Turbo-FORTH-83 * \ * * \ * Fred Behringer - Forth-Gesellschaft - 30.8.2011 * \ * * \ **************************************************** \ ==================================================== \ Auch im vorliegenden Artikel wird wieder darauf ver- \ zichtet, die Programme fuer das ZF-System ebenfalls \ einzurichten. Sie sind nur fuer Turbo-Forth gedacht! \ Neu ist putdiskimage mit Zubringer (putdiskimage). \ Die aus Teil 8 benoetigten Worte werden wiederholt. \ ==================================================== hex \ Alle Eingaben im vorliegenden Listing sind hexadezimal ! \ 32-Bit-Assembler-Erweiterung \ ---------------------------- \ Es wurde alles mit einem AMD-K6-2/500 ausprobiert (vergleiche Teil 8). only forth also assembler definitions : fs: 64 c, ; : gs: 65 c, ; : opsize: 66 c, ; : adrsize: 67 c, ; : eax opsize: ax ; : ecx opsize: cx ; : edx opsize: dx ; : ebx opsize: bx ; : esp opsize: sp ; : ebp opsize: bp ; : esi opsize: si ; : edi opsize: di ; \ Freischalten der Adressleitung a20 \ ---------------------------------- forth definitions \ himem.sys in der config.sys reicht an sich. Ansonsten free-a20 verwenden: code free-a20 ( -- ) cli begin 64 # al in 02 # al and 0= until 0d1 # al mov 64 # al out begin 64 # al in 02 # al and 0= until 0df # al mov 60 # al out begin 64 # al in 02 # al and 0= until sti next end-code : a20? ( -- fl ) 0ffff 10 l@ 0. l@ <> 0= ; \ fl=-1 --> a20 gesperrt \ Segment-Register fs und gs (im PC ab 80486 vorhanden) bearbeiten \ ---------------------------------------------------------------- \ cs ds es benoetigen im vorliegenden Artikel keine Sonderbehandlung. code fs@ ( -- cc ) 0f c, 0a0 c, next end-code \ f-Segment holen: fs push code gs@ ( -- cc ) 0f c, 0a8 c, next end-code \ g-Segment holen: gs push code fs! ( cc -- ) 0f c, 0a1 c, next end-code \ f-Segment setzen: fs pop code gs! ( cc -- ) 0f c, 0a9 c, next end-code \ g-Segment setzen: gs pop \ Datenverkehr ueber den gesamten 32-Bit-Systembereich \ ---------------------------------------------------- \ Die folgenden Store- und Fetch-Befehle laufen ueber fs. Lineare Adressen \ per 0 fs! - In dump-32 ist fs=0 Default. fs wird dabei 'gerettet'. Beachte \ das in Teil 8 ueber Stack-Kommentare bei Punktzahl-Eingaben Gesagte. code c@-32 ( ad. -- c ) \ Unmittelbares Byte c von Adresse ad. zum Stack ebx pop opsize: 0c1 c, 0c3 c, 10 c, ( 10 # ebx rol ) ax ax xor fs: adrsize: 8a c, 03 c, ( fs:[ebx] al mov ) 1push end-code code c!-32 ( c ad. -- ) \ Byte c vom Stack nach Adresse ad. speichern ebx pop opsize: 0c1 c, 0c3 c, 10 c, ( 10 # ebx rol ) ax pop fs: adrsize: 88 c, 03 c, ( al fs:[ebx] mov ) next end-code code cc@-32 ( ad. -- cc ) \ Doppelbyte cc von Adresse ad. zum Stack ebx pop opsize: 0c1 c, 0c3 c, 10 c, ( 10 # ebx rol ) fs: adrsize: 8b c, 03 c, ( fs:[ebx] ax mov ) 1push end-code code cc!-32 ( cc ad. -- ) \ Doppelbyte cc nach Adresse ad. speichern ebx pop opsize: 0c1 c, 0c3 c, 10 c, ( 10 # ebx rol ) ax pop fs: adrsize: 89 c, 03 c, ( ax fs:[ebx] mov ) next end-code code @-32 ( ad. -- d ) \ 32-Bit-Wert d von Adresse ad. zum Stack (32-Bit) ebx pop opsize: 0c1 c, 0c3 c, 10 c, ( 10 # ebx rol ) opsize: fs: adrsize: 8b c, 03 c, ( fs:[ebx] eax mov ) opsize: 0c1 c, 0c0 c, 10 c, ( 10 # eax rol ) eax push next end-code code !-32 ( d ad. -- ) \ 32-Bit-Wert d in 4 Bytes ab Adresse ad. legen ebx pop opsize: 0c1 c, 0c3 c, 10 c, ( 10 # ebx rol ) eax pop opsize: 0c1 c, 0c0 c, 10 c, ( 10 # eax rol ) opsize: fs: adrsize: 89 c, 03 c, ( eax fs:[ebx] mov ) next end-code \ Absicherung gegen RAM-Bereichs-Ueberschreitung \ ---------------------------------------------- \ Man achte auf fs (lineare Adressen erfordern 0 fs!). Dieser RAM-Test \ schleift beliebige Eingaben fuer ad. durch. Im weiter unten stehenden \ Forth-Wort putdiskimage wird vor der Fehlermeldung erst noch 161f00 \ hinzuaddiert, um sicherzustellen, dass es sich wirklich um einen als \ Disk-Image plausiblen RAM-Bereich handelt. : ramtest ( ad. -- ad./abort ) \ ad. = Punktzahl (doppeltgenau) fs@ -rot \ fs aufbewahren 2dup @-32 2swap \ (ad.) aufbewahren 5a5a5a5a. 2over !-32 2dup @-32 5a5a5a5a. d= -rot a5a5a5a5. 2over !-32 2dup @-32 a5a5a5a5. d= -rot 2swap and not >r 2swap 2over !-32 rot fs! r> cr abort" Bei dieser 32-Bit-Eingabe wird RAMmax ueberschritten!" ; \ 32-Bit-RAM dumpen (dump-32). Ausfuehrlicherer Kommentar in Teil 8. \ ------------------------------------------------------------------ code 2tuck ( d1 d2 -- d2 d1 d2 ) \ Zur Vereinfachung von dump-32 eax pop ecx pop eax push ecx push eax push next end-code : d0<= ( d -- fl ) 0. d> not ; \ Zur Vereinfachung von dump-32 variable ascfilt ascfilt on \ Nur ASCII-Zeichen bei dump ? on = ja variable fs-dump-32 0 fs-dump-32 ! \ fs fuer dump-32. Vorgabe 0 ('linear'). : ?ascii ( n1 -- n2 ) \ Nicht-ASCII-Zeichen --> Punkt ? ascfilt @ \ true = ja if dup 20 7e between not \ nicht ASCII ? if drop 2e then \ dann Punkt else dup 7 = \ Bell over 8 = or \ Del over a = or \ Linefeed over d = or \ cr over 1b = or \ esc over 0ff = or \ Bell usw. ? if drop bl then \ dann Blank then ; : dump^ ( -- ) ." 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF" ; : dump-32 ( ad. len. -- ) \ Stop: SPACE. Weiter: SPACE. Exit: CR CR cr >r >r >r >r fs@ fs-dump-32 @ fs! \ Mehr: + . Nochmal zurueck: - base @ hex r> r> r> r> 2over \ Anzeige in Hex 10 mu/mod 2drop >r 2swap r@ 0 d- r> dup 3 * dup 17 > if 1+ then 0a + (cursor) nip 0b swap at dump^ (cursor) nip at bold ." \/" 3c + (cursor) nip at ." V" attoff cr begin begin 2dup <# # # # # # # # # #> type space space \ Adresse anzeigen 8 0 do 2dup c@-32 0 <# # # #> type space 1. d+ loop space 8 0 do 2dup c@-32 0 <# # # #> type space 1. d+ loop space 10. d- 10 0 do 2dup c@-32 ?ascii emit 1. d+ loop cr 2swap 10. d- 2tuck d0<= stop? or \ Ende oder Abbruch ? until (cursor) dup 18 = if 0 0 at 0b spaces dump^ then at bold (cursor) -1 0 (frame) ." Weiter: + Zurueck: - Exit: RET" attoff at key dup ascii + = if drop 2swap 2drop 100. 2swap false else ascii - = if 2swap 2drop 100. 2swap 200. d- dark 0b 0 at dump^ cr false else 2drop 2drop base ! fs! true then then until 0c 0d (frame) cr ; \ Es folgt der 18d-Sektor-Puffer trackbuf (vergleiche Teil 8 der Serie). \ ---------------------------------------------------------------------- 2410 allot here \ Platz fuer mind. 18d Sektoren = 512d * 18d Bytes here 0f and - \ trackbuf an Paragraphenanfang 2400 - \ Anfang des Uebertragungs-Puffers constant trackbuf \ Liefert Anfangsadresse des Puffers fuer 1 Spur. 2variable imgbuf \ Anfangsadresse des Disk-Image-Puffers (Punktzahl). 2variable imgbyte \ Adresse 1. freies Byte nach dem letzten Doppelbyte. variable spur \ Letzte geschriebene Spur variable seite \ Letzte geschriebene Seite variable flag \ Fehler-Flag (0/1) fuer (putdiskimage) : 1 = Fehler \ Die bis hierher aufgefuehrten Worte sind auch in Teil 8 enthalten. Mehr \ Kommentare siehe dort. Das Listing aus Teil 8 kann auch mit dem hiesigen \ Listing von hier ab zusammengefuehrt werden, ohne etwas zu verlieren. \ Das folgende Wort (putdiskimage) ist die Code-Vorstufe zu putdiskimage. \ Es ist analog zu (getdiskimage) aufgebaut, nur dass jetzt 'int 13h/3' \ (Sektoren schreiben) statt 'int 13h/2' (Sektoren lesen) angesprochen wird. \ Ausserdem kehrt sich natuerlich jetzt der Datentranfer um, also Spur fuer \ Spur von imgbuf ueber trackbuf (je 1 Spur) per INT 13h/3 zur Diskette. code (putdiskimage) ( -- ) ds push \ ds (push = mit Stack als Zwischenstation) es pop \ nach es uebertragen. 0 # flag #) cmp 0= if \ di frei verwendbar. trackbuf \ Anfang des Puffers fuer Transfer der naechsten Spur # di mov \ nach di legen. imgbyte #) push \ Hi-Anteil der 2Variablen imgbyte auf den Stack legen. imgbyte 2 + #) push \ Lo-Anteil der 2Variablen imgbyte auf den Stack legen. ebx pop \ Disk-Image-Zeiger liegt jetzt little-endian in ebx. clc \ ------------------------------------------------- 1200 do \ imgbuf>trackbuf: 1200h = 4608d Doppelbytes = 9216 B \ ------------------------------------------------- \ cx = Zaehler fuer do-loop. cl wird dann neu gesetzt. adrsize: fs: \ Auf Segment fs bezogen und mit 32-Bit-Adressbreite. 8b c, 03 c, \ Das entspricht [ebx] ax mov (beachte adrsize). ax 0 [di] mov \ di = trackbuf (16 Bit). Keine edi-Erweiterung noetig. \ ------------------------------------------------- di inc \ ax uebertraegt ein Doppelbyte. Die Adresse di muss di inc \ sich also anschliessend um 2 erhoehen: di = di+2. ebx inc \ ebx muss 1,44 MB ansprechen. Die 16 Bit fuer bx ebx inc \ reichen nicht. ax fasst 2 Bytes. Also ebx = ebx+2. loop \ ------------------------------------------------- ebx push \ Adresse des ersten freien Bytes nach dem zuletzt imgbyte 2 + #) pop \ gesendeten Doppelbyte. Lo-Anteil davon forthrichtig imgbyte #) pop \ in 2Variable imgbyte legen, Hi-Anteil entsprechend. then \ ------------------------------------------------- 3 # di mov \ Zaehler fuer 3 Disketten-Schreibversuche ansetzen. begin \ ------------ Bis zu 3 Schreibversuche ----------- 0 # flag #) cmp 0<> \ ------------- Laufwerk initiieren ? ------------- if dl dl xor \ dl = 0 = Diskettenlaufwerk a: ah ah xor \ Funktion 0 von Int 13h aufrufen, also 13 int \ das Disketten-System zuruecksetzen. then \ ---------- Bemerkung hierzu siehe unten --------- \ -------- 1 Spur (18d Sektoren) schreiben -------- seite #) dh mov \ Seitennummer dieses Spurendurchgangs spur #) ch mov \ Spurennummer dieses Spurendurchgangs trackbuf \ 18d Sektoren (= 1 Spur = 9216d = 2400h B) schreiben # bx mov \ bx = Anfangs-Offset des Spur-Transfer-Puffers 1 # cl mov \ cl = Sektor 1 in der laufenden Spur (18d Sektoren) dl dl xor \ dl = 0 = Diskettenlaufwerk a: 3 # ah mov \ ah = 3 (Schreibfunktion von Int 13h) 12 # al mov \ al = 12h = 18d Sektoren = 1 Spur auf ch/dh schreiben 13 int \ Schreib-Interrupt aufrufen. u>= if \ Wenn danach cf = 0 (kein Lesefehler), dann Aussprung di di xor \ aus der Schleife per di = 0 vorbereiten und 0 # flag #) mov \ flag = 0 (kein Fehler) setzen. else \ Wenn jedoch cf <> 0 (Lesefehler), di dec \ dann Zaehler fuer 3 Schreibversuche dekrementieren 1 # flag #) mov \ und flag = 1 (Fehler) setzen. then 0 # di cmp 0= \ Aussprung aus der 3er-Schleife mit flag = 0 oder 1. until \ ------------------------------------------------- next end-code \ -- Bemerkung zu int 13h/0 (Laufwerk initiieren) -- \ Das Disketten-Laufwerk braucht nicht fuer jede Spur \ neu initiiert zu werden (vergl. Bemerkung in Teil 8). : printparams ( -- ) \ Ausdruck von flag-Meldung imgbuf imgbyte seite spur cr cr ." =================================================" cr flag @ 1 = if ." Fehler beim Beschreiben der Diskette" cr then flag @ 0 = if ." Die Diskette wurde ohne Schreibfehler erstellt" cr then ." -------------------------------------------------" cr ." Erstes geschrieben. Byte v. RAM-Adresse : " imgbuf 2@ ud. cr ." Letztes geschrieben. Byte v. RAM-Adresse : " imgbyte 2@ 1. d- ud. cr ." Letzte Seite; oder erste, die Fehler hat : " seite @ u. cr ." Letzte Spur ; oder erste, die Fehler hat : " spur @ u. cr ." -------------------------------------------------" cr ." Falls Sp/S = 0/0 : Wirklich Diskette im Laufwerk?" cr ." =================================================" cr cr ; : putdiskimage ( imgbuf -- ) 0 fs! \ Disk-Image im RAM auf f-Segment = 0 beziehen. 161f00. d+ \ Laenge der Diskette (in Bytes) hinzuaddieren. ramtest \ Ist das vielleicht gar kein Disk-Image (Platz)? 161f00. d- \ Wieder zum Anfang des Disk-Image-Puffers gehen. 2dup \ Zunaechst lege ich den imgbuf 2! \ Anfang des Disk-Image-Puffers in die 2Variable imgbuf imgbyte 2! \ und dann (zunaechst auch) in die 2Variable imgbyte. 0 spur ! \ Letzte geschriebene Spur zunaechst auf 0 setzen. 0 seite ! \ Letzte geschriebene Seite zunaechst auf 0 setzen. 0 flag ! \ Fehler-Flag zunaechst auf 0 setzen. begin (putdiskimage) seite @ 0 = \ Spuren und Seiten beginnen bei 0. Seiten: 0/1. if \ Wenn bei einer bestimmten Spur die Seite auf 0 steht, 1 seite ! \ dann Seite auf 1 schalten und Spur beibehalten. else \ Ansonsten steht die Seite auf 1. 0 seite ! \ Dann Seite wieder auf 0 setzen spur 1+! \ und Spur (es gibt 50h davon) weiterschalten. then spur @ 50 = \ Verwendbare Spuren: 0-4f. Wenn 50h erreicht ist, flag @ 0<> or \ oder auch schon vorher, wenn naemlich ein Fehler until \ aufgetreten ist, dann begin-until-Schleife verlassen. seite @ 1 = \ Spuren- und Seiten-Fortschaltung rueckgaengig machen. if \ if-then hier analog zu if-then eben. 0 seite ! else 1 seite ! spur 1-! then printparams ; \ Bildschirm-Ausgabe der (Fehler-)Parameter. \ Das folgende Wort falsify, mit dem Zubringer-Wort (falsify), steigert das \ Vertrauen in das Gelungensein einer Disketten-Kopieraktion (Klonen). 2variable ad1 \ Anfangsadresse des 1. Disketten-Images (Punktzahl) 2variable ad2 \ Anfangsadresse des 2. Disketten-Images (Punktzahl) 2variable ad3 \ 1. Fehladresse (Punktzahl) ab ad2, falls ueberhaupt 2variable falsi \ Anzahl der unstimmigen Bytepaare aus ad1 und ad2, \ bezogen auf ad2, Punktzahl. \ Es wird vorausgesetzt, dass ad2 > ad1+161f00h. \ Das Folgende ist nur ein zaghafter Versuch. Versierte \ Programmierer wuerden das sicher geschickter angehen. \ Im Grunde genommen berechne ich die 'effektiven' \ Adressen explizit, um mich in der 32-Bit-Adresslogik \ innerhalb einer 16-Bit-Umgebung nicht zu verlieren. code (falsify) ( -- ) ad1 #) push \ Hi-Anteil der 2Variablen ad1 auf den Stack legen ad1 2 + #) push \ Lo-Anteil der 2Variablen ad1 auf den Stack legen ebx pop \ Adress-Zeiger aus ad1 jetzt little-endian in ebx ad2 #) push \ Hi-Anteil der 2Variablen ad2 auf den Stack legen ad2 2 + #) push \ Lo-Anteil der 2Variablen ad2 auf den Stack legen edx pop \ Adress-Zeiger aus ad2 jetzt little-endian in edx opsize: b9 c, \ Zaehler ecx fuer Bytevergleiche wird jetzt little 1f00 , 0016 , \ endian mit 161f00 (Anzahl der Disk-Bytes) geladen. begin \ -------------------------------------------------- ecx dec \ Naechster Schritt: Zaehler ecx um 1 vermindern ebx push \ ebx auf Stack zwischenspeichern ecx bx add \ ebx := ebx+ecx fs: adrsize: 8a c, 03 c, \ fs:[ebx] al mov (al = Byte an effektiver Adresse [ebx]) edx bx mov \ ebx := edx ecx bx add \ ebx := ebx+ecx fs: adrsize: 8a c, 23 c, \ fs:[ebx] ah mov (ah = Byte an effektiver Adresse [ebx]) ah al cmp \ al <> ah ? 0<> if \ Wenn ja, dann ebx push \ ebx auf den Stack legen ad3 2 + #) pop \ Neuen Lo-Anteil der 2Variablen ad3 vom Stack holen ad3 #) pop \ Neuen Hi-Anteil der 2Variablen ad3 vom Stack holen falsi #) push \ Hi-Anteil der 2Variablen falsi auf den Stack legen falsi 2 + #) push \ Lo-Anteil der 2Variablen falsi auf den Stack legen ebx pop \ Inhalt von falsi little endian nach ebx legen. ebx inc \ ebx um 1 erhoehen. ebx push \ Den erhoehten Wert auf den Stack legen. falsi 2 + #) pop \ Den Lo-Anteil von falsi vom Stack holen falsi #) pop \ Den Hi-Anteil von falsi vom Stack holen then ebx pop \ ebx fuer naechsten Schritt vom Stack zurueckholen eax ax xor \ eax := 0 opsize: eax cx cmp \ cmp = 0 ? 0= \ Aussprung nach Abarbeiten von 161f00h Bytes (= 1 Disk) until \ ------------------------------------------------------ next end-code : printfalsi ( -- ) \ Ausgabe der Parameter von falsify auf den Bildschirm \ Die Bytepaare werden von hohen nach niedrigen Adressen \ hin abgefragt. Die Adressen der unstimmigen Bytes des \ 2. Disketten-Images (soweit solche vorhanden sind) \ werden voruebergehend aufbewahrt. Was uebrig bleibt, \ ist gegebenenfalls die erste solche Adresse. cr cr ." ==========================================" cr ." 1. Disketten-Image ab RAM-Adresse: " ad1 2@ ud. cr ." 2. Disketten-Image ab RAM-Adresse: " ad2 2@ ud. cr falsi 2@ 0. d= if ." Die Disk-Images waren byteweise gleich" cr else ." Die Disketten-Images waren verschieden" cr ." Anzahl der unstimmigen Byte-Paare: " falsi 2@ ud. cr ." 1rst unstimmiges Byte im 2. Image: " ad3 2@ ud. cr then ." ==========================================" cr cr ; : falsify ( ad1. ad2. -- ) \ ad1. ad2. Punktzahlen 0 fs! \ Disk-Image im RAM auf f-Segment = 0 beziehen. ad2 2! \ Anfangsadresse des 2. Images in die 2Variable ad2 ad1 2! \ Anfangsadresse des 1. Images in die 2Variable ad1 0. falsi 2! \ Anfangswert fuer Anzahl der unpaarigen Bytes 0. ad3 2! \ Ruecksetzen fuer erneuten Aufruf (falsify) \ Bytepaare vergleichen und Parameter sammeln printfalsi ; \ Bildschirmausgabe der Parameter \ =======================================================================