BDOS Call in MBASIC (oder Turbo-Pascal)?

  • Der Autor von RunCPM war so nett, die BDOS function 248 zu realisieren

    (gewuenscht vom User SergeyDr, die die Millisekunden seit dem Systemstart zurueckgeben soll

    - so wie es TIMER bei GW-BASIC unter DOS macht.


    Da der Autor nicht ganz so viel Zeit hat, bat er, dass jemand dies testen koennte (unter Arduino, Linux, Windows).


    Kann mir evtl. jemand beim MBASIC-Code zum Aufrug des BDOS-Call 248 helfen?
    Muesste wohl aehnlich sein, wie jemand schon LED-Output-Input-Aufrufe in MBASIC realisiert hat.


    Dies wuerde dann auch helfen, die Laufzeit fuer FRACTAL.BAS zu stoppen ohne dass man Auge/hand nutzen muss :)

  • Hallo Guido,

    die Millisekunden werden vom BDOS Aufruf in den CPU Registern DE und HL gespeichert.

    Die sind jeweils nur 16 Bit groß und im normalen Ablauf auch ruckzuck wieder überschrieben.

    Nutzen kann man die Millisekunden wohl am ehesten als Real-Zahl.

    Hier mein Vorschlag für Turbo Pascal 3 auf CP/M mit einem kleinen Z80 Inline-Programm.

    (ohne Compiler frei getextet, nicht getestet)


    LG Gerd



    Function GetMillis : real;

    Var MillisHi, MillisLo : integer;

    Begin

    Inline(

    $3e/$f8/ { ld a,248 ; load Bdos function call number }

    $cd/$05/$00/ { call $0005 ; call Bdos function }

    $22/MillisLo/ { ld (MillisLo),hl ; store Millis lower 16 bit }

    $ed/$53/MillisHi); { ld (MillisHi),de ; store Millis higher 16 bit }

    GetMillis := 65536.0 * MillisHi + MillisLo; { convert to real }

    End;

    Einmal editiert, zuletzt von Gerd5 ()

  • Die Millisekunden, die in den unteren 16 Bits des Ergebnisses (also in HL) zurückkommen, kann man einfach mit


    Code
    VAR millis: INTEGER;
    ....
    millis := BdosHL (248);

    bekommen. Das reicht immerhin, um eine starke Minute zu messen. Wenn man nur LEDs blinken lassen will, reicht das wohl dicke.

  • Beim Nutzen von BdosHL kann auch während einer Minute ein Überlauf erfolgen.

    Hängt halt davon ab, wann der Rechner gestartet wurde.

  • Beim Nutzen von BdosHL kann auch während einer Minute ein Überlauf erfolgen.

    Hängt halt davon ab, wann der Rechner gestartet wurde.

    Und?

    Das macht ja nix, solange man die nur die Differenz zwischen zwei Aufrufen haben will und unsigned rechnet.

  • Beim Nutzen von BdosHL kann auch während einer Minute ein Überlauf erfolgen.

    Hängt halt davon ab, wann der Rechner gestartet wurde.

    Ein Ueberlauf kannst du immer bekommen. Egal ob 8, 16, 32 oder sonstwieviel Bits.

    ;------------------------------------
    ;----- ENABLE NMI INTERRUPTS
    (aus: IBM BIOS Source Listing)

  • Ergänzung:

    Ich hatte oben nicht berücksichtigt, dass in Turbo Pascal 3 der Zahlentyp integer vorzeichenbehaftet ist, also nicht von 0 bis 65535 geht, sondern -32768 bis + 32767.

    Jedoch kommen von der BDOS Funktion vorzeichenlose 16-Bit Zahlen.

    Also muss noch eine Vorzeichenbehandlung bei der Umwandlung in eine Real-Zahl her.


    Function GetMillis : real;

    Var MillisHi, MillisLo : integer;

    Var Millis : real;

    Begin

    Inline(

    $3e/$f8/ { ld a,248 ; load Bdos function call number }

    $cd/$05/$00/ { call $0005 ; call Bdos function }

    $22/MillisLo/ { ld (MillisLo),hl ; store Millis lower 16 bit }

    $ed/$53/MillisHi); { ld (MillisHi),de ; store Millis higher 16 bit }

    { convert from double 16-bit integer to real }

    If MillisHi < 0

    Then Millis := 65536.0 + MillisHi

    Else Millis := MillisHi;

    If MillisLo < 0

    Then GetMillis := 65536.0 * Millis + 65536.0 + MillisLo

    Else GetMillis := 65536.0 * Millis + MillisLo;

    End;

  • Weil ich mir schon dachte, dass sich keiner an die moderne Hochsprache BASIC herantraut ... mit Microsoft BASIC sollte es auch gehen (in Z80Emu getestet).


    Die Umrechnung der beiden Worte in eine Fließkommazahl sei dem geneigten Leser und BASIC Programmierer überlassen ;)



    Die DATA Worte bilden diese Assembler Routine



  • Beim Nutzen von BdosHL kann auch während einer Minute ein Überlauf erfolgen.

    Hängt halt davon ab, wann der Rechner gestartet wurde.

    Ein Ueberlauf kannst du immer bekommen. Egal ob 8, 16, 32 oder sonstwieviel Bits.

    Da stimme ich dir völlig zu.

    Jedes Zahlenformat kann überlaufen.

    Die Frage ist, wie oft und stört mich das?


    Überlauf macht Arbeit beim Berechnen von Ausführungszeiten: man kann sich zB. nicht darauf verlassen, dass die Millisekundenzahl bei der zweiten Messung größer ist als beim ersten Mal. Und ich brauche irgendwelche zusätzliche Informationen, ob da vielleicht ein oder sogar mehrere Überläufe waren.


    Im Millisekundentakt gibt es einen Überlauf:

    - bei 8 Bit (256) ca. 4x die Sekunde.

    - bei 16 Bit (65536) nach ca. 1 Minute. Wer aber zB. die Ausführungszeit von Apfelmännchen auf dem Rechner messen möchte, hat eventuell bereits nach einer Sekunde einen Überlauf; je nachdem wie lange der Rechner bereits läuft und somit der interne Millisekunden-Zähler bereits läuft.

    - bei 32 Bit (ca. 4 Milliarden): das normale Jahr hat ca. 31.5 Millionen Sekunden, in Millisekunden gerechnet gibt es also etwa 7 mal pro Jahr einen 32-Bit Überlauf, somit alle 1 bis 2 Monate.


    Für mich heißt das, dass ein 32-bit Zähler bei Zeitmessungen im Millisekundentakt für die meisten Anwendungen, zB. Apfelmännchen-Vergleich, angemessen und ausreichend sein sollte, ohne Überlauf.


    Und es macht sicher Sinn, die Komplexität möglichst einer Funktion zu bündeln und dann einfach anwenden zu können.


    Millis1 := GetMillis;

    { irgend etwas ausprobieren }

    Millis2 := GetMillis;

    MilliDauer := Millis2 - Millis1;

    { und dann mit dieser Erkenntnis etwas tun, zB. auf dem Bildschirm anzeigen :) }

  • Martin Hepperle

    Hallo Martin,

    wieder etwas über BASIC gelernt :)


    Im Eingangs-Post von guidol wird DE aus dem Timer-Wert durch Rechts-Shift gewonnen, sollte also das Hi-Word sein.


    Frage an dich: warum wird im Assemblercode Register DE nach Basic LO% gespeichert und Register HL nach HI% ?

    Wie siehst du die Zuordnung?

  • Ah, einer hat mitgelesen ... Ja, es ist anscheinend so, dass ich HI und LO vertauscht habe, da ich nur den nackten Code ausprobiert und nicht die modifizierte CPM Emulation getestet habe.

    Ich hätte es wohl erst gemerkt, wenn ich den Zähler zu einer 32-bit bzw. Fließkommazahl zusammengesetzt hätte. Im Nachhinein dachte ich auch, dass man nicht unbedingt die Bytes einzeln nach (IX) kopieren sondern das auch mit einem LD DE,(IX) machen kann, um noch ein, zwei Bytes zu sparen. Andererseits sieht man so klar, was wohin geht.


    Es ist schon wieder einige Monate her, dass ich für Z80 oder 8080 programmiert habe und wenn man zwischen diversen Motorola und Intel Prozessoren hin- und herwechselt, geht immer wieder etwas Eleganz verloren. Ich habe auch erst einen Fehlversuch gebraucht, bis ich gesehen habe, dass ich die Bytes in den DATA Statements mit dem Maschinencode vertauschen muss.

  • Mitgelesen habe ich auch. :fp:

    Ich fand die Aufgabe nicht so interessant für mich.

    Kein VGA für Z80 Systemen vorhanden!


    Auch bei meinen Fragen und Anregungen ist

    wenig Interesse vorhanden.



    retro

    :)

  • Martin Hepperle - d.h. in Deinem Beitrag muss noch was geändert werden (HI/LO vertauscht?) ?

    Man muss im Assemblercode nichts ändern (man könnte die Kommentare im Assembler-Code zu LO% und HI% tauschen), es reicht, hinterher aus A% und B% die Summe richtig berechnen, ähnlich wie im Turbo-Pascal Programm über Fließkommazahlen, wegen des Wertebereichs. Es wird im BDIOS Ergänzung ja der 32-bit Zähler auf zwei 16-bit Zahlen aufgeteilt. Der BASIC Code braucht nicht verändert zu werden, weil ich dort schlauerweise A% und B% als Variablennamen gewählt habe.

    Der Aufruf wäre dann CALL TIME%(LO%,HI%)und aus HI%*65536.0 + LO% (plus zusätzliche Vorzeichenkorrektur wie im TP Beispiel) könnte man dann den 32-Bit Zähler "rekonstruieren".


    Mir ging es mehr darum, ein konkretes Beispiel für die Anwendung des CALLs in Microsoft BASIC zu bringen. Das steht zwar alles auch im Handbuch, manchmal ist ein Beispiel aber ganz schön. Dort steht auch wie die Parameter des CALLs in den Registerpaaren DE und HL übergeben werden - als Adressen auf die Variablen, sodass man dort auch die Zählerteile zurückgeben kann.

  • Nachhinein dachte ich auch, dass man nicht unbedingt die Bytes einzeln nach (IX) kopieren sondern das auch mit einem LD DE,(IX) machen kann, um noch ein, zwei Bytes zu sparen.

    wäre schön, wenn es so einen Word-Befehl beim Z80 gäbe