Das Schneider CPC Systembuch

Grundlagen

Datentypen

Das Betriebssystem des Schneider CPCDas Betriebssystem des Schneider CPC kennt zwei unterschiedliche Zahlenformate: Integer und Datentypen: RealReal.

Mit INTEGER werden kleine, ganze Zahlen bezeichnet, die sich in 16 Datenbreite: Bits
Port B - Input: &F5xx: Bit 0:
Port B - Input: &F5xx: Bit 4:
Port B - Input: &F5xx: Bit 5:
Port B - Input: &F5xx: Bit 6:
Port B - Input: &F5xx: Bit 7:
Port C - Output: &F6xx: Bit 4:
Port C - Output: &F6xx: Bit 5:
Bit
, also einem Datenbreite: WordsWord abspeichern lassen.

Datentypen: RealREAL sind Fließkomma-Zahlen, deren interne Kodierung 5 Datentypen: Bytes
Datenbreite: Bytes
Bytes
beansprucht. Hiermit können Zahlen auf etwa neun Stellen genau gespeichert werden. Die Darstellung umfasst dabei den Bereich von etwa +/-10^(-38) bis +/-10^(+38). Für den alltaeglichen Gebrauch ist das sicherlich ausreichend.

Die Z80-CPU selbst kennt mehrere Kodierungsarten, die sich aber alle nur auf ganze Zahlen beziehen. Neben Datenbreite: Bits
Port B - Input: &F5xx: Bit 0:
Port B - Input: &F5xx: Bit 4:
Port B - Input: &F5xx: Bit 5:
Port B - Input: &F5xx: Bit 6:
Port B - Input: &F5xx: Bit 7:
Port C - Output: &F6xx: Bit 4:
Port C - Output: &F6xx: Bit 5:
Bits
sind Datentypen: Bytes
Datenbreite: Bytes
Bytes
und Datenbreite: WordsWords mit oder ohne Vorzeichen möglich und die Darstellung von Zahlen im sogenannten 'Datentypen: Packed BCDpacked BCD'-Amsdos: FormateFormat wird unterstuetzt.

BCD-Kodierung

BCD heist: 'Binary Coded Decimals' also 'binär kodierte Dezimalzahlen'. Diese Darstellungsart ist vorgesehen für Leute, die überhaupt nicht vom Dezimalsystem lassen können. Die Dezimalziffern werden, meist in ihrer ASCII-Kodierung, im Speicher nacheinander abgelegt. Zusätzlich werden meist noch zwei oder drei Datentypen: Bytes
Datenbreite: Bytes
Bytes
beansprucht, in denen Vorzeichen, Kommaposition und/oder ein Exponent gespeichert werden. Folgender Auszug stellt eine mögliche Kodierung im BCD-Format für eine Zahl im Speicher dar:

ZAHL1:  DEFB VZE       ; Vorzeichen Real: Der Exponentdes Exponenten
        DEFB EXPONENT0 ; Exponent (1. Dezimalziffer)
        DEFB EXPONENT1 ; Exponent (2. Dezimalziffer)
        DEFB VZM       ; Vorzeichen der Mantisse
        DEFB ZIFFER0   ; erste Dezimalziffer (höchstwertige)
        DEFB ZIFFER1   ;
        DEFB ZIFFER2   ;  .
        DEFB ZIFFER3   ;  .
        DEFB ZIFFER4   ;  .
        DEFB ZIFFER5   ;
        DEFB ZIFFER6   ; letzte Dezimalziffer (niederwertige)

Das reine BCD-Format wird von der Die ICs im Überblick: Die CPU Z80
Das Innenleben der CPC-Rechner: Die CPU Z80
Die Anschlussbelegungen der wichtigsten ICs im CPC: Die CPU Z80
Z80
nicht unterstuetzt und ist ansonsten auch recht unbeliebt, weil es zuviel Speicherplatz beansprucht. Sollen Zahlen in Datentypen: StringsStrings oder in Dateien auf Massenspeichern gespeichert werden, kann ein solches Amsdos: FormateFormat aber sinnvoll sein, um zu vermeiden, dass einzelne Datentypen: Bytes
Datenbreite: Bytes
Bytes
innerhalb der Zahl zufällig ein Die Tastatur: Steuerzeichen des Key Managers und des ZeileneditorsSteuerzeichen darstellen.

Packed BCD

Der Hauptnachteil des BCD-Formates, der übergroße Speicherplatzbedarf, wird bei der Verwendung von 'Datentypen: Packed BCDpacked BCD' verringert. Zur eindeutigen Darstellung einer Dezimalziffer sind nämlich nur vier Datenbreite: Bits
Port B - Input: &F5xx: Bit 0:
Port B - Input: &F5xx: Bit 4:
Port B - Input: &F5xx: Bit 5:
Port B - Input: &F5xx: Bit 6:
Port B - Input: &F5xx: Bit 7:
Port C - Output: &F6xx: Bit 4:
Port C - Output: &F6xx: Bit 5:
Bits
notwendig. Hiermit lassen sich Zahlen von 0 bis 15 kodieren, wobei für Dezimalziffern natürlich nur die Werte 0 bis 9 zulässig sind. In einem Datentypen: Bytes
Datenbreite: Bytes
Byte
lassen sich so zwei Dezimalzahlen unterbringen, wobei eine im höherwertigen Datenbreite: NibblesNibble (Port C - Output: &F6xx: Bits 0 bis 3:Bits 4 bis 7) und die andere im niederwertigen Datenbreite: NibblesNibble (Port C - Output: &F6xx: Bits 0 bis 3:Bits 0 bis 3) zu liegen kommt.

Anhang: Die Z80Die Z80 unterstuetzt das Packed-BCD-Format mit ihrem Befehl 'DAA'. Hiermit kann nach jeder normalen, binären Rechnen im Binärsystem: AdditionAddition oder Rechnen im Binärsystem: SubtraktionSubtraktion mit dem A-Register das Ergebnis entsprechend der BCD-Darstellung korrigiert werden. Aber auch die Befehle 'RLD_(HL)' und 'RRD_(HL)' unterstuetzen die Nibble-weise Behandlung von BCD-Zahlen.

Das Betriebssystem des Schneider CPCDas Betriebssystem des Schneider CPC benutzt dieses Amsdos: FormateFormat nie. Eine Zahl könnte im Packed-BCD-Format aber wie folgt im Speicher abgelegt sein:

ZAHL1:  DEFB VZE       ; Vorzeichen Real: Der Exponentdes Exponenten
        DEFB EXPONENT  ; Exponent. BCD-codiert                  ; im oberen
        DEFB VZM       ; Vorzeichen der Mantisse                ; Datenbreite: NibblesNibble ist
        DEFB ZIFFERN01 ; 1. & 2. Dezimalziffern (höchstwertig) ; jeweils die
        DEFB ZIFFERN23 ; .                                      ; höherwertige
        DEFB ZIFFERN45 ; .                                      ; Dezimalziffer
        DEFB ZIFFERN67 ; .                                      ; enthalten.
        DEFB ZIFFERN89 ; 9. & 10. Dezimalziffern (niederwertig)

Bytes

Anhang: Die Z80Die Z80 unterstuetzt als 8-Bit-CPU in der Hauptsache das Rechnen mit Datentypen: Bytes
Datenbreite: Bytes
Bytes
. Das Haupt-Rechenregister ist der Akku, der für Rechnen im Binärsystem: AdditionAddition und Rechnen im Binärsystem: SubtraktionSubtraktion immer benötigt wird. Einfaches Incrementieren und Decrementieren ist aber mit allen anderen Registern auch möglich.

Im Schneider CPC werden Datentypen: Bytes
Datenbreite: Bytes
Bytes
direkt nur für die Speicherung von (ASCII-) Zeichen benutzt, beispielsweise in Datentypen: StringsStrings. Mithin besteht in Einleitung: BASIC
Anhang: Basic
Basic
keine vorgegebene Möglichkeit, mit Datentypen: Bytes
Datenbreite: Bytes
Bytes
zu rechnen oder Byte-Variablen anzulegen, was beispielsweise bei großen Datenfeldern von Nutzen sein kann.

Datentypen: Bytes
Datenbreite: Bytes
Bytes
können von der Die ICs im Überblick: Die CPU Z80
Das Innenleben der CPC-Rechner: Die CPU Z80
Die Anschlussbelegungen der wichtigsten ICs im CPC: Die CPU Z80
CPU
sowohl vorzeichenbehaftet als auch nur positiv behandelt werden. Im ersten Fall können Zahlen von -128 bis +127, und im zweiten Fall von 0 bis 255 dargestellt werden.

Negative Zahlen werden in komplementärer Form dargestellt. Die Zahlen von 0 bis 127 sind in beiden Fällen identisch. Die Zahlen -128 bis -1 entsprechen 128 bis 255. Ein gesetztes siebtes Datenbreite: Bits
Port B - Input: &F5xx: Bit 0:
Port B - Input: &F5xx: Bit 4:
Port B - Input: &F5xx: Bit 5:
Port B - Input: &F5xx: Bit 6:
Port B - Input: &F5xx: Bit 7:
Port C - Output: &F6xx: Bit 4:
Port C - Output: &F6xx: Bit 5:
Bit
signalisiert bei der Komplementär-Darstellung eine negative Zahl.

Dabei gibt es keine Unterscheidungsmöglichkeit für die beiden Darstellungsarten. Ein Datentypen: Bytes
Datenbreite: Bytes
Byte
wird nur 'per definition' der einen oder anderen Art zugeordnet. Auch die Rechenoperationen sind identisch! Man muss nur, je nach Darstellungsart, andere Die Z80: Wirkung der Z80-Befehle auf die FlagsFlags auswerten, um eine Überschreitung des darstellbaren Bereiches festzustellen.

Für die normale Darstellung nur positiver Zahlen muss für einen Übertrag das Carry-Flag getestet werden. Beim Rechnen mit Komplementärzahlen muss man das Parity/Overflow-Flag auswerten.

Die folgende Tabelle zeigt eine Gegenüberstellung der Interpretation eines Datentypen: Bytes
Datenbreite: Bytes
Bytes
(im Akku etwa) als Zahl mit oder ohne Vorzeichen:

Datentypen: Bytes
Datenbreite: Bytes
Byte
= kompl / pos ------------------ &02 = 2 / 2 &01 = 1 / 1 &00 = 0 / 0 &FF = -1 / 255 &FE = -2 / 254 ... &81 = -127 / 129 &80 = -128 / 128 &7F = 127 / 127 &7LOW KERNEL JUMPBLOCK: 000E: LOW PCBC INSTRUCTION
LOW KERNEL JUMPBLOCK: 001E: LOW PCHL INSTRUCTION
E
= 126 / 126

Words - Integer

Obwohl Anhang: Die Z80die Z80 nur ein 8-Bit-Mikroprozessor ist, enthält ihr Befehlssatz bereits Kommandos, um mit 16 Datenbreite: Bits
Port B - Input: &F5xx: Bit 0:
Port B - Input: &F5xx: Bit 4:
Port B - Input: &F5xx: Bit 5:
Port B - Input: &F5xx: Bit 6:
Port B - Input: &F5xx: Bit 7:
Port C - Output: &F6xx: Bit 4:
Port C - Output: &F6xx: Bit 5:
Bit
breiten 'Datenbreite: WordsWords' zu rechnen. Als Hauptrechenregister dient hierbei HL. Alle 16-Bit-Doppelregister können eine nur positive Zahl im Bereich von 0 bis 65535 oder eine vorzeichenbehaftete Zahl zwischen -32768 und +32767 enthalten. Für letztere wird wieder die Komplementär-Darstellung benutzt, die wie bei den Datentypen: Bytes
Datenbreite: Bytes
Bytes
gehandhabt wird.

Die Darstellung der Integerzahlen in Einleitung: BASIC
Anhang: Basic
Basic
und Betriebssystem entspricht der Kodierung in der Die ICs im Überblick: Die CPU Z80
Das Innenleben der CPC-Rechner: Die CPU Z80
Die Anschlussbelegungen der wichtigsten ICs im CPC: Die CPU Z80
Z80
, wobei natürlich Zahlen mit Vorzeichen benutzt werden. Eine weitere Vorgabe des Prozessors ist die Reihenfolge, mit der die beiden Datentypen: Bytes
Datenbreite: Bytes
Bytes
eines Integer-Words im Speicher stehen: Auf der niedrigeren Adresse steht das niederwertige Datentypen: Bytes
Datenbreite: Bytes
Byte
und auf der folgenden, höheren Adresse das höherwertige Datentypen: Bytes
Datenbreite: Bytes
Byte
mit dem Vorzeichen.

Die folgende Tabelle zeigt eine Gegenüberstellung der Interpretation eines Datenbreite: WordsWords (in HL etwa) als Zahl mit oder ohne Vorzeichen:

 Datenbreite: WordsWord = kompl /   pos
---------------------
&0002 =     2 /     2
&0001 =     1 /     1
&0000 =     0 /     0
&FFFF =    -1 / 65535
&FFFE =    -2 / 65534
     ...
&8002 = -32766/ 32770
&8001 = -32767/ 32769
&8000 = -32768/ 32768
&7FFF =  32767/ 32767
&7FFE =  32766/ 32766

Real

Grundsätzlich werden Fließkomma-Zahlen durch 2 getrennte Zahlen dargestellt: Durch eine MANTISSE und einen EXPONENTEN. Real: Die MantisseDie Mantisse gibt die Ziffernfolge an und Real: Der Exponentder Exponent die Lage des Kommas. Der Wert einer solchen Zahl ergibt sich dann aus:

mantisse * ( z ^ exponent )

wobei z die Zahlenbasis ist: 10 bei dezimaler oder 2 bei binärer Darstellung. Als Beispiel eine Zahl in Exponentialschreibweise im Dezimalsystem:

Mantisse = 12,3456
Exponent =  3         ---->      12,3456 * 10^3
                               = 12345,6 * 10^0

10 hoch 0 ist bekanntlich 1 und kann daher weggelassen werden:

                                 12345,6 * 10^0
                               = 12345,6 * 1
                               = 12345,6

Die Fließkomma-Rechenroutinen des Schneider CPC benutzen auch die Exponentialform, um die Realzahlen zu kodieren. Sinnvollerweise rechnet der CPC aber im Binärsystem. Die gesamte Zahl beansprucht dabei 5 Datentypen: Bytes
Datenbreite: Bytes
Bytes
. Die ersten vier Bytes enthalten Real: Die Mantissedie Mantisse incl. Vorzeichen und das fünfte Datentypen: Bytes
Datenbreite: Bytes
Byte
den Binärexponenten:

ZAHL1:  DEFB M0
        DEFB Alle noch folgenden Anschlüsse fallen unter die Rubrik STEUER- oder auch CONTROLBUS:: M1 - Machine Cycle OneM1
        DEFB M2
        DEFB M3 + VZ
        DEFB EXP+128
Die Mantisse

M0 bis M3 bilden Real: Die Mantissedie Mantisse, wobei M3 das höchstwertige Datentypen: Bytes
Datenbreite: Bytes
Byte
ist. Real: Die MantisseDie Mantisse wird in normalisierter Form angegeben: 0,1xxxxxx... (binär!).

Dadurch kann Real: Die Mantissedie Mantisse Werte zwischen 0,5 (incl.) und 1,0 (excl.) annehmen:

[ 0,5 ... Mantisse ... [ 1,0

Normalisiert bedeutet, dass die signifikanten Ziffern der Mantisse quasi von hinten gegen den Dezimalpunkt (im deutschen: Das Komma) gerückt werden. Vor dem Komma steht somit immer eine Real: NullNull und dahinter eine Eins.

In M0 bis M3 wird nur der Nachkommateil der Mantisse gespeichert. Irgendeine Vornull mit abzuspeichern wäre nur unnötiger Ballast.

Außerdem steht aber auch die Eins nach dem Komma fest. Anders als im Dezimalsystem kann die erste Nachkommastelle der normalisierten Mantisse nur den Wert Eins annehmen. Sie soll ja ungleich Real: NullNull sein. Und im Binärsystem bleibt da nur die Eins als einzige Alternative (Im Dezimalsystem hätte man immerhin noch die Auswahl zwischen 1,2,3 usw. bis 9).

Auch diese Ziffer explizit abzuspeichern ist überfluessig. Trotzdem wird diese Stelle in der Mantisse mitgeführt. Sie belegt Datenbreite: Bits
Port B - Input: &F5xx: Bit 0:
Port B - Input: &F5xx: Bit 4:
Port B - Input: &F5xx: Bit 5:
Port B - Input: &F5xx: Bit 6:
Port B - Input: &F5xx: Bit 7:
Port C - Output: &F6xx: Bit 4:
Port C - Output: &F6xx: Bit 5:
Bit
7 von M3. Dass ihr Wert aber feststeht, hat man sich zunutze gemacht und hier einfach das Vorzeichen gespeichert. Ist dieses Datenbreite: Bits
Port B - Input: &F5xx: Bit 0:
Port B - Input: &F5xx: Bit 4:
Port B - Input: &F5xx: Bit 5:
Port B - Input: &F5xx: Bit 6:
Port B - Input: &F5xx: Bit 7:
Port C - Output: &F6xx: Bit 4:
Port C - Output: &F6xx: Bit 5:
Bit
gesetzt, ist die Zahl negativ. Erst zum Rechnen wird das Vorzeichen herausgezogen, getrennt gespeichert und diese Ziffer (Datenbreite: Bits
Port B - Input: &F5xx: Bit 0:
Port B - Input: &F5xx: Bit 4:
Port B - Input: &F5xx: Bit 5:
Port B - Input: &F5xx: Bit 6:
Port B - Input: &F5xx: Bit 7:
Port C - Output: &F6xx: Bit 4:
Port C - Output: &F6xx: Bit 5:
Bit
7 von M3) auf Eins gesetzt.

Der Exponent

Um sehr kleine Zahlen darstellen zu können, muss die Darstellung Real: Der Exponentdes Exponents auch negative Zahlen ermöglichen. Normalerweise bedient man sich dafür des Zweierkomplements. Beim Exponenten hat es sich aber irgendwann eingebuergert, einen bestimmten, festen Betrag zu addieren. Dadurch erhält man die sogenannte 'Charakteristik'.

Den Offset wählt man normalerweise so, dass das Komma der Mantisse gleich weit nach links und nach rechts verschoben werden kann. Real: Der ExponentDer Exponent 'Real: NullNull' muss also möglichst in der Mitte des für die Charakteristik zur Verfügung stehenden Zahlenbereiches liegen. Da Real: Der Exponentder Exponent (bzw. die Charakteristik) ein Datentypen: Bytes
Datenbreite: Bytes
Byte
beanspruchen kann, ergibt sich als 'Mittelwert' 255/2 = 127,5. Üblicherweise wird aufgerundet, weil der kleinste zur Verfügung stehende Wert für eine Sonderfunktion reserviert ist.

Auch die im Schneider CPC integrierte Fließkomma-Arithmetik bildet die Charakteristik, indem sie 128 zum Exponenten addiert. Real: Der ExponentDer Exponent Real: NullNull, was soviel bedeutet wie "Keine Verschiebung der Kommaposition in der Mantisse", entspricht der Charakteristik 128.

Der größtmögliche Exponent ist 255-128 = 127. Real: Die MantisseDie Mantisse kann also maximal um 127 Binärstellen nach links verschoben werden, was einer Rechnen im Binärsystem: MultiplikationMultiplikation mit 2^127 entspricht. Umgerechnet ergibt das einem maximal möglichen Dezimalexponenten von etwa +38,2.

Der kleinstmögliche Exponent entspricht der Charakteristik 1 (0 hat eine Sonderbedeutung) und ist daher 1-128 = -127. Das entspricht einem Dezimalexponent von -38,2. Real: Die MantisseDie Mantisse kann also auch um maximal 127 Binärstellen nach rechts verschoben werden.

Null

Die Konvention, dass Real: Die Mantissedie Mantisse in ihrer normalisierten Form vorliegen soll, bereitet aber einer Zahl enorme Schwierigkeiten: Der Real: NullNull.

Zahlen, deren Betrag größer als (0,111111...)2 * 2^127 ist, lassen sich nicht mehr darstellen. Nach Dezimal konvertiert entspricht das etwa 1,70141*10^38, was man leicht überprüfen kann, wenn man den Computer 10^38*9 berechnen lässt. In diesem Fall wird ein 'Overflow error' gemeldet:

PRINT 1e38*9 [ENTER]
Overflow
 1.70141LOW KERNEL JUMPBLOCK: 000E:  LOW PCBC INSTRUCTION
LOW KERNEL JUMPBLOCK: 001E: LOW PCHL INSTRUCTION
E
+38 Erklärung zu den Bezeichnungen: READY
Erklärungen zu den Anschlussbezeichnungen: READY
Ready

Andererseits lassen sich aber auch keine Zahlen darstellen, deren Betrag kleiner als (0,1000...)2 * 2^(-127) ist! Die betragsmäßig kleinste Zahl, die der Schneider CPC darstellen kann, lässt sich ganz leicht ausrechnen:

PRINT 0.5*2^-127 [ENTER]         : REM (0,5)10 = (0,1)2
 2.93874E-39
Erklärung zu den Bezeichnungen: READY
Erklärungen zu den Anschlussbezeichnungen: READY
Ready

Naeher ran an Real: NullNull geht es nicht. Noch kleinere Zahlen werden immer auf Real: NullNull gerundet. An dieser Stelle entsteht also ein Genauigkeitssprung: Solange eine Zahl nicht nach Real: NullNull gerundet werden musste, bleibt das Ergebnis einer Rechenoperation normalerweise auf etwa 9 Stellen genau. Die Rechnung 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
*LOW KERNEL JUMPBLOCK: 000B: LOW KL LOW PCHL
LOW KERNEL JUMPBLOCK: 001B: LOW KL FAR PCHL
LOW KERNEL JUMPBLOCK: 003B: LOW EXT INTERRUPT
b
wird einen Wert liefern, der nicht oder nur kaum von Operationen: BD5B / 349A / 349A: FLO SUBa abweicht. Muss aber das Ergebnis des Terms 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
auf Real: NullNull gerundet werden, wozu im Einzelfall eine ganz geringe Variation von Operationen: BD5B / 349A / 349A: FLO SUBa und LOW KERNEL JUMPBLOCK: 000B: LOW KL LOW PCHL
LOW KERNEL JUMPBLOCK: 001B: LOW KL FAR PCHL
LOW KERNEL JUMPBLOCK: 003B: LOW EXT INTERRUPT
b
genügt, wird das Ergebnis von 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
*LOW KERNEL JUMPBLOCK: 000B: LOW KL LOW PCHL
LOW KERNEL JUMPBLOCK: 001B: LOW KL FAR PCHL
LOW KERNEL JUMPBLOCK: 003B: LOW EXT INTERRUPT
b
ebenfalls Real: NullNull sein.

Zur Darstellung der Zahl Real: NullNull selbst wird ein Ausnahmefall in der Zahlenkodierung konstruiert: Der allerkleinste darstellbare Exponent bleibt ausschließlich für die Real: NullNull reserviert! Ist Real: Der Exponentder Exponent -128, wird Real: Die Mantissedie Mantisse also nicht ausgewertet. Die Charakteristik ist dann 0, was sich besonders einfach Erklärung der Anschlussbelegung: Testtesten lässt.

Dezimalwandlung

Eine Fließkommazahl, die in binärer Mantisse/Exponent-Schreibweise abgespeichert ist, ist nur mit großen Schwierigkeiten dezimal ausdruckbar! Das liegt daran, dass man nicht einfach Real: Die Mantissedie Mantisse und Real: Der Exponentden Exponenten getrennt in's Dezimalsystem wandeln kann, und so die Dezimalzahl erhält. Die Basis Real: Der Exponentdes Exponenten ist ja ebenfalls verschieden. Eine Erhöhung des Binär-Exponenten bedeutet eben NUR IM BINAERSYSTEM einfach eine Verschiebung der Kommaposition um eine Stelle. Im Dezimalsystem muss man sie zu Fuss als das Bearbeiten, was es ist: Eine Rechnen im Binärsystem: MultiplikationMultiplikation mit Zwei.

Für den umgekehrten Weg, die Auswertung einer Ziffernfolge, die in dezimaler Exponential-Schreibweise eingegeben wurde, um sie intern abzuspeichern, gilt das Selbe. Hier muss binär für jeden Dezimalexponenten Real: Die Mantissedie Mantisse mit 10 multipliziert werden.

Die Vorteile binärer Kodierung liegen deshalb ausschließlich in ihrem geringeren Speicherplatz-Bedarf und in der höheren Rechengeschwindigkeit. Eine Datentypen: BCD-KodierungBCD-Kodierung (auch Datentypen: Packed BCDpacked BCD) hat dagegen eindeutige Vorteile, wenn Zahlen oft ausgedruckt werden sollen, und der Rechenaufwand in den Hintergrund tritt, also beispielsweise bei der Darstellung von Tabellen und Dateien.

Da der Schneider CPC in erster Linie ein 'Rechner' ist, und in akzeptabler Zeit auch 'mal so eben einen komplizierten trigonometrischen Funktionenplot auf dem Bildschirm darstellen soll, ist die Entscheidung bei Amstrad für die binäre Kodierung sicher nur zu begruessen.

Strings

Neben Integer- und Fließkomma-Zahlen kennt der Basic-Interpreter des Schneider CPC noch einen weiteren Daten-Typ: Zeichenketten (Datentypen: StringsStrings).

Die Kodierung von Datentypen: StringsStrings ist von der Die ICs im Überblick: Die CPU Z80
Das Innenleben der CPC-Rechner: Die CPU Z80
Die Anschlussbelegungen der wichtigsten ICs im CPC: Die CPU Z80
CPU
nur sehr schwach vorgegeben. Allenfalls die Tatsache, dass der CPC für jeden Buchstaben ein Datentypen: Bytes
Datenbreite: Bytes
Byte
verwendet, wird von ihr bestimmt.

Sind die Integer- und Fließkomma-Rechenroutinen nur mit Vorsicht dem Betriebssystem zuzurechnen, so ist für die Bearbeitung von Zeichenketten ausschließlich der Basic-Interpreter selbst zuständig.

Eine prinzipielle Schwierigkeit bei der Speicherung von Datentypen: StringsStrings liegt darin, dass sie mit jeder 'Rechen'Die Fließkomma-Routinen: Operationenoperation ihre Länge ändern können. Sie können Garbage Collection: ... beim CPC 464beim CPC deshalb nicht direkt im Variablenbereich des Basic-Interpreters untergebracht werden. Dieser verfügt ja über pre-compilierende Der Sound Manager: FähigkeitenFähigkeiten, für die im Variablenbereich feste Adressen benötigt werden. Datentypen: StringsStrings dürfen sich hier also nicht ausdehnen und wieder zusammenziehen, wodurch sich die Lage aller dahinter abgelegten Unterprogramme: VariablenVariablen verschieben würde.

Das von Amstrad hier verwendete Verfahren ist aber recht verbreitet. Zur Speicherung eines Datentypen: StringsStrings wird im Variablenbereich nur ein sogenannter 'Stringdescriptor', ein String-Beschreiber eingetragen. Dieser ist immer drei Datentypen: Bytes
Datenbreite: Bytes
Bytes
lang, egal ob der Datentypen: StringsString selbst aus 0, 1, 2 oder 255 Zeichen besteht.

Der Stringdescriptor ist wie folgt aufgebaut:

DESCR1: DEFB LAENGE
        DEFW ADRESSE

Das erste Datentypen: Bytes
Datenbreite: Bytes
Byte
gibt an, wie lang die Zeichenkette ist. Dadurch ergibt sich die Beschränkung auf auf maximal 255 Zeichen. Die beiden folgenden Datentypen: Bytes
Datenbreite: Bytes
Bytes
bilden einen Zeiger auf den Datentypen: StringsString, der bei einer Länge 'Real: NullNull' natürlich bedeutungslos ist.

Der Datentypen: StringsString kann dabei entweder im Programmtext selbst liegen oder im 'Memory Pool', also dem frei verfügbaren Speicherbereich. Bei eine einfache Zuweisung eines Textes zu einer Stringvariablen innerhalb eines Programmes wird der Datentypen: StringsString nicht in den Memory Pool kopiert, sondern der Zeiger einfach auf die Zeichenfolge im Basicprogramm eingestellt. Das ist wichtig zu beachten, wenn ein Maschinencode-Programm einen Datentypen: StringsString ändert, damit nicht im Programmtext selbst Änderungen vorgenommen werden!

Sobald einer String-Variablen eine neue Zeichenkette zugeordnet wird (durch Verknüpfung mit einem anderen Datentypen: StringsString oder die Anwendung von MID$, LEFT$, RIGHT$), wird diese im Memory Pool angelegt. Das folgende Beispiel zeigt das:

10 LET Operationen: BD5B / 349A / 349A:  FLO SUBa$="123456" : REM Descr. von Operationen: BD5B / 349A / 349A:  FLO SUBa$ zeigt in das Basic-Programm hinein
20 LET Operationen: BD5B / 349A / 349A:  FLO SUBa$=Operationen: BD5B / 349A / 349A:  FLO SUBa$+"789" : REM Operationen: BD5B / 349A / 349A:  FLO SUBa$ wird im Memory Pool neu eingerichtet
30 LET LOW KERNEL JUMPBLOCK: 000B:  LOW KL LOW PCHL
LOW KERNEL JUMPBLOCK: 001B: LOW KL FAR PCHL
LOW KERNEL JUMPBLOCK: 003B: LOW EXT INTERRUPT
b
$="1234"+"": REM Einleitung: BASIC
Anhang: Basic
Basic
muss rechnen. Deshalb wird LOW KERNEL JUMPBLOCK: 000B: LOW KL LOW PCHL
LOW KERNEL JUMPBLOCK: 001B: LOW KL FAR PCHL
LOW KERNEL JUMPBLOCK: 003B: LOW EXT INTERRUPT
b
$ auch in den Memory Pool gelegt. Guter Trick, wenn man jetzt LOW KERNEL JUMPBLOCK: 000B: LOW KL LOW PCHL
LOW KERNEL JUMPBLOCK: 001B: LOW KL FAR PCHL
LOW KERNEL JUMPBLOCK: 003B: LOW EXT INTERRUPT
b
$ an ein Maschinen-Programm übergeben will, das LOW KERNEL JUMPBLOCK: 000B: LOW KL LOW PCHL
LOW KERNEL JUMPBLOCK: 001B: LOW KL FAR PCHL
LOW KERNEL JUMPBLOCK: 003B: LOW EXT INTERRUPT
b
$ ändert. 40 Maschinencode über HIMEM: CALLCALL &AF00,@LOW KERNEL JUMPBLOCK: 000B: LOW KL LOW PCHL
LOW KERNEL JUMPBLOCK: 001B: LOW KL FAR PCHL
LOW KERNEL JUMPBLOCK: 003B: LOW EXT INTERRUPT
b
$ : REM zum Beispiel an dieses.
Garbage Collection

Jede Manipulation an einem Datentypen: StringsString bewirkt, dass die alte Zeichenkette einfach vergessen wird und die neu gebildete einfach im noch freien Speicherbereich angelegt wird. Dieser wird dadurch natürlich immer kleiner und irgendwann ist er vollständig aufgebraucht. Dann kommt es zur berüchtigten 'Strings: Garbage CollectionGarbage Collection', der Muellsammlung, bei der alle unbenutzten Speicherstellen ermittelt und zu einem neuen Memory Pool zusammengezogen werden. Recycling im Computer.

Dabei wurde ab dem CPC 664 eine entscheidende Änderung bei der Abspeicherung von Datentypen: StringsStrings vorgenommen, die vor allem die Strings: Garbage CollectionGarbage Collection in ausgedehnten String-Feldern beschleunigt.

... beim CPC 464

Garbage Collection: ... beim CPC 464Beim CPC 464 werden die Datentypen: StringsStrings folgendermassen gespeichert: Im Variablenbereich steht zu jedem Datentypen: StringsString ein Descriptor. Dieser zeigt auf einen Datentypen: StringsString, der entweder im Basic-Programm selbst (s.o.) oder im Memory Pool liegt. Im Memory Pool liegen alle Datentypen: StringsStrings dicht an dicht. Die einzelnen Zeichenketten können nur mittels der Descriptoren auseinandergehalten werden. Ab und zu liegt dazwischen ein vergessener Datentypen: StringsString, zu dem kein Descriptor mehr existiert. Je weiter es auf die nächste Strings: Garbage CollectionGarbage Collection zugeht, umso mehr dieser Lücken gibt es.

Um nun die Lücken zu schließen und wieder dem freien Speicherbereich zuzuordnen, geht der Basic-Interpreter wie folgt vor:

Alle Descriptoren werden durchsucht, um den am höchsten im Memory Pool gelegenen Datentypen: StringsString zu bestimmen. Dieser wird jetzt nach oben an Die Aufteilung des RAM durch den Basic-Interpreter: Chaos über HIMEMHIMEM angerückt und die Adresse im Descriptor aktualisiert. Dann werden alle Descriptoren erneut durchsucht, um den nächst-höchsten Datentypen: StringsString zu finden. Dieser wird dann ebenfalls bündig unten an die oberste Zeichenkette 'rangeschoben und der Descriptor aktualisiert. Das geht so weiter, bis kein Descriptor mehr auffindbar ist, oder dessen Zeiger in den Programmtext zeigt. Die folgende Grafik veranschaulicht den Prozess:

Die Aufteilung des RAM durch den Basic-Interpreter: Chaos über HIMEMHIMEM -1-    Die Aufteilung des RAM durch den Basic-Interpreter: Chaos über HIMEMHIMEM -2-    Die Aufteilung des RAM durch den Basic-Interpreter: Chaos über HIMEMHIMEM -3-    Die Aufteilung des RAM durch den Basic-Interpreter: Chaos über HIMEMHIMEM -4-    Die Aufteilung des RAM durch den Basic-Interpreter: Chaos über HIMEMHIMEM -5-    Die Aufteilung des RAM durch den Basic-Interpreter: Chaos über HIMEMHIMEM -6-
             +----------+ +----------+ +----------+ +----------+ +----------+
+----------+ | Datentypen: StringsString 2 | | Datentypen: StringsString 2 | | Datentypen: StringsString 2 | | Datentypen: StringsString 2 | | Datentypen: StringsString 2 |
| Datentypen: StringsString 2 | +----------+ +----------+ +----------+ +----------+ +----------+
+----------+              | Datentypen: StringsString 5 | | Datentypen: StringsString 5 | | Datentypen: StringsString 5 | | Datentypen: StringsString 5 |
| Datentypen: StringsString 5 | +----------+ +----------+ +----------+ +----------+ +----------+
+----------+ | Datentypen: StringsString 5 |              | Datentypen: StringsString 3 | | Datentypen: StringsString 3 | | Datentypen: StringsString 3 |
             +----------+              +----------+ +----------+ +----------+
                                                    | Datentypen: StringsString 4 | | Datentypen: StringsString 4 |
+----------+ +----------+ +----------+              +----------+ +----------+
| Datentypen: StringsString 3 | | Datentypen: StringsString 3 | | Datentypen: StringsString 3 |                           | Datentypen: StringsString 1 |
+----------+ +----------+ +----------+                           +----------+
+----------+ +----------+ +----------+ +----------+
| Datentypen: StringsString 4 | | Datentypen: StringsString 4 | | Datentypen: StringsString 4 | | Datentypen: StringsString 4 |
+----------+ +----------+ +----------+ +----------+ +----------+
| Datentypen: StringsString 1 | | Datentypen: StringsString 1 | | Datentypen: StringsString 1 | | Datentypen: StringsString 1 | | Datentypen: StringsString 1 |
+----------+ +----------+ +----------+ +----------+ +----------+

Enthält der Variablenbereich 'n' String-Descriptoren, so müssen 'n' Zeichenketten verschoben werden. Schlimmer ist jedoch, dass für jede der 'n' Zeichenketten alle 'n' Descriptoren abgeklappert werden müssen, um den noch verbliebenen, höchsten Datentypen: StringsString zu finden. Es sind deshalb 'n'*'n' Vergleiche von String-Adressen nötig, der Aufwand steigt QUADRATISCH mit der Anzahl der definierten Datentypen: StringsStrings!

Vor allem bei umfangreichen String-Feldern (z.LOW KERNEL JUMPBLOCK: 000B: LOW KL LOW PCHL
LOW KERNEL JUMPBLOCK: 001B: LOW KL FAR PCHL
LOW KERNEL JUMPBLOCK: 003B: LOW EXT INTERRUPT
B
.: DIM Operationen: BD5B / 349A / 349A: FLO SUBa$(499,1) = 1000 Datentypen: StringsStrings = 1 Million Vergleiche!) benötigt Einleitung: BASIC
Anhang: Basic
BASIC
für eine Strings: Garbage CollectionGarbage Collection einige Zeit. Zwar ist es nicht so schlimm wie beim C64, wo man mitunter schon 'mal zwischendurch einkaufen gehen kann, aber ein paar Minuten können es schon sein.

... beim CPC 664 und 6128

Speziell für professionelle Anwendungen wird das ansonsten sehr schnelle Locomotive Einleitung: BASIC
Anhang: Basic
Basic
hier inakzeptabel langsam. Was soll man da in die Anleitung eines Programmes schreiben:

   "Und wenn sich das Programm nicht mehr rührt, geraten Sie nicht gleich in
   Panik. Manchmal ist das ganz normal, und nach ein paar Minuten können Sie
   weiterarbeiten." ???

In der Version 1.1 des Basic-Interpreters hat man deshalb das Abspeicherungs-Format der Zeichenketten im Memory Pool geändert. Zusätzlich zu jedem Datentypen: StringsString werden noch zwei Datentypen: Bytes
Datenbreite: Bytes
Bytes
reserviert, in denen normalerweise nur die String-Länge abgespeichert wird (zusätzlich zur Längenangabe im Descriptor). Durch diese zwei Datentypen: Bytes
Datenbreite: Bytes
Bytes
wird der Platzverbrauch pro Datentypen: StringsString erhöht. Mithin kann es vorkommen, dass Basic-Programme, die auf dem CPC 464 (gerade noch) laufen, auf einem CPC 664 oder 6128 mit der Fehlermeldung

Datentypen: StringsString space full

passen müssen. Entscheidende Vorteile hat dieses Verfahren aber, sobald eine Strings: Garbage CollectionGarbage Collection durchgeführt werden muss. Hier geht der Basic-Interpreter jetzt nämlich vollkommen anders vor.

Zunächst werden alle Descriptoren im Variablenbereich abgeklappert, und in die zwei Datentypen: Bytes
Datenbreite: Bytes
Bytes
vor jedem Datentypen: StringsString, das im Memory Pool liegt, die Adresse des Descriptors eingetragen. Danach zeigt also in jedem Descriptor ein Zeiger auf den Datentypen: StringsString und in jedem (noch benutzten!) Datentypen: StringsString ein Zeiger zurück auf den Descriptor.

Im zweiten Durchgang fängt Einleitung: BASIC
Anhang: Basic
Basic
mit dem untersten Datentypen: StringsString an. (Dessen Lage ist bekannt, weil hier eine Systemvariable draufzeit. Die Lage des untersten Datentypen: StringsStrings im Memory Pool muss ja jederzeit bestimmbar sein, um hier weitere Zeichenketten anzufügen.)

Hier können nun zwei Fälle auftreten. Ist die 'Adresse', die in den zwei Datentypen: Bytes
Datenbreite: Bytes
Bytes
zu diesem Datentypen: StringsString eingetragen wurde, kleiner oder gleich 255, so handelt es sich wohl nicht um die Adresse des zugehörigen Descriptors, sondern um die Datentypen: StringsString- Länge. Die Länge wurde im ersten Durchgang nicht gegen die Descriptor-Adresse ausgetauscht, also gibt es keinen Descriptor mehr zu dieser Zeichenkette. Es handelt sich also um eins der vielen 'vergessenen' Datentypen: StringsStrings, die nun aus dem Speicher gelöscht werden können. Mit Hilfe der Länge kann sofort der Anfang des nächsten Datentypen: StringsStrings festgestellt werden.

Der andere Fall ist, dass tatsächlich eine Adresse gefunden wird. Dann wird der Datentypen: StringsString bündig (plus zwei Datentypen: Bytes
Datenbreite: Bytes
Bytes
) an den letzten Datentypen: StringsString nach unten angerückt und der Zeiger im Descriptor aktualisiert. Mit Hilfe der alten Lage des Datentypen: StringsStrings und der Länge (die nun aus dem Descriptor bestimmt werden muss) lässt sich auch hier der nächste Datentypen: StringsString finden.

Das geht so lange weiter, bis Die Aufteilung des RAM durch den Basic-Interpreter: Chaos über HIMEMHIMEM erreicht ist. Danach wird in einem dritten Durchgang das gesamte String-Paket von der Unterkante des Memory Pools bündig nach oben an Die Aufteilung des RAM durch den Basic-Interpreter: Chaos über HIMEMHIMEM herangeschoben und viertens alle Descriptoren um die Verschiebeweite erhöht. Fertig.

Das klingt nicht nur umständlicher, das ist es auch. Aber dieses Verfahren hat einen entscheidenden Vorteil: Die Dauer einer Strings: Garbage CollectionGarbage Collection nimmt jetzt nicht mehr quadratisch mit der Anzahl der definierten String-Variablen zu!

Der folgende Erklärung der Anschlussbelegung: TestTest wurde auf einem CPC 464 und auf einem CPC 6128 laufen gelassen:

100 DIM Operationen: BD5B / 349A / 349A:  FLO SUBa$(0):z=TIME:Operationen: BD5B / 349A / 349A:  FLO SUBa=5
110 WHILE Operationen: BD5B / 349A / 349A:  FLO SUBa<1000
120 Operationen: BD5B / 349A / 349A:  FLO SUBa=Operationen: BD5B / 349A / 349A:  FLO SUBa+Operationen: BD5B / 349A / 349A:  FLO SUBa
130 ERASE Operationen: BD5B / 349A / 349A:  FLO SUBa$
140 DIM Operationen: BD5B / 349A / 349A:  FLO SUBa$(Operationen: BD5B / 349A / 349A:  FLO SUBa)
150 FOR i=0 TO Operationen: BD5B / 349A / 349A:  FLO SUBa:Operationen: BD5B / 349A / 349A:  FLO SUBa$(i)="#"+"":NEXT
160 t=TIME:Operationen: BD5B / 349A / 349A:  FLO SUBa$(0)=SPACE$(255)
170 IF t+5>TIME THEN 160
180 PRINT CHR$(7)
190 WEND

Zwar war ursprünglich gedacht, die verbrauchten Zeiten mit der Systemvariablen TIME auf 1/300 Sekunden genau zu messen. Das ist aber leider nicht möglich, weil während einer Strings: Garbage CollectionGarbage Collection zeitweise der Alle noch folgenden Anschlüsse fallen unter die Rubrik STEUER- oder auch CONTROLBUS:: INT - InterruptInterrupt abgestellt wird. Die Zeiten sind also mit der Hand gestoppt, sprechen aber für sich.

Das Programm dimensioniert in der WHILE/WEND-Schleife immer größere String-Arrays (Zeilen 120-140), die dann in Zeile 150 'gefüllt' werden, damit es auch wirklich was im Memory Pool zu verschieben gibt. Danach wird in Zeile 160/170 so lange Operationen: BD5B / 349A / 349A: FLO SUBa$(0) verändert, bis eine Strings: Garbage CollectionGarbage Collection notwendig wird. Das kann man (trotz abgestelltem Alle noch folgenden Anschlüsse fallen unter die Rubrik STEUER- oder auch CONTROLBUS:: INT - InterruptInterrupt) noch gut an der Systemvariablen TIME erkennen.

Folgende Zeiten wurden gemessen:

          CPC 464                           CPC 6128
-------------------------------------------------------------------------------
DIM       Zeit ab Start  /  Differenz       Zeit ab Start  /  Differenz
-------------------------------------------------------------------------------
Operationen: BD5B / 349A / 349A:  FLO SUBa$(10)    001.5 Sek.        001.5 Sek.      001.5 Sek.        001.5 Sek.
Operationen: BD5B / 349A / 349A:  FLO SUBa$(20)    003   Sek.        001.5 Sek.      003   Sek.        001.5 Sek.
Operationen: BD5B / 349A / 349A:  FLO SUBa$(40)    005.5 Sek.        002.5 Sek.      004.5 Sek.        001.5 Sek.
Operationen: BD5B / 349A / 349A:  FLO SUBa$(80)    008   Sek.        002.5 Sek.      006   Sek.        001.5 Sek.
Operationen: BD5B / 349A / 349A:  FLO SUBa$(160)   013   Sek.        005   Sek.      008   Sek.        002   Sek.
Operationen: BD5B / 349A / 349A:  FLO SUBa$(320)   028   Sek.        015   Sek.      011   Sek.        003   Sek.
Operationen: BD5B / 349A / 349A:  FLO SUBa$(640)   078   Sek.        050   Sek.      015   Sek.        004   Sek.
Operationen: BD5B / 349A / 349A:  FLO SUBa$(1280)  266   Sek.        188   Sek.      021   Sek.        006   Sek.
-------------------------------------------------------------------------------

Valid HTML   Valid CSS