Das Schneider CPC Systembuch

Das Betriebssystem des Schneider CPC

Basic und Maschinencode

Parameter

Wird eine Assembler-Routine aufgerufen, so können an sie bis zu 32 Basic und Maschinencode: ParameterParameter übergeben werden. Dabei ist es egal, ob der Aufruf via Maschinencode über HIMEM: RSXRSX oder mit Maschinencode über HIMEM: CALLCALL erfolgte:

Maschinencode über HIMEM: CALLCALL box, 100,200,300,400
    |BOX, 100,200,300,400

Als Basic und Maschinencode: ParameterParameter kommen allerdings nur Integer-Werte in Frage, und die Parameter: Parameter-ÜbergabeParameter-Übergabe funktioniert auch nur in die eine Richtung: Von Einleitung: BASIC
Anhang: Basic
Basic
an das Assembler-Programm. Will man mehr erreichen, so muss man tricksen.

Jeder Basic und Maschinencode: ParameterParameter kann im Bereich -32768 bis +65535 liegen (Im Die Firmware des Schneider CPCFirmware Manual wird als Obergrenze +32767 angegeben, was aber nicht stimmt). Diese werden als 16-Bit-Words an die Mcode-Routine übergeben.

Integer-Variablen sind 16-Bit-Words mit Vorzeichen und können Zahlenwerte von -32768 bis +32767 darstellen. Es ist aber auch möglich, Datenbreite: WordsWords ohne Vorzeichen im Bereich von 0 bis 65535 zu übergeben. Dabei ist auf die Doppeldeutigkeit der negativen Zahlen zu achten:

 Integer (mit VZ):   0 --- 32767 /-32768 --- -1
 ohne Vorzeichen:    0 --- 32767 / 32768 --- 65535

Die Übergabe an die Maschinencode-Routine wird vom Schneider-Basic wie folgt realisiert. Diese Methode ist zwar nicht verbindlich und, wegen ihrer Einschränkungen, auch nicht besonders galant. Trotzdem sollte sie, um die Kompatibilität zu wahren, auch von anderen Vordergrund-Programmen so gehandhabt werden:

Parameter-Übergabe

Im A-Register wird der Routine die Anzahl der übergebenen Basic und Maschinencode: ParameterParameter mitgeteilt. Die Basic und Maschinencode: ParameterParameter befinden sich auf einem Stapel (und zwar auf dem Hardware-Stack, was für den Anwender aber uninteressant ist). Der erste Basic und Maschinencode: ParameterParameter liegt dabei an der obersten Adresse und das IX-Register zeigt auf das LSB (unterstes Datentypen: Bytes
Datenbreite: Bytes
Byte
) des letzten Parameters (unterster Stapel-Eintrag):

     Die Tonausgabe: Das Kontrollregister  (Reg. 7)
Die Tonausgabe: Die möglichen Hüllkurvenformen (Reg. 13)
Register
Speicher -------- -------- Operationen: BD5B / 349A / 349A: FLO SUBA = n MSB PARAM1 LSB PARAM1 MSB PARAM2 LSB PARAM2 ... ... ... MSB PARAMn IX --------> LSB PARAMn
Parameter-Übernahme

Die Mcode-Routine kann nun über das IX-Register auf die übergebenen Basic und Maschinencode: ParameterParameter zugreifen:

ROUT1:  CP   2          ; Erklärung der Anschlussbelegung: TestTest, ob Anzahl Basic und Maschinencode: ParameterParameter stimmt (hier 2)
        RET  NZ         ; Nein: Der Linien-Algorithmus: Fehler 3Fehler.
        LD   L,(IX+0)
        LD   H,(IX+1)   ; HL = letzter ( = zweiter) Basic und Maschinencode: ParameterParameter
        LD   LOW KERNEL JUMPBLOCK: 000E:  LOW PCBC INSTRUCTION
LOW KERNEL JUMPBLOCK: 001E: LOW PCHL INSTRUCTION
E
,(IX+2) LD D,(IX+3) ; DE = vorletzter ( = erster) Basic und Maschinencode: ParameterParameter ...

Dass das IX-Register immer auf den letzten übergebenen Basic und Maschinencode: ParameterParameter zeigt, ist für all diejenigen Routinen ungünstig, die eine veränderliche Anzahl an Argumenten übernehmen können. Dabei sind nämlich in aller Regel die hinteren Basic und Maschinencode: ParameterParameter optional. Der Zugriff auf die tatsächlich im Aufruf angegebenen Basic und Maschinencode: ParameterParameter ist dann erschwert, weil hier zum IX-Register ein anderer Erklärungen zu den Anschlussbezeichnungen: INDEXIndex addiert werden müsste. Das Problem kann man wie folgt angehen:

; Beispiel: Routine nimmt 2 oder 3 Basic und Maschinencode: ParameterParameter:
;
ROUT2:  CP   2          ; Erklärung der Anschlussbelegung: TestTest, ob mind. 2 Argumente
        RET  C          ; Nein: Der Linien-Algorithmus: Fehler 3Fehler
;
        LD   BC,#0001   ; Default-Annahme für dritten Basic und Maschinencode: ParameterParameter machen
        CP   3          ; Erklärung der Anschlussbelegung: TestTest, ob dritter Basic und Maschinencode: ParameterParameter angegeben ist.
        JR   C,ROUT2A   ; Nein, nur zwei -> Default übernehmen.
;
        RET  NZ         ; Der Linien-Algorithmus: Fehler 3Fehler, falls nicht 3 (also mehr als 3) Basic und Maschinencode: ParameterParameter
;
        LD   C,(IX+0)
        LD   LOW KERNEL JUMPBLOCK: 000B:  LOW KL LOW PCHL
LOW KERNEL JUMPBLOCK: 001B: LOW KL FAR PCHL
LOW KERNEL JUMPBLOCK: 003B: LOW EXT INTERRUPT
B
,(IX+1) ; letzten (dritten) Basic und Maschinencode: ParameterParameter übernehmen ; INC IX ; und jetzt so tun, als seien nur zwei Basic und Maschinencode: ParameterParameter INC IX ; übergeben Datenbreite: Wordsworden! ; ROUT2A: LD LOW KERNEL JUMPBLOCK: 000E: LOW PCBC INSTRUCTION
LOW KERNEL JUMPBLOCK: 001E: LOW PCHL INSTRUCTION
E
,(IX+0) ; Übernehme letzten (zweiten) Basic und Maschinencode: ParameterParameter LD D,(IX+1) LD L,(IX+2) ; Übernehme vorletzten (ersten) Basic und Maschinencode: ParameterParameter LD H,(IX+3) ...
Übergabe von Fließkommazahlen

Obwohl man an Mcode-Routinen eigentlich nur Integerzahlen übergeben kann, ist es mit einem kleinen Trick trotzdem möglich, auch Real-Zahlen oder Datentypen: StringsStrings zu übergeben. Man speichert den Wert einfach in einer Unterprogramme: VariablenVariablen und übergibt dann deren Adresse an das aufgerufene Programm:

    100 Operationen: BD5B / 349A / 349A:  FLO SUBa!=123.45
    110 LOW KERNEL JUMPBLOCK: 000B:  LOW KL LOW PCHL
LOW KERNEL JUMPBLOCK: 001B: LOW KL FAR PCHL
LOW KERNEL JUMPBLOCK: 003B: LOW EXT INTERRUPT
b
!=234.56 120 |Maschinencode über HIMEM: RSXRSX,@Operationen: BD5B / 349A / 349A: FLO SUBa!,@LOW KERNEL JUMPBLOCK: 000B: LOW KL LOW PCHL
LOW KERNEL JUMPBLOCK: 001B: LOW KL FAR PCHL
LOW KERNEL JUMPBLOCK: 003B: LOW EXT INTERRUPT
b
! ---> ROUT3: CP 2 RET NZ ; Parameterzahl kontrollieren. ; LD LOW KERNEL JUMPBLOCK: 000E: LOW PCBC INSTRUCTION
LOW KERNEL JUMPBLOCK: 001E: LOW PCHL INSTRUCTION
E
,(IX+0) LD D,(IX+1) ; DE = @LOW KERNEL JUMPBLOCK: 000B: LOW KL LOW PCHL
LOW KERNEL JUMPBLOCK: 001B: LOW KL FAR PCHL
LOW KERNEL JUMPBLOCK: 003B: LOW EXT INTERRUPT
b
! LD L,(IX+2) LD H,(IX+3) ; HL = @Operationen: BD5B / 349A / 349A: FLO SUBa! ...

Da die Rechenroutinen der Fließkomma-Arithmetik sowieso immer die Zeiger auf die zu bearbeitenden Zahlen benötigen, ist das geradezu ideal. Man muss nur beachten, dass die Unterprogramme: VariablenVariablen möglicherweise (und bei kurzen Basic-Programmen ziemlich sicher) im untersten Speicherviertel liegen. Die Rechenroutinen blenden aber das untere RAM aus, weil sie selbst im Betriebssystems-ROM liegen. Die Routinen der Fließkomma-Arithmetik funktionieren nicht im unteren RAM-Viertel. Hier muss man die Zahlen zunächst immer in Zwischenspeicher über &4000 kopieren, bevor man mit ihnen rechnen kann.

Übergabe von Strings

Bei Datentypen: StringsStrings funktioniert es genauso. Nur dass man jetzt die Adresse des Descriptors einer String-Variablen übergeben muss. Dabei wurde beim 664/6128-Einleitung: BASIC
Anhang: Basic
Basic
eine Vereinfachung vorgenommen. Garbage Collection: ... beim CPC 464Beim CPC 464 muss man immer den Klammeraffen '@' vor den Variablennamen stellen, um die Übergabe der Stringdescriptor-Adresse zu erreichen. Garbage Collection: ... beim CPC 664 und 6128Beim CPC 664 und 6128 ist das nicht mehr nötig:

    100 Operationen: BD5B / 349A / 349A:  FLO SUBa$="TESTTESTTEST"
    110 |Maschinencode über HIMEM: RSXRSX,@Operationen: BD5B / 349A / 349A:  FLO SUBa$            <-- CPC 464
    110 |Maschinencode über HIMEM: RSXRSX, Operationen: BD5B / 349A / 349A:  FLO SUBa$            <-- CPC 664/6128

    ----->

    ROUT4:  CP   1
            RET  NZ         ; Parameterzahl Erklärung der Anschlussbelegung: Testtesten
    ;
            LD   L,(IX+0)
            LD   H,(IX+1)   ; HL = @Operationen: BD5B / 349A / 349A:  FLO SUBa$ = Adresse des Descriptors
    ;
            LD   Operationen: BD5B / 349A / 349A:  FLO SUBA,(HL)     ; Operationen: BD5B / 349A / 349A:  FLO SUBA = Länge des Datentypen: StringsStrings
            INC  HL
            LD   LOW KERNEL JUMPBLOCK: 000E:  LOW PCBC INSTRUCTION
LOW KERNEL JUMPBLOCK: 001E: LOW PCHL INSTRUCTION
E
,(HL) INC HL LD D,(HL) ; DE = Adresse des Datentypen: StringsStrings ...
Übernahme von Ergebnissen

Mit Hilfe des Variablenpointers '@' ist es auch möglich, wieder Ergebnisse von einer Assembler-Routine zurückzubekommen. Man übergibt, eventuell zusätzlich zu den Parametern für die Routine, die Adresse einer Rücknahme-Variable. Diese kann von jedem beliebigen Typ sein. Der Typ muss nur mit dem Grundlagen: UnterprogrammeUnterprogramm eindeutig vereinbart werden, weil man in der Mcode-Routine keine Möglichkeit hat, zu überprüfen, was man da an Parametern übergeben bekommen hat:

  |RSX ,100 ,@i% ,@Operationen: BD5B / 349A / 349A:  FLO SUBa! ,@s$

  --->

  einen Zahlenwert oder
  die Adresse einer Integer-Variablen oder
  die Adresse einer Real-Variablen oder
  die Adresse einer String-Variablen.

Das ist besonders kritisch, wenn das Maschinencode-Programm die adressierte Unterprogramme: VariablenVariable ändern will. Stimmt da der Typ nicht, oder ist es noch nicht einmal die Adresse einer Unterprogramme: VariablenVariable, kann die Speicher-Organisation des Basic-Interpreters recht schnell und recht gründlich durcheinander geraten.

Übernahme eines Integer-Ergebnisses
    100 i%=0
    110 |Maschinencode über HIMEM: RSXRSX,100,200,@i%
    120 PRINT i%

    ---->

    RSX5:   CP   3
            RET  NZ         ; Parameterzahl überprüfen.
    ;
            LD   L,(IX+2)
            LD   H,(IX+3)   ; zweitletzter = zweiter Basic und Maschinencode: ParameterParameter
            LD   LOW KERNEL JUMPBLOCK: 000E:  LOW PCBC INSTRUCTION
LOW KERNEL JUMPBLOCK: 001E: LOW PCHL INSTRUCTION
E
,(IX+4) LD D,(IX+5) ; drittletzter = erster Basic und Maschinencode: ParameterParameter ; ADD HL,DE ; Die Fließkomma-Routinen: FunktionenFunktion dieser Maschinencode über HIMEM: RSXRSX: Addiere [1] + [2] -> [3] EX DE,HL ; Ergebnis nach DE ; LD L,(IX+0) LD H,(IX+1) ; HL = Adresse von i% ; LD (HL),LOW KERNEL JUMPBLOCK: 000E: LOW PCBC INSTRUCTION
LOW KERNEL JUMPBLOCK: 001E: LOW PCHL INSTRUCTION
E
INC HL LD (HL),D ; Ergebnis in i% abspeichern ; RET ; Fertig

Bei Fließkommazahlen und Datentypen: StringsStrings funktioniert es entsprechend, nur dass man hier 5 bzw. 3 Datentypen: Bytes
Datenbreite: Bytes
Bytes
ändern muss. Ändert man bei Datentypen: StringsStrings aber nicht den Descriptor sondern den String-Inhalt, so muss man beim Aufruf von solchen Routinen darauf achten, dass der Descriptor nicht mehr in das Basicprogramm selbst zeigt. Das ist bei allen 'direkten' Definitionen der Fall, wo der Basic-Interpreter nicht mehr zu rechnen braucht:

    100 LET Operationen: BD5B / 349A / 349A:  FLO SUBa$="abcde"     ' <-- Descriptor zeigt in den Programmtext!!

    100 LET Operationen: BD5B / 349A / 349A:  FLO SUBa$="abcde"+""  ' <-- Einleitung: BASIC
Anhang: Basic
Basic
muss rechnen -> Datentypen: StringsString liegt im Stringbereich

Valid HTML   Valid CSS