Hilfe: Segmentgrenzenüberlauf des DMA



  • Also ich komme nun einfach seit Stunden nicht mehr weiter! 😞 Ich möchte ganz einfach Daten von einer Floppydisk in den Arbeitsspeicher kopieren, was auch ganz gut klappt, bis plötzlich eben dieser Fehler kommt. Ich habe festgestellt, dass 3 Spuren komplett eingelesen werden, bei der 4rten Spur jedoch kommt plötzlich eben dieser Fehler: Fehlercode 9 : Segmentgrenzenüberlauf des DMA! Ich habe sämtliche Parameter der Funktion 02h des Interrupts 13h überprüft und die Werte sind absolut korrekt. Die Zieladdresse beträgt 0007c0:006c00, was absolut korrekt ist: 3 Spuren * 18 Sektoren * 512 Bytes gleich 27648 Bytes = 6c00h Bytes.

    Leider habe ich auch überhaupt keine Idee, was dieser Fehler überhaupt bedeutet, geschweige denn, wodurch er verursacht wird!

    Hat jemand von euch eine Idee, was ich falsch mache?



  • DMA-Transfers funktionieren nur innerhalb von 64KByte-Bloecken. Von der phys. Addr. 0 an wird der Speicher in 64kByte grosse Bloecke unterteilt.
    Bei Adresse 07c0:6c00 => phys. E800 ueberschreitest du eine solche 64K-Grenze, wenn du mehr als 12 Sektoren am Stueck einliest.



  • Sag mal, woher weisst du dass eigentlich alles? 😮 Ich hatte diesen Fehler bei google eingegeben und 4 mal diesselbe Interrupttabelle vorgefunden, jedoch keine Erklährung! 😞

    Wenn ich dich nicht hätte, hätte ich vermutlich längst aufgegeben! 😃

    Lg Ishildur



  • Ok, das klappt nun ganz gut! 🙂 Schade, kommt bereits das nächste Problem! :p

    Ich rufe den Interrupt 13h mit folgenden Argumenten auf: (Zu Debugzwecken)

    es = 17c0h (2. 64Kb Block nach 07c0h)
    ax = 0201h (1 Sektor = 512 Bytes lesen)
    bx = 0000h (An die physikalische Adresse 17c0h*10h+00h=17c00h kopieren)
    cx = 0703h (Spur 8, Sektor 3 lesen)
    dx = 0000h (Seite 1, Laufwerk A lesen)
    int 13h (Loooooos!)

    Eigentlich sollte er doch auf der Diskette folgenden Sektor lesen:
    (7*18+3)*512 = 2^16+512!

    Weil die Indexierung der Sektoren aber ja offenbar bei 1 anstatt bei 0 beginnt, kommen wir auf 2^16+512-512 = 2^16.

    Das wäre auf der Diskette an der physikalischen Addresse 10000h!

    Aus irgendeinem mir absolut nicht nachvollziebaren Grund lädt er aber die Daten, welche sich auf der Diskette an der physikalischen Adresse 1eeffh befinden?! 😮

    Ich habe das Image, welches ich anschliessend mit RawWrite auf die Diskette schreibe mit einem Hexeditor analysiert und herausgefunden, dass sich die gewünschten Daten genau bei 10000h befinden!

    Das Assemblerprogramm habe ich anschliessend so verändert, dass es nach dem Laden jenes Sektors diesen komplett auf dem Bildschirm ausgibt! Nach einem Vergleich jener Daten mit dem denjenigen des Hexeditors habe ich herausgefunden, dass sich diese eben an der Adresse 1eeffh im Image befinden!

    Was mache ich falsch! Ich bin echt ratlos!



  • Ishildur schrieb:

    Sag mal, woher weisst du dass eigentlich alles? 😮

    ~5Jahre(?) mehr oder minder planloses Gefrickel und Fragen in diesem Forum lesen?
    Das mit dem DMA auf jeden Fall Eigenerfahrung beim Programmieren eines SoundBlaster-Treibers. Mit dem Ueberschreiten dieser 64K-Grenzen kann man emm386.exe ganz schoen ins Schwitzen bringen. 😉

    Ishildur schrieb:

    Ich hatte diesen Fehler bei google eingegeben und 4 mal diesselbe Interrupttabelle vorgefunden, jedoch keine Erklährung! 😞

    Wenn ich dich nicht hätte, hätte ich vermutlich längst aufgegeben! 😃

    Tja, dazu sage ich immer: Freut mich, wenn ich mit meinem begrenzten Wissen ueber museumsreife PC-Techniken noch jemandem helfen konnte. 😃

    ...

    Ishildur schrieb:

    Eigentlich sollte er doch auf der Diskette folgenden Sektor lesen:
    (7*18+3)*512 = 2^16+512!

    Weil die Indexierung der Sektoren aber ja offenbar bei 1 anstatt bei 0 beginnt, kommen wir auf 2^16+512-512 = 2^16.

    Das wäre auf der Diskette an der physikalischen Addresse 10000h!

    Komische Umrechnerei, die du da betreibst... Zudem ist schon deine 1. Formel zur CHS-Umrechnung falsch.
    Die Formel fuer LBA lautet
    LBA = Cyl * Num_Head * Num_Sec + Head * Num_Sec + (Sec - 1)
    Bzw. *512 fuer das absolute Offset.

    Fuer dein Beispiel oben ergibt das:
    (7 * 18 * 2 + 0 * 2 + (3 - 1)) * 512 = 0001 FC00
    Die von dir angegebene Adresse muss ansonsten irgendwie durch anderen Fehler in deinem Code oder Image (...) zustande kommen.

    Andersrum sieht die Umrechnung Offset->CHS uebrigens so aus (Bzw. LBA->CHS).
    Cyl = (Offs >> 9) / (Num_Head * Num_Sec)
    Head = ((Offs >> 9) mod (Num_Head * Num_Sec)) / Num_Sec
    Sec = (((Offs >> 9) mod (Num_Head * Num_Sec)) mod Num_Sec) + 1

    Am besten liest du dir nochmal genau die Artikel ueber LBA und CHS durch, die ich dir bereits verlinkt hatte.



  • Ich dachte bisher immer, die Daten werden auf einer Diskette folgendermassen gespeichert:

    Head 0, Track 0, Sektor 1 bis Head 0 Track 79 Sektor 18 dann
    Head 1, Track 0, Sektor 1 bis Head 1 Track 79 Sektor 18 usw.

    Bei mir ist es allerdings so, dass nach der ersten Spur, erst einmal der Kopf gewechselt wird also:

    Head 0, Track 0, Sektor 1 bis Head 0 Track 0 Sektor 18 dann
    Head 1, Track 0, Sektor 1 bis Head 1 Track 0 Sektor 18 dann
    Head 0, Track 1, Sektor 1 bis HEad 0 Track 1 Sektor 18 usw.

    Ist das normal? Ich erstelle die Datei mit dem DOS Befehl
    copy /B <File 1>+<File 2>+<File x> <Image>

    Lg Ishildur



  • Ja, ist normal, macht auch Sinn und laesst sich aus der Umrechnungsformel eigentlich auch klar erkennen...



  • Ich habe eben diese Umrechnungsformel gar nie betrachtet, weil ich ein eigenes Adressierungssystem entwicklet habe! 😃

    Du ich habe da schon wieder etwas, was ich nicht verstehe, aber ich traue mich nicht mehr, desswegen schon wieder einen neuen Thread zu eröffnen! :p

    ScrAct dw 0000h
    
    ; alle klar, funktioniert!
    call MyFnc
    
    ; funktioniert nicht
    mov  ax,MyFnc
    call ax
    
    ; functioniert ebenfalls nicht
    mov [ScrAct],MyFnc
    call [ScrAct]
    
    MyFnc: <Testcode>
           ret
    

    Wieso geht dass denn bei den beiden unteren nicht?! 😮

    Lg Ishildur

    P.S.
    Ich verwende den Netwide Assembler



  • Ich rate mal ins Blaue:

    Zum 2.:
    Klappt AFAIK erst ab 386. Ueberpruefe mal, fuer welche CPU du genau assemblierst.

    Zum 3.:
    Da fehlt die Groessenangabe des Operanden (word, dword, fword, whatever).

    Sonst: Gewoehn dir bitte mal an, bei deinen Fragestellungen etwas genauer zu werden. Das erhoeht bekanntlich auch die Chance, dass dir bei der Formulierung schon eine Loesung einfaellt.
    Speziell hier: Wie genau aeussert sich "geht nicht"? Fehler beim Assemblieren? Wenn ja: Welche? Exceptions? Spruenge ins Nirvana? Welche/wohin, und was sagt dein Lieblings-Debugger dazu?



  • Hallo Nobuo T
    Es ist alles für den 8086 und assemblieren lässt es sich problemlos! Was hinterher passiert, kann ich nicht sagen, weil das ganze ja im Bootsektor stattfindet, und ich da AFAIK keinen Debugger verwenden kann. Es äussert sich einfach dadurch, dass er ganz offensichtlich nicht zu der entsprechenden Marke springt!

    Ich habe mir inzwischen überlegt, dass es vielleicht etwas mit der Art des Opcodes zu tun haben könnte. Während ja bei bspw. call <Label> die relative Adresse zum call verwendet wird, wird bei call <variable> oder call <register> die absolute Adresse verwendet. Kann es sein, dass man dem Assembler noch spezielle sagen muss, dass er hier die absolute und nicht die relative Adresse einsetzen soll? ich denke doch aber, dass er das selbst herausfindet, immerhin findet er auch den korrekten Opcode heraus?!



  • Ja, die richtige Art der Adressbildung kriegt selbst der NASM gerade noch hin. 😉

    Zum Debuggen dieses Codes gibt es nun wirklich mehr als eine Moeglichkeit.
    Wenn du tatsaechlich deinen Bootsektor-Code unveraendert laufen lassen willst, kannst du das in einem Emulator wie Bochs machen - der hat auch einen eingebauten Debugger.
    Sonst besteht noch die Moeglichkeit, dass du deinen Code mehr oder weniger umschreibst, so dass er in DOS lauffaehig wird (praktisch musst du dazu nur darauf achten, DOS nicht zu ueberschreiben und brav alle geaenderten Interrupts vor dem Beenden wieder herzustellen), oder du bastelst dir fuer den 16Bit-Fall ein kleines DOS-Programm zum Testen, bzw. fuer 32Bit auch ein Win32-Programm, oder was auch immer.


Anmelden zum Antworten