Turbo Pascal - execute(......);

  • Hallo zusammen,


    ich habe ein kleines Problem im Zusammenhang mit der EXECUTE-Anweisung aus Turbo Pascal 3.0 unter CP/M heraus.


    Ich möchte aus meinem Pascal-Programm heraus eine andere .COM-Datei starten und dabei Parameter übergeben. D.h. ich möchte aus dem laufenden Programm eraus SUBMIT.COM starten und die Stapeldatei EXE.SUB abarbeiten lassen:


    Code
    procedure comaufruf;
    var comdatei : Text;
    
    begin
      Assign(comdatei,'SUBMIT.COM EXE.SUB');
      execute(comdatei);
    end;


    Offenbar scheitert die Zuweisung mittels ASSIGN, da es eben keine Datei mit dem Namen SUBMIT.COM EXE.SUB gibt.

    Hat jemand eine Idee, wie ich das Problem lösen und Parameter bzw. den Namen der .SUB-Datei mitgeben kann?



    Viele Grüße,

    Marcus

  • Die Kommandozeilen-Parameter in CP/M stehen ab Adresse Hex 80, das Programm dann ab Hex 100.

    Die Funktion execute() sollte nur "submit.com" aufrufen.

    Unter CP/M kannst Du die Kommandozeilenparameter normalerweise so erreichen: CmdLine : String[128] Absolute $80;

    Keine Ahnung ob das in Zusammenarbeit mit execute() funktioniert.

    "The biggest communication problem is we do not listen to understand. We listen to reply." - Stephen Covey


    Webseite und Blog ist immer noch - seit fast 20 Jahren - online.

  • Hab' nochmal aus Langeweile danach gesucht - vielleicht ist es besser, Du nutzt die BDOS function 47 (Chain to program), BDOS Calls gehen ja auch via Turbo PASCAL 3.0 Funktion bdos() bzw. bdoshl() unter CP/M zu nutzen. Es gibt Hinweise, dass das mit execute() halt so nicht vorgesehen ist, Parameter zu übergeben.

    "The biggest communication problem is we do not listen to understand. We listen to reply." - Stephen Covey


    Webseite und Blog ist immer noch - seit fast 20 Jahren - online.

  • Vielen Dank für die Hinweise. Ich werde es dann mal über BDOS probieren, mit execute() habe ich jetzt eine Systemabstürze fabriziert.

  • Hallo Peter,


    einen kleinen Fortschritt habe ich mit deiner Hilfe gemacht. Der folgende Code liefert mir die Ausgabe: M:SUBMIT.COM?


    Code
    var cmdline : string[128] absolute $80;
    begin
      cmdline:='M:SUBMIT.COM EXE.SUB'
      bdos(47,$80);
    end.

    Die Dateien SUBMIT.COM und EXE.SUB sind natürlich beide vorhanden.


    Warum die Kommandozeile aber nicht ausgeführt wird, ist mir noch rätselhaft.



    Viele Grüße,

    Marcus

  • mit der bdos()-Funktion aus Turbo Pascal kann ich nur Integerwerte übergeben. Ein Aufruf bdos(47,0L) wird leider nicht kompiliert. :(

  • Mit einem kleinen Trick kann ich das gewünschte auch über einen anderen Weg erreichen:


    Wenn ich mit meine Anwendung über SUBMIT SUB1.SUB starte, und SUB1.SUB dann die aus meinem Programm erzeugte Datei SUB2.SUB aufruft, funktioniert es.



    Hier ein Beispiel:


    SUB1.SUB:

    Code
    PCWERK
    SUBMIT SUB2.SUB


    SUB2.SUB

    Code
    DIR M:
    PCWERK


    Mit SUBMIT SUB1.SUB wird zunächst meine Anwendung PCWERK.COM gestartet. Mit dieser wird die Datei SUB2.SUB erstellt. Nach Beendigung der Anwendung PCWERK wird SUB2.SUB aufgerufen, welche zum Ende wiederum PCWERK startet. Nun muss ich nur noch eine Schleife in SUB1.SUB einbauen, mal schauen...

    Schöner wäre natürlich der zuerst angedachte Weg.

    Einmal editiert, zuletzt von sucram ()

  • Die zur Laufzeit erstellte Datei SUB2.SUB muss lediglich mittels SUBMIT SUB1.SUB den Prozess von vorne beginnen. Soll die Stapelverarbeitung beendet werden, muss eine leere Datei SUB2.SUB erzeugt werden.


    Das Video zeigt den gegenseitigen Aufruf zweier Stapelverarbeitungen. In diesem Fall handelt es sich um eine Endlosschleife, da die Datei SUB2.SUB nicht zur Laufzeit erzeugt wird.


    SUBMIT2.zip

  • mit der bdos()-Funktion aus Turbo Pascal kann ich nur Integerwerte übergeben. Ein Aufruf bdos(47,0L) wird leider nicht kompiliert. :(

    Oweia. Das was ich als Link angegeben hatte, ist ja auch ein 'C' Programm. '0L' steht für ein Long Integer, also 4-Byte-Integer, nicht 2-Byte.


    Das sollte eher so in Turbo PASCAL funktionieren:


    1. var cmdline : string[128] absolute $80;
    2. begin
    3. cmdline:='_M:SUBMIT.COM EXE.SUB';
    4. bdos(26,$80); /* addr(cmdline) entspricht der Adresse $80 , CP/M bdos(26,address) sets the DMA address/sets buffer address */
    5. cmdline[0]:=length(cmdline)-1; /* bin mir nicht sicher, ob Byte 0 des Strings tatsächlich die Länge des Gesamtkommandos beinhalten muss */
    6. bdos(47,0); /* chain program, uses 0 as chain flag */
    7. end.


    Ich hab's aber nicht selbst ausprobiert.

    "The biggest communication problem is we do not listen to understand. We listen to reply." - Stephen Covey


    Webseite und Blog ist immer noch - seit fast 20 Jahren - online.

  • Leider führt das auch nicht zum gewünschten Ergebnis. cmdline ist vom Type String, daher muss zunächst die Stringlänge in eine Ganzzahl überführt werden. Die Übergabe an cmdline[0] führt dann zum nächsten Problem, da die Zahl in der Regel zweistellig sein wird, aber cmdline[0] nur ein Zeichen aufnehmen kann.

    Ich habe es dementsprechend auch mit Anweisungen probiert, die entsprechend kurz sind, aber auch das hat nicht funktioniert.

    Ich habe es mal mit und mal ohne Laufwerksangabe probiert, auch den Unterstrich habe ich mal gesetzt und mal weggelassen.

    Weitergekommen bin ich aber leider nicht.

  • Das ist gelinde gesagt etwas wirr was Du da schreibst. Wenn z.B. die Länge 12 ist, passt der Wert 12 locker in ein Byte bzw. ein Char (ein Character kann 0..255 als Wert annehmen). Mach dann halt aus dem string[128] ein Array [0..127] of Char, wenn es Dir besser gefällt. Ich habe aber wohl anscheinend nicht genug Geduld mit Dir, verzeih' mir das. In 'C' ist es einfacher, direkt mit Hilfe einer Speicheradresse (also einem char * pointer) was zu machen, da ist PASCAL einfach zu beschränkt. Normalerweise speichert man halt die Länge in der Speicherstelle $80, danach kommt die Kommando-Zeichenkette und am Schluß nochmal ein '\0', die Zeichenkette ist also "Null-terminiert". In PASCAL spielt das erstmal keine Rolle, in 'C' schon. Der BDOS Call will das halt so in diesem Format.

    "The biggest communication problem is we do not listen to understand. We listen to reply." - Stephen Covey


    Webseite und Blog ist immer noch - seit fast 20 Jahren - online.

  • Hallo ihr zwei,


    Ihr seid etwas zu weit voneinander entfernt.


    Peter, Du hast sicherlich nicht zu wenig Geduld, ich denke eher, Du überforderst Marcus etwas. Was Dir selbstverständlich erscheint ist für Marcus offenbar noch fremd.


    Wenn die Länge des Strings (die natürlich immer ganzzahlig ist, an der Stelle kann ich Marcus auch nicht folgen) in dem Byte $80 abgelegt werden soll, dann natürlich nicht als String bzw. Text - so scheint Marcus das verstanden zu haben. Ist die Kommandozeile z.B. 32 Zeichen lang, muss diese 32 nicht als zweistelliger Text '32', sondern als Byte - genauer als Char - mit dem Wert 32 abgelegt werden, in diesem Fall also als ' ' (Leerzeichen). Für C-Programmierer ist das alles irgendwie gleich, aber Pascal hat strenge Regeln für seine Typen, dort ist Byte<>Char.

    Um von der Zahl 32 auf das Zeichen ' ' zu kommen brauchst Du Umwandlungsfunktionen. In diese Richtung ist es die Funktion chr(), umgekehrt wäre es ord().


    Also so etwas wie

    cmdline[0] := chr(length(cmdline))


    Die maximale Stringlänge ist deshalb bei (Turbo-) Pascal 255!


    Die Nullterminierung gibt es bei Pascal-Strings übrigens nicht, dafür speichert Pascal ja eben die Länge an Position 0. Allerdings versucht Ihr ja an dieser Stelle die speziellen CP/M-Regeln umzusetzen - die kenne ich gerade nicht, aber auch da bin ich sicher, dass nur eine Variante nötig ist: Längenbyte davor *oder* Nullterminierung. Ich tippe aber auf ersteres. Das wäre dann aber übereinstimmend mit der Pascal-Speicherung - dann könnte man sich das eigentlich alles sparen und einfach nur die Kommandozeile zuweisen.


    Mir fehlt im Augenblick leider der Zugriff auf einen CP/M-Emulator, um das selber nachzuprüfen, deshalb ist der letzte Satz mit Vorsicht zu genießen.

  • Ich habe jetzt noch ein wenig nachgelesen.

    Grundsätzlich sind die Überlegungen für den Kommandozeilenpuffer ab $80 korrekt: erst ein Längenbyte, dann die Kommandozeile (ohne Nullterminierung).

    Ansonsten scheint CP/M aber eine völlig andere Darstellung von Texten zu nutzen: Die BIOS-Funktion printstring erwartet eine mit '$' terminierte Zeichenkette (ohne Längenbyte). Aber das hier nur nebenbei ...

    Leider habe ich dabei auch gesehen, dass die BDOS-Funktion 47 in CP/M 2.2 nicht unterstützt wird. Das würde heißen, dass wir damit sowieso nicht weiterkommen :(

  • Wenn er wirklich nur CP/M 2.2 nutzt (es könnte ja doch noch CP/M 3.0 sein), hatte er ja mit dem Aufruf in einer SUBMIT-Datei und der Generierung einer (weiteren) SUBMIT Datei die einzige andere Möglichkeit schon erwähnt, das ist aber insgesamt arg limitiert.

    Allerdings frage ich mich dann, wie Turbo PASCAL 3.0 unter CP/M die "execute()" Funktion implementiert hat - denn dort wird kein SUBMIT genutzt....

    (dann müsste man die generierte .COM Datei debuggen oder disassemblieren, um das herauszufinden)


    Edit: Theoretisch kann man einfach auch das RAM ab $100 mit dem Inhalt einer ausführbaren Datei "befüllen" und am Schluss den Instruction Pointer auf $100 setzen, um ein Programm "zu starten". Das bedingt aber ein Platzieren des zuvor auszuführenden Codes, der das macht, ans Ende der TPA. Das würde sicherlich den Rahmen sprengen und üblicherweise macht man das nur mit Assembler, nicht mit Turbo PASCAL.

    Oder vielleicht kann man auch einen CP/M-Warmstart machen, und legt ab $80 die auszuführende Kommandozeile ab.

    "The biggest communication problem is we do not listen to understand. We listen to reply." - Stephen Covey


    Webseite und Blog ist immer noch - seit fast 20 Jahren - online.

    Einmal editiert, zuletzt von Peter z80.eu ()

  • Stimmt, er kann CP/M 3 haben. Das hatte ich über einen Kamm geschert, weil in der Doku nur stand: "MP/M II and later; CP/M-86 v1.1" Dass CP/M 3 dort unter "MP/M II and later" aufgeführt ist habe ich eben erst gesehen. Und die Familiengeschichte von CP/M ist mir nicht so geläufig :(

  • Guten Morgen zusammen,


    in der Tat habe ich mich wohl etwas mißverständlich ausgedrückt, Georg hat mich dennoch verstanden.

    Natürlich ist mir klar, dass ein Byte 256 verschiedene Zeichen bzw. Werte repräsentieren kann. Ich bin aber nicht ganz selbstverständlich darauf gekommen die ermittelte Stringlänge durch das ASCII-Zeichen des entsprechenden Wertes an cmdline[0] zu übergeben.


    Das habe ich inzwischen ausprobiert, leider scheitert der Aufruf noch immer.


    Ich nutze CP/M 3 des Schneider Joyce / Amstrad PCW, einen entsprechenden Emulator kann ich gerne verlinken.



    Turbo Pascal scheint sich anders zu verhalten als C. Ich kenne mich mit C nicht aus, somit ist das ist nicht meine Erkenntniss.

    Ich hatte das Problem auch in einem anderen Forum geschildert:


    http://www.cpcwiki.eu/forum/nc…-program-with-parameters/


    John Elliott läuft auch auf den gleichen Fehler wie ich.



    Viele Grüße

  • Wenn ich die Beispiele im verlinkten Thread richtig verstehe, klappt das (unter SID), wenn das Kommando als C-String (d.h. 0-terminiert) abgelegt wird, obwohl die Doku sagt, dass CP/M dort eher etwas wie einen Pascal-String erwartet (zunächst Längenbyte, dann Text).


    Seltsam. Es wird Zeit, mal den Emulator anzuwerfen. Geht aber erst heute abend ...

  • Das mit dem "Null-terminated" hatte ich ja vorgeschlagen, aber dann kam jemand um die Ecke und meinte, das braucht man bei Turbo PASCAL nicht...


    Übrigens kann man auch noch einen weiteren Trick anwenden. Man erzeugt eine Textdatei mit dem Kommando namens "$$$.SUB" und löst danach einen Warmstart (wie oben schon mal übrigens erwähnt) aus. Den Warmstart kriegt man hin, wenn man in Maschinensprache ein Sprung auf die Adresse 0 durchführt (also "JMP 0", geht wohl auch als Inline-Code in Turbo PASCAL, also mit der "inline()" Funktion und drei Werten, $C3, $0, $0).

    "The biggest communication problem is we do not listen to understand. We listen to reply." - Stephen Covey


    Webseite und Blog ist immer noch - seit fast 20 Jahren - online.

  • Also gegenüber der Idee mit dem Warmstart finde ich die Methode mit den beiden Stapeldateien, von denen eine zur Laufzeit erzeugt bzw. geändert wird, doch noch attraktiver.

  • Hallo sucram,


    jetzt zu allem Überfluß auch noch mein Senf. :prof:

    Für die BDOS Funktion 47 sollte die gewünschte Befehlszeile ab DMA Adresse (also bei dir 0080h) beginnen und mit einem Nullbyte (also 00h) abgeschlossen werden.

    var cmdline : string[128] absolute $80;

    Mit der Zeile legst Du aber die Stringlänge auf den Start des DMA Puffers und machst damit die Längeninformation des Strings zu einem Teil des zu ladenden Programmnamens, den CP/M dann natürlich nicht finden kann (also kriegste ein Fragezeichen).

    Probier mal "var cmdline : string[128] absolute $7F";

    Das Nullbyte mußt Du auch noch in den String basteln (vielleicht mit "#0"), da Pascal Strings nicht automatisch mit 00h abgeschlossen werden.


    Viele Grüße

    netmercer

  • Das mit dem "Null-terminated" hatte ich ja vorgeschlagen, aber dann kam jemand um die Ecke und meinte, das braucht man bei Turbo PASCAL nicht...

    Hey, nicht gleich so bissig :)


    Meine Aussage war, dass Turbo Pascal keine Nullterminierung bei Strings kennt, sondern statt dessen die Länge im ersten Byte ablegt.

    Dass CP/M im DMA-Buffer ebenfalls so arbeitet war zunächst nur eine *Annahme*, weil ich Hinweise darauf gefunden hatte, dass an Position $80 ein Längenbyte steht - ohne Hinweise auf eine abschließende 0.


    Die Versuchsergebnisse von John Elliott haben mich deshalb irritiert, und deshalb habe ich weiter recherchiert. Dabei habe ich das hier gefunden:

    Zitat von CP/M Operating System Manual

    default buffer: Default 128-byte buffer maintained at 0080H in page zero. When the CCP loads a COM file, this buffer is initialized to the command tall; that is, any characters typed after the COM file name are loaded into the buffer. The first byte at 0080H contains the length of the command tall, while the command tail itself begins at 0081H. The command tail is terminated by a byte containing a binary zero value.

    Außerdem habe ich eigene Untersuchungen angestellt, allerdings unter CP/M 2, und leider aktuell ohne Turbo Pascal. Aber das obige bestätigt sich zumindest im Fall der Kommandozeile:



    Wie man sieht, Längenbyte an $80, und Terninierung mit 0.


    Das ist aber meiner Meinung nach nicht das Format, mit dem John in der Praxis Erfolg hatte :nixwiss:

    Die Verwirrung bleibt ...

  • Also gegenüber der Idee mit dem Warmstart finde ich die Methode mit den beiden Stapeldateien, von denen eine zur Laufzeit erzeugt bzw. geändert wird, doch noch attraktiver.

    Naja, der Warmstart tut nicht wirklich weh, und so braucht man keine 2.te Submit Datei zusätzlich...

    "The biggest communication problem is we do not listen to understand. We listen to reply." - Stephen Covey


    Webseite und Blog ist immer noch - seit fast 20 Jahren - online.

  • Das hat jetzt leider etwas gedauert. Ich habe den Emulator von John Elliott unter Linux installiert, aber der hat Probleme mit Deinem TP-Image. Da habe ich eine Weile mit gekämpft, und dann bin ich wohl oder übel auf den Windows-Emulator Dosbox umgestiegen.


    Zuerst das Ganze nochmal mit sid.com getestet, und dann nach TP portiert. Es ist tatsächlich so, dass - entgegen der Doku und anders als die "übliche" Verwendung des Puffers für die Kommandozeile - die BDOS-Funktion 2F offenbar einen nullterminierten String *ohne* Längenbyte haben will.


    Folgendes hat bei mir funktioniert (ich habe zunächt der Einfachheit halber die Variante von netmercer genommen, obwohl sie das Byte 7F zerstört - aber das schadet vermutlich nicht):


    Code
    program exetest;
    var cmd: string[128] absolute $7F;
    begin
      cmd := 'A:TYPE.COM EXETEST.PAS'+#0;
      bdos(47,0);
    end.

    Wenn man wirklich nur in den Puffer schreiben will, muss man die Befehlszeile zeichenweise eintragen, d.h. nicht als String. Z.B. so:


    Code
    var s: string[127];
        cmd: string[127] absolute $80;
        i: integer;
    begin
      s := 'A:TYPE.COM EXETEST.PAS';
      for i := 0 to length(s)-1 do
        cmd[i] := s[i+1];  /* s[0] ist das Längenbyte! */
      cmd[length(s)] := #0;
      bdos(47,0);
    end.

    Habe ich jetzt nicht getestet; ich hoffe, dass ich jetzt mit den Indexen nicht durcheinander gekommen bin :tüdeldü:


    Kürzer geht es mit dem move - Befehl:

    Code
    var s: string[127];
        cmd: string[127] absolute $80;
    begin
      s := 'A:TYPE.COM EXETEST.PAS'+#0;
      move(s[1],cmd[0],length(s));
      bdos(47,0);
    end

    Viel Erfolg!

  • Guten Morgen!


    Leider bin ich erst jetzt dazu gekommen Georgs Code auszuprobieren, es funktioniert, ich bin begeistert! :)



    Vielen Dank, für Eure Hilfe!



    Viele Grüße und frohe Feiertage!

  • Hi!

    Der Aufruf von anderen Programmen funktioniert wirklich sehr schön mit diesem Code!

    Gibt es aber eine Möglichkeit das danach wieder in das TP Programm zurückgesprungen wird??? :(

    Ich möchte gerne von einem TP Programm aus das "SAY" der multitasking C/PM Version auf dem TTGO VGA32 aus aufrufen und danach mit dem Programm weiter arbeiten...

    Für einen (leider nur) einmaligen Aufruf funktioniert der obige Code hervorragend!


    :)Franky

  • Ein "Shell" Konzept wie in Linux bzw. wie etwas rudimentärer in MS-DOS (mit der COMMAND.COM "Shell") gab es nie in CP/M.

    Du kannst das aber mit SUBMIT/XSUB bzw. auch mit dem "Autoload" des SUBMIT Befehls hinkriegen, also mit dem Erzeugen einer $$$.SUB Datei, indem drin steht, was nach einem CP/M Warmstart von SUBMIT/XSUB ausgeführt werden soll. Damit sollte man auch eine Art von Verkettung von Programmaufrufen hinbekommen.

    "The biggest communication problem is we do not listen to understand. We listen to reply." - Stephen Covey


    Webseite und Blog ist immer noch - seit fast 20 Jahren - online.