Hans geht zur Post

“Hans geht zur Post” ist ein Programm, welches in Verbindung mit der CTB (Communication Toolbox) aus System 7 den vollautomatischen Tausch mit Mäusen und Quark-Mailboxen ermöglicht.

Es entfällt das Schreiben eines Skripts für ein Terminalprogramm (zumal z. B. ZTerm gar keinen automatischen Tausch-Script ermöglicht).

Üblicherweise wird man “Hans geht zur Post” in Verbindung mit der Mausefalle – als MausTausch Frontend – einsetzen, dies ist jedoch nicht zwangsweise nötig. “Hans geht zur Post” funktioniert übrigens auch bei Maus-Sysops, die in ihrer Mailbox per Nullmodem tauschen.

“Hans geht zur Post” erlaubt zudem beim Tausch mit Mäusen das automatische Packen der Tausch-Files mit dem ARJ-Packer (ist bereits eingebaut – kein externer Packer nötig!)

In Verbindung mit der Mausefalle und ZModem-Modulen wird ein Upload in Mäuse per Tausch unterstützt. Dazu muß “Schnulli” (Saugtausch) in den entsprechenden Mäusen installiert sein! Hierzu bitten beim eigenen Sysop nachfragen.

Ab Version 1.3 von Hans wird zudem ein “Onlinezeit Protokoll” angelegt, welches man sich mit einem beliebigen Textprogramm – z.B. TeachText oder der Mausefalle – ansehen kann. Es enthält alle gebührenpflichten Anrufe von Hans mit Namen der Mailbox, Dauer des Tausches, sowie Datum und Uhrzeit des Logins.

Woher kommt der neue Name für das Programm?

Man sieht der Maus Hans beim Besuch seines örtlichen Postamtes zu, während “Hans geht zur Post” sich in die MAUS einloggt und den MausTausch durchführt.

Systemanforderungen

Um “Hans geht zur Post” einsetzen zu können, benötigt man:

  • Mac OS 7.0 oder neuer
  • Mindestens 2MB Hauptspeicher (4MB empfohlen)
  • Ein Modem
  • CTB-Module für den Aufbau der Verbindung, den Datentransfer und ein Terminalmodul.
  • Vertrieb von “Hans geht zur Post”

Die aktuelle Programmversion

mastercast.

This product died because of the missing support from model agencies (they probably had thoughs that mastercast makes it too easy to find models from other agencies)

Just think that you are looking for a next door girl. Or the exact opposite! How do you find her? With mastercast you just have to turn on your computer, feed it with the mastercast-CD and you will find models, models, models. Single pictures, portfolios, latest pictures and publications – everything in perfect digital quality.

mastercast and the mastercast logo is a trademark of Dekotec GmbH.

mastercast.
Postfach 50 03 24
22703 Hamburg
Tel: 040-896065
Fax: 040-896069

CD-ROM Image

This is the Toast CD-ROM image of Mastercast. It is a multi-session CD-ROM (Macintosh & Windows)

Info-Sys Journal 1

Parse

I developed this small ANSI C program at the university. It is a simple formula parser, useful for many situations, like an intelligent input in dialog boxes.

The gag in this parser is the possibility to compile formulas into p-code to get a major performance improvement while calculating. This is useful for formula plotters, etc., that uses the same formula only with different values.

In Parse.h some configurations can be done:

  • ‘‘Dbl’’ is a typedef for the type of the calculation of the parser. Normally it should be ‘‘float’’ or ‘‘double’’. ‘‘TERM_COMPILER’’ activates the p-code compiler. If you use the formula only once, than you should disable it, otherwise enable it.
  • buildTerm is called to compile an ASCII expression into p-code. If the ‘‘TERM_COMPILER’’ is not active, this routine does nothing.
  • ‘‘calcTerm’’, ‘‘calcTerm1’’, ‘‘calcTerm2’’, ‘‘calcTerm3’’ calculates the current expression with no, 1, 2 or 3 parameters. The parameters are put into the variables ‘‘X’’, ‘‘Y’’ and ‘‘Z’’. Undefined variables have the value ‘‘HUGE_VAL’’.

Besides the trivial operations (‘’+‘’, ‘’-‘’, ‘’*‘’, ‘’/‘’, ‘’^‘’, with brackets) the parser also know the following functions:

Name Description
‘‘PI’’ the value PI (3.1415926…)
‘‘E’’ the value e (2.7182818)
‘‘X’’, ‘‘Y’’, ‘‘Z’’ the values X, Y, Z
‘‘acos’’, ‘‘arccos’’ the function cos–1(a)
‘‘asin’’, ‘‘arcsin’’ the function sin–1(a)
‘‘atan’’, ‘‘arctan’’ the function tan–1(a)
‘‘atan2’’, ‘‘arctan2’’ the function tan–1(a,b)
‘‘ceil’’ the function ceil(a)
‘‘cos’’ the function cos(a)
‘‘cosh’’ the function cosh(a)
‘‘exp’’ the function exp(a)
‘‘floor’’ the function floor(a)
‘‘fmod’’ the function fmod(a)
‘‘log’’ the function log(a)
‘‘log10’’ the function log10(a)
‘‘pow’’ the function pow(a,b)
‘‘sin’’ the function sin(a)
‘‘sinh’’ the function sinh(a)
‘‘sqrt’’ the function sqrt(a)
‘‘tan’’ the function tan(a)
‘‘tanh’’ the function tanh(a)

Sourcecode for the Parser

Futura Aquariencomputer Firmware

Die Firmware vom Futura Aquariencomputer ist für die Z80 CPU geschrieben worden. Ich habe sie ausgelesen und kommentiert. Der komplette Download auf GitHub.

Future Aquarium Computer

 

 

 

 

 

 

 

 

;   Futura Aquariencomputer ROM F- 1.89 (8K)
                PRINT   "Anfang..."
;Speicheraufteilung:
;0000h-1FFFh    8K EPROM
;2000h-27FFh    2K RAM
;4000h          Uhrenchip mit 4 Bit Registern
;4000h : Register lesen (0Fh = Fehler)
;4001h : Register wählen
;4002h : Register-Schreiben
;Register: 4,5: Stunden (BCD); 2,3: Minuten (BCD); 0,1: Sekunden (BCD)
;6000h          Keyboard-Spalte auf 0 zurücksetzen. Zeilendaten mit IN A,(00h) abfragen
;8000h          Port-Adresse (0...2:frei, 3...9: Keyboard, 10...15: Display)
;A000h          LED-Daten an gewähltes LED-Segment; (Port-Adresse zurücksetzen?)
;C000h          Schreibzugriff => Watchdog zurücksetzen
;E000h          Ausgangsport für die Steckdosen

ROMTop          = 1FF0h         ;bis hier wird die Prüfsumme berechnet
RAMBase         = 2000h
RAMTop          = 2800h         ;Endadresse vom RAM + 1

NewVersion      = 0             ;0 = Originalversion, 1 = neue Version

                ORG RAMBase     ;Basisadresse vom RAM. IX zeigt stets auf diese Adresse => (IX+d)
DisplayBufStart
KLED            DEFS    2       ;LEDs der Tasten
;  Bit
;   0       0:Tag-LED an
;   1       0:Nacht-LED an
;   2       0:Ein-LED an
;   3       0:Aus-LED an
;   4       0:Zeit-LED an
;   5       0:Momentan-LED an
;   6       0:Manuelle-LED an
;   7       0:Setzen-LED an
;  Bit
;   0       0:pH-LED an
;   1       0:Temp-LED an
;   2       0:Leitwert-LED an
;   3       0:Redox-LED an
;   4       0:Kanal 1-LED an
;   5       0:Kanal 2-LED an
;   6       0:Licht-LED an
;   7       0:CO2-LED an

WarnLED         DEFS    1       ;6 Warn-LEDs neben dem Display
;  Bit
;   0       0:Kanal 2 an
;   1       0:CO2 an
;   2       0:ph-Alarm an
;   3       0:Kanal 1 an
;   4       0:Heizung an
;   5       0:Temp-Alarm an
;   6       unbenutzt
;   7       unbenutzt


DisplayBuf      DEFS    6
DisplayBufEnd:

;Display-Buffer, wird bei jedem Mainloop-Durchlauf aus dem Display-RAM aufgebaut.
;Hier liegen die für die LEDs kodierten Zeichen drin.

;Font => LED-Tabelle
;    01
;    --
;20 |40| 02     low-active!
;    --
;10 |  | 04
;    --  .
;    08  80


Display         DEFS    6       ;Display-RAM

;Zeichensatz im Display:
;00 - 0   01 - 1   02 - 2   03 - 3
;04 - 4   05 - 5   06 - 6   07 - 7
;08 - 8   09 - 9   0A - A   0B - B
;0C - C   0D - D   0E - E   0F - F
;10 - H   11 - L   12 - P   13 - r
;14 - U   15 - µ   16 - u   17 - n
;18 - °   19 - o   1A - /F  1B - /A         "/" = umgedrehter Buchstabe
;1C - -   1D - _   1E - N   1F - Space

LastKey         DEFS    1       ;zuletzt gedrückte Taste (FFh = keine)

Flags           DEFS    1       ;diverse Flags
;      Bit      Aktion, wenn gesetzt
;       0       Zahleingabe an, ansonsten wird ein Wert dargestellt
;       1       zuletzt gedrückte Taste abgearbeitet. Wird erst gelöscht, wenn Taste losgelassen
;       2       Strom wurde eingeschaltet. Uhrzeit beim Einschalten blinkt.
;       3       Momentane Werte durchschalten
;       4       String im Display (0 = Zahl im Display)
;       5       (unbenutzt)
;       6       während der Kommunikation mit dem Hauptgerät (Meßwerte abholen)
;       7       Führende Zeichen aufgetreten. Nullen ab jetzt ausgeben.

DispLine        DEFS    1       ;"Rasterzeile" beim Display-Refresh
DPunkt          DEFS    1       ;Punkte im Display (Bit 0 = 6.Stelle, Bit 1 = 5.Stelle, ...)
BCDZahl         DEFS    2       ;gewandelte BCD-Zahl
CO2EinZeit      DEFS    3       ;Einschaltzeit von CO2
CO2AlarmZeit    DEFS    3       ;Alarmzeit, wenn pH-Wert nicht den Sollwert erreicht hat
LichtEin        DEFS    3       ;Uhrzeit, wann das Licht angeschaltet wird (3 Bytes: ss:mm:hh)
CO2Ein          DEFS    3       ;Uhrzeit, wann das CO2 ausgeschaltet wird (3 Bytes: ss:mm:hh)
Mult24          DEFS    3       ;Multiplikator
Mult24Erg       DEFS    3       ;Ergebnis der 24 Bit Multiplikation
LichtAus        DEFS    3       ;Uhrzeit, wann das Licht ausgeschaltet wird (3 Bytes: ss:mm:hh)
CO2Aus          DEFS    3       ;Uhrzeit, wann das CO2 ausgeschaltet wird (3 Bytes: ss:mm:hh)
TagZeit         DEFS    3       ;Uhrzeit, wann der Tag beginnt (3 Bytes: ss:mm:hh)
SollTempTag     DEFS    1       ;Temperatur für den Tag
NachtZeit       DEFS    3       ;Uhrzeit, wann die Nacht beginnt (3 Bytes: ss:mm:hh)
SollTempNacht   DEFS    1       ;Temperatur für die Nacht
ManuellZeit     DEFS    3       ;Zeit, wie lange das Licht nach Druck auf "Manuell" an bleibt
SollpH          DEFS    1       ;Soll-pH-Wert

MWpHLow         DEFS    1       ;unteres Byte des Meßwertes
MWphHigh        DEFS    1       ;oberes Byte des Meßwertes (nur 4 Bit)
IstpH           DEFS    1       ;gemessener skalierter pH-Wert (obere 8 Bits des Meßwertes)
MWTempLow       DEFS    1       ;unteres Byte des Meßwertes
MWTempHigh      DEFS    1       ;oberes Byte des Meßwertes (nur 4 Bit)
IstTemp         DEFS    1       ;gemessener skalierter Temp-Wert (obere 8 Bits des Meßwertes)
MWLeitwLow      DEFS    1       ;unteres Byte des Meßwertes
MWLeitwHigh     DEFS    1       ;oberes Byte des Meßwertes (nur 4 Bit)
IstLeitw        DEFS    1       ;gemessener skalierter Leitwert-Wert (obere 8 Bits des Meßwertes)
MWRedoxLow      DEFS    1       ;unteres Byte des Meßwertes
MWRedoxHigh     DEFS    1       ;oberes Byte des Meßwertes (nur 4 Bit)
IstRedox        DEFS    1       ;gemessener skalierter Redox-Wert (obere 8 Bits des Meßwertes)

Messcounter     DEFS    1       ;Zähler von 16 abwärts; es wird nur alle 16 Durchläufe gemessen
TempTime        DEFS    3       ;ss:mm:hh (temporäre Zeit während der Eingabeauswertung)
ManuellEinZeit  DEFS    3       ;Zeit, wann "Manuell" gedrückt wurde
Counter         DEFS    2       ;Blink-Timer (Bit 0 toggelt mit 0.5Hz; wird im IRQ abwärts gezählt)
Steckdosen      DEFS    1       ;Steckdose (Bit = 1: Steckdose an)
;      Bit      Steckdose
;       0       CO2
;       1       Heizung
;       2       Licht
;       3       Kanal 1
;       4       Kanal 2
;       5,6,7   die oberen 3 Bits dienen der Kommunikation mit dem Hauptgerät

AktTime         DEFS    3       ;aktuelle Uhrzeit (ss:mm:hh)
PowerOnZeit     DEFS    3       ;Uhrzeit beim Einschalten des Stromes
ManuellAusZeit  DEFS    3       ;Ausschaltzeit nach Druck auf "Manuell"
TempAlarmZeit   DEFS    3       ;Heizungsalarm (Einschaltzeit + 1h)
                DEFS    3
AktSchaltzeit   DEFS    1       ;aktuelle Schaltzeit der Universaltimer (1...10 sind möglich)
SollLeitwertS   DEFS    1       ;Soll-Leitwert (Süßwasser)
SollLeitwertM   DEFS    1       ;Soll-Leitwert (Meerwasser)
Uni1Flag        DEFS    1       ;55h => Kanal 1 = UNI-1
                                ;AAh => Kanal 1 = Redox-Regler
                                ;<>  => Kanal 1 = inaktiv
Uni2Flag        DEFS    1       ;55h => Kanal 2 = UNI-2
                                ;AAh => Kanal 2 = Leitwert-Regler
                                ;<>  => Kanal 2 = inaktiv
Uni2Flag2       DEFS    1       ;55h = Leitwert EIN Regelung
                                ;AAh = Leitwert AUS Regelung
SollRedox       DEFS    1       ;Soll-Redoxwert
LeitwertKomp    DEFS    1       ;kompensierter Leitwert
AktSollTemp     DEFS    3       ;aktuelle Solltemperatur (Tag oder Nacht)
Kanal1Uni       DEFS    11*2*3  ;Universaltimer-Zeiten von Kanal 1 (10 Stück a 3 Bytes, erst Ein-, dann Ausschaltzeiten)
Kanal2Uni       DEFS    10*2*3  ;Universaltimer-Zeiten von Kanal 1 (10 Stück a 3 Bytes, erst Ein-, dann Ausschaltzeiten)
MomentanSek     DEFS    1       ;Momentan-Sekunden-Merker für Momentan-Momentan
DelayTimer      DEFS    1       ;Variable für Verzögerungen, etc.
KeyboardMatrix  DEFS    7       ;Keyboard-Matrix-Zeilen (untere 4 Bit, gelöscht = gedrückt)
InputBuf:       DEFS    10      ;Buffer für GetNumInput()
                IF !NewVersion
LaufschriftFlag DEFS    1       ;55h = Laufschrift an
LaufschriftInit DEFS    1       ;55h = Laufschrift ist initialisiert
LaufschriftPtr  DEFS    2       ;Ptr auf eine Laufschrift
ScrollPtr       DEFS    2       ;Ptr auf das nächste Zeichen in der Laufschrift
                ENDIF
SollChecksum    DEFS    2       ;Soll-Prüfsumme über die Sollwerte
                IF !NewVersion
                DEFS    2
Dummy0:         DEFS    2       ;= 0, wird in der Init-Laufschrift ausgegeben
                ENDIF
AktROMChecksum  DEFS    2       ;Prüfsumme über das ROM _während_ der Berechnung
CalcChecksum    DEFS    2       ;letzte errechnete Prüfsumme
ChecksumFinal   DEFS    1       ;Prüfsumme in CalcChecksum ist gültig (aber evtl. falsch!)
ROMTopAdr       DEFS    2       ;Endadresse vom ROM (läuft bis 0 rückwärts während der Prüfsummenberechnung)
ErrorCode       DEFS    1       ;aufgetretener Fehler (0 = keiner)
GesamtBZeit     DEFS    5       ;Gesamt-Betriebsstunden
Kanal1BZeit     DEFS    4       ;Betriebsstunden für Kanal 1 (4 Bytes: mm:hhhhhh)
Kanal2BZeit     DEFS    4       ;Betriebsstunden für Kanal 2 (4 Bytes: mm:hhhhhh)
CO2BZeit        DEFS    4       ;Betriebsstunden für CO2 (4 Bytes: mm:hhhhhh)
TempBZeit       DEFS    4       ;Betriebsstunden für Heizung (4 Bytes: mm:hhhhhh)
LichtBZeit      DEFS    4       ;Betriebsstunden für Licht (4 Bytes: mm:hhhhhh)
                IF !NewVersion
StringBuf       DEFS    200     ;???
StringBufPtr    DEFS    2       ;???
InitLaufschr    DEFS    1       ;0xAA = Init-Laufschrift an, 0x55 = Init-Laufschrift aus
InitLaufschrSek DEFS    1       ;Sekunden-Merker für den Init-Laufschrift-Start
Dummy           DEFS    3       ;??? wird in der Init-Laufschrift ausgegeben, aber nie gesetzt
                ENDIF
                IF NewVersion
MomentanZeit    DEFS    1       ;Weiterschaltzeit für Momentan (in Sekunden)
                ENDIF
StackTop        = RAMTop        ;der Stack fängt ganz oben im RAM an

;Flags von: IN C,(C) (externe Schalter)
;       4       0:Programmiersperre an
;       5       0:Meerwasser, 1:Süßwasser

                ORG 0000h
                DI                              ;IRQs aus (an sich unnötig, sind nach einem Reset eh aus...)
                IM      1                       ;bei IRQs stets RST 38h ausführen!
                LD      SP,StackTop
                LD      (C000h),A
                LD      IX,RAMBase              ;Basisadresse vom RAM
                LD      B,FFh
                LD      (IX+DispLine),DisplayBufEnd ;Display-Refresh (im IRQ)
                IF !NewVersion
                LD      A,AAh
                LD      (InitLaufschr),A        ;Init-Laufschrift AN
                ENDIF
                EI                              ;IRQs wieder an
                LD      HL,0
                LD      (AktROMChecksum),HL     ;ROM-Prüfsumme zurücksetzen
                LD      HL,ROMTop
                LD      (ROMTopAdr),HL          ;Endadresse vom ROM
                JP      Startup

                ORG 0038h                       ;der RST 0x38 bzw. RST 7 Interrupt
                DI
                EXX
                EX      AF,AF'
                JP      DoIRQ
                IF !NewVersion
                JP      DoIRQ                   ;???
                ENDIF

                ORG 0066h                       ;der NMI-Vektor des Z80
;NMI-Routine ("Reset" = alles zurücksetzen)
DoNMI:          IF NewVersion
                CALL    ResetVars
                RETN
                ENDIF

;sämtliche Variablen zurücksetzen!
ResetVars:      PUSH    HL
                PUSH    AF
                PUSH    BC
                PUSH    DE
                LD      BC,23
                LD      HL,Kanal1BZeit
                LD      DE,Kanal1BZeit+1
                LD      (HL),0
                LDIR                            ;Betriebszeiten löschen
                LD      DE,GesamtBZeit
                LD      HL,TempTime             ;temp.Zeit übertragen
                LD      BC,3
                LDIR                            ;Gesamtbetriebszeit setzen
                IN      C,(C)
                BIT     5,C                     ;Süßwasser/Meerwasser-Schalter abfragen
                JR      Z,DoNMI1                ;Meerwasser =>
                LD      A,64                    ;Soll-pH-Wert ((64/2+38)/10 = 7.0)
                JR      DoNMI2
DoNMI1:         LD      A,90                    ;(90/2+38)/10 = 8.3
DoNMI2:         LD      (IX+SollpH),A           ;Soll-pH-Wert
                LD      A,145                   ;(145+100)/10 = 24.5°
                LD      (IX+SollTempTag),A      ;Soll-Temperatur (Tag)
                LD      A,130                   ;(130+100)/10 = 23°
                LD      (IX+SollTempNacht),A    ;Soll-Temperatur (Nacht)
                LD      A,150                   ;150/10+35.0 = 50mS
                LD      (IX+SollLeitwertM),A    ;Soll-Leitwert (Meerwasser)
                LD      A,80                    ;80*10 = 800µS
                LD      (IX+SollLeitwertS),A    ;Soll-Leitwert (Süßwasser)
                LD      A,125                   ;125*2 = 250µV
                LD      (IX+SollRedox),A        ;Soll-Redoxwert
                LD      A,0
                LD      (IX+AktSchaltzeit),A    ;keine aktuelle Schaltzeit
                LD      (IX+Uni1Flag),A         ;Kanal 1 inaktiv schalten
                LD      (IX+Uni2Flag),A         ;Kanal 2 inaktiv schalten
                LD      (IX+Uni2Flag2),A        ;Leitwert-Regelung inaktiv
                LD      HL,CO2Ein
                LD      B,3
DoNMI3:         LD      (HL),A                  ;CO2 Sperrzeit ein = 00.00.00
                INC     HL
                DJNZ    DoNMI3
                LD      HL,CO2Aus
                LD      B,3
DoNMI4:         LD      (HL),A                  ;CO2 Sperrzeit aus = 00.00.00
                INC     HL
                DJNZ    DoNMI4
                IF      NewVersion
                LD      A,7
                LD      (MomentanZeit),A        ;Momentan-Zeit : 7 Sekunden
                ENDIF
                LD      A,80h
                LD      (LichtEin+2),A
                LD      (TagZeit+2),A
                LD      A,81h
                LD      (LichtAus+2),A
                LD      (NachtZeit+2),A
                POP     DE
                POP     BC
                POP     AF
                POP     HL
                IF NewVersion
                RET
                ELSE
                RETN
                ENDIF

;IRQ-Routine für Tastatur und Display
DoIRQ:          LD      A,0
                LD      (A000h),A               ;Port-Adresse auf 0 zurücksetzen
                LD      HL,8000h
                LD      B,7                     ;7 Keyboard-Spalten
                LD      DE,KeyboardMatrix+6
                LD      (HL),A
                LD      (HL),A                  ;auf Adresse 3 weiterschalten
                LD      (HL),A
                LD      (6000h),A               ;Port auslesen
DoIRQ1:         IN      A,(00h)                 ;Keyboard-Spalte auslesen
                LD      (DE),A                  ;und merken
                LD      (HL),A                  qqww;Port-Adresse hochzählen
                DEC     DE                      ;eine Spalte nach vorne
                DJNZ    DoIRQ1                  ;alle Spalten durch? Nein =>

                LD      B,(IX+DispLine)         ;LED-Daten
                DJNZ    DoIRQ2                  ;einmal durch?
                LD      B,DisplayBufEnd         ;wieder von vorne
DoIRQ2:         LD      (IX+DispLine),B         ;aktuelle Zeile setzen
                LD      H,DisplayBufStart>>8
                LD      L,B                     ;DisplayBuf + B - 1 (DisplayBuf...DisplayBufEnd)
                DEC     L
                LD      A,(HL)                  ;Speicherzelle aus dem Display auslesen
                LD      HL,8000h
DoIRQ3:         LD      (HL),A                  ;Port-Adresse hochzählen (10...15)
                DJNZ    DoIRQ3                  ;und zwar B mal
                CPL
                LD      (A000h),A               ;und das Display-Segment setzen
                LD      HL,(Counter)
                DEC     HL                      ;IRQ-Zähler (für Display-Blinken)
                LD      (Counter),HL
                EXX
                EX      AF,AF'
                EI
                RETI


Startup:        LD      (C000h),A
                HALT                            ;Verzögerung
                DJNZ    Startup
                LD      HL,VersionNoDisp
                LD      DE,DisplayBuf
                LD      BC,6
                LDIR                            ;Versionsnummer in die LED-Anzeige "F- 1.89"
                LD      B,FFh
Startup1:       HALT
                HALT
                LD      (C000h),A               ;vierfache Verzögerung
                HALT
                HALT
                LD      (C000h),A
                DJNZ    Startup1
                CALL    KeyStern                ;Display löschen
                SET     2,(IX+Flags)            ;PowerOn-Flag setzen
                SET     6,(IX+KLED)             ;Manuell-LED aus
                LD      DE,PowerOnZeit
                LD      HL,AktTime
                LD      BC,3
                LDIR                            ;aktuelle Uhrzeit merken
                IF !NewVersion
                LD      DE,StringBuf
                LD      (StringBufPtr),DE
                LD      HL,MsgEscHEscJ
                CALL    CopyString
                LD      HL,MsgMessdaten
                CALL    CopyString
                LD      A,55h
                LD      (InitLaufschr),A        ;Init-Laufschrift AUS
                JP      DoLEDKonv
                ENDIF

; Hier beginnt die Hauptschleife...
DoLEDKonv:      LD      B,6                     ;6 LED-Anzeigen updaten
                LD      IY,Display              ;Ptr auf Display-RAM (unkodiert)
                LD      HL,DisplayBuf
                RES     7,(IX+Flags)            ;noch kein 1.Zeichen ausgegeben
                LD      C,(IX+DPunkt)           ;(6) Dezimalpunkte holen
                SLA     C
                SLA     C                       ;um 2 Bits nach oben an den "Byterand"
DoLEDKonv1:     PUSH    HL
                LD      HL,FontLEDTable         ;Ptr auf "Zeichensatz"-Tabelle
                LD      E,(IY+0)                ;Zeichen aus dem Display-RAM
                LD      D,0
                ADD     HL,DE
                LD      A,(HL)                  ;Zeichencode holen
                SLA     A                       ;A << 1
                SLA     C                       ;C << 1 (ins Carry)
                RR      A                       ;A >> 1; Carry in Bit 7
                BIT     7,(IX+Flags)            ;1.Zeichen schon ausgegeben?
                JR      NZ,DoLEDKonv3           ;Ja! =>
                CP      C0h                     ;"0"?
                JR      NZ,DoLEDKonv3           ;Nein => normal ausgeben
                LD      A,B
                CP      1                       ;letztes Anzeigeelement?
                JR      Z,DoLEDKonv2            ;Ja! =>
                LD      A,FFh                   ;LED komplett ausschalten (keine führenden Nullen ausgeben)(
                JR      DoLEDKonv4
DoLEDKonv2:     LD      A,C0h                   ;"0" darstellen
DoLEDKonv3:     SET     7,(IX+Flags)            ;1.Zeichen bereits ausgegeben
DoLEDKonv4:     POP     HL
                LD      (HL),A                  ;LED-Element neu setzen
                INC     IY                      ;weiter im Display-RAM
                INC     HL                      ;zum nächsten Element
                DJNZ    DoLEDKonv1              ;alle LEDs durch?

DoGetMess:      LD      B,(IX+Messcounter)      ;alle 16 Durchläufe umrechnen?
                DJNZ    DoGetMess2              ;Nein! =>
                LD      B,4                     ;4 Meßwerte holen (pH-Wert, Temperatur, Leitwert, Redox)
                LD      IY,MWpHLow
DoGetMess1:     LD      L,(IY+0)                ;unteres Byte lesen
                LD      H,(IY+1)                ;oberes Byte lesen
                ADD     HL,HL
                ADD     HL,HL
                ADD     HL,HL                   ;mal 8
                ADD     HL,HL
                LD      (IY+2),H                ;nur das obere Byte merken
                LD      (IY+0),0                ;Meßwert zurücksetzen
                LD      (IY+1),0
                INC     IY                      ;zum nächsten Meßwert
                INC     IY
                INC     IY
                DJNZ    DoGetMess1
                LD      B,16                    ;Durchlaufzähler neu setzen
DoGetMess2:     LD      (IX+Messcounter),B

                LD      HL,SpezKeyTable         ;Ptr auf den Tabellenanfang
DoSpezKey:      LD      DE,KeyboardMatrix
                LD      B,7                     ;7 Bytes pro Eintrag (= 7 Zeilen) (+ 2 Byte Adresse)
DoSpezKey1:     LD      A,(DE)                  ;Spaltenwert holen
                OR      F0h
                CP      (HL)                    ;Eintrag in der Tabelle?
                JR      Z,DoSpezKey3            ;Ja! => stimmen die nächsten 6 Bytes auch?
DoSpezKey2:     INC     HL
                DJNZ    DoSpezKey2              ;Eintrag überspringen
                INC     HL
                INC     HL
                LD      A,(HL)                  ;Folgebyte holen
                CP      0                       ;Tabellenende?
                JR      Z,DoKeyboard            ;Ja! =>
                JR      DoSpezKey               ;weiter vergleichen...
DoSpezKey3:     INC     DE                      ;nächste Tastaturspalte
                INC     HL
                DJNZ    DoSpezKey1              ;alle 7 Bytes gleich? Nein! => Weiter
                LD      D,(HL)
                INC     HL
                LD      E,(HL)                  ;Sprungadresse holen
                PUSH    DE                      ;Sprungadresse merken
                POP     IY
                CALL    CallIY                  ;gefundene Routine anspringen

DoKeyboard:     LD      A,0                     ;Tastencode = 0
                LD      B,7                     ;7 Tastaturspalten abklopfen
                LD      HL,KeyboardMatrix       ;Ptr auf Tastaturmatrix-Basis
DoKeyboard1:    LD      C,4                     ;maximal 4 Zeilen pro Spalte
                LD      D,(HL)                  ;Byte holen
DoKeyboard2:    RR      D
                JR      NC,DoKeyboard3          ;Bit gesetzt? (Taste gedrückt) => raus
                INC     A                       ;Tastencode++
                DEC     C                       ;alle Zeilen dieser Spalte zusammen?
                JR      NZ,DoKeyboard2          ;Nein =>
                INC     HL                      ;zur nächsten Spalte
                LD      (C000h),A
                DJNZ    DoKeyboard1             ;alle Spalten durch?
                LD      A,FFh                   ;dann keine Taste gedrückt
DoKeyboard3:    CP      (IX+LastKey)            ;mit der zuletzt gedrückten Taste vergleichen
                LD      (IX+LastKey),A          ;als letzte Taste merken
                JR      NZ,DoRecMess            ;ungleich? => ignorieren (entprellen)
                BIT     1,(IX+Flags)            ;Taste abgearbeitet?
                JR      Z,DoKeyboard4           ;Nein =>
                CP      FFh                     ;keine Taste gedrückt?
                JR      NZ,DoRecMess            ;doch! =>
                RES     1,(IX+Flags)            ;Abgearbeitet-Flag löschen
                JR      DoRecMess
DoKeyboard4:    CP      FFh                     ;keine Taste gedrückt?
                JR      Z,DoRecMess             ;genau =>
                SET     1,(IX+Flags)            ;Taste abgearbeitet!

                LD      HL,TastaturTab          ;Tastaturtabelle
                ADD     A,A
                LD      E,A
                LD      D,0
                ADD     HL,DE
                LD      D,(HL)                  ;Sonderflag
                INC     HL
                LD      E,(HL)                  ;Tastencode
                BIT     7,D                     ;normale Ziffer?
                JR      Z,DoKeyboard6           ;Nein! =>
                BIT     0,(IX+Flags)            ;Zahleingabe an?
                JR      NZ,DoKeyboard5          ;Ja! =>
                CALL    KeyStern
                SET     0,(IX+Flags)            ;Zahleingabe an!
DoKeyboard5:    LD      A,E                     ;gedrückte Ziffer
                LD      BC,5
                LD      DE,Display
                LD      HL,Display+1
                LDIR                            ;Display ein Zeichen nach links
                LD      (DE),A                  ;neues Zeichen einfügen
                SCF                             ;Carry-Flag setzen
                RL      (IX+DPunkt)             ;Punkte auch ein Zeichen nach links
                LD      (C000h),A
                JR      DoRecMess
DoKeyboard6:    PUSH    DE                      ;Sprungadresse merken
                POP     IY
                CALL    CallIY                  ;Sondertaste behandeln

;Meßwerte vom Hauptgerät empfangen
DoRecMess:      LD      B,4                     ;4 Meßwerte
                LD      IY,MWRedoxLow           ;Ptr auf den letzten Meßwert
                LD      (C000h),A
DoRecMess1:     LD      C,B
                DEC     C                       ;Meßwertnummer (0...3)
                PUSH    BC
                CALL    GetMesswert             ;empfangen
                POP     BC
                JR      C,DoRecMess2            ;ok? Ja =>
                PUSH    BC
                CALL    GetMesswert             ;nochmal probieren
                POP     BC
                JR      C,DoRecMess2            ;ok? Ja =>
                LD      A,82h
                LD      (ErrorCode),A           ;Übertragungsfehler!
                JR      DoRecMess3              ;=> zum nächsten Meßwert
DoRecMess2:     LD      H,(IY+1)                ;Highbyte vom Meßwert
                LD      L,(IY+0)                ;Lowbyte vom Meßwert
                LD      E,A                     ;Meßwert dazuaddieren
                LD      D,0
                ADD     HL,DE
                LD      (IY+1),H                ;Meßwert neu setzen
                LD      (IY+0),L
DoRecMess3:     DEC     IY
                DEC     IY
                DEC     IY
                LD      (C000h),A
                DJNZ    DoRecMess1              ;alle Meßwerte durch? Nein =>

;Uhrzeit (Uhrenchip liegt ab 4000h) auslesen
DoReadClock:    LD      IY,4000h                ;Basisadresse des Uhrenchips
                LD      DE,AktTime+2            ;Ptr auf die Stunden der Uhrzeit
                LD      B,3                     ;3 Werte (Stunden,Minuten,Sekunden)
                LD      C,5                     ;mit Register 5 geht es los
                HALT
DoReadClock1:   LD      (IY+1),C                ;Register 5 auswählen
                LD      A,(IY+0)                ;Register auslesen
                AND     0Fh                     ;nur 4 Bit-Register!
                CP      0Fh                     ;0Fh?
                JR      Z,DoReadClock3          ;Fehler =>
                ADD     A,A
                ADD     A,A
                ADD     A,A                     ;mal 16 + 0Fh
                ADD     A,A
                OR      0Fh
                DEC     C
                LD      (IY+1),C                ;Register 4 auswählen
                AND     (IY+0)                  ;unteren Teil der BCD-Zahl dazu
                PUSH    AF
                AND     0Fh
                CP      0Fh                     ;0Fh?
                JR      NZ,DoReadClock2         ;Nein => ok!
                POP     AF
                JR      DoReadClock3            ;Fehler =>
DoReadClock2:   POP     AF
                LD      (DE),A                  ;Stunden, Minuten, Sekunden merken
                DEC     DE                      ;eine Stelle weiter
                DEC     C                       ;Register 3,2 und dann Register 1,0
                DJNZ    DoReadClock1            ;alle Register durch? Nein =>
                RES     7,(IX+AktTime+2)        ;Uhrzeit fehlerfrei gelesen
                JR      DoErrorOut              ;Ok =>
DoReadClock3:   LD      A,83h
                LD      (ErrorCode),A           ;Fehler im interen Zeitschalter

DoErrorOut:     LD      (C000h),A
                LD      A,(ErrorCode)           ;Fehlercode lesen
                CP      0                       ;kein Fehler?
                JR      Z,DoFlashResTime        ;genau =>
                LD      E,A
                JP      ErrorOut

DoFlashResTime: BIT     2,(IX+Flags)            ;PowerOn-Flag gesetzt?
                JR      Z,DoPrintTime           ;Nein =>
                BIT     0,(IX+Counter+1)        ;Blink-Timer gesetzt?
                JP      Z,EraseDisplay
                LD      HL,PowerOnZeit
                CALL    PrintTime               ;Uhrzeit beim Einschalten ausgeben
                JP      DoLicht

DoPrintTime:    BIT     5,(IX+KLED)             ;"Zeit" an?
                JP      NZ,DoLicht              ;Nein =>
                BIT     4,(IX+KLED)             ;"Momentan" an?
                JR      NZ,DoDispMess           ;Nein =>
                LD      HL,AktTime
                CALL    PrintTime               ;aktuelle Uhrzeit ausgeben

DoDispMess:     LD      B,4                     ;4 Meßwerte
                LD      C,(IX+KLED+1)           ;Meßwert-Tastatur-LED auslesen
                LD      E,11h                   ;mit Fehlermeldung 11 geht es los
                LD      HL,IstpH                ;Ptr auf Ist-Wert vom pH-Wert
                LD      IY,DoDispMessTab        ;Ptr auf Sprungtabelle für die verschiedenen Meßwerte
DoDispMess1:    SRL     C                       ;LED nach unten schieben
                JR      NC,DoDispMess2          ;LED an? => ja
                INC     HL
                INC     HL                      ;zum nächsten Meßwert
                INC     HL
                INC     IY                      ;ein Eintrag weiter in der Sprungtabelle
                INC     IY
                INC     E                       ;Fehlermeldung + 2
                INC     E
                DJNZ    DoDispMess1
                JR      DoLicht                 ;keinen Meßwert darstellen
DoDispMess2:    LD      A,(HL)                  ;Meßwert auslesen
                CP      0                       ;= 0?
                JR      Z,ErrorOut              ;Meßbereich unterschritten =>
                CP      FFh
                JR      NZ,DoDispMess3          ;Meßbereich i.O. =>
                INC     E
                JR      ErrorOut                ;Meßbereich überschritten =>
DoDispMess3:    LD      D,(IY+0)                ;Sprungtabelle auslesen
                LD      E,(IY+1)
                PUSH    DE                      ;Sprungadresse merken
                POP     IY
                LD      L,A                     ;Meßwert / 2
                SRL     A
                CALL    CallIY                  ;gefundene Routine anspringen
                JR      DoLicht

CallIY:         JP      (IY)

;Fehlermeldung ausgeben, Fehlercode in E
ErrorOut:       BIT     0,(IX+Counter+1)        ;Blink-Timer gesetzt?
                JR      NZ,ErrorOut1            ;An =>
EraseDisplay:   LD      HL,Display+5
                LD      DE,Display+4
                LD      BC,5
                LD      (HL),1Fh                ;Display mit Leerzeichen füllen
                LDDR
                LD      (IX+DPunkt),FFh         ;Dezimalpunkte aus
                SET     4,(IX+Flags)            ;keine Zahl im Display
                JR      DoLicht
ErrorOut1:      LD      A,E
                CALL    MakeErrCode

DoLicht:        LD      (C000h),A
                LD      HL,LichtEin
                LD      DE,LichtAus
                CALL    InTimeRange             ;am Tage?
                JR      NC,DoLicht9             ;ja => Licht an
                BIT     6,(IX+KLED)             ;Manuell?
                JR      NZ,DoLicht8             ;Nein => Licht aus
                LD      HL,ManuellZeit          ;Momentan-Timer-Wert (ss:mm:hh)
                LD      DE,ManuellEinZeit       ;Zeit, wann "Manuell" gedrückt wurde
                LD      BC,ManuellAusZeit
                LD      A,(DE)                  ;Sekunden der Einschaltzeit
                ADD     A,(HL)                  ;+ Sekunden der Dauer
                DAA
                INC     DE                      ;Ptr auf die Minuten
                INC     HL
                JR      NC,DoLicht2             ;ein Sekundenüberlauf? Nein =>
DoLicht1:       SUB     60h                     ;Sekunden um 60 zurücksetzen
                DAA
                SCF                             ;Carry setzen (Sekunden-Übertrag)
                JR      DoLicht3
DoLicht2:       CP      60h                     ;Sekunden-Überlauf?
                JR      NC,DoLicht1             ;Ja =>
                CCF                             ;Nein, Carry löschen
DoLicht3:       LD      (BC),A                  ;Ausschaltzeit-Sekunden
                INC     BC
                LD      A,(DE)
                ADC     A,(HL)                  ;Ausschaltzeit-Minuten (+ Sekunden-Übertrag)
                DAA
                INC     DE                      ;Ptr auf die Stunden
                INC     HL
                JR      NC,DoLicht5             ;ein Minutenüberlauf? Nein =>
DoLicht4:       SUB     60h                     ;Minuten um 60 zurücksetzen
                DAA
                SCF                             ;Carry setzen (Minuten-Übertrag)
                JR      DoLicht6
DoLicht5:       CP      60h                     ;Minuten-Überlauf?
                JR      NC,DoLicht4             ;Ja =>
                CCF                             ;Nein, Carry löschen
DoLicht6:       LD      (BC),A                  ;Ausschaltzeit-Minuten
                INC     BC
                LD      A,(DE)
                ADC     A,(HL)                  ;Ausschaltzeit-Stunden (+ Minuten-Übertrag)
                DAA
                CP      24h                     ;24h Überlauf?
                JR      C,DoLicht7              ;Nein =>
                SUB     24h                     ;Uhrzeit des nächsten Tages
                DAA
DoLicht7:       LD      (BC),A                  ;Aussschaltzeit-Stunden
                LD      HL,ManuellEinZeit
                LD      DE,ManuellAusZeit
                CALL    InTimeRange             ;im "Manuell"-Einschaltzeitraum?
                JR      NC,DoLicht9             ;Ja => Licht an
                SET     6,(IX+KLED)             ;Manuell aus
DoLicht8:       RES     2,(IX+Steckdosen)       ;Licht aus
                JR      DoKanal1
DoLicht9:       SET     2,(IX+Steckdosen)       ;Licht an

DoKanal1:       LD      HL,Kanal1Uni            ;Einschaltzeiten
                LD      DE,Kanal1Uni+30         ;Ausschaltzeiten
                CALL    Kanal1Regel
                JR      C,DoKanal11
                SET     3,(IX+Steckdosen)       ;Kanal 1 an
                RES     3,(IX+WarnLED)          ;Kanal 1-LED an
                JR      DoKanal2
DoKanal11:      RES     3,(IX+Steckdosen)       ;Kanal 1 aus
                SET     3,(IX+WarnLED)          ;Kanal 1-LED aus

DoKanal2:       LD      HL,Kanal2Uni            ;Einschaltzeiten
                LD      DE,Kanal2Uni+30         ;Ausschaltzeiten
                CALL    Kanal2Regel
                JR      C,DoKanal21
                SET     4,(IX+Steckdosen)       ;Kanal 2 an
                RES     0,(IX+WarnLED)          ;Kanal 2-LED an
                JR      DoTemp
DoKanal21:      RES     4,(IX+Steckdosen)       ;Kanal 2 aus
                SET     0,(IX+WarnLED)          ;Kanal 2-LED aus

DoTemp:         LD      HL,TagZeit
                LD      DE,NachtZeit
                CALL    InTimeRange             ;ist es Tag?
                JR      C,DoTemp1               ;Nein =>
                LD      A,(IX+SollTempTag)      ;Soll-Temperatur (Tag)
                JR      DoTemp2
DoTemp1:        LD      A,(IX+SollTempNacht)    ;Soll-Temperatur (Nacht)
DoTemp2:        PUSH    AF                      ;Soll-Temperatur merken
                LD      (IX+AktSollTemp),A      ;aktuelle Soll-Temperatur merken
                BIT     4,(IX+WarnLED)          ;Heizung-LED an?
                JR      Z,DoTemp6               ;Ja! => Heizung regeln
                CP      (IX+IstTemp)            ;Ist-Temp-Wert >= Soll-Temperatur?
                JR      C,DoTemp4               ;Ja! => Heizung ausschalten
                SET     1,(IX+Steckdosen)       ;Heizung an
                RES     4,(IX+WarnLED)          ;Heizung-LED an
                LD      HL,AktTime
                LD      DE,TempAlarmZeit
                LD      BC,3
                LDIR                            ;Einschaltzeit der Heizung merken
                DEC     DE
                LD      A,(DE)                  ;Stunden holen
                INC     A                       ;+1
                CP      24h                     ;24 Uhr?
                JR      C,DoTemp3               ;kleiner als 24 Uhr? Ja =>
                LD      A,0                     ;0 Uhr annehmen
DoTemp3:        LD      (DE),A                  ;Stunden setzen
                POP     AF
                JR      DoTemp10

DoTemp4:        POP     AF
                SET     5,(IX+WarnLED)          ;Temp.Alarm aus
DoTemp5:        SET     4,(IX+WarnLED)          ;Heizung-LED aus
                RES     1,(IX+Steckdosen)       ;Heizung aus
                JR      DoTemp10

DoTemp6:        LD      HL,AktTime
                LD      DE,TempAlarmZeit
                CALL    CompareTimes            ;eine Stunde heizen um?
                JR      NC,DoTemp7              ;Nein =>
                BIT     0,(IX+Counter+1)        ;Blink-Timer gesetzt?
                JR      Z,DoTemp7               ;Nein =>
                RES     5,(IX+WarnLED)          ;Temp.Alarm an
                JR      DoTemp8
DoTemp7:        SET     5,(IX+WarnLED)          ;Temp.Alarm aus
DoTemp8:        POP     AF                      ;Soll-Temperatur wieder vom Stack holen
                ADD     A,1
                CP      (IX+IstTemp)            ;Ist-Temp-Wert >= Soll-Temp + 0.1°?
                JR      C,DoTemp5               ;Ja =>
                SET     1,(IX+Steckdosen)       ;Heizung an

DoTemp10:       LD      A,(IX+IstTemp)          ;Ist-Temp-Wert
                SUB     8
                CP      (IX+AktSollTemp)        ;Soll-Temp >= Ist-Temp - 0.8°? (Temperatur zu kalt?)
                JR      C,DoPh                  ;Ja =>
                BIT     0,(IX+Counter+1)        ;Blink-Timer gesetzt?
                JR      Z,DoTemp11              ;Nein =>
                SET     5,(IX+WarnLED)          ;Temp.Alarm aus
                JR      DoPh
DoTemp11:       RES     5,(IX+WarnLED)          ;Temp.Alarm an

DoPh:           LD      (C000h),A
                LD      B,(IX+IstpH)            ;Ist-pH-Wert
                SRL     B
                PUSH    BC
                LD      HL,CO2Ein
                LD      DE,CO2Aus
                CALL    InTimeRange             ;CO2-Sperrzeit?
                JR      C,DoPh2                 ;Ja =>
                BIT     1,(IX+WarnLED)          ;CO2-LED an?
                JR      Z,DoPh4                 ;Ja! =>
                LD      A,(IX+SollpH)           ;Soll-pH-Wert
                POP     BC
                CP      B                       ;>= Ist-pH-Wert?
                JR      NC,DoPh3                ;zu groß =>
                RES     1,(IX+WarnLED)          ;CO2-LED an
                SET     0,(IX+Steckdosen)       ;CO2 an
                LD      HL,AktTime
                LD      DE,CO2EinZeit
                LD      BC,3
                LDIR                            ;Einschaltzeit des CO2
                LD      HL,AktTime
                LD      BC,3
                LDIR                            ;Alarmzeit des CO2
                DEC     DE
                LD      A,(DE)
                ADD     A,3                     ;= Einschaltzeit + 3h
                DAA
                CP      24h                     ;24h Überlauf?
                JR      C,DoPh1                 ;Nein =>
                SUB     24h                     ;- 24h
DoPh1:          LD      (DE),A
                JR      DoPh7

DoPh2:          POP     BC
DoPh3:          SET     2,(IX+WarnLED)          ;CO2-Alarm aus
                SET     1,(IX+WarnLED)          ;CO2-LED aus
                RES     0,(IX+Steckdosen)       ;CO2 aus
                JR      DoPh7

DoPh4:          LD      HL,CO2EinZeit
                LD      DE,CO2AlarmZeit
                CALL    InTimeRange             ;CO2 schon 3h an?
                JR      NC,DoPh5                ;Nein =>
                LD      A,(IX+IstpH)            ;Ist-pH-Wert
                SRL     A
                CP      (IX+SollpH)             ;= Soll-pH-Wert
                JR      Z,DoPh5                 ;Ja => (kein Alarm)
                BIT     0,(IX+Counter+1)        ;Blink-Timer gesetzt?
                JR      Z,DoPh5
                RES     2,(IX+WarnLED)          ;pH-Alarm an
                JR      DoPh6
DoPh5:          SET     2,(IX+WarnLED)          ;pH-Alarm aus
DoPh6:          LD      A,(IX+SollpH)           ;Soll-pH-Wert
                SUB     1                       ;- 0.05
                POP     BC
                CP      B                       ;=> Ist-pH-Wert? (Vergleich: Ist-pH-Wert < Soll-pH-Wert)
                JR      NC,DoPh3                ;zu groß => CO2 aus

DoPh7:          LD      A,(IX+IstpH)            ;Ist-pH-Wert
                SRL     A
                ADD     A,6                     ;+ 0.3
                CP      (IX+SollpH)             ;Soll-pH-Wert (Ist-pH-Wert <= Soll-pH-Wert + 0.35)
                JR      NC,DoMomentan
                BIT     0,(IX+Counter+1)        ;Blink-Timer gesetzt?
                JR      Z,DoPh8
                SET     2,(IX+WarnLED)          ;pH-Alarm aus
                JR      DoMomentan
DoPh8:          RES     2,(IX+WarnLED)          ;pH-Alarm an

DoMomentan:     CALL    TempKomp                ;Leitwert mit der Temperatur kompensieren
                LD      (C000h),A

                BIT     5,(IX+KLED)             ;"Momentan" an?
                JR      NZ,DoLaufschr           ;Nein =>
                BIT     3,(IX+Flags)            ;Momentane Werte durchschalten?
                JR      Z,DoLaufschr            ;Nein =>
                LD      HL,MomentanSek
                LD      A,(AktTime)             ;Sekunden beim letzten Durchlauf
                CP      (HL)                    ;Sekunden geändert?
                JR      Z,DoLaufschr            ;Nein =>
                LD      (HL),A                  ;letzten Sekundenstand merken
                LD      HL,DelayTimer           ;Pause für die Darstellung
                LD      A,(HL)
                INC     A
                IF      NewVersion
                PUSH    HL
                LD      HL,MomentanZeit
                CP      (HL)                    ;Momentane Sekunden abgelaufen?
                POP     HL
                ELSE
                CP      7                       ;7 Sekunden darstellen
                ENDIF
                JR      C,DoMomentan4           ;Zeit abgelaufen? Nein =>
                LD      A,(IX+KLED+1)           ;LEDs rechte Spalte auslesen
                OR      F0h
                CP      FFh                     ;alle LEDs aus?
                JR      NZ,DoMomentan1          ;Nein =>
                SET     4,(IX+KLED)             ;Uhrzeit ausschalten
                RES     0,(IX+KLED+1)           ;pH-Wert anschalten
                JR      DoMomentan3
DoMomentan1:    RLCA                            ;Anzeige weiterschalten
                BIT     4,A                     ;Überlauf?
                JR      NZ,DoMomentan2          ;Nein =>
                LD      A,(IX+KLED+1)
                OR      0Fh                     ;rechte Spalte ausschalten
                LD      (IX+KLED+1),A
                RES     4,(IX+KLED)             ;Uhrzeit anschalten
                JR      DoMomentan3
DoMomentan2:    LD      B,A                     ;rotierte Matrix merken
                LD      A,(IX+KLED+1)           ;rechte Spalte erneut auslesen
                OR      0Fh                     ;alle LEDs aus
                AND     B                       ;rotierte Matrix dazu
                LD      (IX+KLED+1),A           ;und neue LEDs anschalten
DoMomentan3:    LD      A,0                     ;Pause wieder zurücksetzen
DoMomentan4:    LD      (HL),A                  ;Delay-Timer setzen

DoLaufschr:     IF !NewVersion
                LD      (C000h),A
                LD      A,(LaufschriftFlag)
                CP      55h
                JR      NZ,DoSollChecksum       ;keine Laufschrift =>
                LD      A,(LaufschriftInit)
                CP      55h                     ;Laufschrift initialisiert?
                JR      Z,DoLaufschr1           ;Ja =>
                LD      HL,(LaufschriftPtr)
                LD      (ScrollPtr),HL          ;Laufschrift-Text setzen
                LD      A,55h
                LD      (LaufschriftInit),A     ;aktiv schalten
                LD      (IX+KLED),FFh           ;alle Tasten-LEDs aus
                LD      (IX+KLED+1),FFh

DoLaufschr1:    LD      A,(DelayTimer)
                DEC     A                       ;DelayTimer runterzählen
                LD      (DelayTimer),A
                JR      NZ,DoSollChecksum       ;noch nicht abgelaufen =>
                LD      A,12
                LD      (DelayTimer),A          ;DelayTimer neu setzen
                LD      HL,(ScrollPtr)
                LD      A,(HL)                  ;nächstes Zeichen aus dem Scrollstring
                INC     HL
                LD      (ScrollPtr),HL
                CP      40h                     ;"Display löschen"? (Stringanfang)
                JR      NZ,DoLaufschr3          ;Nein! =>
                LD      HL,Display              ;Display löschen
                LD      B,6
DoLaufschr2:    LD      (HL),1Fh                ;Leerzeichen
                INC     HL
                DJNZ    DoLaufschr2
                JR      DoSollChecksum
DoLaufschr3:    CP      42h                     ;"Leerzeichen (langsam)"?
                JR      Z,DoLaufschr4           ;Ja! =>
                CP      41h                     ;Neustart vom Anfang an? (Stringende)
                JR      NZ,DoLaufschr5          ;Nein! =>
                LD      HL,(LaufschriftPtr)
                LD      (ScrollPtr),HL
                LD      A,80
                LD      (DelayTimer),A          ;6.7-fache Pause vorher einlegen
                JR      DoSollChecksum          ;nix ausgeben =>
DoLaufschr4:    LD      A,48
                LD      (DelayTimer),A          ;4-fache Pause
                LD      A,1Fh                   ;Leerzeichen ausgeben
DoLaufschr5:    LD      (IX+DPunkt),FFh         ;Dezimalpunkte aus
                LD      (IX+KLED),FFh           ;alle Tasten-LEDs aus
                LD      (IX+KLED+1),FFh
                LD      DE,Display
                LD      HL,Display+1
                LD      BC,5
                LDIR                            ;nach links scrollen
                LD      (DE),A                  ;neues Zeichen einfügen
                ENDIF

DoSollChecksum: LD      (C000h),A
                CALL    CalcSollChecksum        ;Prüfsumme über die Sollwerte errechnen
                LD      HL,(SollChecksum)       ;alte Prüfsumme holen
                XOR     A                       ;kein Fehler
                SBC     HL,DE                   ;Prüfsummen gleich?
                JR      Z,DoBetrStd             ;Ja! =>
                LD      (SollChecksum),DE       ;als neue Prüfsumme merken
                LD      A,80h                   ;Prüfsumme über die Sollwerte geändert!
                LD      (ErrorCode),A

DoBetrStd:      LD      (C000h),A
                LD      A,(IX+AktTime+2)        ;aktuelle Stunden
                LD      HL,GesamtBZeit+3
                CP      (HL)                    ;= Gesamtzeitstunden (low-Byte der Stunden)
                JR      Z,DoBetrStd1            ;Ja =>
                LD      (HL),A
                LD      HL,GesamtBZeit
                CALL    IncHour                 ;Gesamtzeit um eine Stunde erhöhen
DoBetrStd1:     LD      A,(IX+AktTime+1)        ;aktuelle Minuten
                LD      HL,GesamtBZeit+4
                CP      (HL)                    ;= Gesamtzeitminuten
                JR      Z,DoInitStr             ;Ja => (noch keine Minute rum)
                LD      (HL),A                  ;neue Minuten merken

                BIT     3,(IX+WarnLED)          ;Kanal 1-LED an?
                JR      NZ,DoBetrStd2           ;Nein =>
                LD      HL,Kanal1BZeit
                CALL    IncMinute
DoBetrStd2:     BIT     0,(IX+WarnLED)          ;Kanal 2-LED an?
                JR      NZ,DoBetrStd3           ;Nein =>
                LD      HL,Kanal2BZeit
                CALL    IncMinute
DoBetrStd3:     BIT     1,(IX+WarnLED)          ;CO2-LED an?
                JR      NZ,DoBetrStd4           ;Nein =>
                LD      HL,CO2BZeit
                CALL    IncMinute
DoBetrStd4:     BIT     4,(IX+WarnLED)          ;Heizung-LED an?
                JR      NZ,DoBetrStd5           ;Nein =>
                LD      HL,TempBZeit
                CALL    IncMinute
DoBetrStd5:     BIT     2,(IX+Steckdosen)       ;Licht an?
                JR      Z,DoInitStr             ;Nein =>
                LD      HL,LichtBZeit
                CALL    IncMinute

DoInitStr:      IF !NewVersion
                LD      (C000h),A
                LD      HL,InitLaufschrSek
                LD      A,(AktTime)             ;Sekunden auslesen
                CP      (HL)
                JP      Z,DoROMChksum           ;gleich der gemerkten Sekunden? =>
                LD      (HL),A
                LD      A,(InitLaufschr)        ;Init-Laufschrift?
                CP      55h
                JP      Z,DoROMChksum           ;gleich =>
                LD      IY,Dummy0
                LD      (IY+0),0                ;???
                LD      (IY+1),0

                LD      DE,StringBuf
                LD      A,2
                LD      (DE),A
                INC     DE
                LD      HL,AktTime
                LD      B,3
DoInitStr1:     LD      A,(HL)
                CALL    HexByteOut              ;Uhrzeit ausgeben
                INC     HL
                DJNZ    DoInitStr1
                LD      A,' '
                LD      (DE),A
                INC     DE
                LD      HL,Dummy                ;???
                LD      B,3
DoInitStr2:     LD      A,(HL)
                CALL    HexByteOut
                INC     HL
                DJNZ    DoInitStr2
                LD      A,' '
                LD      (DE),A
                INC     DE
                LD      A,(IX+IstpH)            ;Ist-pH-Wert
                SRL     A
                CALL    HexByteOut
                LD      A,' '
                LD      (DE),A
                INC     DE
                LD      A,(IX+IstTemp)          ;Ist-Temp-Wert
                CALL    HexByteOut
                LD      A,' '
                LD      (DE),A
                INC     DE
                LD      A,(IX+LeitwertKomp)     ;kompensierter Leitwert
                CALL    HexByteOut
                LD      A,' '
                LD      (DE),A
                INC     DE
                LD      A,(IX+IstRedox)         ;Ist-Redox-Wert
                CALL    HexByteOut
                LD      A,' '
                LD      (DE),A
                INC     DE
                IN      A,(C)
                AND     20h                     ;Süßwasser/Meerwasser-Schalter
                LD      B,A
                LD      A,(IX+Steckdosen)       ;Steckdosen-Status
                AND     1Fh
                OR      B
                CALL    HexByteOut
                LD      A,' '
                LD      (DE),A
                INC     DE
                LD      HL,(Dummy0)             ;= 0
                LD      A,H
                CALL    HexByteOut
                LD      A,L
                CALL    HexByteOut
                LD      A,13
                LD      (DE),A
                INC     DE
                LD      A,3
                LD      (DE),A
                LD      HL,StringBuf
                LD      (StringBufPtr),HL
                LD      A,55h
                LD      (InitLaufschr),A        ;Init-Laufschrift AUS
                ENDIF

;Prüfsummenberechnung über das ROM
DoROMChksum:    LD      (C000h),A
                LD      DE,(AktROMChecksum)     ;alte Prüfsumme lesen
                LD      HL,(ROMTopAdr)          ;Endadresse vom ROM - 1
                DEC     HL
                LD      (ROMTopAdr),HL          ;Endadresse - 1
                LD      A,L
                OR      H                       ;Adresse zusammen"OR"n
                LD      L,(HL)                  ;Speicherstelle auslesen
                LD      H,00h
                ADD     HL,DE                   ;alte Prüfsumme dazuaddieren
                LD      (AktROMChecksum),HL     ;Prüfsumme neu merken
                CP      00h                     ;Anfang vom ROM erreicht?
                JR      NZ,DoMainloop           ;Nein =>
                LD      HL,(AktROMChecksum)     ;Prüfsumme auslesen
                LD      (CalcChecksum),HL       ;errechnete Prüfsumme merken
                LD      A,0
                LD      (ChecksumFinal),A
                LD      DE,(ROMChecksum)        ;erwartete Prüfsumme
                XOR     A
                SBC     HL,DE                   ;Prüfsumme gleich?
                JR      Z,DoROMChksum1          ;Ja! =>
                LD      A,81h                   ;Programmstörung!
                LD      (ErrorCode),A
DoROMChksum1:   LD      HL,0
                LD      (AktROMChecksum),HL     ;alte Prüfsumme zurücksetzen
                LD      HL,ROMTop
                LD      (ROMTopAdr),HL          ;Ende vom ROM neu setzen

DoMainloop:     LD      (C000h),A
                JP      DoLEDKonv               ;und wieder von vorne...

; Unbenutzer Code:
                IF !NewVersion
DoComm:         LD      A,(IX+Steckdosen)       ;Steckdoses-Status
                RES     5,A
                CALL    DoComm4                 ;0??
                LD      B,7
DoComm1:        RR      C
                JR      C,DoComm2               ;8 Bits übertragen
                RES     5,A
                JR      DoComm3
DoComm2:        SET     5,A
DoComm3:        CALL    DoComm4
                DJNZ    DoComm1
                PUSH    HL
                POP     HL
                SET     5,A
                LD      (E000h),A               ;1??
                RET
DoComm4:        LD      (E000h),A
                NOP
                NOP
                NOP
                RET
                ENDIF

;Hexbyte nach DE schreiben. (IY+0/1) enthält die Prüfsumme
                IF !NewVersion
HexByteOut:
                PUSH    AF
                SRL     A
                SRL     A
                SRL     A
                SRL     A
                CALL    HexByteOut1
                POP     AF
                AND     0Fh
HexByteOut1:    CP      10                      ;größer als 10?
                JR      C,HexByteOut2           ;Nein! =>
                ADD     A,'7'                   ;+ '7' = 'A'...'F'
                JR      HexByteOut3
HexByteOut2:    ADD     A,'0'                   ;sonst + '0' = '0'...'9'
HexByteOut3:    LD      (DE),A                  ;in den Buffer schreiben
                INC     DE
                ADD     A,(IY+0)                ;alte Summe dazuaddieren
                LD      (IY+0),A                ;als neue Summe merken
                LD      (C000h),A
                RET     NC                      ;Überlauf der Prüfsumme? Nein => raus
                INC     (IY+1)                  ;Prüfsummen-Highbyte hochzählen
                RET
                ENDIF

; String ab HL nach DE bis zum "$" kopieren
                IF !NewVersion
CopyString:
                LD      (C000h),A
                LD      A,(HL)                  ;Zeichen aus dem String holen
                CP      '$'                     ;Textende erkannt?
                RET     Z                       ;dann raus =>
                LD      (DE),A                  ;Zeichen übertragen
                INC     DE
                INC     HL
                JR      CopyString

; String mit Fehlermeldung in HL (zwei ASCII-Zeichen) zusammensetzen
ErrorString:
                PUSH    HL
                LD      HL,MsgFehl              ;"FEHL." ausgeben
                CALL    CopyString
                POP     HL
                LD      A,H
                LD      (DE),A                  ;Fehlernummer übertragen
                INC     DE
                LD      A,L
                LD      (DE),A
                INC     DE
                LD      HL,Msg6Space            ;"      ",13,10,10 anhängen
                CALL    CopyString
                RET

; "." an den String anhängen
ConcatPunkt:    EX      DE,HL
                LD      (HL),'.'
                EX      DE,HL
                INC     DE
                RET
                ENDIF

; Prüfsumme über die Sollwerte berechnen, Ergebnis nach DE
CalcSollChecksum:
                PUSH    HL
                LD      HL,0
                LD      D,0
                LD      E,(IX+SollpH)           ;Soll-pH-Wert
                ADD     HL,DE
                LD      E,(IX+SollTempTag)      ;Soll-Temperatur (Tag)
                ADD     HL,DE
                LD      E,(IX+SollTempNacht)    ;Soll-Temperatur (Nacht)
                ADD     HL,DE
                LD      E,(IX+SollLeitwertS)    ;Soll-Leitwert (Süßwasser)
                ADD     HL,DE
                LD      E,(IX+SollLeitwertM)    ;Soll-Leitwert (Meerwasser)
                ADD     HL,DE
                LD      E,(IX+SollRedox)        ;Soll-Redoxwert
                ADD     HL,DE
                EX      DE,HL
                POP     HL
                LD      (C000h),A
                RET

;langer Timer (1 Byte Minuten, 3 Bytes Stunden) um eine Minute erhöhen
IncMinute:      LD      A,(HL)
                INC     A
                CP      60                      ;Sekundenüberlauf?
                JR      C,IncMinute1            ;Nein =>
                LD      (HL),0                  ;Sekunden auf 0 zurücksetzen
                INC     HL
                JR      IncHour
IncMinute1:     LD      (HL),A
                RET
IncHour:        LD      B,3                     ;3 Bytes für Stunden
IncHour1:       LD      A,(HL)
                ADD     A,1                     ;Stunden um eins erhöhen
                DAA
                LD      (HL),A
                JR      NC,IncHour2
                INC     HL
                DJNZ    IncHour1
IncHour2:       RET

;"Zeit" gedrückt
KeyZeit:        RES     0,(IX+Flags)            ;Zahleingabe aus
                LD      A,(IX+KLED)             ;Tastenlampen holen
                BIT     4,A                     ;Zeit war bereits an?
                JR      NZ,KeyZeit6             ;Nein =>
                BIT     4,(IX+KLED+1)           ;"Kanal 1"
                JR      NZ,KeyZeit1             ;Nein =>
                LD      HL,Kanal1BZeit+1
                CALL    PrintTime
                LD      (IX+DPunkt),FFh         ;Dezimalpunkte aus
                RET
KeyZeit1:       BIT     5,(IX+KLED+1)           ;"Kanal 2"
                JR      NZ,KeyZeit2             ;Nein =>
                LD      HL,Kanal2BZeit+1
                CALL    PrintTime
                LD      (IX+DPunkt),FFh         ;Dezimalpunkte aus
                RET
KeyZeit2:       BIT     7,(IX+KLED+1)           ;"CO2"
                JR      NZ,KeyZeit3             ;Nein =>
                LD      HL,CO2BZeit+1
                CALL    PrintTime
                LD      (IX+DPunkt),FFh         ;Dezimalpunkte aus
                RET
KeyZeit3:       BIT     6,(IX+KLED+1)           ;"Licht"
                JR      NZ,KeyZeit4             ;Nein =>
                LD      HL,LichtBZeit+1
                CALL    PrintTime
                LD      (IX+DPunkt),FFh         ;Dezimalpunkte aus
                RET
KeyZeit4:       BIT     1,(IX+KLED+1)           ;"Temperatur"
                JR      NZ,KeyZeit5             ;Nein =>
                LD      HL,TempBZeit+1
                CALL    PrintTime
                LD      (IX+DPunkt),FFh         ;Dezimalpunkte aus
                RET
KeyZeit5:       LD      HL,GesamtBZeit          ;Gesamtbetriebsstunden
                CALL    PrintTime
                LD      (IX+DPunkt),FFh         ;Dezimalpunkte aus
                RET

KeyZeit6:       RES     4,A                     ;Zeit-LED-Flag an
                OR      0Fh                     ;Tag,Nacht,Ein,Aus-LEDs aus
                LD      (IX+KLED),A             ;LED-Status setzen
                LD      A,(IX+KLED+1)
                IF NewVersion
                OR      0Fh                     ;alle LEDs aus
                ELSE
                OR      0Dh                     ;bis auf Temperatur alle LEDs aus (WARUM???)
                ENDIF
                LD      (IX+KLED+1),A
                BIT     7,(IX+KLED)             ;"Setzen" an?
                JR      NZ,KeyZeit8             ;Nein =>
                BIT     4,(IX+KLED+1)           ;"Kanal 1" an?
                JR      NZ,KeyZeit7             ;Nein! =>
                LD      (IX+Uni1Flag),55h
                LD      HL,StrLrUNI1            ;" UNI-1"
                SET     7,(IX+KLED)             ;"Setzen" aus
                JP      SetDisplayString
KeyZeit7:       BIT     5,(IX+KLED+1)           ;"Kanal 2" an?
                RET     NZ                      ;Nein =>
                LD      (IX+Uni2Flag),55h
                LD      HL,StrLrUNI2            ;" UNI-2"
                SET     7,(IX+KLED)             ;"Setzen" aus
                JP      SetDisplayString
KeyZeit8:       RET

;"Ein" gedrückt
KeyEin:         LD      HL,StrLrEIN             ;"Lr-EIn"
                LD      B,55h                   ;Ein-Flag
                RES     2,(IX+KLED)             ;Ein-LED an
                SET     3,(IX+KLED)             ;Aus-LED aus
                JR      KeyAus1

;"Aus" gedrückt
KeyAus:         LD      HL,StrLrAUS             ;"Lr-AUS"
                LD      B,AAh                   ;Aus-Flag
                RES     3,(IX+KLED)             ;Aus-LED an
                SET     2,(IX+KLED)             ;Ein-LED aus
KeyAus1:        RES     0,(IX+Flags)            ;Zahleingabe aus
                BIT     5,(IX+KLED+1)           ;"Kanal 2"
                JP      NZ,KeyAus2              ;Nein =>
                BIT     2,(IX+KLED+1)           ;"Leitwert"
                JP      NZ,KeyAus2              ;Nein =>
                BIT     7,(IX+KLED)             ;"Setzen" an?
                JP      NZ,KeyAus2              ;Nein! =>
                LD      (IX+Uni2Flag),AAh       ;Leitwert-Regelung
                LD      (IX+Uni2Flag2),B        ;Ein- oder Aus-Regelung setzen
                SET     7,(IX+KLED)             ;"Setzen"-LED aus
                JP      SetDisplayString

;Display löschen
KeyStern:       LD      HL,Display
                LD      B,6
KeyStern1:      LD      (HL),0                  ;6 mal '0' ins Display (führende Nullen werden NICHT ausgegeben)
                INC     HL
                DJNZ    KeyStern1
                INC     HL
                RES     0,(HL)                  ;Zahleingabe aus
                INC     HL
                INC     HL
                LD      (HL),FFh                ;alle Punkte im Display aus
                LD      B,A
                LD      (IX+KLED+1),FFh
                LD      A,BFh
                OR      (IX+KLED)
                LD      (IX+KLED),A             ;Bis auf manuelles Licht alle Tasten-LEDs aus
                SET     0,(IX+Flags)            ;Zahleingabe an
                RES     2,(IX+Flags)            ;PowerOn-Flag zurücksetzen
                RES     3,(IX+Flags)            ;keine Momentan-Werte durchschalten
                IF !NewVersion
                LD      A,AAh
                LD      (LaufschriftFlag),A     ;Laufschrift ausschalten
                LD      (LaufschriftInit),A
                ENDIF
                LD      A,6
                LD      (DelayTimer),A
                RES     4,(IX+Flags)            ;Zahl im Display
                LD      A,0
                LD      (ErrorCode),A           ;Fehlercode löschen
                LD      A,B
                LD      (C000h),A
                RET

;Diese Routine wird bei Druck auf "." angesprungen
KeyPunkt:       BIT     0,(IX+Flags)            ;Zahleingabe an?
                CALL    Z,KeyStern              ;Nein! => erstmal das Display löschen
                SET     0,(IX+Flags)            ;Zahleingabe aktivieren
                LD      A,(IX+DPunkt)
                XOR     01h                     ;Dezimalpunkt toggeln
                LD      (IX+DPunkt),A
                LD      (C000h),A
                RET

;pH-Taste gedrückt
KeyPh:          LD      (IX+KLED+1),FEh         ;pH-LED an
                RES     0,(IX+Flags)            ;Zahleingabe aus
                LD      A,(IX+KLED)
                OR      18h                     ;Zeit und Aus LEDs aus
                LD      (IX+KLED),A
                BIT     5,(IX+KLED)             ;"Momentan"?
                RET     Z                       ;Ja => raus
                BIT     7,(IX+KLED)             ;"Setzen" an?
                JR      Z,KeyPh1                ;Ja! =>
                JP      DispSollPh              ;Soll-pH-Wert darstellen
KeyPh1:         LD      HL,Display
                IF !NewVersion
                LD      A,0
                OR      (HL)
                INC     HL
                OR      (HL)
                INC     HL
                OR      (HL)
                JR      NZ,KeyPh2
                INC     HL
                LD      A,(HL)
                CP      1
                JR      NZ,KeyPh2               ;"15.0" eingegeben?
                INC     HL
                LD      A,(HL)
                CP      5
                JR      NZ,KeyPh2               ;Nein =>
                INC     HL
                LD      A,(HL)
                CP      0
                JR      NZ,KeyPh2
                LD      A,(IX+DPunkt)
                CP      FDh                     ;Dezimalpunkt
                JR      NZ,KeyPh2
                LD      A,55h
                LD      (LaufschriftFlag),A     ;Laufschrift an
                LD      HL,MsgBasis
                LD      (LaufschriftPtr),HL     ;Laufschrift-Text
                RET
                ENDIF
KeyPh2:         CALL    GetNumInput             ;Eingabe holen
                LD      A,D                     ;Anzahl der Dezimalpunkte holen
                CP      0
                JR      Z,KeyPh3
                CP      1                       ;0 oder 1 ist i.O.
                JR      Z,KeyPh4
                LD      A,3
                JP      MakeErrCode             ;mehrere Dezimalpunkte bei pH-Werteingabe
KeyPh3:         LD      IY,InputBuf+7           ;Ptr auf die letzte Ziffer
KeyPh4:         LD      B,E                     ;Position des Dezimalpunktes (1...6)
                LD      A,0
                INC     IY
                PUSH    IY
KeyPh5:         OR      (IY-2)                  ;alle Ziffern _VOR_ der 1.Vorkommastelle zusammen"OR"n
                DEC     IY
                DJNZ    KeyPh5
                POP     IY
                CP      0                       ;gibt es dort Ziffern <> "0"?
                JR      Z,KeyPh6                ;Nein! =>
                LD      A,1
                JP      MakeErrCode             ;pH-Wert zu groß!
KeyPh6:         CALL    ConvertInput            ;pH-Wert holen
                LD      A,L
                SUB     38                      ;3.8 ist für einen pH-Wert zu klein!
                JR      C,KeyPh7                ;< 3.8 => Fehler
                JR      Z,KeyPh7                ;= 3.8 => Fehler
                JR      KeyPh8                  ;alles ok =>
KeyPh7:         LD      A,2
                JP      MakeErrCode             ;pH-Wert zu klein
KeyPh8:         SLA     A                       ;mal 2
                LD      B,A
                LD      A,(IY+1)                ;2.Nachkommastelle holen
                CP      3
                JR      C,KeyPh10               ;<0.03? => nicht aufrunden
                CP      8
                JR      C,KeyPh9                ;<0.08? => auf 0.05 aufrunden
                INC     B                       ;>=0.08? => auf 0.10 aufrunden
KeyPh9:         INC     B
KeyPh10:        LD      (IX+SollpH),B           ;als neuen pH-Sollwert merken ((pH-Wert*10-38)*2)
                CALL    CalcSollChecksum        ;Prüfsumme über die Sollwerte errechnen
                LD      (SollChecksum),DE       ;und merken
                SET     7,(IX+KLED)             ;"Setzen" aus
                LD      (C000h),A
DispSollPh:     LD      A,(IX+SollpH)           ;neuen pH-Wert holen

;pH-Wert darstellen
DispPh:         BIT     0,A                     ;Bit 0 = 2.Nachkommastelle
                JR      Z,DispPh1
                LD      B,5                     ;gesetzt = 0.05
                JR      DispPh2
DispPh1:        LD      B,0                     ;gelöscht = 0.00
DispPh2:        LD      (IX+Display+2),B
                SRL     A                       ;(pH-Wert / 2) + 38
                ADD     A,38
                LD      E,A
                LD      D,0
                PUSH    DE
                POP     IY                      ;Zahl nach IY
                CALL    MakeBCD
                LD      HL,BCDZahl              ;Ptr auf die BCD-Zahl
                LD      A,0
                RRD     (HL)                    ;unteres Nibble ab HL nach A holen
                LD      (IX+Display+1),A        ;2.Stelle
                RRD     (HL)                    ;oberes Nibble ab HL nach A holen
                LD      (IX+Display),A          ;1.Stelle
                LD      (IX+Display+3),1Fh      ;Leerzeichen
                LD      (IX+Display+4),12h      ;"P"
                LD      (IX+Display+5),10h      ;"H"
                LD      (IX+DPunkt),DFh         ;Dezimalpunkt nach der 1.Stelle
                LD      (C000h),A
                RET

;"Setzen" gedrückt
KeySetzen:      RES     0,(IX+Flags)            ;Zahleingabe aus
                IN      A,(C)                   ;Sperre gesetzt?
                BIT     4,A                     ;Ja! =>
                JR      Z,KeySetzen1
                BIT     4,(IX+Flags)            ;Zahl im Display?
                CALL    NZ,KeyStern             ;Nein! =>
                LD      A,(IX+KLED)
                XOR     80h                     ;Setzen-toggeln
                OR      3Fh                     ;bis auf "Manuell" alle LEDs ausschalten
                LD      (IX+KLED),A
                LD      (IX+KLED+1),FFh
                LD      (C000h),A
                RET
KeySetzen1:     LD      A,99h                   ;Programmiersperre gesetzt
                JP      MakeErrCode

;"Momentan" gedrückt
KeyMomentan:    RES     0,(IX+Flags)            ;Zahleingabe aus
                BIT     5,(IX+KLED)             ;"Momentan" bereits an?
                JR      NZ,KeyMomentan1         ;Nein =>
                SET     3,(IX+Flags)            ;Momentane Werte durchschalten
                IF NewVersion
                LD      A,0
                LD      (DelayTimer),A          ;sofortige Ausgabe der Werte erzwingen
                DEC     A
                LD      (MomentanSek),A
                ENDIF
                JR      KeyMomentan2
KeyMomentan1:   RES     3,(IX+Flags)            ;Momentane Werte nicht mehr durchschalten
KeyMomentan2:   RES     5,(IX+KLED)             ;Momentan-LED an
                LD      A,(IX+KLED)
                OR      0Fh                     ;Tag, Nacht, Ein, Aus LEDs ausschalten
                LD      (IX+KLED),A
                LD      (IX+KLED+1),FFh         ;rechte LEDs ausschalten
                BIT     7,(IX+KLED)             ;"Setzen" an?
                LD      (C000h),A
                RET     NZ                      ;Nein =>
                BIT     4,(IX+KLED)             ;"Zeit" an?
                JR      Z,KeyMomentan4          ;Ja! =>
                IF NewVersion
                LD      A,0
                LD      B,4
                LD      HL,Display              ;die ersten 4 Ziffern müssen stets = 0 sein
KeyMomentan6:   OR      (HL)
                INC     HL
                DJNZ    KeyMomentan6
                JR      NZ,KeyMomentan7         ;wenn nicht => Fehler
                LD      IY,Display+5            ;Ptr auf die letzte Stelle vom Display
                CALL    ConvertInput            ;Zahl nach HL holen
                LD      A,L
                LD      HL,MomentanZeit
                LD      (HL),A                  ;aktuelle Schaltzeit merken
                SET     7,(IX+KLED)             ;Setzen-LED aus
                ENDIF
KeyMomentan3:   SET     5,(IX+KLED)             ;Momentan-LED aus
                RET
                IF NewVersion
KeyMomentan7:   LD      A,20h
                JP      MakeErrCode             ;Eingabe falsch!
                ENDIF
KeyMomentan4:   LD      BC,0A00h                ;2560 Schleifendurchläufe
KeyMomentan5:   HALT
                LD      A,(KeyboardMatrix+6)
                BIT     2,A                     ;Manuell immer noch gedrückt?
                JR      NZ,KeyMomentan3         ;Nein => Zeit nicht neu setzen
                LD      (C000h),A
                HALT
                DEC     BC                      ;Zähler runterzählen
                LD      A,B                     ;0 erreicht?
                OR      C
                JR      NZ,KeyMomentan5         ;Nein => weiter warten
                SET     5,(IX+KLED)             ;Momentan-LED aus
                JP      SetSystemTime

;"Temperatur"-Taste gedrückt
KeyTemperatur:  RES     0,(IX+Flags)            ;Zahleingabe aus
                LD      A,(IX+KLED+1)
                RES     1,A                     ;Temperatur an
                OR      FDh                     ;andere LEDs aus
                LD      (IX+KLED+1),A
                LD      A,(IX+KLED)
                OR      1Fh                     ;außer Momentan, Manuell und Setzen alles aus
                LD      (IX+KLED),A
                LD      (C000h),A
                RET

;"Kanal 1" gedrückt
KeyKanal1:      RES     0,(IX+Flags)            ;Zahleingabe aus
                LD      A,(IX+KLED)
                OR      1Fh                     ;Tag,Nacht,Ein,Aus,Zeit im linken Bereich aus
                LD      (IX+KLED),A
                LD      A,(IX+KLED+1)
                OR      FFh                     ;alle LEDs im rechten Bereich aus
                RES     4,A                     ;und "Kanal 1"-LED an
                LD      (IX+KLED+1),A
                BIT     5,(IX+KLED)             ;"Momentan" an?
                RET     NZ                      ;Nein => raus
                LD      A,(IX+Uni1Flag)         ;Zustand von Kanal 1
                CP      55h
                JR      Z,KeyKanal12            ;=> Universaltimer
                CP      AAh
                JR      Z,KeyKanal11            ;=> Redox-Regler
                LD      HL,Str6Minus            ;"------"
                JP      SetDisplayString
KeyKanal11:     LD      HL,StrrErE              ;" rE-rE"
                JP      SetDisplayString
KeyKanal12:     LD      HL,StrLrUNI1            ;" UNI-1"
                JP      SetDisplayString

;"Kanal 2" gedrückt
KeyKanal2:      RES     0,(IX+Flags)            ;Zahleingabe aus
                LD      A,(IX+KLED)
                OR      1Fh                     ;Tag,Nacht,Ein,Aus,Zeit im linken Bereich aus
                LD      (IX+KLED),A
                LD      A,(IX+KLED+1)
                OR      FFh                     ;alle LEDs im rechten Bereich aus
                RES     5,A                     ;und "Kanal 2"-LED an
                LD      (IX+KLED+1),A
                BIT     5,(IX+KLED)             ;"Momentan" an?
                RET     NZ                      ;Nein => raus
                LD      A,(IX+Uni2Flag)         ;Zustand von Kanal 2
                CP      55h
                JR      Z,KeyKanal24            ;=> Universaltimer
                CP      AAh
                JR      Z,KeyKanal21            ;=> Leitwert-Regler
                LD      HL,Str6Minus            ;"------"
                JP      SetDisplayString
KeyKanal21:     LD      A,(IX+Uni2Flag2)        ;Ein- oder Aus-Regelung?
                CP      55h
                JR      Z,KeyKanal23            ;=> Ein-Regelung
                CP      AAh
                JR      Z,KeyKanal22            ;=> Aus-Regelung
                LD      HL,Str6Minus            ;"------"
                JP      SetDisplayString
KeyKanal22:     LD      HL,StrLrAUS             ;"Lr-AUS"
                JP      SetDisplayString
KeyKanal23:     LD      HL,StrLrEIN             ;"Lr-EIN"
                JP      SetDisplayString
KeyKanal24:     LD      HL,StrLrUNI2            ;" UNI-2"
                JP      SetDisplayString

;"CO2" gedrückt
KeyCO2:         RES     0,(IX+Flags)            ;Zahleingabe aus
                LD      A,(IX+KLED+1)
                RES     7,A                     ;CO2-LED an
                OR      7Fh                     ;alle LEDs aus
                LD      (IX+KLED+1),A
                LD      A,(IX+KLED)
                OR      3Fh                     ;bis auf "Manuell" und "Setzen" alle LEDs aus
                LD      (IX+KLED),A
                LD      (C000h),A
                RET

;"Licht" gedrückt
KeyLicht:       RES     0,(IX+Flags)            ;Zahleingabe aus
                LD      A,(IX+KLED+1)
                RES     6,A                     ;Licht-LED an
                OR      BFh                     ;alle LEDs aus
                LD      (IX+KLED+1),A
                LD      A,(IX+KLED)
                OR      3Fh                     ;bis auf "Manuell" und "Setzen" alle LEDs aus
                LD      (IX+KLED),A
                LD      (C000h),A
                RET

;"Tag" gedrückt
KeyTag:         LD      HL,TagZeit              ;Ptr auf Tagdaten
                RES     0,(IX+KLED)             ;Tag an
                SET     1,(IX+KLED)             ;Nacht aus
                JR      KeyNacht1

;"Nacht" gedrückt
KeyNacht:       LD      HL,NachtZeit            ;Ptr auf Nachtdaten
                RES     1,(IX+KLED)             ;Nacht an
                SET     0,(IX+KLED)             ;Tag aus
KeyNacht1:      RES     0,(IX+Flags)            ;Zahleingabe aus
                LD      A,(IX+KLED)
                OR      2Ch                     ;Ein, Aus und Momentan aus
                LD      (IX+KLED),A
                LD      A,(IX+KLED+1)
                OR      FDh                     ;bis auf "Temperatur" alles aus
                LD      (IX+KLED+1),A
                BIT     7,(IX+KLED)             ;"Setzen" an?
                JR      Z,KeyNacht2             ;Ja! =>
                BIT     4,(IX+KLED)             ;"Zeit" an?
                JP      Z,PrintTime             ;Ja! =>
                BIT     1,(IX+KLED+1)           ;"Temperatur" an?
                JP      Z,PrintSollTemp         ;°C ausgeben
                JR      KeyNacht3
KeyNacht2:      BIT     4,(IX+KLED)             ;"Zeit" an?
                JP      Z,GetDispTime           ;Ja! => Zeit für Tag oder Nacht setzen
                BIT     1,(IX+KLED+1)           ;"Temperatur" an?
                JP      Z,SetSollTemp           ;Temperatur für Tag oder Nacht setzen =>
                SET     7,(IX+KLED)             ;"Setzen" aus
KeyNacht3:      SET     1,(IX+KLED)             ;Nacht aus
                SET     0,(IX+KLED)             ;Tag aus
                LD      (C000h),A
                RET

;"Manuell" gedrückt
KeyManuell:     RES     0,(IX+Flags)            ;Zahleingabe aus
                LD      A,(IX+KLED)
                BIT     7,A                     ;"Setzen" an?
                JR      Z,KeyManuell2           ;Ja! =>
                LD      BC,3
                LD      DE,ManuellEinZeit
                LD      HL,AktTime              ;Uhrzeit retten
                LDIR
                LD      HL,ManuellZeit
                BIT     4,A                     ;"Zeit" an?
                JR      NZ,KeyManuell1          ;Nein =>
                BIT     5,A                     ;"Momentan" an?
                JP      NZ,PrintTime            ;Nein! (Zeit an, Momentan aus) => Ausschaltzeit ausgeben
KeyManuell1:    XOR     40h
                LD      (IX+KLED),A             ;Manuell-Flag toggeln
                RET
KeyManuell2:    LD      HL,ManuellZeit
                IF !NewVersion
                CALL    GetDispTime             ;Zeit für "Manuell"-Taste setzen
                LD      A,(IX+Display)
                CP      0Fh                     ;"FEHL"er...
                RET     Z                       ;Ja => raus
                RES     4,(IX+KLED)             ;Zeit-LED an
                LD      B,6
KeyManuell3:    LD      HL,Display+5
                LD      A,(HL)
                CP      1                       ;"11.11.11" eingegeben?
                RET     NZ                      ;Nein =>
                INC     HL
                DJNZ    KeyManuell3
                LD      A,55h
                LD      (LaufschriftFlag),A     ;Laufschrift an
                LD      HL,MsgPause
                LD      (LaufschriftPtr),HL     ;Laufschrift-Text
                RET
                ENDIF

;Uhrzeit nach HL aus dem Display setzen
GetDispTime:    LD      DE,TempTime
                LD      A,(IX+DPunkt)           ;Dezimalpunkte holen
                CP      FBh                     ;hh.mm
                JR      Z,GetDispTime2
                CP      EBh                     ;hh.mm.ss
                JR      Z,GetDispTime1
                LD      A,7
                JP      MakeErrCode             ;Dezimalpunkte an falscher Position
GetDispTime1:   LD      B,2                     ;zwei Dezimalpunkte (noch zwei Zahlen holen)
                LD      C,4                     ;zuerst: Fehler bei den Sekunden
                JR      GetDispTime4
GetDispTime2:   LD      A,0
                OR      (IX+Display+1)          ;zwei Ziffern (ganz links) eingegeben?
                OR      (IX+Display)
                JR      Z,GetDispTime3          ;Nein =>
                LD      A,6
                JP      MakeErrCode             ;mehr als 23h eingegeben
GetDispTime3:   LD      (DE),A                  ;ohne Sekunden: 0 Sekunden setzen
                INC     DE
                LD      B,1                     ;ein Dezimalpunkt (noch eine Zahl holen)
                LD      C,5                     ;zuerst: Fehler bei den Minuten
GetDispTime4:   LD      IY,Display+5            ;Ptr auf die letzte Ziffer im Display
GetDispTime5:   LD      A,(IY-1)                ;Ziffer davor holen
                ADD     A,A
                ADD     A,A                     ;*16
                ADD     A,A
                ADD     A,A
                OR      (IY+0)                  ;und die Ziffer dazu (=> BCD-Zahl)
                CP      5Ah                     ;5A = 50+10 = 60!
                JR      C,GetDispTime6          ;kleiner? => ja
                SET     7,C                     ;Fehler!
GetDispTime6:   LD      (DE),A                  ;Zahl merken
                INC     DE
                DEC     IY                      ;zwei Ziffern nach vorne
                DEC     IY
                INC     C                       ;Fehlernummer hochsetzen (Sekunden => Minuten)
                DJNZ    GetDispTime5            ;alle Dezimalpunkte durch?
                LD      A,(IY-1)
                ADD     A,A
                ADD     A,A
                ADD     A,A                     ;Stunden in BCD wandeln
                ADD     A,A
                OR      (IY+0)
                CP      24h                     ;größer als 24h?
                JR      C,GetDispTime7          ;Nein =>
                SET     7,C                     ;Fehler!
GetDispTime7:   LD      (DE),A                  ;Stunden merken
                BIT     7,C                     ;ein Fehler aufgetreten?
                JR      Z,GetDispTime8          ;Nein =>
                RES     7,C                     ;Flag löschen
                LD      A,C
                JP      MakeErrCode             ;Fehler melden
GetDispTime8:   EX      DE,HL
                DEC     HL
                DEC     HL
                LD      BC,3
                LDIR                            ;Uhrzeit nach HL übertragen
                SET     7,(IX+KLED)             ;"Setzen" aus
                LD      (C000h),A
                RET

; 3 Bytes ab HL als 6 stellige Uhrzeit ausgeben
PrintTime:      LD      DE,Display+5            ;Ptr auf die letzte Stelle vom Display
                LD      B,3
                PUSH    HL
                INC     HL
                INC     HL
                LD      A,'0'
                CP      (HL)                    ;Stunden < '0'?
                POP     HL
                JR      NC,PrintTime1           ;Nein =>
                LD      HL,Str6Minus            ;"------" (Uhrzeit nicht gesetzt)
                JP      SetDisplayString
PrintTime1:     LD      A,(HL)                  ;Byte holen
                AND     0Fh
                LD      (DE),A                  ;unteres Nibble nach (DE)
                DEC     DE
                LD      A,(HL)
                RRCA
                RRCA
                RRCA
                RRCA
                AND     0Fh
                LD      (DE),A                  ;oberes Nibble nach (DE-1)
                DEC     DE
                INC     HL
                DJNZ    PrintTime1
                LD      (IX+DPunkt),EBh         ;Dezimalpunkte nach der 2. und der 4.Ziffer
                LD      (C000h),A
                RET

;Tag-/Nachttemperatur setzen
SetSollTemp:    PUSH    HL
                CALL    GetNumInput
                POP     HL
                LD      A,D                     ;Anzahl der Dezimalpunkte
                CP      0
                JR      Z,SetSollTemp1
                CP      1
                JR      Z,SetSollTemp2
                LD      A,10h
                JP      MakeErrCode             ;Dezimalpunkte bei der Temperatur...
SetSollTemp1:   LD      IY,InputBuf+7
                IF !NewVersion
                LD      A,0
                CP      (IY+0)
                JR      NZ,SetSollTemp2
                CP      (IY-1)                  ;100° C
                JR      NZ,SetSollTemp2
                LD      A,1
                CP      (IY-2)
                JR      NZ,SetSollTemp2
                LD      A,55h
                LD      (LaufschriftFlag),A     ;Laufschrift an
                LD      HL,MsgHeiss
                LD      (LaufschriftPtr),HL     ;Laufschrift-Text
                RET
                ENDIF
SetSollTemp2:   LD      B,E
                LD      A,0
                INC     IY
                PUSH    IY
SetSollTemp3:   OR      (IY-3)
                DEC     IY
                DJNZ    SetSollTemp3
                POP     IY
                CP      0
                JR      Z,SetSollTemp4
                LD      A,08h
                JP      MakeErrCode             ;eingegeben Temp. zu groß
SetSollTemp4:   PUSH    HL
                CALL    ConvertInput
                LD      A,(IY+1)
                CP      5
                JR      C,SetSollTemp5          ;<0.05°? =>
                INC     HL                      ;aufrunden (+ 0.1°)
SetSollTemp5:   LD      DE,100
                XOR     A
                SBC     HL,DE
                JR      C,SetSollTemp8          ;<10.0° =>
                JR      Z,SetSollTemp8          ;=10.0° =>
                LD      A,00h
                CP      H
                JR      NZ,SetSollTemp6         ;>(10° + 25.5°) =>
                LD      A,FFh
                CP      L
                JR      NZ,SetSollTemp7         ;<>(10° + 25.5°) =>
SetSollTemp6:   LD      A,8
                POP     HL
                JP      MakeErrCode             ;eing. Temp zu groß
SetSollTemp7:   LD      A,L
                POP     HL
                INC     HL
                INC     HL
                INC     HL
                LD      (HL),A                  ;Temperatur (10.1°...35.4°) setzen
                CALL    CalcSollChecksum        ;Prüfsumme über die Sollwerte errechnen
                LD      (SollChecksum),DE       ;und merken
                SET     7,(IX+KLED)             ;"Setzen" aus
                LD      L,A
                LD      (C000h),A
                JR      DispTemp
SetSollTemp8:   LD      A,09h
                POP     HL
                JP      MakeErrCode             ;eing. Temp zu tief

;Soll-Tag-/Nachttemperatur ausgeben
PrintSollTemp:  INC     HL
                INC     HL
                INC     HL
                LD      L,(HL)                  ;Soll-Temperatur auslesen

;Temperatur-Wert darstellen
DispTemp:       LD      H,0
                LD      DE,100
                ADD     HL,DE                   ;+10.0°
                PUSH    HL
                POP     IY
                CALL    MakeBCD
                LD      HL,(BCDZahl)
                LD      DE,Display
                LD      B,3                     ;3 Ziffern
DispTemp1:      LD      A,H
                AND     0Fh
                LD      (DE),A
                ADD     HL,HL
                ADD     HL,HL                   ;HL *= 16
                ADD     HL,HL
                ADD     HL,HL
                INC     DE
                DJNZ    DispTemp1
                EX      DE,HL
                LD      (HL),1Fh                ;Space
                INC     HL
                LD      (HL),18h                ;°
                INC     HL
                LD      (HL),0Ch                ;C
                LD      (IX+DPunkt),EFh         ;Punkt in der 2.Ziffer an
                LD      (C000h),A
                RET

;Meßwert C (0...3) vom Hauptgerät empfangen (Wert nach A)
GetMesswert:    LD      HL,E000h
                LD      A,(IX+Steckdosen)       ;Steckdosen-Bits
                AND     5Fh                     ;Bit 0...4 übernehmen, Bit 5&7 löschen
                OR      40h                     ;Bit 6 setzen
                SET     6,(IX+Flags)
                HALT
                LD      (HL),A                  ;567:010 (Bit 7 löschen = Übertragung init)
                CALL    Delay
                LD      E,A
                SET     5,E
                LD      (HL),E                  ;567:110 (Bit 5 toggeln: Übertragung start)
                CALL    Delay

;Meßwert-Nummer (2 Bits) senden
                LD      D,A
                LD      B,2                     ;2 Bits (für 4 Meßwerte) senden
                LD      (HL),D                  ;567:010
                CALL    Delay
                LD      (HL),E                  ;567:110 (1-Bit senden = Startbit)
                CALL    Delay
                LD      (HL),D                  ;567:010
GetMesswert1:   SRL     C
                JR      C,GetMesswert2          ;gesetzt =>
                RES     6,D                     ;Bit 6 löschen, wenn Carry gelöscht
                RES     6,E
                LD      (HL),D                  ;567:000
                CALL    Delay
                LD      (HL),E                  ;567:100 (0-Bit senden)
                CALL    Delay
                LD      (HL),D                  ;567:000
                DJNZ    GetMesswert1            ;alle 2 Bits übertragen? Nein =>
                JR      GetMesswert3
GetMesswert2:   SET     6,D                     ;Bit 6 setzen, wenn Carry gesetzt
                SET     6,E
                LD      (HL),D                  ;567:010
                CALL    Delay
                LD      (HL),E                  ;567:110 (1-Bit senden)
                CALL    Delay
                LD      (HL),D                  ;567:010
                DJNZ    GetMesswert1            ;alle 2 Bits übertragen? Nein =>

GetMesswert3:   LD      B,8                     ;8 Bits empfangen
                LD      (HL),E                  ;567:1?0 (Bit erwarten)
                CALL    Delay
                IN      C,(C)                   ;Bit auslesen
                BIT     5,C                     ;0-Startbit?
                JR      Z,GetMesswert4          ;Ja =>
                SET     7,E
                SET     6,E
                LD      (IX+Steckdosen),E
                LD      (HL),E                  ;567:1?1 (Übertragung beendet)
                SCF
                CCF                             ;Carry = NOT 1 = 0 (Übertragung mit Fehler)
                RET
GetMesswert4:   LD      (HL),D                  ;567:0?0 (Bit empfangen)
                CALL    Delay
                LD      A,0                     ;Bytewert = 0
                LD      (HL),E                  ;567:1?0 (Bit erwarten)
GetMesswert5:   CALL    Delay
                ADD     A,A                     ;Bytewert * 2
                IN      C,(C)
                BIT     5,C                     ;Bit abfragen
                LD      (HL),D                  ;567:0?0 (Bit empfangen)
                CALL    Delay
                JR      Z,GetMesswert6          ;Bit gelöscht =>
                SET     0,A                     ;unterstes Bit setzen
GetMesswert6:   LD      (HL),E                  ;567:1?0 (Bit erwarten)
                CALL    Delay
                DJNZ    GetMesswert5            ;alle 8 Bits empfangen? Nein =>
                SET     7,E
                SET     6,E
                LD      (IX+Steckdosen),E
                LD      (HL),E                  ;567:111 (Übertragung beendet)
                CALL    Delay
                RES     6,(IX+Flags)
                SCF                             ;Carry = 1 (Übertragung ok)
                RET

;ein paar Takte verzögern
Delay:          NOP
                NOP
                NOP
                NOP
                NOP
                NOP
                NOP
                NOP
                NOP
                NOP
                NOP
                NOP
                NOP
                RET

;Potential in L ausgeben
DispRedox:      LD      (IX+DPunkt),FFh         ;Dezimalpunkte aus
                LD      (IX+Display+5),19h      ;o
                LD      (IX+Display+4),12h      ;P
                LD      (IX+Display+3),1Fh      ;Space
                LD      H,0
                ADD     HL,HL
                PUSH    HL
                POP     IY
                CALL    MakeBCD
                LD      HL,(BCDZahl)
                LD      DE,Display
                LD      B,3
DispRedox1:     LD      A,H
                AND     0Fh
                LD      (DE),A
                ADD     HL,HL
                ADD     HL,HL                   ;HL *= 16
                ADD     HL,HL
                ADD     HL,HL
                INC     DE
                DJNZ    DispRedox1
                LD      (C000h),A
                RET

;Systemzeit (aus dem Display) in dem RTC setzen
SetSystemTime:  LD      HL,AktTime
                CALL    GetDispTime             ;Uhrzeit aus dem Display lesen
                LD      A,(IX+Display)
                CP      0Fh                     ;"F"ehler?
                RET     Z                       ;Ja => raus
                LD      HL,AktTime
                CALL    PrintTime               ;Uhrzeit formatiert ausgeben
                LD      HL,Display
                SET     3,(HL)                  ;Bit 3 in der 1.Stundenziffer setzen (24h Format)
                LD      B,6                     ;6 Register setzen
                LD      C,5
                LD      IY,4000h                ;Adresse vom RTC
                HALT
SetSystemTime1: LD      (IY+1),C                ;Register auswählen
                LD      A,(HL)                  ;Ziffer auslesen
                LD      (IY+2),A                ;und ins RTC-Register schreiben
                INC     HL
                DEC     C                       ;Register - 1
                DJNZ    SetSystemTime1
                RES     5,(IX+KLED)             ;"Momentan"-LED ausschalten
                LD      (C000h),A
                RET

;Universaltimer-Verwaltung
UniTimer:       LD      B,10                    ;maximal 10 Zeiten
UniTimer1:      PUSH    HL
                INC     HL
                INC     HL
                BIT     7,(HL)                  ;Ende der Liste? (Einschaltzeit)
                POP     HL
                JR      Z,UniTimer2             ;Nein =>
                SCF                             ;Ja, Carry setzen und raus
                RET
UniTimer2:      PUSH    DE
                INC     DE
                INC     DE
                LD      A,(DE)                  ;Ende der Liste? (Ausschaltzeit)
                POP     DE
                BIT     7,A
                JR      Z,UniTimer3             ;Nein =>
                SCF                             ;Ja, Carry setzen und raus
                RET
UniTimer3:      PUSH    BC
                PUSH    DE
                PUSH    HL
                CALL    InTimeRange             ;im Einschaltbereich?
                POP     HL
                POP     DE
                POP     BC
                RET     NC                      ;Ja! => raus
                INC     HL
                INC     HL                      ;nächste Einschaltzeit
                INC     HL
                INC     DE
                INC     DE                      ;nächste Ausschaltzeit
                INC     DE
                DJNZ    UniTimer1               ;alle Zeiten durch?
                LD      (C000h),A
                RET

;HL: Einschaltzeit
;DE: Ausschaltzeit
;Carry = 0, wenn im Zeitraum
InTimeRange:    PUSH    DE
                PUSH    HL
                CALL    CompareTimes            ;sind die beiden Zeiten gleich?
                POP     HL
                POP     DE
                RET     Z                       ;Ja => raus
                JR      C,InTimeRange1          ;Ausschaltzeit < Einschaltzeit? => Zeiten und Logik drehen
                PUSH    DE
                LD      DE,AktTime
                CALL    CompareTimes            ;HL mit der aktuellen Uhrzeit vergleichen
                POP     DE
                RET     Z                       ;Einschaltzeit = aktuelle Zeit? => raus
                RET     C                       ;Einschaltzeit > aktuelle Zeit? => raus
                LD      HL,AktTime
                CALL    CompareTimes
                RET     C                       ;Ausschaltzeit > aktuelle Zeit? => raus
                RET     NZ                      ;Ausschaltzeit <> aktuelle Zeit? => raus
                SCF                             ;Carry = 1 (außerhalb)
                RET
InTimeRange1:   EX      DE,HL
                PUSH    DE
                LD      DE,AktTime
                CALL    CompareTimes
                POP     DE
                CCF                             ;Carry = NOT Carry
                RET     NC
                JR      NZ,InTimeRange2
                SCF                             ;Carry = 1 (außerhalb)
                RET
InTimeRange2:   LD      HL,AktTime
                CALL    CompareTimes
                RET     Z
                CCF                             ;Carry = NOT Carry
                RET

;Zeit DE und HL vergleichen, Z = 1, wenn gleich
CompareTimes:   LD      BC,0300h                ;3 Bytes (Sekunden,Minuten,Stunden) vergleichen
                XOR     A
CompareTimes1:  LD      A,(DE)
                SBC     A,(HL)
                JR      Z,CompareTimes2
                SET     0,C                     ;Flag setzen, wenn ungleich!
CompareTimes2:  INC     HL
                INC     DE
                DJNZ    CompareTimes1
                LD      (C000h),A
                BIT     0,C                     ;Z = 1, wenn gleich
                RET

;Leitwert-Wert darstellen
DispLeitw:      LD      H,0
                BIT     5,(IX+KLED)             ;"Momentan" an?
                JR      NZ,DispLeitw4           ;Nein =>
                LD      A,(IX+IstTemp)          ;Ist-Temp-Wert
                CP      FFh
                JR      Z,DispLeitw6            ;Temperatur außerhalb des Meßbereiches?
                CP      00h
                JR      Z,DispLeitw6            ;Ja =>
                LD      A,(IX+LeitwertKomp)     ;kompensierter Leitwert
                LD      L,A
                CP      FFh                     ;ungültig?
                JR      NZ,DispLeitw2           ;Nein =>
                BIT     0,(IX+Counter+1)        ;Blink-Timer gesetzt?
                JR      Z,DispLeitw1
                LD      HL,Str6Space            ;"      "
                JP      SetDisplayString
DispLeitw1:     LD      HL,StrFEHL16            ;"FEHL16"
                JP      SetDisplayString
DispLeitw2:     CP      00h                     ;Bereich unterschritten?
                JR      NZ,DispLeitw4           ;Nein =>
                BIT     0,(IX+Counter+1)        ;Blink-Timer gesetzt?
                JR      Z,DispLeitw3
                LD      HL,Str6Space            ;"      "
                JP      SetDisplayString
DispLeitw3:     LD      HL,StrFEHL15            ;"FEHL15"
                JP      SetDisplayString

DispLeitw4:     IN      C,(C)
                BIT     5,C                     ;Süßwasser/Meerwasser-Schalter abfragen
                JR      Z,DispLeitw5            ;Meerwasser =>
                LD      DE,1505h                ;"µS"
                LD      (IX+Display+3),00h      ;"0"
                LD      (IX+DPunkt),FFh         ;Dezimalpunkte aus
                JR      DispLeitw9
DispLeitw5:     LD      DE,350                  ;35.0mS Vorgabe (Minumum bei Meerwasser-Leitwert)
                ADD     HL,DE
                LD      DE,1705h                ;"nS"
                LD      (IX+Display+3),1Fh      ;Space
                LD      (IX+DPunkt),EFh
                JR      DispLeitw9

DispLeitw6:     LD      L,(IX+IstLeitw)         ;Ist-Leitwert (nicht kompensiert)
                IN      C,(C)
                BIT     5,C                     ;Süßwasser/Meerwasser-Schalter abfragen
                JR      Z,DispLeitw7            ;Meerwasser =>
                LD      DE,1505h                ;"µS"
                LD      (IX+Display+3),00h      ;"0"
                LD      (IX+DPunkt),FFh         ;Dezimalpunkte aus
                JR      DispLeitw8
DispLeitw7:     LD      DE,350                  ;35.0mS Vorgabe (Minumum bei Meerwasser-Leitwert)
                ADD     HL,DE
                LD      DE,1705h                ;"nS"
                LD      (IX+Display+3),1Fh      ;Space
                LD      (IX+DPunkt),EFh         ;Punkt nach der 2.Ziffer
DispLeitw8:     BIT     0,(IX+Counter+1)        ;Blink-Timer gesetzt?
                JR      Z,DispLeitw9
                LD      DE,1F1Fh                ;"  "

DispLeitw9:     LD      (IX+Display+5),E        ;Einheit setzen
                LD      (IX+Display+4),D
                PUSH    HL
                POP     IY
                CALL    MakeBCD                 ;Leitwert nach BCD wandeln
                LD      HL,(BCDZahl)            ;BCD-Zahl holen
                LD      DE,Display
                LD      B,3                     ;3 Ziffern ins Display
DispLeitw10:    LD      A,H
                AND     0Fh
                LD      (DE),A                  ;Ziffer ins Display
                ADD     HL,HL
                ADD     HL,HL                   ;HL * 16
                ADD     HL,HL
                ADD     HL,HL
                INC     DE                      ;eine Stelle weiter
                DJNZ    DispLeitw10             ;alle drei Ziffern durch? Nein =>
                LD      (C000h),A
                RET

;Fehlermeldung A für das Display zusammensetzen
MakeErrCode:    LD      HL,Display
                LD      (HL),0Fh                ;"F"
                INC     HL
                LD      (HL),0Eh                ;"E"
                INC     HL
                LD      (HL),10h                ;"H"
                INC     HL
                LD      (HL),11h                ;"L"
                LD      (IX+DPunkt),FBh         ;FBh (Bit 2 gelöscht): Punkt in der 4.Stelle setzen?!?
                LD      B,A
                AND     0Fh
                LD      (IX+Display+5),A        ;Fehlercode (untere Ziffer)
                LD      A,B
                RRCA
                RRCA
                RRCA
                RRCA
                AND     0Fh
                JR      NZ,MakeErrCode1         ;zweistellige Ziffer? Ja =>
                LD      A,1Fh                   ;Nein = Feld frei
MakeErrCode1:   LD      (IX+Display+4),A        ;erste Ziffer
                SET     4,(IX+Flags)            ;keine Zahl im Display
                LD      (C000h),A
                RET

;Eingabe (drei Ziffern) in eine binäre Zahl in HL wandeln
ConvertInput:   LD      HL,0                    ;Zahlenwert
                LD      A,(IY-2)                ;1.Ziffer holen
                CP      0                       ;= "0"?
                JR      Z,ConvertInput2         ;Ja! =>
                LD      B,A
                LD      DE,100
ConvertInput1:  ADD     HL,DE                   ;100 * Wert der 1.Ziffer addieren
                DJNZ    ConvertInput1
ConvertInput2:  LD      A,(IY-1)                ;2.Ziffer holen
                CP      0                       ;= "0"?
                JR      Z,ConvertInput4         ;Ja! =>
                LD      B,A
                LD      DE,10
ConvertInput3:  ADD     HL,DE                   ;10 * Wert der 2.Ziffer addieren
                DJNZ    ConvertInput3
ConvertInput4:  LD      E,(IY+0)                ;3.Ziffer
                LD      D,0
                ADD     HL,DE                   ;zum Wert addieren
                LD      (C000h),A
                RET

;binäre Zahl in IY nach BCDZahl in gepacktem BCD wandeln
MakeBCD:        ADD     IY,IY                   ;Zahl * 4
                ADD     IY,IY
                LD      B,14                    ;14 Bits (2 Bits sind durch * 4 weg)
                LD      HL,BinDezTableEnd-1     ;Multiplikationstabelle
                LD      DE,BCDZahl
                LD      (IX+BCDZahl),0
                LD      (IX+BCDZahl+1),0
MakeBCD1:       ADD     IY,IY                   ;Zahl * 2
                JR      C,MakeBCD2              ;Überlauf? => Ja!
                DEC     HL                      ;eine Stelle in der Mult-Tabelle zurück
                DEC     HL
                DJNZ    MakeBCD1                ;alle Stellen durch? Nein =>
                RET
MakeBCD2:       XOR     A                       ;A = 0
                LD      A,(DE)
                ADC     A,(HL)                  ;untere Bytes addieren
                DAA                             ;in gepacktes BCD wandeln
                LD      (DE),A                  ;und zurückschreiben
                DEC     HL
                INC     DE
                LD      A,(DE)                  ;obere Bytes addieren
                ADC     A,(HL)
                DAA                             ;in gepacktes BCD wandeln
                LD      (DE),A                  ;und zurückschreiben
                DEC     HL
                DEC     DE
                LD      (C000h),A
                DJNZ    MakeBCD1                ;zur nächsten Stelle
                RET

KeyAus2:        BIT     4,(IX+KLED+1)           ;"Kanal 1"
                JP      Z,KeyAus15              ;Ja =>
                BIT     5,(IX+KLED+1)           ;"Kanal 2"
                JP      Z,KeyAus16              ;Ja =>
                BIT     6,(IX+KLED+1)           ;"Licht"
                JR      NZ,KeyAus4              ;Nein =>
                BIT     2,(IX+KLED)             ;"Ein"
                JR      NZ,KeyAus3              ;Nein =>
                BIT     7,(IX+KLED)             ;"Setzen" an?
                LD      HL,LichtEin
                JP      Z,GetDispTime           ;Ja! =>
                JP      PrintTime
KeyAus3:        BIT     7,(IX+KLED)             ;"Setzen" an?
                LD      HL,LichtAus
                JP      Z,GetDispTime           ;Ja! =>
                JP      PrintTime
KeyAus4:        BIT     7,(IX+KLED+1)           ;"CO2"
                JR      NZ,KeyAus6
                BIT     2,(IX+KLED)             ;"Ein"
                JR      NZ,KeyAus5
                BIT     7,(IX+KLED)             ;"Setzen" an?
                LD      HL,CO2Ein
                JP      Z,GetDispTime           ;Ja! =>
                JP      PrintTime
KeyAus5:        BIT     7,(IX+KLED)             ;"Setzen" an?
                LD      HL,CO2Aus
                JP      Z,GetDispTime           ;Ja! =>
                JP      PrintTime
KeyAus6:        BIT     4,(IX+KLED)             ;"Zeit"
                JR      Z,KeyAus7               ;Ja =>
                LD      A,(IX+KLED)
                OR      0Ch                     ;Ein und Aus LEDs ausschalten
                LD      (IX+KLED),A
                RET
KeyAus7:        BIT     7,(IX+KLED)             ;"Setzen" an?
                JR      Z,KeyAus11              ;Ja! =>
                LD      HL,Display
                LD      B,6
KeyAus8:        LD      (HL),0                  ;Display löschen
                INC     HL
                DJNZ    KeyAus8
                LD      (IX+DPunkt),FEh         ;Dezimalpunkt in der 6.Ziffer an
                LD      A,(IX+AktSchaltzeit)    ;aktuelle Schaltzeit
                AND     0Fh
                INC     A
                CP      10
                JR      Z,KeyAus9
                LD      (IX+Display+5),A        ;1...9
                JR      KeyAus10
KeyAus9:        LD      (IX+Display+4),1        ;'10'
KeyAus10:       LD      (C000h),A
                RET
KeyAus11:       LD      A,0
                LD      B,4
                LD      HL,Display              ;die ersten 4 Ziffern müssen stets = 0 sein
KeyAus12:       OR      (HL)
                INC     HL
                DJNZ    KeyAus12
                JR      NZ,KeyAus13             ;wenn nicht => Fehler
                LD      IY,Display+5            ;Ptr auf die letzte Stelle vom Display
                CALL    ConvertInput            ;Zahl nach HL holen
                LD      A,L
                CP      0
                JR      Z,KeyAus13              ;Schaltzeiten zwischen 1 und 10
                CP      11
                JR      NC,KeyAus13
                DEC     A
                LD      (IX+AktSchaltzeit),A    ;aktuelle Schaltzeit merken
                SET     7,(IX+KLED)             ;"Setzen" aus
                LD      (C000h),A
                RET
KeyAus13:       LD      A,19h
                JP      MakeErrCode             ;ill. Nummer für die Schaltzeiten

                IF      !NewVersion
KeyAus14:       LD      A,20h
                JP      MakeErrCode             ;???
                ENDIF

KeyAus15:       LD      HL,Kanal1Uni            ;Kanal 1 Schaltzeiten
                JR      KeyAus17

KeyAus16:       LD      HL,Kanal2Uni            ;Kanal 2 Schaltzeiten
KeyAus17:       BIT     2,(IX+KLED)             ;"Ein" gedrückt?
                JR      Z,KeyAus18              ;Ja! =>
                LD      DE,30
                ADD     HL,DE                   ;Ausschaltzeiten
KeyAus18:       LD      DE,3
                LD      A,(IX+AktSchaltzeit)    ;aktuelle Schaltzeit holen
                CP      0
                JR      Z,KeyAus21              ;Schaltzeit gültig?
                CP      10                      ;Ja =>
                JR      C,KeyAus19
                LD      A,0
                LD      (IX+AktSchaltzeit),A    ;Schaltzeit löschen
                JR      KeyAus21
KeyAus19:       LD      B,A
KeyAus20:       ADD     HL,DE                   ;je 3 Bytes pro Schaltzeit
                DJNZ    KeyAus20
KeyAus21:       BIT     7,(IX+KLED)             ;"Setzen" an?
                JP      NZ,PrintTime            ;Nein! =>
                LD      A,(IX+Display)
                OR      (IX+Display+1)
                OR      (IX+Display+2)
                OR      (IX+Display+3)          ;0 als Uhrzeit eingegeben?
                OR      (IX+Display+4)
                OR      (IX+Display+5)
                JR      Z,KeyAus22              ;Ja =>
                JP      GetDispTime             ;Schaltzeit setzen
KeyAus22:       LD      A,(IX+DPunkt)           ;Dezimalpunkte?
                CP      FFh
                JP      NZ,GetDispTime          ;Ja =>
                INC     HL
                INC     HL
                SET     7,(HL)                  ;Alarmzeit ungültig machen
                LD      DE,30
                BIT     2,(IX+KLED)             ;"Ein"?
                JR      Z,KeyAus23              ;Ja =>
                XOR     A
                SBC     HL,DE                   ;Ptr auf Einschaltzeit
                JR      KeyAus24
KeyAus23:       ADD     HL,DE                   ;Ptr auf Ausschaltzeit
KeyAus24:       SET     7,(HL)                  ;entsprechende Zeit ebenfalls ausschalten
                SET     7,(IX+KLED)             ;"Setzen" aus
                LD      HL,Str6Minus            ;"------"
                JP      SetDisplayString

;"Redox"-Taste gedrückt
KeyRedox:       RES     0,(IX+Flags)            ;Zahleingabe aus
                BIT     5,(IX+KLED)             ;"Momentan"?
                JR      NZ,KeyRedox1            ;Nein =>
                LD      A,(IX+KLED)
                OR      9Fh                     ;Mometan und Manuell an lassen (Rest aus)
                LD      (IX+KLED),A
                LD      A,(IX+KLED+1)
                OR      F7h                     ;LEDs bis auf Redox ausschalten
                LD      (IX+KLED+1),A
                RES     3,(IX+KLED+1)           ;Redox anschalten
                RET
KeyRedox1:      BIT     7,(IX+KLED)             ;"Setzen" an?
                JR      Z,KeyRedox2             ;Ja! =>
                LD      A,(IX+KLED)
                OR      BFh                     ;bis auf Manuell alles ausschalten
                LD      (IX+KLED),A
                LD      A,(IX+KLED+1)
                OR      F7h                     ;bis auf Redox alles ausschalten
                LD      (IX+KLED+1),A
                RES     3,(IX+KLED+1)           ;Redox anschalten
                LD      L,(IX+SollRedox)        ;Soll-Redoxwert
                JP      DispRedox               ;Potential anzeigen
KeyRedox2:      RES     3,(IX+KLED+1)           ;Redox anschalten
                BIT     4,(IX+KLED+1)           ;"Kanal 1" an
                JR      NZ,KeyRedox3            ;Nein =>
                LD      (IX+Uni1Flag),AAh       ;Kanal 1 auf Redox-Regelung schalten
                SET     7,(IX+KLED)             ;"Setzen" aus
                LD      HL,StrrErE              ;" rE-rE"
                JP      SetDisplayString
KeyRedox3:      CALL    GetNumInput             ;Eingabe holen
                LD      A,D
                CP      0
                JR      Z,KeyRedox4             ;0 oder 1 Dezimalpunkt in der Eingabe
                CP      1
                JR      Z,KeyRedox5
                LD      A,23h
                JP      MakeErrCode             ;mehrere Dezimalpunkte bei Redox-Eingabe
KeyRedox4:      LD      IY,InputBuf+7
                LD      E,5
KeyRedox5:      LD      B,E
                DEC     B
                JR      Z,KeyRedox7
                LD      B,1
                LD      A,0
                PUSH    IY
KeyRedox6:      OR      (IY-3)                  ;Ziffern vor der erwarteten 1.Ziffer zusammen'OR'n
                DEC     IY
                DJNZ    KeyRedox6
                POP     IY
                CP      0
                JR      Z,KeyRedox7
                LD      A,21h
                JP      MakeErrCode             ;zu großer Redox-Wert eingegeben
KeyRedox7:      CALL    ConvertInput
                BIT     0,L                     ;Redox-Wert gerade?
                JR      Z,KeyRedox8             ;Ja =>
                INC     HL                      ;ansonsten aufrunden
KeyRedox8:      LD      A,L
                OR      H                       ;Redox-Wert = 0?
                JR      NZ,KeyRedox9            ;Nein =>
                LD      A,22h
                JP      MakeErrCode             ;0 Volt Redox-Wert eingegeben
KeyRedox9:      LD      DE,509                  ;509mV = maximaler Redox-Wert
                XOR     A
                PUSH    HL
                SBC     HL,DE
                POP     HL
                JR      C,KeyRedox10
                LD      A,21h
                JP      MakeErrCode             ;Redox-Wert zu groß!
KeyRedox10:     RR      H                       ;Redox-Wert / 2
                RR      L
                LD      (IX+SollRedox),L        ;Soll-Redoxwert setzen
                CALL    CalcSollChecksum        ;Prüfsumme über die Sollwerte errechnen
                LD      (SollChecksum),DE       ;und merken
                SET     7,(IX+KLED)             ;"Setzen" aus
                JP      DispRedox               ;Potential anzeigen

;"Leitwert"-Taste gedrückt
KeyLeitwert:    RES     0,(IX+Flags)            ;Zahleingabe aus
                BIT     5,(IX+KLED)             ;"Momentan"?
                JR      NZ,KeyLeitwert1         ;Nein =>
                LD      A,(IX+KLED)
                OR      9Fh                     ;Mometan und Manuell an lassen (Rest aus)
                LD      (IX+KLED),A
                LD      A,(IX+KLED+1)
                OR      FBh                     ;LEDs bis auf Leitwert ausschalten
                LD      (IX+KLED+1),A
                RES     2,(IX+KLED+1)           ;Leitwert anschalten
                LD      (C000h),A
                RET
KeyLeitwert1:   BIT     7,(IX+KLED)             ;"Setzen" an?
                JR      Z,KeyLeitwert3          ;Ja! =>
                LD      A,(IX+KLED)
                OR      3Fh                     ;bis auf Manuell und Setzen alles ausschalten
                LD      (IX+KLED),A
                LD      A,(IX+KLED+1)
                OR      EFh                     ;bis auf Kanal 1 alles ausschalten
                LD      (IX+KLED+1),A
                RES     2,(IX+KLED+1)           ;Leitwert anschalten
                IN      C,(C)
                BIT     5,C                     ;Süßwasser/Meerwasser-Schalter abfragen
                JR      Z,KeyLeitwert2          ;Meerwasser =>
                LD      L,(IX+SollLeitwertS)    ;Soll-Leitwert (Süßwasser)
                JP      DispLeitw
KeyLeitwert2:   LD      L,(IX+SollLeitwertM)    ;Soll-Leitwert (Meerwasser)
                JP      DispLeitw
KeyLeitwert3:   BIT     5,(IX+KLED+1)           ;"Kanal 2"?
                JR      NZ,KeyLeitwert4         ;Nein =>
                RES     2,(IX+KLED+1)           ;Leitwert an
                LD      (C000h),A
                RET
KeyLeitwert4:   RES     2,(IX+KLED+1)           ;Leitwert an
                LD      A,(IX+KLED)
                OR      3Fh                     ;bis auf Manuell und Setzen alles ausschalten
                LD      (IX+KLED),A
                LD      A,(IX+KLED+1)
                OR      FBh                     ;bis auf Leitwert alles ausschalten
                LD      (IX+KLED+1),A
                IN      C,(C)
                BIT     5,C                     ;Süßwasser/Meerwasser-Schalter abfragen
                JR      Z,KeyLeitwert15         ;Meerwasser =>
                LD      A,(IX+DPunkt)           ;Dezimalpunkt
                CP      DFh                     ;an 2.Stelle?
                JR      NZ,KeyLeitwert5         ;Nein =>
                LD      A,(IX+Display)
                CP      5                       ;5?
                JR      C,KeyLeitwert13
                LD      L,1
                JR      KeyLeitwert14
KeyLeitwert5:   CALL    GetNumInput
                DEC     IY
                LD      A,D
                CP      0
                JR      Z,KeyLeitwert6          ;0 oder 1 Dezimalpunkt?
                CP      1                       ;Nein =>
                JR      Z,KeyLeitwert7
                LD      A,26h
                JP      MakeErrCode             ;zu viele Dezimalpunkte beim Leitwert
KeyLeitwert6:   LD      IY,InputBuf+6
                LD      B,2
                JR      KeyLeitwert8
KeyLeitwert7:   LD      A,E
                SUB     4
                JR      Z,KeyLeitwert10
                JR      C,KeyLeitwert10
                LD      B,A
KeyLeitwert8:   LD      A,0
                PUSH    IY
KeyLeitwert9:   OR      (IY-3)                  ;Ziffern vor der erwarteten Eingabe zusammen 'OR'n
                DEC     IY
                DJNZ    KeyLeitwert9
                POP     IY
                CP      0
                JR      Z,KeyLeitwert10
                LD      A,24h
                JP      MakeErrCode             ;Leitwert zu groß!
KeyLeitwert10:  CALL    ConvertInput
                LD      A,(IY+1)                ;4.Ziffer
                CP      5                       ;>= 5?
                JR      C,KeyLeitwert11         ;Nein =>
                INC     HL                      ;aufrunden
KeyLeitwert11:  LD      DE,255
                PUSH    HL
                XOR     A
                SBC     HL,DE                   ;Leitwert >= 255? (2550 µS)
                POP     HL
                JR      C,KeyLeitwert12         ;Nein =>
                LD      A,24h
                JP      MakeErrCode             ;Leitwert zu groß!
KeyLeitwert12:  LD      A,L
                OR      H                       ;Leitwert = 0?
                JR      NZ,KeyLeitwert14        ;Nein =>
KeyLeitwert13:  LD      A,25h
                JP      MakeErrCode             ;Leitwert zu klein
KeyLeitwert14:  LD      (IX+SollLeitwertS),L    ;Soll-Leitwert (Süßwasser) setzen
                CALL    CalcSollChecksum        ;Prüfsumme über die Sollwerte errechnen
                LD      (SollChecksum),DE       ;und merken
                SET     7,(IX+KLED)             ;"Setzen" aus
                JP      DispLeitw

;Leitwert für Meerwasser:
KeyLeitwert15:  CALL    GetNumInput
                LD      A,D
                CP      0
                JR      Z,KeyLeitwert16
                CP      1
                JR      Z,KeyLeitwert17
                LD      A,26h
                JP      MakeErrCode             ;Dezimalpunktfehler beim Leitwert
KeyLeitwert16:  LD      IY,InputBuf+7
KeyLeitwert17:  LD      B,E
                LD      A,0
                INC     IY
                PUSH    IY
KeyLeitwert18:  OR      (IY-3)
                DEC     IY
                DJNZ    KeyLeitwert18
                POP     IY
                CP      0
                JR      Z,KeyLeitwert19
                LD      A,24h
                JP      MakeErrCode             ;Leitwert zu groß
KeyLeitwert19:  CALL    ConvertInput
                LD      A,(IY+1)                ;3.Ziffer
                CP      5                       ;>= 5?
                JR      C,KeyLeitwert20         ;Nein =>
                INC     HL                      ;aufrunden
KeyLeitwert20:  LD      DE,353
                XOR     A
                SBC     HL,DE                   ;35.3mS abziehen
                JR      NC,KeyLeitwert21        ;Unterlauf? Nein =>
                LD      A,25h
                JP      MakeErrCode             ;Leitwert zu klein
KeyLeitwert21:  ADC     HL,DE                   ;wieder dazuaddieren
                LD      DE,601
                XOR     A
                SBC     HL,DE                   ;60.1mS abziehen
                JR      C,KeyLeitwert22         ;Überlauf? Nein =>
                LD      A,24h
                JP      MakeErrCode             ;Leitwert zu groß
KeyLeitwert22:  ADC     HL,DE
                LD      DE,350                  ;35.0mS abziehen
                SBC     HL,DE
                LD      (IX+SollLeitwertM),L    ;Soll-Leitwert (Meerwasser)
                CALL    CalcSollChecksum        ;Prüfsumme über die Sollwerte errechnen
                LD      (SollChecksum),DE       ;und merken
                SET     7,(IX+KLED)             ;"Setzen" aus
                JP      DispLeitw

;String ab HL ins Display übertragen
SetDisplayString:
                LD      BC,6
                LD      DE,Display
                LDIR                            ;String ins Display
                INC     DE
                INC     DE
                INC     DE
                LDI                             ;Dezimalpunkte übertragen
                SET     4,(IX+Flags)            ;keine Zahl im Display
                RET

;Kanal 1-Regelung
Kanal1Regel:
                LD      A,(IX+Uni1Flag)
                CP      55h                     ;Universal-Timer?
                JP      Z,UniTimer              ;Ja =>
                CP      AAh                     ;Redox-Regelung?
                JR      Z,Kanal1Regel1          ;Ja =>
                SCF
                RET
Kanal1Regel1:   CALL    UniTimer
                RET     C                       ;nichts gefunden =>
                LD      A,(IX+SollRedox)        ;Soll-Redoxwert
                BIT     3,(IX+WarnLED)          ;Kanal 1-LED an?
                JR      NZ,Kanal1Regel2         ;Nein =>
                ADD     A,1                     ;Soll-Wert um 0.5µV erhöhen, wenn Regelung bereits an
Kanal1Regel2:   CP      (IX+IstRedox)           ;mit Sollwert vergleichen
                RET

;Kanal 2-Regelung
Kanal2Regel:
                LD      A,(IX+Uni2Flag)
                CP      55h                     ;Universal-Timer?
                JP      Z,UniTimer              ;Ja =>
                CP      AAh                     ;Leitwert-Regelung?
                JR      Z,Kanal2Regel1          ;Ja =>
                SCF
                RET
Kanal2Regel1:   CALL    UniTimer
                RET     C                       ;nichts gefunden =>
                LD      A,(IX+IstTemp)          ;Ist-Temp-Wert
                CP      FFh
                JR      Z,Kanal2Regel2          ;außerhalb des Meßbereiches?
                CP      0
                JR      Z,Kanal2Regel2          ;Ja =>
                LD      D,(IX+LeitwertKomp)     ;kompensierter Leitwert
                JR      Kanal2Regel3
Kanal2Regel2:   LD      D,(IX+IstLeitw)         ;Ist-Leitwert
Kanal2Regel3:   IN      C,(C)
                BIT     5,C                     ;Süßwasser/Meerwasser-Schalter abfragen
                JR      Z,Kanal2Regel4          ;Meerwasser =>
                LD      B,(IX+SollLeitwertS)    ;Soll-Leitwert (Süßwasser)
                JR      Kanal2Regel5
Kanal2Regel4:   LD      B,(IX+SollLeitwertM)    ;Soll-Leitwert (Meerwasser)
Kanal2Regel5:   LD      A,(IX+Uni2Flag2)
                CP      AAh                     ;Aus-Regelung
                JR      Z,Kanal2Regel8
                CP      55h                     ;Ein-Regelung
                JR      Z,Kanal2Regel6
                SCF                             ;Regelung illegal => raus
                RET
Kanal2Regel6:   LD      A,B
                BIT     0,(IX+WarnLED)          ;Kanal 2-LED an?
                JR      Z,Kanal2Regel7          ;Ja =>
                ADD     A,2                     ;Soll-Wert um 2 erhöhen, wenn Regelung bereits an
Kanal2Regel7:   CP      D                       ;mit Sollwert vergleichen
                CCF
                RET
Kanal2Regel8:   LD      A,B
                BIT     0,(IX+WarnLED)          ;Kanal 2-LED an?
                JR      NZ,Kanal2Regel9         ;Nein =>
                ADD     A,2                     ;Soll-Wert um 2 erhöhen, wenn Regelung bereits an
Kanal2Regel9:   CP      D                       ;mit Sollwert vergleichen
                RET

;Temperatur-Kompensation des Leitwertes errechnen (er weicht etwa 2% pro Grad Temperatur-Änderung
;von 25° vom Sollwert ab)
TempKomp:       LD      HL,65
                LD      (Mult24),HL
                LD      C,(IX+IstTemp)          ;Ist-Temp-Wert (= (Temperatur-10.0°)*10)
                CALL    Mult24Bit
                LD      HL,42518
                LD      DE,(Mult24Erg)          ;DE = 42518 - Ist-Temp * 65 (Ist-Temp = 25°: DE = 8000h = 1)
                XOR     A
                SBC     HL,DE                   ;DE: Bit 15 = 1, Bit 14...0 = Nachkommastellen
                LD      (Mult24),HL             ;als neuen Multiplikator merken
                PUSH    HL
                LD      C,(IX+IstLeitw)         ;Ist-Leitwert als Multiplikant
                CALL    TempKomp3               ;= 2 * Kompensations-Wert (= ganzer Anteil im oberen Byte!)
                POP     HL                      ;Leitwert = Mess-Leitwert * (1 - 2% * (Temp - 25°))
                LD      A,(IX+LeitwertKomp)     ;Kompensations-Wert holen
                CP      FFh
                RET     Z                       ;ungültig => raus
                CP      0
                RET     Z
                IN      C,(C)
                BIT     5,C                     ;Süßwasser/Meerwasser-Schalter abfragen
                RET     NZ                      ;Süßwasser => raus
                LD      C,175
                LD      (Mult24),HL
                CALL    Mult24Bit               ;(Multiplikator * 175) * 2
                LD      HL,(Mult24Erg+1)
                ADD     HL,HL
                LD      A,H
                SUB     175
                ADD     A,A                     ;((Erg/256) - 175) * 2
                JP      P,TempKomp1             ;Positiv =>
                CPL                             ;negieren
                LD      B,A
                LD      A,(IX+LeitwertKomp)     ;Temp.Kompensation holen
                SUB     B
                JR      NC,TempKomp2            ;Wert groß genug? Ja =>
                LD      A,0                     ;Unterlauf der Kompensation!
                JR      TempKomp2
TempKomp1:      ADD     A,(IX+LeitwertKomp)     ;jetzige Temp.Kompensation dazu
                JR      NC,TempKomp2            ;Überlauf? Nein =>
                LD      A,FFh
TempKomp2:      LD      (IX+LeitwertKomp),A     ;Temp.Kompensation setzen
                RET

TempKomp3:      CALL    Mult24Bit
                LD      HL,(Mult24Erg+1);16-Bit Kompensation holen
                BIT     7,H                     ;Bit 15 gesetzt (Wert zu groß)
                JR      Z,TempKomp5             ;Nein =>
TempKomp4:      LD      (IX+LeitwertKomp),FFh   ;Temp.Kompensation ungültig!
                RET
TempKomp5:      ADD     HL,HL                   ;Wert * 2
                LD      A,H                     ;oberes Byte nehmen
                CP      FFh                     ;ungültig?
                JR      Z,TempKomp4             ;Ja =>
                BIT     7,L                     ;Bit 7 gesetzt?
                JR      Z,TempKomp6             ;Nein =>
                INC     A                       ;aufrunden (auf 8 Bit)
                CP      FFh                     ;ungültig?
                JR      Z,TempKomp4             ;Ja =>
TempKomp6:      LD      (IX+LeitwertKomp),A     ;Temp.Kompensation setzen
                RET

;24 Bit Multiplikation (Mult24...Mult24+2) * C = (Mult24Erg...Mult24Erg+2)
Mult24Bit:      LD      (C000h),A
                LD      B,4
                LD      HL,Mult24Erg+2
Mult24Bit1:     LD      (HL),0                  ;Ergebniss und Buffer löschen
                DEC     HL
                DJNZ    Mult24Bit1
                LD      B,8                     ;8 Bits
Mult24Bit2:     LD      HL,Mult24               ;Multiplikator
                LD      DE,Mult24Erg            ;Ergebnis
                RRC     C                       ;Temperatur nach rechts ins Carry schieben
                JR      NC,Mult24Bit3

                LD      A,(DE)
                ADD     A,(HL)
                LD      (DE),A
                INC     HL                      ;(DE) = (DE)+(HL)   (16 Bit Addition mit 24 Bit Ergebnis)
                INC     DE
                LD      A,(DE)
                ADC     A,(HL)
                LD      (DE),A
                INC     HL
                INC     DE
                LD      A,(DE)
                ADC     A,(HL)
                LD      (DE),A

                LD      HL,Mult24
Mult24Bit3:     SLA     (HL)
                INC     HL
                RL      (HL)                    ;Summand * 2 (24 Bit)
                INC     HL
                RL      (HL)
                DJNZ    Mult24Bit2              ;8 mal durchlaufen (8 Bit Multiplikant)
                LD      (C000h),A
                RET

;Eingabe aus dem Display holen und Dezimalpunkte auswerten
;D-Register = Anzahl der Punkte
;IY zeigt auf die Nachkommastellen
GetNumInput:    LD      A,0
                LD      DE,InputBuf
                LD      (DE),A                  ;Byte 1 und 2 im Buffer löschen
                INC     DE
                LD      (DE),A
                INC     DE
                LD      HL,Display
                LD      BC,6
                LDIR                            ;Anzeige in den Buffer (Byte 3...8) übertragen
                LD      (DE),A                  ;Byte 9 und 10 im Buffer löschen
                INC     DE
                LD      (DE),A
                LD      IY,InputBuf+7           ;Ptr auf die letzte Ziffer
                LD      C,(IX+DPunkt)           ;Dezimalpunkte holen
                LD      D,0                     ;Anzahl der Punkte = 0
                LD      B,6                     ;maximal 6 Punkte auswerten
GetNumInput1:   LD      E,B                     ;Position des _letzten_ Punktes (= 6.Stelle)
                BIT     0,C                     ;Punkt gesetzt?
                JR      Z,GetNumInput2          ;Ja (low-active!) => Nachkommastellenanfang gefunden
                RR      C                       ;Punkte eine Position nach rechts
                DEC     IY                      ;IY zeigt auf die letzte Vorkommastelle
                DJNZ    GetNumInput1            ;weiter nach Dezimalpunkt suchen
                RET
GetNumInput2:   INC     D                       ;ein Dezimalpunkt mehr...
GetNumInput3:   DJNZ    GetNumInput4            ;alle Punktpositionen durch? Nein =>
                RET
GetNumInput4:   RR      C                       ;Punkte eine Position nach rechts
                BIT     0,C                     ;Punkt gesetzt?
                JR      NZ,GetNumInput3         ;Nein (low-active!) => (nächste Position)
                JR      GetNumInput2            ;Punkt zählen

StrrErE:        DEFB 1Fh,13h,0Eh,1Ch,13h,0Eh,F6h    ;" rE-rE"
StrLrEIN:       DEFB 11h,13h,1Ch,0Eh,01h,1Eh,EFh    ;"Lr-EIN"
StrLrAUS:       DEFB 11h,13h,1Ch,0Ah,14h,05h,EFh    ;"Lr-AUS"
StrLrUNI1:      DEFB 1Fh,14h,1Eh,01h,1Ch,01h,FBh    ;" UNI-1"
StrLrUNI2:      DEFB 1Fh,14h,1Eh,01h,1Ch,02h,FBh    ;" UNI-2"
Str6Space:      DEFB 1Fh,1Fh,1Fh,1Fh,1Fh,1Fh,FFh    ;"      "
                IF NewVersion
StrFEHL16:      DEFB 0Fh,0Eh,10h,11h,01h,06h,FBh    ;"FEHL16"
                ELSE
StrFEHL16:      DEFB 0Fh,0Eh,10h,11h,01h,06h,FFh    ;"FEHL16"
                ENDIF
Str6Minus:      DEFB 1Ch,1Ch,1Ch,1Ch,1Ch,1Ch,FFh    ;"------"
                IF NewVersion
StrFEHL15:      DEFB 0Fh,0Eh,10h,11h,01h,05h,FBh    ;"FEHL15"
                ELSE
StrFEHL15:      DEFB 0Fh,0Eh,10h,11h,01h,05h,FFh    ;"FEHL15"
                ENDIF

;Versionsdatum im LED-Format
                IF NewVersion
VersionNoDisp:  DEFB 8Eh,BFh,FFh,79h,90h,C0h        ;"F- 1.90"
                ELSE
VersionNoDisp:  DEFB 8Eh,BFh,FFh,79h,80h,90h        ;"F- 1.89"
                ENDIF

;Font => LED-Tabelle
;                     0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
FontLEDTable:   DEFB C0h,F9h,A4h,B0h,99h,92h,82h,F8h,80h,90h,88h,83h,C6h,A1h,86h,8Eh
                DEFB 89h,C7h,8Ch,AFh,C1h,8Dh,E3h,ABh,9Ch,A3h,87h,FEh,BFh,F7h,C8h,FFh
;                     H   L   P   r   U   µ   u   n   °   o  /F  /A   -   _   N  ' '

;Rechentabelle Binär => Dezimal
BinDezTable:    DEFW 0001h,0002h,0004h,0008h,0016h,0032h,0064h
                DEFW 0128h,0256h,0512h,1024h,2048h,4096h,8192h
BinDezTableEnd:

;Tastaturtabelle
TastaturTab:
;                     "."      "3"   "6"   "9"   "0"   "2"   "5"   "8"   "*"      "1"   "4"   "7"
                DEFW KeyPunkt,8003h,8006h,8009h,8000h,8002h,8005h,8008h,KeyStern,8001h,8004h,8007h
;                     "Re"     "Lw"        "Tp"          "pH"  "CO2"  "Li"    "K2"       "K1"
                DEFW KeyRedox,KeyLeitwert,KeyTemperatur,KeyPh,KeyCO2,KeyLicht,KeyKanal2,KeyKanal1
;                     "Aus"  "Ein"  "Nacht"  "Tag"  "Setzen"  "Manuell"  "Momentan"  "Zeit"
                DEFW KeyAus,KeyEin,KeyNacht,KeyTag,KeySetzen,KeyManuell,KeyMomentan,KeyZeit

SpezKeyTable:   DEFB FFh,FEh,FDh,FFh,FFh,FFh,FEh    ;"0" "1" "Setzen" - Seriennummer (= 283062)
                DEFB PrintSerial>>8,PrintSerial
                DEFB FBh,FEh,FFh,FFh,FFh,FFh,FEh    ;"0" "6" "Setzen" - Soll-ROM-Prüfsumme (= 5Fd6)
                DEFB PrintROMChksum>>8,PrintROMChksum
                DEFB FDh,FEh,FFh,FFh,FFh,FFh,FEh    ;"0" "3" "Setzen" - Produktionsdatum (= 692)
                DEFB PrintProdDatum>>8,PrintProdDatum
                DEFB FFh,FEh,FBh,FFh,FFh,FFh,FEh    ;"0" "4" "Setzen" - (1FF9h) = 12050
                DEFB PrintUnknown>>8,PrintUnknown
                DEFB FFh,FEh,F7h,FFh,FFh,FFh,FEh    ;"0" "7" "Setzen" - Errechnete ROM-Prüfsumme
                DEFB PrintRealChksum>>8,PrintRealChksum
SpezKeyLicht:   DEFB FFh,FEh,FFh,FFh,FDh,FFh,FEh    ;"0" "Licht" "Setzen" - alle LEDs anschalten
                DEFB CheckAllLED>>8,CheckAllLED
                DEFB FFh,FEh,FFh,FFh,FFh,FDh,FEh    ;"0" "Ein" "Setzen" - alle Relais testen
                DEFB CheckDosen>>8,CheckDosen
                IF  NewVersion
                DEFB FFh,FEh,FFh,FFh,FFh,FFh,FAh    ;"0" "Momentan" "Setzen" - Computer zurücksetzen
                DEFB ResetComputer>>8,ResetComputer
                ENDIF
                DEFB 00h

PrintSerial:    CALL    KeyStern
                LD      HL,SerialNo             ;Seriennummer des Gerätes (6 BCD-Stellen)
                CALL    PrintTime
                LD      (IX+DPunkt),FFh         ;Dezimalpunkte aus
                RET

PrintROMChksum: CALL    KeyStern
                LD      HL,ROMChecksum          ;Prüfsumme über das ROM (binäres Wort)
                CALL    PrintTime
                LD      (IX+DPunkt),FFh         ;Dezimalpunkte aus
                RET

PrintProdDatum: CALL    KeyStern
                LD      HL,ProduktDatum         ;Produktionsdatum (oberes Byte: Monat, unteres Byte: Jahr)
                CALL    PrintTime
                LD      (IX+DPunkt),FFh         ;Dezimalpunkte aus
                RET

PrintUnknown:   CALL    KeyStern
                LD      HL,Unknown              ;???
                CALL    PrintTime
                LD      (IX+DPunkt),FFh         ;Dezimalpunkte aus
                RET

PrintRealChksum:CALL    KeyStern
                LD      HL,CalcChecksum
                CALL    PrintTime
                LD      (IX+DPunkt),FFh         ;Dezimalpunkte aus
                RET

;_ALLE_ LEDs am Bedienteil an
CheckAllLED:    LD      HL,KLED
                LD      B,9
CheckAllLED1:   LD      (HL),0                  ;2000h-2008h löschen => alle LEDs an
                INC     HL
                DJNZ    CheckAllLED1
CheckAllLED2:   LD      HL,SpezKeyLicht
                LD      DE,KeyboardMatrix
                LD      B,7
CheckAllLED3:   LD      A,(DE)
                OR      F0h
                CP      (HL)
                JR      NZ,CheckAllLED4         ;anlassen, solange die Tastenkombination gedrückt wird
                INC     HL
                INC     DE
                DJNZ    CheckAllLED3
                LD      (C000h),A
                JR      CheckAllLED2
CheckAllLED4:   CALL    KeyStern                ;Display löschen
                SET     6,(IX+KLED)             ;Manuell-LED ausschalten
                RET

;_ALLE_ Steckdosen an/auschalten
CheckDosen:     IF NewVersion
                IN      A,(C)                   ;Sperre gesetzt?
                BIT     4,A                     ;Nein! =>
                JR      NZ,CheckDosen0
                JP      KeySetzen1              ;Fehler 99!
                ENDIF
CheckDosen0:    LD      A,(004Eh)               ;??? sollte wohl (IX+Steckdosen) heissen (ist eh unnötig)
                LD      C,A
                LD      A,0
                CALL    SetDoseStatus           ;alle Steckdosen aus
                LD      B,3
                LD      A,1
CheckDosen1:    CALL    SetDoseStatus           ;3 Steckdosen (CO2, Heizung, Licht) nacheinander an
                SLA     A
                DJNZ    CheckDosen1
                SLA     A
                CALL    SetDoseStatus           ;4.Steckdose (Kanal 1) an
                SRL     A
                CALL    SetDoseStatus           ;5.Steckdose (Kanal 2) an
                LD      A,1Fh
                CALL    SetDoseStatus           ;alle Steckdosen aus
                LD      A,C
                LD      (004Eh),A               ;??? sollte wohl (IX+Steckdosen) heissen (ist eh unnötig)
                RET

SetDoseStatus:  LD      (E000h),A               ;Port schreiben
                PUSH    BC
                LD      B,0
SetDoseStatus1: LD      (C000h),A               ;kleine Pause
                HALT
                DJNZ    SetDoseStatus1
                POP     BC
                RET

;Computer zurücksetzen
ResetComputer:  IF NewVersion
                IN      A,(C)                   ;Sperre gesetzt?
                BIT     4,A                     ;Nein! =>
                JR      NZ,ResetComputer1
                JP      KeySetzen1              ;Fehler 99!
ResetComputer1: JP      ResetVars
                ENDIF

;Sprungtabelle für die Ausgabe eines Meßwertes im Akku
DoDispMessTab:  DEFW DispPh,DispTemp,DispLeitw,DispRedox

                IF !NewVersion
; 40h,"PAUL UND ULLI P0PPEN HANNA UND ELLI",42h,"SIE HABEN SPASS AN BUSEN UND PO",42h,42h,"OO LA-LA",41h
MsgFutura:      DEFB 40h,12h,0Ah,14h,11h,1Fh,14h,17h,0Dh,1Fh,14h,11h,11h,01h,1Fh,12h
                DEFB 00h,12h,12h,0Eh,1Eh,1Fh,10h,0Ah,1Eh,1Eh,0Ah,1Fh,14h,17h,0Dh,1Fh
                DEFB 0Eh,11h,11h,01h,42h,05h,01h,0Eh,1Fh,10h,0Ah,0Bh,0Eh,1Eh,1Fh,05h
                DEFB 12h,0Ah,05h,05h,1Fh,0Ah,17h,1Fh,0Bh,14h,05h,0Eh,1Eh,1Fh,14h,17h
                DEFB 0Dh,1Fh,12h,00h,42h,42h,00h,10h,1Fh,11h,0Ah,1Ch,11h,0Ah,41h
; 40h,"HALLO",42h,"SIE HABEN PAUSE",41h
MsgPause:       DEFB 40h,10h,0Ah,11h,11h,00h,42h,05h,01h,0Eh,1Fh,10h,0Ah,0Bh,0Eh,1Eh
                DEFB 1Fh,12h,0Ah,14h,05h,0Eh,41h
; 40h,"HUI-",42h,"DAS S0LL ABER SEHR HEISS SEIN",41h
MsgHeiss:       DEFB 40h,10h,14h,01h,1Ch,42h,0Dh,0Ah,05h,1Fh,05h,00h,11h,11h,1Fh,0Ah
                DEFB 0Bh,0Eh,13h,1Fh,05h,0Eh,10h,13h,1Fh,10h,0Eh,01h,05h,05h,1Fh,05h
                DEFB 0Eh,01h,17h,41h
; 40h,"HUI-",42h,"DAS S0LL ABER SEHR SAUER SEIN",41h
MsgSauer:       DEFB 40h,10h,14h,01h,1Ch,42h,0Dh,0Ah,05h,1Fh,05h,00h,11h,11h,1Fh,0Ah
                DEFB 0Bh,0Eh,13h,1Fh,05h,0Eh,10h,13h,1Fh,05h,0Ah,14h,0Eh,13h,1Fh,05h
                DEFB 0Eh,01h,17h,41h
; 40h,"HUI-",42h,"DA5 S0LL ABER SEHR BASISCH SEIN",41h
MsgBasis:       DEFB 40h,10h,14h,01h,1Ch,42h,0Dh,0Ah,05h,1Fh,05h,00h,11h,11h,1Fh,0Ah
                DEFB 0Bh,0Eh,13h,1Fh,05h,0Eh,10h,13h,1Fh,0Bh,0Ah,05h,01h,05h,0Ch,10h
                DEFB 1Fh,05h,0Eh,01h,17h,41h

MsgMessdaten:   DEFM "************************** Messdatenerfassung **************************"
                DEFB 13,10,10
                DEFM "(c) By FUTURA Aquarien-Systeme, KLEVE, Kalkarer Str.24  Tel. 02821/17574"
                DEFB 13,10,10,3,'$'
MsgEscH4Lf:     DEFB 1Bh,'H',10,10,10,10,'$'
MsgEscHEscJ:    DEFB 1Bh,'H',1Bh,'J','$'
Msg6Space:      DEFM "      "
                DEFB 13,10,10,'$'
MsgZeit:        DEFM "ZEIT   : $"
MsgPhWert:      DEFM "PH-WERT: $"
MsgGrad:        DEFM "GRAD   : $"
MsgMikroS:      DEFM "MIKRO-S: $"
MsgMilliS:      DEFM "MILLI-S: $"
MsgMilliV:      DEFM "MILLI-V: $"
MsgFehl:        DEFM "FEHL.$"
MsgCopyright:   DEFM "Copyrigt (c) 1989 by Ulrich Forke & FUTURA Aquariensysteme"
                DEFB 13,10
                DEFM "4190 Kleve, Deutschland"
                DEFB 13,10
                DEFM "Die Verwendung dieses Programms oder Teilen davon ist nicht gestattet !"
                DEFB 13,10
                ENDIF

                ORG ROMTop
SerialNo:       DEFB 62h,30h,28h        ;Seriennummer (= 283062)
ROMChecksum:    DEFB D6h,5Fh,00h        ;Prüfsumme über das ROM, "5Fd6"
ProduktDatum:   DEFB 92h,06h,00h        ;Produktionsdatum 6.92
Unknown:        DEFB 50h,20h,01h        ;??? (= 12050)
                PRINT   "Ende..."

HyperText

HyperText is a text-only very fast hypertext engine for the Atari ST. All the files can be found on GitHub.

∑-Soft ~~Birkhahnkamp 38~~ ~~2000 Norderstedt 1~~ 05.12.1992

~~Tel: 040–5223955~~ ~~FAX: 040–5263307~~ ~~BBS: 040–5267185 (300–14400 Baud 8N1, MNP 1–5, V32(bis), V42(bis), HST)~~

Sehr geehrte Damen und Herren,

Wir bedanken uns für den Erwerb von HyperText. Sie dürfen HyperText zusammen mit den Druckertreibern beliebig ihren Programmen beilegen. Aus der Anleitung sollten sie nur das nötigste zusammenstellen und ebenfalls ihren Kunden zur Verfügung stellen. Ein Abdruck in ihrer Anleitung zu ihren Programmen ist ausdrücklich erlaubt. Den Help-Compiler dürfen sie jedoch NICHT weitergeben!

Beachten Sie auch, daß Sie für jedes Produkt, welches Sie mit HyperText ergänzen, ZWEI nicht eingeschränkte Originale an Sigma-Soft schicken müssen. Weitere Lizenzgebühren sind NICHT fällig! Wer dies jedoch aus bestimmten Gründen nicht kann bzw. will, weil das Programm z.B. 30000,- DM kostet, dermuß uns für jedes verkaufte Exemplar von diesem Programm 10,- DM zukommen lassen!

Eine wichtige Änderung am Help-Compiler: Ab sofort kann man normale Hilfstexte auch OHNE 1st Word Plus eingeben! Die Seiten müssen dazu lediglich durch eine “~” am Zeilenanfang getrennt werden. Dies verbietet natürlich die Verwendung einer “~” am Zeilenanfang. Wer sie unbedingt darstellen will, muß lediglich 126 am Zeilenanfang eingeben. ist ein sog. ESC-Code. Die drei Ziffern dahinter geben den ASCII-Code an. Damit kann man problemlos sämtliche 255 Zeichen aus dem ASCII-Code darstellen.

Falls ihnen mal ihr Originaltext verloren geht, so ist es möglich aus der HYP-Datei wieder einen ASCII-Text zu erzeugen. Wir haben soetwas schon einmal machen müssen. Jedoch ist dieser “Recompiler” mit der aktuellen Version nicht mehr funktionsfähig, so daß er überarbeitet werden müßte. Das kostet Zeit und Geld. Aber falls ihnen der Originalsource verloren gegangen ist, würden wir versuchen ihnen zu helfen.

So, nun noch einige Bemerkungen zur Version 1.1 von HyperText. In dieser Version sind im wesentlichen folgende Fehler behoben worden:

  • So wurde Fclose() mehrfach aufgerufen! Es konnte somit passieren, daß eine falsche Datei geschlossen wurde! (jaja, das GEMDOS ist sehr fehlertolerant, wenn fremde Prozesse einem die Datei schließen…)
  • Anpassung an die neuere XAcc-Doku 1.1. Beim Start von HyperText als Accessory meldet sich HyperText mit ACC_ID für ein eventuelles Autostart- Programm (ab TOS 1.04).
  • Lesezugriffe auf das SR-Register vom Prozessor entfallen, obwohl durchaus erlaubt. Aber einige unsaubere Programme (z.B. GFA-Basic) verbiegen auf dem TT einen Vektor, der diese Opcodes unmöglich macht. Dies ist kein Fehler von HyperText!

Bei TOS 2.0x kann es nach der Benutzung der Slider zu Abstürzen kommen. Dies ist ein Fehler im TOS 2.0x und NICHT in HyperText! Der Fehler wird durch das Programm ARROWFIX V1.1 behoben. Aus rechtlichen Gründen dürfen wir es nicht beilegen, es ist wohl in diversen Mailboxen und über Atari zu beziehen.

Die maximale Länge einer Seite in HyperText liegt bei 64K. Dies ist genaugenommen eine Beschränkung des Help-Compilers und nicht von HyperText. Wer es unbedingt braucht, soll sich mit uns in Verbindung setzen. Da beim Springen zu Stichwörtern aber nur an den Seitenanfang gesprungen wird, sollte man solche Seiten vermeiden. Zudem sollte die Seite eh möglichst nie wesentlich länger als eine Bildschirmseite sein, der User will Hilfe zu einem Punkt und keine Roman lesen.

Die maximale Anzahl der Stichwörter liegt bei 4096. Wer mehr braucht, kann eine spezielle Version des Help-Compilers erhalten. Jedoch ist zu bedenken, daß der Help-Compiler dann erheblich mehr Speicher benötigt!

Mit freundlichen Grüßen,

Markus Fritze, ∑-Soft

German Manual for HyperText 1.1d

©1990–2 ∑-Soft, Christoph Pagalies und Markus Fritze

Einführung

HyperText ist ein System, das Hilfstexte möglichst komfortabel anzeigt. Es kann sowohl “Stand-Alone-Texte” bearbeiten, als auch zusammen mit Programmen verwendet werden, die damit eine Hilfestellung realisieren.

Die wesentlichen Vorteile eines Hilfstextes auf dem Computer gegenüber dem auf Papier sind,

  • daß der Text sofort zur Hand ist, und man nicht erst nach der Anleitung kramen muß,
  • daß die Hilfestellung kontextsensitiv erfolgt, d. h., daß Hilfe zu genau dem Thema angeboten wird, bei dem Probleme aufgetreten sind. Das Suchen des richtigen Kapitels entfällt,
  • daß der Text sehr schnell nach verschiedenen Methoden durchsucht werden kann,
  • daß man sehr schnell zu anderen Themen springen kann, ohne seine Finger in einem Buch verteilen zu müssen.

Diese Anleitung beschreibt HyperText in der Version 1.0d. Eine englische Version ist auch erhältlich.

HyperText ist ein SHAREWARE-Programm. Wer diese TOS-Dokumentation mag und verwendet, sollte uns 20,- DM zukommen lassen.

Wer selbst Texte für HyperText schreiben will, kann für 50,- DM zzgl. Porto und Verpackung (In Deutschland 5,- DM bei Vorkasse, 15,- DM bei Nachnahme, Ausland nur Vorkasse möglich, dort 10,- DM) ein Programm erwerben, welches 1st Word Plus Texte in HyperText-Texte wandelt. Auf der Diskette befindet sich dann auch der originale 1st Word Plus Text zu dieser TOS-Doku (zum Ansehen, Ändern und Lernen), sowie die englische Version von HyperText. Wer HyperText in eigenen Programmen verwendet, hat keine Lizenzgebühren zu entrichten, lediglich 2 Originale des Programms an ∑-Soft zu schicken. Der Konverter ermöglicht auch das Einbinden eines eigenen Copyright-Dialoges in HyperText. Der Konverter ist KEIN PD- bzw. Shareware-Programm und darf NICHT weitergegeben werden!

∑-Soft
HyperText
xxxxxxx xx
2000 Norderstedt 1
Konto-Nr xxxxxxx, BLZ 20069111 (Norderstedter Bank)
Tel: (040)xxxxxxx
FAX: (040)xxxxxxx
BBS: (040)xxxxxxx 300-14400 Baud, 8N1, MNP, V32bis, V42bis, HST

Über unsere Mailbox kann man auch Updates von HyperText downloaden. Registrierte Konverter-Besitzer erhalten neben Updates für den Konverter auch Unterstützung bei Problemen.

Die Bedienung

Der Anfang ist einfach: Starten Sie HyperText als Programm, oder installieren Sie es als Accessory. Das Programm kann in beiden Modi verwendet werden; nur die Extension ist entsprechend zu ändern.

Nach dem Start öffnet HyperText ein Fenster, in dem die Startseite zu sehen ist. Oben am Rand sehen Sie eine Reihe von Buttons, auch unten können sogenannte Bill-Buttons erscheinen. Diese werden in den folgenden Abschnitten behandelt.

Kurz grundlegendes zum Konzept von HyperText:

Jeder Hilfetext besteht aus einer Reihe von Seiten. Diese ist nicht unbedingt identisch mit einer Bildschirm- oder Druckerseite, sondern es ist nur eine logische Unterteilung. Auf dem Bildschirm sehen Sie jeweils die aktuelle Seite, auf der ein Stichwort erklärt wird. In dieser Erklärung können weitere Stichwörter auftauchen, die auf anderen Seiten beschrieben sind. Diese Wörter erkennen Sie daran, daß sie fett geschrieben sind.

Wenn Sie nähere Erläuterungen zu einem dieser weiteren Stichwörter wünschen, klicken Sie einfach mit der linken Maustaste auf das fett geschriebene Wort — und es wird zu der entsprechenden Seite verzweigt. Das Stichwort, daß auf der aktuellen Seite erklärt wird, ist fett und unterstrichen dargestellt; auf dieses können Sie nicht klicken.

Haben Sie die “tieferliegende” Erklärung nun durchgelesen und möchten zur “darüberliegenden” zurück, so drücken Sie einfach die Undo-Taste oder klicken Sie mit der rechten Maustaste auf eine beliebige Stelle. Wenn Sie dieses mehrfach ausführen, können Sie bis zu 20 Ebenen zurückblättern.

2.1 Der “Atari”-Button

Der Button oben links ruft die Info-Box auf. Sie gibt Ihnen Auskunft darüber, welchen Text Sie gerade lesen und von wem er vertrieben wird. Mit “OK” können Sie diese Box wieder verlassen, die “Info”-Box führt zu der ∑-Soft Info-Box.

2.2 Die Pfeil-Buttons und die Seitenzahl

Rechts daneben sehen Sie zwei Pfeile mit einer Zahl dazwischen. Die Zahl stellt die Nummer der aktuellen Seite dar; mit den Pfeilen können Sie unabhängig von irgendwelchen Verweisen vor- bzw. zurückblättern.

2.3 Einstellungen

Mit diesem Button gelangen Sie zu einer Dialogbox, in der Sie Voreinstellungen für HyperText tätigen können.

Die Prozentangabe legt fest, wieviel von Ihrem Hauptspeicher für HyperText belegt werden soll. Sie werden sich jetzt vielleicht fragen: “Warum sollte ich einem Programm mehr Speicher gönnen, als unbedingt nötig?” Die Antwort ist einfach: Wenn HyperText mehr Speicher bekommt, versucht es, bereits gelesene Seiten im Speicher zu halten. Falls Sie später auf diese Seite zurückblättern, muß diese nicht mehr neu von Festplatte oder Diskette geladen werden. Das spart Zeit, insbesondere bei der Ganztextsuche (wird später erklärt).

Diese Einstellung wird nur beachtet, wenn Sie HyperText als Accessory installiert haben. Als Programm gestartet, tut es so, als ob Sie 100% eingestellt hätten.

Für technisch Eingeweihte: HyperText sieht nach der Größe des größten Speicherblocks, und bezieht darauf die Prozentangabe. Bei einem TT mit Fast- und ST-Ram, führt das dazu, daß man z. B. 90% einstellt und hinterher immer noch den halben Speicher frei hat, weil beispielsweise das Fast-Ram genutzt wurde, das ST-Ram aber frei blieb. HyperText belegt natürlich niemals 0% oder wirklich 99,9%, es wird immer eine Mindestreserve berücksichtigt.

Unter “Diverses” finden Sie Grow/Shrinkboxes; das sind die größer bzw. kleiner werdenden Kästchen beim öffnen und schließen von Dialogboxen. Wer sie hübsch findet, kann sie hier einstellen, wer Geschwindigkeitsfanatiker ist, der stellt sie aus.

Darunter befindet sich das sogenannte 8er-Clipping. Wenn dieses eingestellt ist und Sie das Window von HyperText verschieben, “hüpft” innerhalb einer 8er-Rasters, läßt sich also nur ein bißchen ruckeliger bewegen. Dafür wird aber die Ausgabe aus technischen Gründen schneller, als wenn es an eine beliebige Position verschoben würde. Je nach Computertyp macht sich dieses mehr oder weniger stark bemerkbar. Was man nutzen will, ist wieder Geschmackssache.

Mit dem “Speichern”-Button sichert man die Einstellungen. Achtung wenn Sie mehrere HyperTexte haben: Die Einstellungen werden in der Hilfe-Datei, nicht in HyperText gesichert.

Der “Protokoll”-Button führt zu einer weiteren Dialogbox:

2.4 Die Protokoll-Dialogbox

Hier wird das Kommunikations-Protokoll definiert. Hinweis: Dieser Abschnitt ist nur für Profis gedacht. Ändern Sie in dieser Dialogbox nichts, wenn Sie nicht genau wissen, was das bewirkt!

HyperText unterstützt sowohl das XAcc- als auch das Turbo-C-Help-Protokoll. Da beide teilweise die gleichen Messagenummern für unterschiedliche Zwecke verwenden, kann HyperText leider nicht automatisch entscheiden, welches Protokoll nun zu nutzen ist.

Wenn Sie HyperText zusammen mit einem Programm bekommen haben, wird das Protokoll, das dieses unterstützt, schon voreingestellt sein. Andernfalls, oder wenn Sie ein anderes wünschen, können Sie dieses hier ändern. Nach einem Protokollwechsel sollten Sie möglichst schnell das Accessory neu starten, da es das XAcc-Protokoll durcheinander bringt, wenn ein Programm beendet wird, daß nie gestartet wurde, und da beim Turbo-C-Protokoll schon in der Boot-Phase bestimmte Aktionen nötig sind. Achtung: Vergessen Sie nicht, in der Einstellungsbox zu sichern.

Das Turbo-C-Protokoll wird beispielsweise von dem Texteditor “Tempus” unterstützt, bei dem Sie ein Wort mit CONTROL-# übergeben können. XAcc unterstützt u. a. “That’s Write”; nähere Informationen erhalten Sie bei Konrad Hinsen (übers MausNet erreichbar).

Rechts neben dem Protokoll kann die Suchart eingestellt werden. Normalerweise interpretiert HyperText das Wort, das ihm per Protokoll übergeben wurde, als Stichwort; wenn es das Wort nicht findet, wird die Synonymsuche aktiviert. Sie können hier aber auch einstellen, daß mit dem Wort eine Ganztextsuche durchgeführt werden soll.

2.5 Suchen

Wenn Sie Hilfe zu einem Thema suchen, zu dem Sie momentan kein Stichwort auf dem Bildschirm sehen, können Sie es über diese Suchfunktion aufspüren. Wenn Sie auf den Button klicken, erscheint eine Dialogbox, in der Sie einfach den Suchbegriff eintippen. Mit “OK” wird die Suche gestartet.

Ist der Begriff genauso irgendwo definiert, wie Sie ihn eingegeben haben, so springt HyperText sofort zu dieser Seite. Vielfach ist man sich aber unsicher, wie der Begriff nun geschrieben wird oder vertippt sich. Dann werden alle vorhandenen Stichwörter mit dem eingegebenen verglichen. Die acht ähnlichsten werden Ihnen in einer weiteren Dialogbox präsentiert. Diesen Vorgang nennt man Synonymsuche.

Bei kleinen Abweichungen befindet sich der gesuchte Begriff fast immer unter diesen ersten acht; Sie können den richtigen mit dem davorstehenden Button anwählen, dann wird zu der entsprechenden Seite gesprungen. Wenn Sie doch keinen davon meinen, können Sie mit dem Button “Suchen” eine erneute Suche starten oder stattdessen abbrechen.

Wenn Sie einen Text suchen, der kein Stichwort ist, können Sie den ganzen Text nach dem Wort durchsuchen lassen. Drücken Sie dazu statt “OK” auf “Ganztext”. Diese Suchart dauert natürlich wesentlich länger, als wenn nur die Stichwörter durchsucht werden müssen, geht aber trotzdem schneller, als wenn Sie so etwas per Hand in einem Buch durchexerzieren — auch hier zeigt sich ein Vorteil des HyperText-Konzepts.

Sie können in der Dialogbox noch einstellen, wo bei der Ganztextsuche begonnen werden soll. Dies ist für den Fall gedacht, daß Sie einen Begriff an mehreren Stellen im Hilfetext haben, aber nur eine bestimmte Stelle suchen. Im Normalfall sollten Sie die Ganztextsuche beim ersten mal “ab Textanfang” starten und, wenn Sie weitersuchen wollen, “ab der nächten Seite” fortsetzen.

Für die Profis gibt es noch die Möglichkeit, das Verhalten der Synonymsuche zu beeinflussen (betrifft nur die Stichwort-, aber nicht die Ganztextsuche). Im Normalfall ist dies wohl nicht nötig, die Autoren haben schon recht lange an den Werten herumexperimentiert, so daß möglichst brauchbare Resultate erreicht werden. Wer mit diesen Werten zufrieden ist, kann den Rest dieses Abschnittes ruhig überlesen.

Erst einmal eine Erklärung, wie die Synonymsuche Überhaupt funktioniert. HyperText vergleicht der Reihe nach alle Stichwörter mit dem eingegebenen. Zwischen den beiden wird eine sogenannte Levenshtein-Distanz berechnet und erst einmal gespeichert. Am Ende werden die Distanzen sortiert und die acht Einträge mit der geringsten Distanz ausgegeben.

Was ist nun eine Levenshtein-Distanz? HyperText überführt das eingegebene Wort in das jeweilige Stichwort, indem er folgende Aktionen durchführt:

  • Ändern von Groß/Kleinschrift
  • Einfügen eines Buchstabens
  • Löschen eines Buchstabens
  • Austauschen eines Buchstabens gegen einen anderen

Diese Aktionen werden solange wiederholt, bis die Wörter identisch sind. Für jede dieser Aktionen gibt es “Strafpunkte”, die addiert werden. Die Endsumme aller Strafpunkte ist dann die Levenshtein-Distanz. Sie können in der Suchbox unter “Synonyme” oder in der Vorschlagsbox unter “Distanz ändern” eine Dialogbox erreichen, in der Sie die Strafpunktvergabe ändern können. Auch diese Werte können unter “Einstellungen” gesichert werden.

Unter den Strafpunkten können Sie angeben, was in der Vorschlagsbox angezeigt werden soll. Normalerweise sind dies die gefundenen Stichwörter. Wenn Sie gerade mit Levenshtein-Werten herumexperimentieren, kann es sinnvoll sein, davor die Distanzen mit auszugeben. Mit etwas nachdenken läßt sich dann sehr schön nachvollziehen, wie HyperText die Umformungen durchgeführt hat. Danach können Sie sich auch noch die Nummer der Seite anzeigen lassen, auf der das jeweilige Stichwort erklärt ist.

2.6 Drucken

Mit diesem Button können Sie die aktuelle Seite ausdrucken. Dazu wird (wenn vorhanden) ein 1st-Word-kompatibler Druckertreiber benutzt. Dieser muß HYPER.CFG heißen und im gleichen Verzeichnis wie HyperText liegen. Wenn Sie schon 1st-Word benutzen, kopieren Sie einfach Ihren Druckertreiber. Sonst können Sie einen der mitgelieferten Treiber benutzen. Kopieren Sie den passenden für Ihren Drucker und benennen Sie ihn um. Wenn kein Druckertreiber vorhanden ist, druckt HyperText ohne aus; dann werden verschiedene Textattribute nicht berücksichtigt, sondern nur reiner ASCII-Text ausgedruckt.

Achtung: Bei Atari-Laserdruckern muß ein Druckeremulator wie der Diablo-Treiber oder der FX–80-Emulator geladen werden!

Wenn für Ihren Drucker kein passender Treiber beiliegt, können Sie eine für einen ähnlichen Drucker nehmen und diese modifizieren. Laden Sie dazu eine .HEX-Datei in einen Texteditor. Sie sehen links in (fast) jeder Zeile ein Steuerbyte für jede Steuerzeichensequenz (z. B. “Fett ein” o. ä.; dieses braucht HyperText bzw. 1st-Word), rechts daneben Hexadezimalzahlen, die dem Drucker signalisieren, was zu tun ist (diese werden also zu passender Gelegenheit an den Drucker gesendet). Welche Steuerzeichen welchem Vorgang entsprechen, entnehmen Sie bitte Ihrem Druckerhandbuch oder fragen Sie Ihren Händler. Ändern Sie die entsprechenden Steuerzeichen ab und speichern Sie die .HEX-Datei erneut. Danach können Sie daraus mit dem mitgelieferten Konverterprogramm MAKE_CFG eine .CFG-Datei erzeugen.

HyperText benötigt folgende Steuerzeichensequenzen:

No. Name & Bedeutung
20 Vortext & Wird am Anfang ausgegeben
1F Vorzeile & Wird vor jeder Zeile ausgegeben
1 Nachzeile & Normalerweise Cr/Lf
21 Nachtext & Wird zum Schluß ausgegeben
6 Bold & Fettschrift einschalten
7 No Bold & Fettschrift ausschalten
E Light & Helle Schrift einschalten
F No Light & Helle Schrift ausschalten
A Italics & Kursivschrift einschalten
B No Italics & Kursivschrift ausschalten
1A Underline & Unterstreichung einschalten
1B No Underline & Unterstreichung ausschalten
2A Elite & Outline ein = Elite
28 Pica & Outline aus = Pica

2.7 Die Bill-Buttons

Neben der oberen Buttonleiste kann auch am unteren Rand eine Reihe von Buttons erscheinen. Darin stehen Stichwörter von verwandten Kapiteln oder anderen wichtigen Abschnitten. Klicken Sie einfach auf einen dieser Buttons; Sie springen damit zu der entsprechenden Seite, als ob das Stichwort (fett) im laufenden Text gestanden hätte.

Erstellung von HyperText-Dokumenten

Dieser Abschnitt ist nur für Entwickler von Hilfstexten für HyperText gedacht; im Gegensatz zu dem vorigen Teil sollte er auch nur diesen zugänglich gemacht werden, wenn man HyperText in eigenen Programmen erklärt und obigen Text teilweise oder komplett in die Anleitung zum eigenen Programm übernimmt. Wichtig: Wer HyperText in eigenen Programmen verwendet, muß den Standard- About-Dialog von ∑-Soft unterstützen!

3.1 Das Diskettenformat

Die Hilfstext-Dateien von HyperText haben ein eigenes Format; diese Dateien tragen die Extension .HYP. Die Datei besteht aus drei Teilen:

  1. Einem Header mit Dateikennung und Versionsnummer
  2. Einem Datenteil, in dem alle Seiten u. ä. gespeichert sind; dieser Teil ist codiert
  3. Einem Directory, das auf die einzelnen Datenteile zeigt.

Dieses bedeutet runtimemäßig folgendes:

Der Header und das Directory werden ständig im Speicher gehalten. Zur Speicherplatzabschätzung: Eine Seite belegt momentan 12 Bytes im Directory. Der Header ist nur ein paar Bytes lang.

3.2 Die Speicherverwaltung

HyperText beinhaltet eine eigene Speicherverwaltung, die nicht Pointer auf Speicherblöcke verwaltet, sondern Handles (Pointer auf Pointer). Sie kann deshalb, im Gegensatz zu der des TOS, die Speicherblöcke frei verschieben und auch nachträglich vergrößern. HyperText holt nur beim Start einmal mit Malloc einen Speicherblock (entsprechend des Einstellungsmenüs), den es nie wieder freigibt. Dieser wird von der eigenen Speicherverwaltung zerteilt, um einzelne Seiten möglichst optimal buffern zu können.

Alle Seiten werden bei Bedarf in den Speicher geladen und erst bei Speicherplatzmangel wieder gelöscht. Die Seiten werden gepackt im RAM gehalten und nur die aktuelle wird entpackt. Bei jedem Aufruf einer Seite wird ein interner Zähler, die “Seitenwichtigkeit”, erhöht. Dadurch wird erreicht, daß die Seiten, die am häufigsten benötigt werden, zuletzt gelöscht werden. Sie können für “Knotenpunkte”, also Seiten, über die viel hinweggeblättert wird, diesen Zähler schon mit einem Wert vorbelegen (siehe unten).

Die Stichwörter sind in einer Art Delta-Compression abgelegt und belegen ein eigenes Segment (stehen also nicht im Directory). Dieses wird nur bei der Stichwortsuche oder bei einem Aufruf von HyperText per Protokoll geladen. Ein Stichwort-Segment selbst bei längeren Texten i.d.R. nur wenige KB lang.

Durch dieses Speicherverwaltungskonzept haben wir erreicht, daß HyperText schon auf sehr kleinen Rechnern läuft (bei nicht übergroßen Texten schon ab 512KB), aber auf größeren Rechnern sinnvoll seine Daten im RAM halten kann.

Anmerkung für TT-Besitzer: Da der Speicher mit Malloc geholt wird, können Sie über die Flags entscheiden, auf welches RAM sich der Einstellungsbereich bezieht. Wenn Sie HyperText hauptsächlich als Programm starten (wo immer 100% angenommen wird), haben Sie am meisten Speicher, wenn Sie beispielsweise das Programm ins ST- und den Buffer in Fast-RAM legen.

Wenn in HyperText eine Weile nicht geblättert wird, wird eine Speicher- reorganisation durchgeführt. Deshalb könnte es in ungünstigen Fällen zu einer Verzögerung führen, wenn große Speicherbereiche zu verschieben sind, auf einem 4MB-Rechner haben wir jedoch noch keine störenden “Aussetzer” bemerkt. Da ein Benutzer typischerweise sowieso mehrere Sekunden zum Lesen einer Seite benötigt, dürfte dieses aber keine Probleme bereiten. Sie können die Reorganisation beobachten, wenn Sie HyperText nur einen kleinen Speicher zubilligen und kontinuierlich blättern; alle paar Seiten stockt es kurz (das ist dann die Reorganisation).

Außerdem gibt es noch eine Blätterverzögerung nach der ersten Seite (nur auf schnellen Rechnern, beim alten ST fällt’s nicht auf), damit man eine einzelne Seite bei schnellen Rechner treffen kann.

3.3 Der Konverter

Wie erstellt man nun einen eigenen Hilfetext?

Zuerst einmal braucht man einen reinen ASCII-Text, in dem alle Seiten hintereinanderweg geschrieben sind. Dann lädt man das ganze in 1st-Word+ und versieht es mit Attributen. Sie können wie gewohnt alle Textattribute verwenden, nur von der Benutzung der Fettschrift wird dringend abgeraten, weil dieses nachher nicht von den Stichwörtern auseinanderzuhalten ist.

Dann sind alle Seiten durch feste Seitenumbrüche zu trennen. Die von 1st-Word+ automatisch eingefügten dynamischen werden vom Konverter ignoriert.

Zusätzlich müssen noch besondere Markierungen enthalten sein:

3.3.1 Die Steuerzeichen für den Konverter

Alle Stichwortdefinitionen müssen von Klammeraffen umschlossen werden. Also, wenn Sie ein Stichwort auf einer Seite erklären, dann schreiben Sie ein @ davor und dahinter. Alle Stichwortreferenzen werden vom Konverter automatisch erkannt und markiert. Achten Sie deshalb darauf, daß Sie nicht unsinnig kurze Stichwörter verwenden; z. B. die Sprache “C”, weil dann alle großen C in Ihrem Text markiert werden.

Nach einer Stichwortdefinition können noch mehrere Bill-Button-Einträge stehen, die durch Komma getrennt werden. Die Bill-Button erscheinen dann auf der Seite, wo das betreffende Stichwort erklärt ist. Beispiel: @Bconin,BIOS,XBIOS@ Wenn der angegebene Eintrag nicht existiert, wird kein Bill-Button generiert.

Es gibt noch optionale Befehle:

Befehl Beschreibung
#memo legt die Voreinstellung für die Speicherbelegung fest. Dahinter
folgt eine Zahl von 0 bis 1000, die die Belegung in Promille angibt.
#impo legt die Wichtigkeit einer Seite fest (wie im Abschnitt über
Speicherverwaltung beschrieben). Danach folgt ein Wert von 0 bis 255,
wobei 0 eine unwichtige Seite (default) und 255 eine sehr wichtige ist.
#imde Ändert die Defaultwichtigkeit. Sie können also vor einer Reihe von
“Menüseiten” die Defaulteinstellung auf beispielsweise 50 hochsetzen und
hinterher mit #imde 0 zurückstellen.

3.3.2 Arbeiten mit dem Konverter

Nach Erstellung des Textes startet man den Konverter und wählt im File-Selector das zu konvertierende 1st Word+ Dokument aus. Dieses wird nun in 3 Passes nach Stichworten durchsucht und gepackt. Der Konverter schafft auf einem Mega STE etwa 100 Seiten pro Minute zu übersetzen, auf dem TT ist er entsprechend schneller. Nach dem 2 Pass kann man im File-Selector die Zieldatei anwählen unter der die gepackten abgelegt werden. Die Extension sollte HYP sein. Momentan lädt HyperText nur die Datei HYPER.HYP. Dies kann sich jedoch später noch ändern.

Wenn man ein 1st Word+ Dokument direkt auf den Konverter zieht, dann wird das Dokument ohne weiteren File-Selector übersetzt.

Mit der rechten Maustaste kann man eine Konvertierung jederzeit abbrechen.

Weitere Möglichkeiten des Konverters:

  • Durch Ändern der Datei DEFAULT.RSC ist man in der Lage einen eigenen “About”-Dialog unter dem Atari-Symbol einzubinden. Der Button zum Aufruf der ∑-Soft-Dialogbox darf dabei NICHT entfernt werden!
  • Mit Hilfe der Datei DEFAULT.TXT kann man die Packrate weiter verbessern, in diese Datei schreibt man möglichst viele Wörter (maximal etwa 4KB) ohne Leerzeichen hintereinander weg. Man sollte nur Wörter verwenden, die auf möglichst vielen Seiten vorkommen und die länger als 3 Zeichen sind. Da HyperText natürlich nur seitenweise packen kann, kann man die Packrate dadurch erhöhen, indem man lange Wörter, die auf vielen Seiten vorkommen (Beispiel: “HyperText”, “Atari” o.ä.) durch den Konverter einmal packen läßt und sodaß diese Worte bereits gepackt in die Seiten eingetragen werden. Diese gepackten Wörter kosten natürlich zusätzlich Speicher (i.d.R. weniger als 4K, da die Datei gepackt im RAM gehalten wird), der ständig durch HyperText belegt bleibt. Wir haben in einem Fall z.B. eine HYPER.HYP ohne DEFAULT.TXT von über 800KB auf 620KB reduzieren können. Dies war bedingt dadurch, daß die vielen Seiten (über 1700) wenig Worte enthielten, jedoch sehr lange, die auf fast allen Seiten vorkamen (Die Rede ist vom Atari Softwareführer, der mit HyperText realisiert wurde).

3.4 “Unsauberkeiten” von HyperText

In HyperText werden drei undokumentierte Features des TOS benutzt, da uns noch nicht eingefallen ist, wie man dieses anders machen könnte. Alle Features sind jedoch relativ sauber, soll heißen: sie funktionieren auf allen uns bekannten TOS-Versionen (ST, STE und TT). Wir haben wegen dieser drei Punkte bei Atari angefragt und uns wurde bestätigt, daß diese drei Punkte akzeptabel sind.

  • In der Hauptschleife fragt HyperText mit einem evnt_multi beide Maustasten ab. (256 auf die Flags addiert)
  • Wenn HyperText als Accessory gestartet wird, ändert es act_pd, da der Malloc noch nicht unmittelbar nach dem Start erfolgt (sondern erst nach dem Einlesen des Dateiheaders, in dem ja die Speicherwunschgröße steht), und ein Accessory ja bekanntlich hinterher keinen Speicher mehr holen darf. Auch mit der später geöffneten Datei gibt’s sonst Probleme.

Wenn dies später zu Konflikten bei einem Multitasking-TOS führt, kann man HyperText ja als Programm starten, Accessorys sollten da eh ihre Bedeutung verlieren. (Wenn jemand Abhilfe weiß, dann bitte melden!)

  • Wenn HyperText auf das Turbo-C-Protokoll reagieren soll, ändert es seinen Namen mit einem menu_register(–1) (natürlich nur als Accessory). Dieses ist nötig, weil das aufrufende Programm das Accessory nur an seinem Namen erkennen kann. Ist zwar nicht schön, aber das Protokoll läßt leider nichts anderes zu. Da durch das “neue” Pure-C, daß Turbo-C-Help-Accessory auf Pure-C-Help umgetauft wurde, was bewirkt, daß z.B. Tempus das Pure-C- Accessory nicht mehr findet, könnte es in Zukunft Probleme geben, wenn Programme auf den Markt kommen, die nicht Turbo-C-Help, sondern Pure-C-Help unterstützen. Wir werden dann wohl noch eine zusätzliche Option einbauen: “Pure-C-Protokoll”. Bis dahin wollen wir die User nicht mit noch mehr Optionen belasten.

Wer das Turbo-C-Protokoll ausschaltet und auf XAcc umschaltet, umgeht diese Unsauberheit (falls es doch irgendwann mal zu Problemen führt), hat dann aber nicht die Möglichkeit z.B. von Tempus aus HyperText aufzurufen.

3.5 Programmierung von HyperText

Weil ich immer wieder danach gefragt werde:

HyperText ist komplett in Assembler geschrieben (natürlich mit dem TurboAss). Es ist nur etwa 9000 Zeilen lang und der Programmcode ist etwa 17KB lang. Dazu kommen noch 7KB Daten (Resourcen der Dialoge) und 7KB BSS-Segment. HyperText alloziert beim Laden einen Speicherblock, der als Buffer für die Seiten, das Directory der Datei, etc. dient. Der Speicher wird völlig dynamisch verwaltet.

Der Konverter ist nur etwa 3000 Zeilen lang, auch in Assembler geschrieben und der Programmcode ist nicht einmal 5KB lang. Dazu kommt noch 1KB Data- Segment (Resource vom Dialog). Er benötigt jedoch SEHR VIEL Speicher! Wer nur 512KB hat, kann den Konverter nichtmal starten. Ich habe ihn zwar so aufgebaut, daß er möglichst wenig Daten im RAM hält, sodaß er auch Dokumente von einigen MB übersetzen kann (so denn 1st Word+ diese erzeugen kann :-), aber er benötigt eine statische Struktur für das Directory und die (noch) ungepackten Stichwörter.

Wer noch Ideen für HyperText hat, schicke sie bitte an die Adresse von ∑-Soft.

Markus Fritze, ∑-Soft

Z80 Assembler/Disassembler

The Z80 CPU was widely used in the 80s in many home computer. Even today it is often used as a cheap embedded CPU. This information is also available on GitHub.

Z80 Disassembler

I created this small disassembler for a Z80 cpu at one afternoon. It is a commandline tool. The size of the ROM and entry points have to be coded directly in the sourcecode.

Every ANSI C compiler should compile this program. It is tested with Think C 5.0 on a Macintosh. It only uses come ANSI functions (look into ‘‘main()’’) for loading a file called “EPROM”.

The program has two parts:

  • Analyze the code. The disassembler tries to analyze what part of the binary data is program code and what part is data. It start with all hardware vectors of the Z80 (‘‘RST’’ opcodes, NMI) and parses all jumps via a recursive analyze via ‘‘ParseOpcode()’’. Every opcode is marked in an array (‘‘OpcodesFlags’’). There are some exceptions, the parser can’t recognize:
    • self modifying code. A ROM shouldn’t contain such code.
    • calculated branches with ‘‘JP (IY)’’, ‘‘JP (IX)’’ or ‘‘JP (HL)’’. The parser can’t recognize them, either. On a Macintosh the low-level debugger is called, when such a jump is found. Set the Symbol ‘‘DEBUGGER’’ to 0 to disable this behavior…
    • Jumptables. These are quite often in a ROM. Only solution: disassemble the program and look into the code. If you found a jumptable – like on my Futura aquarium computer – insert some more calls of ‘‘ParseOpcodes()’’.
    • Unused code. Code that is never called by anybody, could not be found. Make sure that the code is not called via a jump table!
  • Disassembly of the code. With the help of the OpcodesFlags table the disassembler now creates the output. This subroutine is quite long. It disassembles one opcode at a specific address in ROM into a buffer. It is coded directly from a list of Z80 opcodes, so the handling of ‘‘IX’’ and ‘‘IY’’ could be optimized quite a lot.

The subroutine ‘‘OpcodeLen()’’ returns the size of one opcode in bytes. It is called while parsing and while disassembling.

The disassembler recognizes no hidden opcodes (the assembler does!). I didn’t had a table for them while writing the disassembler and they were not needed anyway.

If a routine wanted an “address” to the Z80 code, it is in fact an offset to the array of code. No pointers! Longs are not necessary for a Z80, because the standard Z80 only supports 64k.

In ‘‘main()’’ is a switch for disassembly with address and hexdump instead of disassembly with labels. This is useful for findings bugs in the disassembler and creating a list of variables.

This program is freeware. It is not allowed to be used as a base for a commercial product!

Z80 Assembler

I created the assembler for the Z80 a few days later to compile the changes code from the disassembler into an EPROM image and build a new firmware for my aquarium computer. I needed almost two days for the assembler, this means: commandline only… If you want to change the filename of the sourcefile, you have to change main().

This small assembler has some nice gadgets: it is a quite fast tokenizing single-pass assembler with backpatching. It knows all official Z80 opcodes and some undocumented opcodes (mainly with ‘‘IX’’ and ‘‘IY’’). The Z80 syntax is documented in the Zilog documentation.

The assembler allows mathematical expressions in operands: ‘’+‘’, ‘’-‘’, ‘’*‘’, ‘’/‘’, ‘’%‘’ (modulo), ‘’&‘’ (and), ‘’|‘’ (or), ‘’!‘’ (not), ‘’^‘’ (xor), ‘’<<‘’ (shift left) and ‘’>>‘’ (shift right). Brackets are also available. The expression parser is located in [[Z80 Calc.c]]. Number can be postpended by a ‘‘D’’, ‘‘H’’ or ‘‘B’’ for decimal, hexadecimal and binary numbers.

The assembler also knows the most commend pseudo opcodes (look into the sourcefile ‘Z80 Tokenize.c’):

  • ‘’;‘’ This line is a comment.
  • ‘‘IF’’ Start the conditional expression. If false, the following sourcecode will be skipped (until ‘‘ELSE’’ or ‘‘ENDIF’’).
  • ‘‘ENDIF’’ End of the condition expression.
  • ‘‘ELSE’’ Include the following code, when the expression on IF was false.
  • ‘‘END’’ End of the sourcecode. The assembler stops here. Optional.
  • ‘‘ORG’’ Set the PC in the 64k address space. E.g. to generate code for address $2000.
  • ‘‘PRINT’’ Print the following text on the console. Great for testing the assembler.
  • ‘‘EQU’’ or ‘’=‘’ Set a variable.
  • ‘‘DEFB’’ Put a byte at the current address
  • ‘‘DEFW’’ But a word at the current address (little endian!)
  • ‘‘DEFM’’ But several bytes in the memory, starting at the current address. Seperated with a “,” or a string.
  • ‘‘DEFS’’ Set the current address n bytes ahead. Defines space for global variables that have no given value.

The Sourcecode

Midi-Maze II

Midi-Maze was one of the first first-person shooter for up to 16 players, long before Wolfenstein 3D, Doom, etc. Midi-Maze II was the sucessor which adds much more reliably, features and fun to the game.

  • MIDIMAZE.LZH (384kb, the game of the game – shareware version)
  • MAZEPOOL.LZH (480kb, only for registered users)
  • Midi-Maze2.zip (440kb, original disk image of the release version – without serial number!)
  • Midi_Maze_2.image.zip (416kb, Macintosh DiskCopy image of the release version, also without serial number!)

Some links to MidiMaze related websites:

TurboAss and Bugaboo

Features

  • 68000 assembler, editor and debugger
  • Compiles up to 1.3 million lines per minute (on a 8MHz 68000)
  • Syntax check at input!
  • very powerful editor
  • source level debugging
  • does not depend on the operating system

TurboAss Start

Bugaboo Start

TurboAss Settings

TurboAss Source Info

TurboAss Assembly

(The “lines per minute” is about 8x higher than on an 8MHz Atari ST, because I did this screenshot in an emulator inside Virtual PC)

Bugaboo source view

If somebody is still interested, here are the old files… everything is available on GitHub.

Manual for the TurboAss and Bugaboo

This is the short version of the original german manual for the TurboAss and Bugaboo

                          TurboAss
                        ©1989 ∑-Soft
                von Markus Fritze & Sören Hellwig

                        Version 1.7


1 Allgemeines

1.1 Einleitung

Nach  langem  hin  und  her  haben  wir  uns  entschlossen  unser
Assembler-Paket  als  Shareware  auf  den  Markt  zu  werfen. Der
Assembler  wurde  vormals  durch OMIKRON.Software unter dem Namen
OMIKRON.Assembler  verkauft.  Die  Shareware-Version  ist  jedoch
eine  erweiterte  Version  des  OMIKRON.Assemblers.  Sie  enthält
weniger   Fehler,   weitere  Features  und  ist  noch  ein  wenig
schneller.  Zum  Paket  gehört (natürlich) auch ein Debugger, der
Bugaboo  (vormals OMIKRON.Debugger). Somit kann nun jeder User in
den  Genuß  eines  schnellen  und leistungsfähigen Assemblers und
Debuggers  kommen;  zumal  es  zwar viele Programmiersprachen als
Shareware   bzw.   PD   gibt,  aber  noch  keinen  (vernünftigen)
Assembler.

DIE  FIRMA OMIKRON.SOFTWARE HAT NICHTS MIT DIESEM ASSEMBLER-PAKET
ZU TUN.  ∑-SOFT  IST ALLEINIGER INHABER ALLER RECHTE AM ASSEMBLER
UND DEBUGGER!

Dieser  Text  ist  nicht  als eine Anleitung gedacht, sondern nur
als  Kurzeinführung  in  den Assembler. Die vollständig Anleitung
auf  ca.  240  Seiten gibt es bei den Autoren.  Zur Not kann auch
das  ST-Magazin  11/89  und  12/89 herangezogen werden.  Dort hat
die Referenzkarte den Assembler und Debugger zum Thema.

Nachtrag:

Es  fehlen ziemlich viele Features in diesem README, im Vergleich
zur Anleitung, es wird mir einfach zu viel... (Markus)


1.2 Features des Turbo-Ass

- Einfache und durchdachte (!) Bedienung.

- Der  Assembler  übersetzt  durchschnittlich 1 300 000 Zeilen
    pro Minute.

- Syntax-Check   bereits  bei  der  Eingabe.  Teilweise  sogar
    Semantik-Check!   Es  werden  sowohl  Tippfehler,  wie  auch
    doppelt   vergebene  Symbolnamen  bereits  bei  der  Eingabe
    gemeldet.

- Viele  besonders  leistungsfähige  Funktionen  des  Editors,
    z.B.  Suche  von Symbolen, Ersetzen von Symbolen, Einklappen
    von Programmteilen (wie in GFA-Basic 3.0), u.v.a.m.

- Fantastische  Kombination  von  Debugger  und Assembler. Bei
    einem   Absturz   eines   Programms  im  Debugger  kann  der
    Assembler    automatisch    die   Absturzadresse   in   eine
    Zeilennummer umrechnen!!!

- Eigene   Tastatur-   und  Bildschirmtreiber  =>  selbst  bei
    härtesten   Programmabstürzen  funktionieren  Assembler  und
    Debugger noch.

- KEINE Makros!!!

- u.v.a.m.


1.3 Was ist Shareware?

- Jeder   kann/darf/muß/soll  den  Turbo-Ass  kopieren!  Dabei
    müssen aber stets ALLE (!) Dateien mitkopiert werden.

- Wem  den  Assembler  gefällt,  sollte  uns  fairerweise 50DM
    überweisen.  Das hat für alle Parteien Vorteile:

    Für uns:
    - Wir  arbeiten  nicht  ganz umsonst (im Turbo-Ass stecken 3
    "Mannjahre" Arbeit)

    Für den User:
    - Er bekommt eine 240 Seiten starke Anleitung

    - Er   bekommt   die   neuste   Version   mit   persönlicher
    Seriennummer

    - Er bekommt das erste Update kostenlos

    - Er erhält bei gefundenen Fehlern eine fehlerfreie Version

    - Er kann sich bei Problemen an uns wenden

    - Er  bekommt  für  jeden  User,  der bei Registierung seine
    Seriennummer  angibt, 10DM (allerdings maximal 10 mal, wir
    wollen  keine PD-Vertriebe finanzieren). Das Geld ist also
    eine  "Entschädigung" für das Vorführen des Assemblers und
    das Kopieren.

    - Nochmal:  Läßt  sich  jemand  registrieren, so gibt er die
    Seriennummer  des vorher benutzen Assemblers an. Der User,
    dem  diese  Seriennummer  gehört,  bekommt  vom  uns 10DM.
    Alles klar?

Wer diese Angebot annehmen will, wende sich bitte an:
                Markus Fritze
                xxxxxxxxxxxx xx
                2000 Norderstedt 1
                Tel: 040/xxxxxxx
                (ab 18Uhr, auch am Wochenende (NIE VOR 12UHR!!!))
                BLZ: 20069111, Norderstedter Bank
                    Kontonummer: xxxxxxx

Ich  bitte  allerdings  teilweise  um etwas Geduld; ich hoffe die
meiste   Post  am  selben  Tag  zu  erledigen,  wenn  aber  keine
Anleitungen  mehr  da sind oder wenn wir gerade einige Neuerungen
implementieren,  kann  es  schon  mal etwas dauern (Wenn nach 2-3
Wochen  allerdings  nix passiert kann eine Anfrage klären, ob die
Post auch angekommen ist).



1.4 Hardwarevorraussetzungen

Atari  ST  mit  512k,  wobei allerdings erst ab 1Mb Assembler und
Debugger   gemeinsam   im   RAM   gehalten   werden  können,  was
seinerseits  wieder ein sehr wirksames Debugging erst ermöglicht.
Die  hohe  Auflösung  bietet  sich wohl eher an, als die mittlere
Auflösung  (Ist  einfach  augenfreundlicher);  aber der Turbo-Ass
läuft sowohl in Farbe, wie auch in S/W.


1.5 Installation

TURBOASS.PRG,   TURBOASS.CFG,   TURBOASS.DAT,   BUGABOO.PRG   und
CALL.PRG  auf  eine  Disk,  bzw.  in  einen  Ordner kopieren. Das
war's.


1.6 Zu den Autoren

Sören  Hellwig  und  ich (Markus Fritze) sind zur Zeit 21 bzw. 22
Jahre  alt  und  studieren  technische Informatik an der FH-Wedel
bei  Hamburg.  Deswegen bitte wir auch teilweise um etwas Geduld,
wenn  Anfragen nach Updates, etc. kommen; das Studium geht leider
vor.    Zudem   beantworte   ich   jeden   Brief,  der  Rückporto
beinhaltet.  Post  ohne  Rückporto  wird  nur  in  Ausnahmefällen
beantwortet (Zur Registrierung ist KEIN Rückporto nötig!)


1.7 Haftung/Rechte

Alle  Rechte  am  Turbo-Ass liegen bei ∑-soft. Wobei jeder seinen
Assembler  beliebig  kopieren  darf. Auch das Upload in Mailboxen
ist  erlaubt.  Der  Turbo-Ass und das Handbuch wurden mit größter
Sorgfalt   erstellt.   Leider   sind  Fehler  nie  auzuschließen,
deswegen  möchten  wir  sie  darauf hinweisen, daß wir weder eine
Garantie  für  die  Fehlerfreiheit  geben,  noch  die Haftung für
irgendwelche  Folgen,  gleich ob durch Fehler im Handbuch, in der
Software  oder  in der Hardware verursacht, übernehmen können. Es
wird  wohl  jeder verstehen, daß wir keine besondere Begeisterung
verspüren,  wenn  jemand  behauptet,  der Assembler habe im seine
Platte  mit  Sourcetexten  im  Werte  von mehreren Millionen Mark
zerstört,  uns  auf Schadenersatz verklagen will. Also: Benutzung
auf   eigene  Gefahr!  (Dieser  Abschnitt  gilt  wohl  bei  allen
Softwarefirmen.


2 Der Assembler


2.1 Benutzeroberfläche

Die  Benutzeroberfläche ist nicht von GEM o.ä. abhängig, d.h. neu
programmiert,  das  hat  den  Vorteil, daß der Assembler sicherer
gegen  Abstürze  und  auch  schneller  ist.  Zudem konnten einige
nette   Features   implementiert   werden.  Trotzdem  sollte  die
Umstellung  von  GEM  wohl  nicht ins Gewicht fallen. Die kleinen
Buchstaben  in  den  Buttons  bewirken mit ALT zusammen gedrückt,
das Anklicken des Buttons wie mit der Maus.


2.2 Die Maus im Assembler

Man  kann  mit  der  Maus eine ganze Menge Dinge machen, hier sei
jedoch nur die Benutzung im Assembler erklärt:

Im Sourcetext:
Linke Maustaste:
Einfachklick: Cursor setzen
langer Klick: Block markieren (mit Scrollen)
Doppelklick : Sprung zur Definition des angeklickten Symbols

Rechte Maustaste:
Einfachklick: Sourcetext scrollen (nur am Rand klicken!)
Doppelklick : Formel in den Rechner übernehmen

In der Statuszeile:
einfach alles mal mit links bzw. rechts anklicken...


2.3 Der Editor

Jede  eingegebene  Zeile  wird  automatisch  auf ihren Syntax und
teilweise  auch  auf  die  Semantik  überprüft.  Wenn  die  Zeile
fehlerfrei  ist, wird sie sofort formatiert ausgegeben. Bei einem
Eingabefehler   wird   in   der   Statuszeile  die  Fehlermeldung
angezeigt.


2.4 Besonderheiten

Als   Adressierungsart   wird   auch   "absolut   short"   direkt
unterstützt.   Dazu   ist  hinter  der  Adresse  ".w"  anzugeben.
Line-A-Routinen  können  mit  "LINEA  #Nummer" eingegeben werden.
Der   Assembler  wandelt  nicht-Motorola  Eingaben  wenn  möglich
automatisch in das Motorola-Format um.


2.5 Optimierungen

Der  Assembler  kann  einige  Optimierungen selbstständig finden.
Dazu gehören:
Absolut lang    -> relativ
relativ         -> relativ short
Absolut lang    -> PC-relativ
MOVE.L #xx,A0   -> LEA xx,A0 (wenn dannach PC-relativ was bringt)
MOVE.L #xx,-(SP)-> PEA xx,A0 (wenn dannach PC-relativ was bringt)
Ferner  werden  einige  für  C-Compiler typische Sprünge gefunden
(z.B.   2   aufeinanderfolgende   BRA,   wobei   das   2.BRA  nie
angesprungen  werden  kann)  Wenn  der Assembler optimieren kann,
erscheint  nach  dem  Assemblieren ein Button "ANPASSEN", welcher
die  Optimierungen  im  Sourcetext  vornimmt.   Dabei  kann  auch
zurück-"optimiert" werden, wenn dies nötig sein sollte.


2.6 Formeln

Alles was so Standard ist:
    + - * / ! (log.NOT) ~ (NOT bzw. EOR)
    << (SHL) >> (SHR) | (OR) & (AND)
    sowie Vergleiche

Als Zahlenbasis sind möglich:
    Dezimal     : . oder nix
    Hexadezimal : $
    Binär       : %
    ASCII       : " oder '

Als   interne   Variablen   sind  ^^DATE  (GEMDOS-Datum),  ^^TIME
(GEMDOS-Zeit)  *  (akt.PC),  ^^RSCOUNT  (akt.Wert des RS-Zählers)
und ^^SYMTAB (<>0, wenn Symboltabelle erzeugt wird)

Symbole   sind   max.23   Zeichen   lang.   Alle   Zeichen   sind
signifikant.   Erlaubt sind auch Umlaute und alle anderen Zeichen
> 126.


2.7 Tastaturkommandos

Cursortasten,  Backspace,  etc.  wie gewohnt CTRL-Y, CTRL-B, etc.
wie in TEMPUS
CTRL-D verdoppelt die Cursorzeile.
CTRL-M/ALT-M ermöglicht das Verschieben der Zeile.
CTRL-W ändert die Zahlenbasis der Zahl unter dem Cursor
CTRL-U  markiert  alle  Zeichen  mit  Warnungen,  in  welchen ein
Symbol  definiert  ist, welches nirgends benutzt wird. Damit kann
man prima unbenutzt Unterprogramme finden!
ALT-Zehnerblock wie bei MS-DOS
ESC+Buchstabe Abkürzung für einen Befehl
CTRL-ESC   Tastaturmakro   beginnen.   Dann  zu  belegende  Taste
drücken.   Alle   folgenden   Tasten  (nicht  die  Maus!)  werden
aufgezeichnet.  Mit  ALT-ESC  wird  die Definition abgeschlossen.
Die  belegte  Taste  kann  mit  CTRL-ESC,  Taste,  ALT-ESC wieder
freigegeben werden.


2.8 Ein paar Worte zu den

Mit  "Laden..."  kann  auch  ein ASCII-Text eingeladen werden. Er
wird  dabei  automatisch  ins  interne Format gewandelt.  "Symbol
suchen..."  und  "Symbol  ersetzen..."  ermöglichen  das schnelle
Finden  bzw.  schnelle  Ersetzen  von Symbolen. Dabei können z.B.
alle  Symbole gefunden werden, die mit "M" anfangen (Maske:"M*"),
ohne  daß jeder MOVE-Befehl gefunden wird (wie bei ASCII-Editoren
üblich).   "Sprung  zu  Zeile.."  ermöglich  auch das Springen zu
Symboldefinitionen   (Eingabe:z.B."M*").    Die   Dialogboxen  im
"Einstellungen"  Menü sind zum Teil selbsterklärend, der Rest ist
entweder  unwichtig  oder zu kompliziert, als daß er hier erklärt
werden könnte.


2.9 Der Assembler

Wird  mit F1 aufgerufen. Treten bei der Assemblierung Fehler auf,
werden  alle fehlerhaften Zeilen markiert. Nach der Assemblierung
kann  man  dann mit CTRL-J bzw. Shift-CTRL-J von Fehler zu Fehler
springen.    Der    Assembler    speichert    die   Fehler,   die
Cursorposition, die Marker etc.  übrigens mit dem Sourcetext ab!

In  der  Dialogbox nach (!) der Assemblierung, können noch einige
Extras eingestellt werden:

- Soll  eine  Symboltabelle  an  das  Programm  gehängt werden
    (bzw. an den Debugger übergeben werden)?

- Ferner  kann das erzeugte Programm in verschiedenen Formaten
    abgespeichern. Möglich sind:

DEBUGGER  :  Sprung in den Debugger mit Programmübergabe (KEIN
SPEICHERN)
STANDARD : Programm abspeichern (halt ganz normal...)
DATAS    : Data-Zeilen erzeugen (für OMIKRON.Basic)
ABSOLUT   :  Programm  für  eine  best.  Adresse  (z.B. EPROM)
erzeugen
BASIC     :  spezielles  Format für Basic, welches sich selbst
reloziert
OMINLINE : INLINE-Zeile für OMIKRON-Basic erzeugen
PARALLEL : spez.öbertragungsformat zum Amiga
SMALLDRI : DRI-Format erzeugen (allerdings OHNE Importe)
GFAINLINE : GFA-Inline-Format erzeugen.
FSTANDARD   :   Fast-Load-Bit  im  Header  setzen  (ST-Magazin
11/89), sonst wie

        STANDARD



2.10 Pseudo-Opcodes des Assemblers

DC,  DS,  BLK,  DCB,  EVEN,  ALIGN, TEXT, DATA, BSS, END, =, EQU,
GLOBAL, CNOP, ORG, ILLEGAL und REG
wie üblich.

DXSET Länge[,Füllwert] Tabelle definieren
DX 'String' Tabelleneintrag auf Länge mit Füllwert aufgefüllt
RS, RSRESET, RSSET, RSEVEN, RSBSS Relatives Segment
OPT D+ normale Symboltabelle an
OPT X+ erweiterte Symboltabelle an
OPT W+ Warnungen an
OPT P+ PC-relatives Programm muß erzeugt werden
OPT O+ Optimierungen an
Mit '-' kann etwas ausgeschaltet werden
OUTPUT 'Filename' Default-Filename
PATH 'Pfad' Pfad für IBYTES setzen
IBYTES  'DEGAS.PI3',32000,34  Lädt ein Daten-File der Länge 32000
ab Byte 34 der Datei ein (Hier: ein Degas-Bild)
BASE is' viel zu kompliziert...
REPT  Anzahl,  ENDR  wiederholt  Anzahl  mal  den  Sourcetextteil
zwischen
REPT und ENDR
IF, ELSE, ENDC bedingte Assemblierung
FAIL Assemblierung abbrechen


3 Der Debugger


3.1 Vorwort

Es  ist  eigentlich  unmöglich  den Debugger KURZ zu beschreiben,
deswegen  hier  nur  die  wichtigsten  Befehle (mit HELP kann man
alle mal sehen bzw.  in der Anleitung)


3.2 Allgemeines

Der  Debugger  benutzt selber keine I/O-Routinen des TOS, d.h. er
ist  ziemlich  unanfällig  gegen  Abstürze.  Bei Lade-Operationen
u.ä.    wird    aber    dann    natürlich   doch   auf's   GEMDOS
zurückgegriffen.

Er  verwaltet  eine  eigene  Bildschirmseite,  sodaß Programm und
Debugger sich nicht auf einer gemeinsamen Seite tummeln.

Das Debugging kann sowohl mit Tastatur und Maus erfolgen.

Der  Debugger  kann  resident im RAM gehalten werden, indem er in
den  AUTO-Ordner  kopiert  wird  oder  mit  dem  Befehl  RESIDENT
gehalten  wird.   Der  Debugger kann dann mit CALL.PRG aufgerufen
werden.  Auch  der  Assembler kann auf den Debugger zugreifen (Er
kann ihn aber auch nachladen).


3.3 Die Bedienung

Die  2  oberen  Zeilen  entsprechen  den Funktionstasten (mit und
ohne  Shift).  Die  3 Folgezeilen stellen die Registerinhalte und
Flags  dar.   Alle  Teilen in diesen 5 Zeilen können mit der Maus
ausgelöst werden.

Die  restlichen  (max.20  Zeilen)  sind  frei  veränderbar.  Hier
erfolgen die Eingaben, die Ausgaben. Hier tobt das Leben...

Die  Tastaturbelegung  (Cursortasten,  CTRL-M,  CTRL-1, etc.) ist
ähnlich der des Assemblers.

Eingaben   erfolgen   stets   ab  Zeilenanfang  bzw.  hinter  der
Hexzahl.  Leerzeichen werden ignoriert bzw. dienen als Trenner.

Wenn   der  Assembler  den  Debugger  aufgerufen  hat,  kann  man
CTRL-HELP  zurückspringen.  Dabei  wird  der aktuelle PC-Stand in
eine  Zeilennummer  UMGERECHNET.  D.h.  wenn  ein  Programm  beim
Debuggen  abgestürzt  ist  und der PC im Programm steht, kann man
CTRL-HELP zur entsprechenden Stelle im Sourcetext springen.

Mit  SHIFT-SHIFT  kann  ein laufendes Programm angehalten werden.
Der  Ring-Indikator  funktioniert  allerdings  auch. Der Debugger
ist zudem RESETFEST. Als Notbremse ab und zu sinnvoll.


F1  - Führt den nächsten Befehl aus

SF1 -  Bricht  beim  nächsten  Sprungbefehl  ab. Simuliert den
    Tracemode des 68020 Prozessors.

F2  -  Setzt  hinter  den nächsten Befehl einen Breakpoint und
    startet   das   Programm.   Damit   kann  z.B.  schnell  ein
    Unterprogramm   ausgeführt  werden  oder  ein  DBRA  beendet
    werden.

SF2 - nicht weiter wichtig

F3  -  Startet  das  Programm und bricht beim nächsten RTS ab.
    ACHTUNG!  Wenn mit MOVEM Werte auf dem Stack gerettet werden
    => Bomb!

SF3 - wie F3 nur bis zum nächsten RTE

F4  -  Führt  den  nächsten TRAP NICHT aus, sondern springt in
    ihn hinein.

SF4 - Programm ohne Endebedingung starten

F5  - nächsten Befehl ignorieren, d.h. überspringen

SF5 - Insert/Overwrite toggeln

F6  - Sourcecode anzeigen (nur wenn vom Assembler übergeben)

SF6 - die umgerechneten Marker des Assemblers anzeigen.

F7  - Memorydump ab dem PC

SF7 - Breakpoints anzeigen

F8  - Disassemble mit Dump ab PC

SF8 - Info über die Speicherbelegung

F9  - List ab PC (mit Symbolen, wenn vorhanden)

SF9 - Screen löschen, Warmstart

F10 - Umschalten der Bildschirmseite (Toggeln)

SF10- Quit mit Sicherheitsabfrage


3.4 Die Befehle

Alle  Befehle  können  auf ein Minimum abgekürzt werden. An ALLEN
Stellen  sind  als  Parameter  beliebige  Ausdrücke erlaubt (d.h.
Formeln etc.)

?Term   Rechnet den Term aus
Term  darf  (bis  auf Vergleiche) alle Operationen des Assemblers
enthalten.  Zudem  ist  mit  {400}.l  eine indirekte Adressierung
möglich.   ".l"  gibt  dabei  die Zugriffsbreite an.  Alle Zahlen
werden  als  HEXZAHLEN  angesehen.  Dezimalzahlen  sind  mit  "."
einzuleiten.  Bei  einer Symboltabelle kann man mit ".Symbolname"
auf dieses zugreifen.

Definierte Variablen (ein Teil davon)
^D0-^D7 Die Register D0-D7
^A0-^A7 Die Register A0-A7
PC, USP, SSP, SR, CCR, SP Die gleichnamigen Register
^M0-^M9 Die umgerechneten Marker des Assemblers


Alle obigen Variablen sind mit
LET Variable=Term änderbar.
LET läßt sich mit "~" abkürzen.

Noch ein paar Read-Only-Variablen:
TEXT,  DATA,  BSS,  START,  END,  BASEPAGE,  BP,  ACTPD, MEMBASE,
SAVEAREA

Es gibt noch etwa 20-30 weitere nicht so wichtige Variablen.

BREAKPOINT [Nummer=Adresse[,[*|=Wert|Anzahl|?Bedingung]|K]]
Breakpoints anzeigen, löschen, ändern, setzen


B5=PC+.100 Breakpoint  5  (0-15  ist  mgl)  auf Adresse PC+100
    setzen

B K        Alle Breakpoints löschen

B          Alle Breakpoints anzeigen

BK3        Breakpoint 3 löschen


B0=Adr,Anzahl   :  Breakpoint  bricht  beim  Anzahl. Erreichen
    ab.

B0=Adr,*        :  Breakpoint  bleibt  auch nach dem Erreichen
    erhalten.     Normale    Breakpoints    werden   automatisch
    gelöscht.

B0=Adr,=Wert    :  Zähler  auf die Adresse Adr setzen. In ^BC0
    wird hochgezählt wie oft die Adresse erreicht wurde.

B0=Adr,?Term    :  Bedingter  BKPT, Abbruch, wenn Term<>0 z.B.
    B0=Adr,?^D0=4711 => Abbrucht, wenn PC=Adr UND D0=

GO [Adr]
Programm an Adresse Adr, bzw. PC starten

TRACE
Wie F1

SHOWMEMORY Term
16 Bytes ab Term stets anzeigen

UNTRACE [Adr]
solange Tracen, bis Bedingung erfüllt

IF Term
Bedingung für UNTRACE

OBSERVE Trapno,Funkno
Abbruch,  wenn  Trapno(Funktion)  ausgeführt  wird.  z.B. OBSERVE
14,8 => Abbruch bei XBIOS(8) = Floprd()

CLS
2.Bildschirmseite löschen

MOUSEON / MOUSEOFF
VDI-Maus an/aus

|Befehl
Befehl ausführen (zum Testen echt prima)

LEXECUTE "Filename"[,"Commandline"]
Programm ausführbar laden (=> Pexec()) Dann mit GO zu starten.

LOAD "Filename"[,Adresse]
File nicht ausführbar laden

SAVE ["Filename"[,Adresse[,Endadresse]]]
File abspeichern

DIR Pfadmaske
Directory anzeigen

Pbefehl
P vor einem Befehl leitet diesen zum Drucker um

FOPEN Filename, Fbefehl, FCLOSE
Ausgabe eines Befehls in eine Datei

ERASE, KILL Filename
Datei löschen (mit Jokern!)

FREE
Freier Hauptspeicher

FREE Drive
Freier Platz auf einem Laufwerk

MKDIRECTORY Name
Ordner erstellen

RMDIRECTORY Name
Ordner löschen (wenn leer)

NAME oldname,newname
File umnennen

FATTRIBUT Filename,attribut
File-Attribut ändern/setzen

FORMAT DS/SS,Laufwerk
Disk formatieren

TYPE Filename
ASCII-Datei anzeigen (SPACE hält Ausgabe an)

READSEKTOR Track,Sektor,Seite,Adresse,Laufwerk (0 oder 1)
Sektor mit XBIOS(8) einlesen

WRITESEKTOR s.o.
Sektor mit XBIOS(9) schreiben

Alle  in  diesem  Abschnitt  angegebenden  Befehle  erlauben  den
gleichen  Syntaxbei den Parametern, der deswegen an dieser Stelle
erklärt   wird,   und  im  folgenden  nur  noch  mit  [Parameter]
bezeichnet wird. Es gilt also:

[Parameter] = [Von][[,]#[Zeilen]|,Bis|[,][Bytes[]]]

Es  sind  also  alle  Parameter  wahlfrei, d.h. man braucht keine
Parameterangeben.  Der  Debugger  nimmt  dann vorgegebene interne
Werte.

Wenn  der  Ausdruck  "Von"  fehlt,  wird ab der aktuellen Adresse
z.B.  disassembliert.  Die  aktuelle  Adresse  ist  die  Zahl  am
Zeilenanfang,   bzw.  wenn  diese  fehlt,  die  zuletzt  benutzte
Adresse.

Als   Endadresse  gilt  der  Ausdruck  "Bis",  der  jedoch  nicht
angegeben  werden  muß.  Wird  statt "Bis" ein "#" angegeben wird
genau  eine  Zeile  ausgegeben.  Ein dem "#" folgender Term, gilt
als  Zeilenanzahl. Es können somit z.B. genau 8 Zeilen ausgegeben
werden.  Es werden jedoch maximal 99 Zeilen ausgegeben. Fehlt die
Endangabe  gänzlich, werden (normalerweise) 16 Zeilen ausgegeben.
Die  Anzahl  läßt  sich jedoch einstellen, indem man die Variable
"Lines"  entsprechend  ändert.  Die  letzte  Möglichkeit  ist die
Angabe  der Byteanzahl in eckigen Klammern. Sie kann genauso, wie
die  Zeilenanzahl  angegeben  werden.  Die "]" ist optional, d.h.
man kann sie auch weglassen.

Beispiel:
"d text #5"
Disassembliert 5 Zeilen ab Anfang des geladenen Programms.

Beispiel:
"m data[30]"
Ein Memorydump des DATA-Segments (48 Bytes lang).

DISASSEMBLE [Parameter]
Disassemble mit Dump, der Dump kann geändert werden!

LIST [Paramter]
Disassemble  ohne  Dump  mit  Symbolen,  Opcodes  können geändert
werden (RETURN nicht vergessen)

SYMBOLTABLE [Parameter]
evtl. vorhandene Symboltabelle anzeigen

MEMORY[.B|.W|.L][Parameter]
Memorydump mit Ausgabebreite (Änderbar)

ASCII [Parameter]
ASCII-Dump (Änderbar)

FIND [Von,Bis],Terme{,Term}
geladenes   Programm   oder   Speicherbereich   nach  den  Termen
durchsuchen

HUNT  s.o.
wie Find, jedoch nur auf geraden Adressen

ASCFIND [Von,Bis],String
ASCII-Suche  im  Disassemblerlisting  (mit Jokern). Langsam, aber
ab und zu praktisch

CONTINUE
Hunt, Find oder ASCFIND fortsetzen, wenn mit ESC abgebrochen

INFO
mal ausprobieren

SYSINFO
auch mal ausprobieren

MOVE, COPY Von,Bis,Nach
Speicherblock kopieren

FILL Von,Bis,Term{,Term}
Speicherblock füllen

CLR [Von,Bis]
Speicherblock löschen (oder alles)

COMPARE Von,Bis,Adr
Bereich Von,Bis mit Bereich ab Adr vergleichen

EXIT, QUIT, SYSTEM
Debugger verlassen

RESIDENT
Debugger resident halten

SET, LET, ~
siehe oben

RESET ALL
Debugger-Kaltstart

HELP
Gibt  alle  Befehle  unsortiert aus (wie man sieht, ein paar mehr
als hier beschrieben)
</file>

====== The Readme of the last release version =======

<file>
                Sigma-Soft's TurboAss V1.7.14                 13.02.93

Version 1.7.14:
Bugaboo:
- Media-Change  im  Debugger  funktioniert nun auch mit Platten, deren
Sektorgröße  >512  Bytes ist. Beispiel für den Fehler: DIR ergab bei
größeren Partitions Grafikmüll auf dem Bildschirm.
- Der  Bugaboo  KÖNNTE  evtl.  mit  KAOS funktionieren. Der Alert beim
Start entfällt somit. Ich habe jedoch KEIN KAOS!
- Probleme   bei   der   Bildschirmumschaltung  bei  einigen  Shiftern
hoffentlich behoben.

Version 1.7.13:
Bugaboo:
- Der  Bugaboo  setzte  die  Adresse  $484 (Tastaturklick, Bell, etc.)
stets  auf $FF. Dies war natürlich ein Fehler. Dadurch konnten evtl.
später gestartete Programme abstürzen.

Version 1.7.12:
Bugaboo:
- Da  es  beim Umschalten der Bildschirmseiten beim STE offensichtlich
Probleme  gab,  habe  ich noch was daran getan. Wenn SWITCH = 1 ist,
wird  nicht  nur  in der Austastlücke umgeschaltet, es wird auch vor
dem  Auslesen  der  Register  des  Shifters  auf einen VBL gewartet,
damit die Register alle die geschriebenen Werte enthalten.

Version 1.7.11:
Bugaboo:
- Der  Prozessor wird wieder richtig erkannt (hoffentlich :-). Vormals
wurde im TT ein 68020 vermutet. Nich tragisch, aber verblüffend...

Version 1.7.10:
Bugaboo:
- Bugfixes  für  den  68030:  UNTRACE  sollte nun besser funktionieren
(vorher:  Format Error), Privileg Verletzung wird anders abgefangen,
falls  die  Privileg Verletzung von einem MOVE SR,<ea> hervorgerufen
wurde,  so wird nun ein MOVE CCR,<ea> ausgeführt. Normalerweise wird
dies durch das TOS vom TT schon so gemacht, da aber der Bugaboo alle
Exceptions  abfängt,  mußte diese Anpassung mit in den Bugaboo über-
nommen werden.

Version 1.7.9:
Bugaboo:
- Neue  Variable:  SWITCH.  öblicherweise ist diese Variable = 0. Dann
ändert  sich  nichts.  Wenn man sie auf 1 setzt (die Variable ist in
den  Einstellungen  speicherbar),  dann wird die Bildschirmseitenum-
schaltung  im  VBL  vorgenommen.  Nachteil: es flackert beim Tracen.
Vorteil:  es sollte bei einigen STEs nicht mehr zu einem Bildversatz
kommen.  Also:  diese Variable nur auf 1 setzen, wenn der Bildschirm
springt!

Version 1.7.8:
Bugaboo:
- Funktioniert nun auch wieder aus dem AUTO-Ordner heraus.
Sonstige Programme:
- Neuste Version vom LHarc V1.1321, sowie dem PFXPAK, u.s.w

Version 1.7.7:
Bugaboo:
- SysInfo erkennt den Mega STE.
TurboAss
- Sourcen können nun auch von BGM-Partitionen geladen werden, ohne daß
der Font zerstört wird.

Version 1.7.6:
TurboAss:
- Es  wird  eine  Meldung ausgegeben, wenn KAOS installiert ist, da es
Probleme geben kann.
- .DCB, .DCB.B, .DCB.W und .DCB.L werden vom Assembler nun erkannt und
in DCB, etc. konvertiert.
Bugaboo:
- Es  wird  eine  Meldung ausgegeben, wenn KAOS installiert ist, da es
Probleme gibt!
- SYSINFO wurde erweitert. Es wird nun die SFP004 (68881 für 68000er),
sowie ein 68881/2 erkannt. Spectre-GCR-Erkennung an den TT angepaßt.
- Der 68040 wird erkannt.
- Da die Variablen REZ und COLORS entfallen sind, gibt es endlich eine
Art  Ersatz:  Die  Variable USERSCR zeigt auf die Struktur des aktu-
ellen  Bildschirms,  INITSCR  zeigt  auf die Stuktur des Bildschirms
beim  Aufruf  des  Debuggers.  Ich  glaube, daß man nun wieder genug
Unsinn mit den Bildschirmseiten treiben kann...

Hier nun noch die Struktur:
                RSRESET
scr_colors:     RS.W 16         ;die 16 Farben
scr_adr:        RS.L 1          ;die Videoadresse
scr_offset:     RS.B 1          ;Offset to next line (STE)
scr_hscroll:    RS.B 1          ;Horizontal Bit-wise Scroll (STE)
scr_rez:        RS.B 1          ;die Video-Auflösung
scr_sync:       RS.B 1          ;das Sync-Bit des Shifters
scr_moni:       RS.B 1          ;der Monitor ($00:s/w $40:Farbe)
scr_overscan:   RS.B 1          ;OverScan ($00:Ja $FF:Nein)

Version 1.7.5:
TurboAss:
- Nach  dem Speichern wird wieder die Cursorposition vor dem Speichern
angesprungen.
- Um  PARTs  einzuklappen  kann  man  nun  auch  auf  den ENDPART Help
drücken.  Auch  kann  ein PART eingeklappt werden, der in der ersten
Sourcetextzeile anfängt.
- Der TurboAss hat nach Shift-F6 gefragt: "Sourcetext löschen?". Dabei
hat  er  allerdings  "JA"  und "NEIN" vertauscht! Sagte man NEIN, so
wurde  der  Source gelöscht. Ein sehr peinlicher Fehler. Wann der in
den Code hineingekommen ist, ist mir ein Rätsel...
- Die  Macintosh-Trap  Macros  sind erstmal wieder gestrichen. Wer sie
will,  kann  sie erhalten. Aber sie haben 11KB Speicher gekostet und
für die allermeisten User nix gebracht.
- a EQU 8:lsl.l  #a,D1 => Es wurde falscher (zufälliger) Code erzeugt.
Sollte  jetzt  richtigen  Code  erzeugen.  ADDQ,  SUBQ,  sowie  alle
Verschiebebefehle (ASL,LSL,ROL,ROXL, etc.) waren davon betroffen.
Bugaboo:
- Die Variable TRACE wurde wieder eingeführt. Näheres siehe Anleitung.
Die Variable ist irgendwann einmal "verloren" gegangen.
- SYSINFO  erkennt  nun  auch,  wenn eine IMP-MMU im Rechner vorhanden
ist. SYSINFO teilweise korrigiert (Taktanzeige, TT-Info, etc).
- Der Bugaboo läuft noch NICHT auf dem Großbildschirm vom TT!
Sonstige Programme:
- Zusätzlich  zu  MODULE.SRC  gibt  es  nun auch RBMODULE.SRC von Ralf
Biedermann.  Man  kann  damit  neben SMALLDRI (siehe Anleitung) auch
SMALLGST erzeugen, um z.B. Assembler-Module in Fortan eionzubinden.
- TURBOASS.DAT bzw. MODULE.SRC: Bei Modula-Modulen werden nicht mehr
doppelt soviele Words erzeugt, wie das Programm lang ist. Wer kein
TDI-Modula hat ( CODE( Hexcodes ) ), der sollte MODULE.SRC neu
assemblieren, wobei vorher das Flag TDI_MODULA = 0 zu setzen ist.
Dann wird INLINE( Hexcodes ) erzeugt.

Version 1.7.4:
TurboAss:
- Angepaßt an den Bugaboo V1.7.4. Um den Debugger nachladen zu können,
muß  man  unter  "Informationen" und "Reorganisieren" nun mindestens
250000  Bytes eintragen. Davon bleiben dann (wie gehabt) 40000 Bytes
übrig.   Diese  Vergrößerung  war  nötig,  weil  das  Nachladen  von
BUGABOO.SYM einen Absturz bei zu wenig Speicher bewirken konnte.
- Funktioniert  nun auch im TT-FastRam. Malloc() darf sich auch Blöcke
aus  dem  FastRam  holen. Der Assembler wird somit nochmal ein Stück
schneller.  Bei  Problemen (ich habe keinen TT, s.u.) evtl. die Bits
dafür  löschen.  Wenn der Fehler dann nicht mehr auftritt => Fehler-
meldung an mich!
- XBRA-ID "TASS" für alle benutzten Vektoren.
Bugaboo:
- Angepaßt an den TT.
Da  ich  selbst  keinen  TT  habe, kann ich Fehler bzgl. des TT auch
nicht  ohne weiteres erkennen bzw. nachvollziehen. Falls es Probleme
gibt,  bitte  ich  um  eine genaue Fehlerbeschreibung (möglichst mit
Beispiel).
- Neues Exception-Handling.
. Exception-Texte nun englisch
. Anpassung an den TT
. Es werden nun alle unbenutzten Vektoren von 2 bis 63 abgefangen.
. kleinere Unschönheiten korrigiert
- SYSINFO an den TT angepaßt
- Symbole  werden  in  der Symboltabelle nun auf 32 Bit verglichen und
nicht  mehr  mit  nur 24 Bit. Ausnahme: Das HiWord (Bits 16-31) wird
wenn  es  gleich  $FF  ist, auf $FFFF erweitert. Dies ist bei Short-
Adreßierung auf den I/O-Bereich sinnvoll.
- XBRA-ID "BUG1" für alle benutzten Vektoren.
Sonstige Programme:
- CALL.PRG, CALL.SRC : Anpassung an den Bugaboo V1.7.4
Der  Bugaboo V1.7.4 wird durch das alte CALL.PRG nicht mehr erkannt!
- umsortiert  und  neu geordnet. Dies war nötig, da das TOOL-Archiv so
groß  war,  daß  es  entpackt nichtmal mehr auf eine Diskette paßte.
Im TOOLS-Archiv sind nun nur noch 3 Ordner: PACKER, TOSPATCH und neu
dazugekommen ist: MIDIMAZE.2

Version 1.7.3:
TurboAss:
- Symbolsuche im Block stürzt nicht mehr ab, wenn das Symbol außerhalb
des Blockes definiert ist, aber im Block nicht vorkommt.

Version 1.7.2:
TurboAss:
- assembliert nun auch auf dem TT
- funktioniert auch in der Bildschirmauflösung TT-mittel
- kann Symboltabelle wieder an den Debugger übergeben (Fehler war seit
Version 1.7.1 drin)
- Wenn  man  ein  SRC-File  durch  Doppelklick  auf dem Desktop in den
TurboAss  geladen  hat, wurde der Filename stets auch an den Bugaboo
weitergereicht,  wenn  dieser z.B. nach der Assemblierung aufgerufen
wurde. Dies ist nun behoben! (Fehler  war seit ARGV-Commandline-Aus-
wertung durch den TurboAss drin)

Einige Änderungen vom TurboAss V1.26 zur Version V1.7.1:

ACHTUNG!!! Wichtige Änderungen im Debugger (siehe unten) !!!

Noch  ein  paar  in  der  Version  V1.70  nicht vorhandene, aber in der
Anleitung aber schon beschriebene, Features:

-lokale Symbole sind immer noch NICHT implementiert
-PC-relative  Optimierungen über Segmentgrenzen können immer noch NICHT
wieder in absolute Adressierungen zurückgewandelt werden
-Absolut-Long  kann  immer  noch  NICHT  automatisch nach Absolut-Short
optimiert werden
-Absolut-Short  kann  immer  noch  NICHT  automatisch nach Absolut-Long
zurückgewandelt werden


Allgemeines:
-Der  TurboAss und Bugaboo sollten auf ALLEN ST bzw. STE funktionieren.
Lediglich  eine  Anpassung an den TT läßt noch auf sich warten, da ich
noch  keinen  TT  habe.  Der Bugaboo in der Version V1.26 funktioniert
nicht auf Rechnern mit IMP-MMU. Der Fehler ist ebenfalls raus.
-Der  Debugger  unterstützt jetzt OverScan, d.h. er arbeitet zwar immer
noch  auf dem kleinen Bildschirm, kann aber auf den OverScan-Modus um-
schalten. Der TurboAss nutzt nun auch eine erhöhte Auflösung, z.B. von
OverScan, Maxon-MGE (s/w-Modus), Atari-Großbildschirm.
-Ich  habe  eine  neue  Versionsnummern-Zählung  eingeführt, also nicht
verwirren  lassen:  V1.7.0  steht  für Version 1, 7.Erweitung im Funk-
tionsumfang  und 0.Bugfix der 7.Erweiterung. Wen diese Versionsnummer-
ierung  an  Apple erinnert, der hat recht (aber nicht weitersagen...).
Ich kann nun die Versionen etwas genauer auseinander halten.

Erweiterungen des Assemblers:
-ASCII-Laden ist nun etwa 1.5 bis 4 (!) mal schneller.
-Bei ADDQ bzw. SUBQ ist kein # mehr nötig. Der Assembler ergänzt es nun
automatisch (wie bei MOVEQ und TRAP auch).
-Wenn  man  mit SHIFT-F2 die Accessory-Leiste aufgerufen hat, kommt man
mit UNDO oder ESC wieder zurück zum Assembler.
-Mit  dem  Pseudo-Opcode  BREAKPT  'String'  kann  man einen speziellen
Breakpoint   bereits  im  Assembler  einsetzen.  Der  Unterschied  zur
ILLEGAL-Directive  ist der anzugebende String. Dieser String wird beim
Erreichen  des  Breakpoints  vom  Debugger  nämlich  als  Befehlszeile
angesehen,  d.h. der String wird vom Debugger sofort ausgeführt. Somit
kann  man  sich  beim  Erreichen  der Directive z.B. automatisch einen
Speicherdump  o.ä.  ausgeben  lassen.  Der  String  darf sogar mehrere
Befehle  enthalten,  welche  mit  ':'  zu trennen sind (':' entspricht
somit  RETURN,  wenn man selbst tippt). Folgt dem ':' noch ein '-', so
wird  der  Befehl  nicht ausgegeben. Ein abschließendes ':' ist jedoch
nicht nötig.

Beispiel: BREAKPT '"Speicherüberlauf":-M ^A0#4:L PC#4'

Der   Text  'Speicherüberlauf'  wird  bei  Erreichen  des  Breakpoints
ausgegeben.  Dann  werden 4 Zeilen ab A0 als Memorydump ausgegeben. Ab
Abschluß  werden  noch  4  Zeilen  ab  PC  (der automatisch hinter die
Directive gesetzt wird) gelistet.
-Beim  Laden von ASCII-Sourcetexten (mit F8) bzw. beim Zuladen wird nun
die  Länge  der  Datei in kb, sowie die schon gelesenen kb ausgegeben.
Dies  ermöglicht  endlich  ein  Abschätzen  der  Ladezeit  bei  großen
Dateien.
-Symbol  ersetzen  (Ctrl-E)  hat  nun eine weiter SEHR (!!!) praktische
Möglichkeit  ganze Symbolgruppen umzubenennen. Jeder, der schon einmal
ein  reassembliertes Programm dokumentiert hat, kennt das Problem: Man
hat ein Unterprogramm (nennen wir es "draw_shape") mit etwa 30 Labeln.
öblich   ist   wohl  die  Bezeichnung  der  Label  mit  "draw_shape1",
"draw_shape2", etc. statt der vorgegebenen Bezeichnung "Lxxxx" o.ä. Ab
sofort  ist  dies  kein  Problem  mehr:  Das  Unterprogramm  als Block
markieren,  als  zu  suchendendes  Symbol  "~"  eingeben  und  als  zu
ersetzendes Symbol (in unserem Beispiel) "draw_shape". Nun OK drücken.
Das  erste  Symbol  im  Block heißt nun "draw_shape"; die Folgesymbole
sind  aufsteigend  numeriert.  Diese Funktion nennt sich "Umbennen von
Symbolen",   d.h.   wenn   ein   Symbol  zweimal  existiert  (doppelte
Deklaration), wird es, obwohl es sich nicht mehr im Block befindet, an
beiden  Stellen  umbenannt (denn intern es es ja dasselbe Symbol). Wer
dies  Vermeiden will, kann nur zu "Symbol ersetzen" übergehen und dort
im Block ersetzen.
-Wieder  habe  ich mich dazu herabgelassen einige weitere Optimierungen
zu  implementieren.  Diesmal  bringen  die  Optimierungen  zwar keinen
Speicherplatz,  dafür  werden pro Opcode 4 Takte eingespart. Es werden
Folgende Befehle optimiert:
CLR.L Dx      in MOVEQ #0,Dx
LSL.x #1,Dx   in ADD.x Dx,Dx  (ACHTUNG: V-FLAG WIRD VERÄNDERT!)
ASL.x #1,Dx   in ADD.x Dx,Dx
ROXL.x #1,Dx  in ADDX.x Dx,Dx
ADDA.W #xx,Ay in LEA xx(Ay),Ay
Die   Optimierungen   können   mit  CTRL-O  ausgeführt  werden  (siehe
Menüeintrag "Assembler" und dort unter "Optimierungen").
-Der  Assembler  kennt  nun  sämtliche  Toolbox  und  OS-Traps des Mac.
Inklusive  der  evtl.  Parameter.  Da  wohl  die wenigsten einen Apple
Macintosh haben, profitieren wohl lediglich Sören und ich davon...
-Die  HELP-Taste  zum Einklappen von PARTS funktioniert nun anders. Man
erinnert sich: PART in eine Zeile, ENDPART irgendwo dahinter. Wenn man
nun auf dem PART stand und HELP gedrückt hat, dann wurde der Textblock
eingeklappt,  bzw.  mit CTRL-HELP auch alle folgenden Blöcke. Nun wird
HELP folgendermaßen behandelt: HELP sucht nun ab der Cursorzeile einen
PART  (aufwärts!),  wird  ein  PART gefunden, so wird der Block einge-
klappt.  Man erspart sich also das Suchen von der PART-Directive. Beim
Ausklappen  von  einem PART wird die Zeile mit dem Part nun auch auto-
matisch auf dem Screen (vertikal :-) zentriert.
-Mit  CTRL-SHIFT-U werden unbenutzte Symbole entfernt. Dies ist für die
Leute  praktisch,  welche die List-Ausgabe in eine Datei umlenken, mit
einem Texteditor nachbearbeiten und nun in den Assembler laden. CTRL-U
markiert  weiterhin  alle unbenutzen Symbole als Warnungen, welche man
ja mit ALT-W an/ausschalten kann.
-Der TurboAss kennt jetzt auch die ARGV Parameterübergabe.


Erweiterungen des Debuggers:
-Einige neue Tastenkombinationen (ähnlich dem MonST2):
CTRL-Y : F1 - aktuellen Befehl tracen
CTRL-S : F5 - aktuellen Befehl überspringen
CTRL-A : F2 - Breakpoint hinter den aktuellen Befehl + GO (!)
-Wenn  der  Debugger  die  Datei  "BUGABOO.SYM"  findet  (dort  wo auch
"BUGABOO.INF"  gesucht  wird),  wird diese Datei geladen (ach nee) und
der Disassembler wird alle absolut short bzw. long Adressierungen über
diese  Tabelle  laufen  lassen.  Soll heißen, wenn in der Tabelle z.B.
_sysbase  =  $4f2  steht,  wird  der  Disassembler bei allen Befehlen,
welche  auf diese Adresse mit obigen Adressierungsarten zugreifen, die
Adresse  durch den Symbolnamen "_sysbase" ersetzen. Eine "BUGABOO.SYM"
Datei  befindet  sich  (mit Sourcetext) bereits im LIBRARY-Ordner. Die
SYM-Dateien lassen sich mit F1 erzeugen (siehe Anleitung).
-Ich habe vergessen zu erwähnen, daß man ohne den Debugger zu verlassen
nun  einen Blick in den Sourcecode werfen kann, wenn man vom Assembler
aus  in  den  Debugger  gesprungen  ist.  Also:  wenn man irgend- wann
während  des  Debuggens  nicht  mehr  weiß  wo  man sich im Source be-
findet;  einfach  F6  drücken.  Dann kann man die aktuelle Position im
Sourcecode  sehen.  Ändern  kann  man  da natürlich nix. Noch was: Der
Blick  in den Sourcecode kostet KEINEN (!) Speicherplatz, nicht einmal
die  Symboltabelle  ist nötig, da der Assembler die Adressen umrechnen
kann.  Die  Adressen  am  linken  Rand sind übrigens alle gleich, dies
liegt  daran,  daß  Sören  und  ich noch keine Muße gefunden haben den
Sourcecode-Teil  auch  nur  irgendwie  zu  optimieren (Wenn der Source
länger  als  10000  Zeilen  ist, wird's ziemlich langsam). Aber besser
langsam  und unflexibel, als gar nix. (P.S. Welcher Assembler-Debugger
hat schon einen solchen Debugger?)
-Bei  Bedingungen der Befehle: Scc, Bcc und DBcc ändert sich der "Ø" am
Zeilenanfang  in  ein  " ", wenn die Bedingung erfüllt ist! Allerdings
nur am aktuellen PC.

ACHTUNG! NEUHEITEN IM DEBUGGER:
-Sämtliche  Filenamen  und Pfade (z.B. LOAD, DIR, etc.) MöSSEN jetzt in
Anführungszeichen  (") eigeschlossen werden. Dies ist zwar lästig, ist
aber durch eine Umstellung der internen Strukturen nötig geworden.
-Bisher  konnte  man ja dem Debugger in der Commandline einen Filenamen
oder  einen  Befehl übergeben (mit "@" davor). Dies kann man nun auch,
wenn man mit dem CALL.PRG den residenten Debugger aufruft.
-Man  kann  nun soviele Befehle pro Zeile angeben, wie nötig. Dazu sind
die  Befehle durch einen ":" zu trennen (deswegen muß man die Pfade in
Anführungszeichen angeben).
-Auch  in  der  oben erwähnten Commandline kann man nun mehrere Befehle
angeben.  Zu  beachten  ist  auch,  daß  bei der BREAKPT-Directive des
Assemblers  nun  auch Doppelpunkte, anstellen von "|" zur Trennung der
Befehle verwandt werden müssen.
-Das  Scrolling  im  Debugger verhält sich etwas anders als vorher. Das
"ruhige"  Scrolling  ohne  Flackern  der  untersten  Zeile  mußte dran
glauben (an was, kommt später).
-Auf  allgemeinen  Wunsch  hin, funktioniert: DIR ".." auch ohne "\" am
Ende  wieder  (für Unwissende, damit kommt man ein Verzeichnis zurück,
d.h. eine Ordnerebene nach oben).
-Wenn   bis   hierher   irgendwelche  Probleme,  Ungereimtheiten,  etc.
auftreten, bitte sofort bei mir melden!
-Man  kann  nun  mit "#load "Filename.Bug" " eine ASCII-Datei einlesen.
Was  das soll? Nun, die Datei kann beliebig Befehle enthalten. Zeilen,
welche mit einem "%" anfangen werden ignoriert. Mal sehen, was sich in
Zukunft bei den Batch-Dateien ergibt (ich bitte um Vorschläge!).
Achtung: Die  Befehle  DIR,  FOPEN,  FCLOSE  und  FORMAT sind in einer
        Batch-Datei nicht möglich.
-Der  KEY-Befehl  wurde  ersatzlos gestrichen. Da die Tastaturmakros im
Debugger diesen überflüssig machen.
-Neue Variablen im Debugger:
MIDI    :<>0  =>  Scancodes  über  MIDI werden ignoriert, sonst werden
        diese als Tastencodes interpretiert (für PC-Tastaturen)
CACHE   :Inhalt des CACR-Registers bei 68020 oder höher
MEMCHECK:<>0  =>  Speichertest  entfällt.  Ein  Dump  würde dann statt
        "--"-Zeichen einfachen einen Busfehler ergeben.
ALL     :Setzt  alle Register (D0-A6) auf den gleichen Wert. Praktisch
        zum Löschen o.ä. (die Variable kann man natürlich nur setzen)
SYMFLAG :<>0  =>  interne Symboltabelle (BUGABOO.SYM) nicht benutzen
SYMTAB  :Zeiger auf die interne Symboltabelle (0=keine vorhanden)
-Vor  dem  Verlassen  des  Debuggers, wird getestet, ob an Adresse $40C
eine  gültige  Adresse steht. Wenn dem so ist, springt der Bugaboo den
Vektor  an.  Wofür das gut ist? Nun, so kann man noch auf die Schnelle
seine  Workstation  schließen  o.ä.  Man braucht nur ein Unterprogramm
einklinken, daß all solche Dinge tut. Damit kann man dann ein Programm
beliebig abbrechen (und wieder in den Assembler zurück), ohne daß beim
nächsten   Start   das  VDI  abstürzt  (weil  eine  Workstation  nicht
geschlossen wurde).
-Mit  dem  neuen  Befehl  COOKIE  kann  man sich das aktuelle CookieJar
ansehen  (wenn  vorhanden).  Damit  folge auch ich der Modeerscheinung
stets  jeden  Kleinkram in den Debugger einzubauen (zumal man sich das
CookieJar mit "m.l {5a0}.l" ausgeben lassen konnte).
-Ich  habe  noch  einige  Vorschläge von Thomas Waldmann berücksichtigt
(auch wenn in Deinem Brief das Gegenteil steht, Thomas):
.Wenn   man   die   (neue)   Variable  RING  auf  1  setzt,  wird  der
Ring-Indikator-IRQ  nicht  mehr  angeschaltet.  Das werden Modem-User
wohl zu schätzen wissen. Default: RING=0
.Die  Variable SAVEAREA ist jetzt auch unter dem Namen S zu erreichen.
Der lange Name hat mich auch schon immer gestört.
.Beim DIR-Befehl wird jetzt vor Programmen mit der Extension PRG, TOS,
TTP,  APP,  ACC,  PRX  und ACX automatisch LE als Default vorgegeben.
Auch  dies  sollte  wohl sinnvoll sein, da man Programme zum Debuggen
sowieso mit LE laden muß.
.Hat  eine  GEMDOS-, (X)BIOS-Funktion einen öbergabeparameter, welcher
ein  Langwort  ist,  wird das Langwort (wenn >0) als Zeiger aufgefaßt
und  der  String  auf  den  er  zeigt  ausgegeben  (maximal jedoch 32
Zeichen).  Damit kann man sofort sehen, welche Datei z.B. bei Fsfirst
gesucht wird. Leider kann man auch sehen, welchen Speicherblock (bzw.
welchen  Inhalt) man freigibt. Läßt sich leider nicht einfach ändern.
-Und wieder einige Vorschläge von Thomas Waldmann:
.Die nachgeladene Symboltabelle "BUGABOO.SYM" kann man nun auch selbst
nutzen. Man kann z.B. ?{.phystop}.l eintippen, um phystop auszulesen.
Der  Debugger  nutzt  jedoch weiterhin zuerst die Symbole der Tabelle
des  nachgeladenen  Programms.  Wenn  dort  das Symbol nicht gefunden
wurde, wird in "BUGABOO.SYM" gesucht.
-Wenn  man  OverScan  im  Debugger nutzen will, muß man zuerst die neue
Variable  OVERSCAN auf 1 setzen und die Einstellungen sichern. Ab dann
erkennt  der  Debugger  OverScan  beim Umschalten. Diese Erkennung hat
aber  einen Nachteil: Der Bildschirm flackert beim Tracen ("wie Sau").
Dies  liegt  daran,  daß zur Erkennung vom aktivierten OverScan min. 2
VBLs  gebraucht  werden und somit die Umschaltung der Bildschirmseiten
auffällt.  Wenn  man  als OverScan-User die Variable auf 0 setzt, kann
man  den Debugger nur in normalen Modus benutzen. Mit dem neuen Befehl
OVERSCAN  kann  man  den  User-Screen  zwischen normaler Auflösung und
OverScan Auflösung umschalten. Dies wird mit dem entsprechenden XBIOS-
Befehl  aus  dem  OverScan Treiber getan. Somit wird also auch das GEM
umgeschaltet.
Halt,  halt, halt: Das mit dem Flackern gibt's nicht mehr! Es ist doch
zu  lästig. Dafür merkt der Debugger allerdings das Umschalten von den
Auflösungen  nicht mehr. Da sowas aber nur selten getraced werden muß,
kann man (so glaube ich (und auch Karsten Isakovic)) darauf verzichten
-Die  Variable  REZ  (und  andere, die mit dem Bildschirm zu tun haben)
sind erstmal ersatzlos entfallen.


Sonstiges:
-Andreas  Alich  hat uns freundlicherweise ein Programm names RSC2S zur
Verfügung  gestellt.  Dieses Programm ermöglicht es RSC-Dateien direkt
in den Assemblerquelltext einzubinden. Näheres im RSC2S-Ordner.


        Markus Fritze

THINK and WORK

A small game published in the Happy Computer magazine, written by Johann Schilcher for the Atari XL. I copied the levels and wrote the game from the scratch for the Atari ST. It was published with the source code together with the TurboAss and can now be found at GitHub.

  • LEVEL.DAT The file with the levels
  • THNKWORK.PIC Atari ST 640×400 b/w bitmap with the b/w images
  • THNKWORK.PI2 Atari ST 640×200 4 color bitmap with the color images

The image files can be opened with GraphicConverter.

The original source code (also on GitHub)

                OPT F+      ;Fast-Load (siehe 68000er 11/89)
                OPT X+      ;Symboltabelle an (für den Debugger)
                IFEQ ^^SYMTAB
                DEFAULT 9
                OUTPUT 'TNHKWORK'
                ELSE
                DEFAULT 8
                ENDC

max_level       EQU 100     ;max.Anzahl der Level
;************************************************************************
;*                     Think & Work ST-Version                          *
;*       nach einer Idee von Johann Schilcher (Atari XL-Version)        *
;*          Die Level entsprechen den Leveln auf dem Atari XL           *
;* ©1989 by ∑-soft, written by Markus Fritze              13-15.01.1989 *
;*      ----------> Graphix by Harald Weinreich <-------------  1.2.'89 *
;************************************************************************
                BASE A6,varbase
                TEXT
                pea     start(PC)
                move.w  #$26,-(SP)
                trap    #14             ;Superexec(start)
                addq.l  #6,SP
                clr.w   -(SP)
                trap    #1              ;Pterm0()

start:          lea     varbase(PC),A6  ;Globale Offset-Variable
                bsr     init
                clr.w   akt_level(A6)   ;Nach dem Demo auf Level 1 schalten
                move.w  #-1,demo_level(A6) ;Start bei Level "-1" = Titeldemo
                bra     demo            ;Titeldemo laufen lassen
restart_clr:    clr.b   dir(A6)         ;Joystick-Richtungen löschen
restart:        move.w  save_level(A6),D0
                bmi.s   restart1
                move.w  D0,akt_level(A6) ;Level setzen
restart1:       clr.w   big_score(A6)   ;Gesamtscore löschen
                sf      demo_mode(A6)   ;Demo-Mode aus
                clr.w   timer2(A6)      ;Timer für den Demo-Mode löschen
                st      save_level(A6)  ;ungültig machen
                st      out_of_game(A6) ;Spiel nicht aktiv
                tst.w   level_anz(A6)   ;Level vorhanden?
                bne.s   menü_loop       ;ja, alles ok
                lea     all_level_data(A6),A0 ;Hier stehen die Level
                moveq   #15,D0
restart2:       tst.l   (A0)+           ;Level komplett leer?
                dbne    D0,restart2
                beq.s   menü_loop       ;ja! =>
                move.w  #1,level_anz(A6) ;Sonst ist min. ein Level vorhanden
menü_loop:      lea     text2(A6),A0
                bsr     print_text
                move.w  level_anz(A6),D1 ;Levelanzahl
                lea     restart(PC),A5  ;Rücksprungadr
                bsr     get_key         ;Tastatur auswerten, Joystick auch
                cmpi.w  #10*200,timer2(A6) ;Das Demo startet nach 10 Sekunden
                bcc     demo
                tst.b   dir(A6)         ;Feuertaste?
                bmi     play_game       ;dann das Spiel starten
                btst    #0,dir(A6)
                bne     inc_level
                btst    #1,dir(A6)
                bne     dec_level
                move.w  key(A6),D0
                subi.w  #$3B,D0
                bmi.s   menü_loop
                cmp.w   #9,D0           ;keine F-Taste ?
                bhi.s   menü_loop
                add.w   D0,D0
                move.w  menü_jumps(PC,D0.w),D0
                jmp     menü_jumps(PC,D0.w)

                BASE DC.W,menü_jumps
menü_jumps:     DC.W play_game,editor,dec_level,inc_level,joykey
                DC.W autorep,copyright,load_levels,save_levels,exit

;*******************************************************************************
;* Der Copyright-Hinweis                                                       *
;*******************************************************************************
copyright:      clr.w   timer2(A6)
                lea     copyright_txt(A6),A0
                bsr     print_text
copyright1:     bsr     get_key
                cmpi.w  #10*200,timer2(A6) ;Demo startet nach 10 Sekunden
                bcc     demo
                tst.w   key(A6)
                beq.s   copyright1
                jmp     (A5)

;*******************************************************************************
;* Der Leveleditor                                                             *
;*******************************************************************************
editor:         lea     all_level_data(A6),A4 ;Hier stehen die originalen Level
                move.w  akt_level(A6),D0
                lsl.w   #6,D0
                adda.w  D0,A4           ;Adresse des akt.editierten Levels
                movea.l A4,A1
                lea     level_buffer(A6),A0
                moveq   #15,D0
editor_save:    move.l  (A1)+,(A0)+     ;Level retten
                dbra    D0,editor_save
                sf      out_of_game(A6) ;Spiel aktiv (Blöcke setzen)
                moveq   #10,D6          ;aktuelle X-Koordinate
                moveq   #6,D7           ;aktuelle Y-Koordinate
                moveq   #2,D5           ;Wall selected
                clr.b   dir(A6)         ;Joystick-Daten löschen
editor_loop:    bsr     init_level      ;Levelarray aufbauen, Zeit löschen, etc.
                bsr     plot_level      ;Level zeichnen
                movea.l logbase(A6),A1
                move.w  D7,D0           ;Y*32*80
                mulu    #32*80,D0
                adda.w  D0,A1           ;zur logbase
                move.w  D6,D0
                lsl.w   #2,D0           ;X*4
                tst.b   rez(A6)         ;Farbe oder s/w
                bpl.s   editor_pnt1
                lea     farb_grafik+7*128(A6),A0
                add.w   D0,D0           ;X*8
                adda.w  D0,A1
                moveq   #15,D1
editor_pnt0:    move.l  (A0)+,D0        ;Cursor darstellen
                or.l    D0,(A1)+
                move.l  (A0)+,D0        ;Cursor darstellen
                or.l    D0,(A1)
                lea     156(A1),A1
                dbra    D1,editor_pnt0
                bra.s   editor_pnt2
editor_pnt1:    lea     mono_grafik+7*128(A6),A0
                adda.w  D0,A1           ;zur logbase
                moveq   #31,D1
editor_pointer: move.l  (A0)+,D0        ;Cursor darstellen
                eor.l   D0,(A1)
                lea     80(A1),A1
                dbra    D1,editor_pointer
editor_pnt2:    lea     text3(A6),A0
                bsr     print           ;untere Textzeile neu ausgeben
                bsr     switch_screen   ;Seite anzeigen
                bsr     get_key         ;Tastatur auswerten, Joystick auch
                btst    #0,dir(A6)      ;Joystickbewegung umsetzen
                beq.s   editor_jump1
                subq.w  #1,D7
                bpl.s   editor_jump1
                moveq   #0,D7
editor_jump1:   btst    #1,dir(A6)
                beq.s   editor_jump2
                cmp.w   #11,D7
                beq.s   editor_jump2
                addq.w  #1,D7
editor_jump2:   btst    #2,dir(A6)
                beq.s   editor_jump3
                subq.w  #1,D6
                bpl.s   editor_jump3
                moveq   #0,D6
editor_jump3:   btst    #3,dir(A6)
                beq.s   editor_jump4
                cmp.w   #19,D6
                beq.s   editor_jump4
                addq.w  #1,D6
editor_jump4:   tst.b   dir(A6)
                beq.s   editor_jump6
                bsr     verzögerung
editor_jump6:   btst    #7,dir(A6)      ;Feuertaste gedrückt
                bne     edt_draw
                clr.b   button_pressed(A6) ;Flag zurücksetzen
                lea     editor_keys-2(PC),A0
                move.w  key(A6),D0      ;aktuelle Taste holen
editor_jump5:   addq.l  #2,A0
                tst.w   (A0)
                bmi     editor_loop
                cmp.w   (A0)+,D0
                bne.s   editor_jump5
                adda.w  (A0),A0
                jmp     (A0)

                BASE DC.W,*
editor_keys:    DC.W $3B,edt_wall ;F1 - Mauer setzen
                DC.W $3C,edt_target ;F2 - Target setzen
                DC.W $3D,edt_block ;F3 - Block setzen
                DC.W $3E,edt_exit ;F4 - EXIT setzen
                DC.W $47,edt_clr_all ;Clr/Home - Level löschen
                DC.W $61,edt_undo ;UNDO - Level rekonstruieren
                DC.W $44,restart_clr ;F10 - Editor verlassen
                DC.W $39,edt_plot ;Space - Zeichen setzen
                DC.W $31,edt_new ;N - Neuen Level einrichten
                DC.W $53,edt_delete ;Delete - Level löschen
                DC.W -1

edt_delete:     bsr.s   edt_clr_all     ;Level erstmal löschen
                move.w  level_anz(A6),D0
                beq.s   edt_delete2     ;keine Level mehr da!
                subq.w  #1,D0
                move.w  D0,level_anz(A6)
                lea     all_level_data+64*max_level(A6),A0 ;Daten aller Level
                movea.l A4,A2
                lea     64(A4),A1
edt_delete1:    move.l  (A1)+,(A2)+     ;Level überkopieren
                cmpa.l  A0,A1
                bcs.s   edt_delete1
edt_delete2:    bra.s   edt_new1        ;ab in die Hauptschleife

edt_new:        move.w  level_anz(A6),D0
                cmp.w   #max_level-1,D0 ;max.100 Level
                beq.s   edt_new1
                move.w  D0,akt_level(A6)
                addq.w  #1,level_anz(A6)
                lea     all_level_data(A6),A4 ;Hier stehen die originalen Level
                lsl.w   #6,D0
                adda.w  D0,A4           ;Adresse des akt.editierten Levels
edt_clr_all:    movea.l A4,A1
                moveq   #15,D0          ;Level löschen
edt_clr_all1:   clr.l   (A1)+
                dbra    D0,edt_clr_all1
edt_new1:       bra     editor_loop     ;ab in die Hauptschleife

edt_wall:       moveq   #2,D5           ;Wand
                bra.s   edt_new1
edt_target:     moveq   #1,D5           ;Zielfeld
                bra.s   edt_new1
edt_block:      moveq   #3,D5           ;Block
                bra.s   edt_new1
edt_exit:       moveq   #-1,D5          ;Ausgang (nur zu verschieben)
                bra.s   edt_new1

edt_undo:       movea.l A4,A1
                lea     level_buffer(A6),A0
                moveq   #15,D0
edt_undo1:      move.l  (A0)+,(A1)+     ;Level zurückholen
                dbra    D0,edt_undo1
                bra.s   edt_new1

edt_plot:       clr.b   button_pressed(A6)
edt_draw:       tas.b   button_pressed(A6)
                bne.s   edt_draw1       ;war schon gedrückt
                moveq   #-1,D4          ;Löschen?
                bsr.s   edt_get         ;akt.Zeichen holen
                cmp.b   D0,D5
                beq.s   edt_draw1       ;steht dort was? =>
                moveq   #0,D4           ;sonst setzen
edt_draw1:      move.w  D5,D3
                tst.b   D4              ;setzen oder löschen?
                beq.s   edt_draw2       ;setzen
                moveq   #0,D3           ;löschen
edt_draw2:      tst.w   D5              ;Exit? (Ist nur verschiebbar)
                bpl.s   edt_draw3       ;Nein =>
                move.w  D6,D0
                add.b   D0,D0
                move.b  D0,60(A4)
                move.w  D7,D0
                add.b   D0,D0
                or.b    #$80,D0
                move.b  D0,61(A4)
                bra     editor_loop

edt_draw3:      move.w  D7,D0           ;Position im Level errechnen
                lsl.w   #2,D0
                add.w   D7,D0
                move.w  D6,D1
                lsr.w   #2,D1
                add.w   D1,D0           ;D0=5*Y+Int(X/4)
                moveq   #3,D1
                and.w   D6,D1           ;D1=X and %11
                move.b  edt_put_tab2(PC,D1.w),D2 ;Byte holen
                and.b   D2,0(A4,D0.w)   ;entsprechendes Feld löschen
                move.b  edt_put_tab(PC,D1.w),D1
                lsl.b   D1,D3           ;Element in Position schieben
                or.b    D3,0(A4,D0.w)   ;und einsetzen
                bra     editor_loop

edt_put_tab:    DC.B 6,4,2,0 ;Verschiebetabelle
edt_put_tab2:   DC.B $3F,$CF,$F3,$FC ;Löschmaske

edt_get:        move.w  D7,D0           ;Position im Level errechnen
                lsl.w   #2,D0
                add.w   D7,D0
                move.w  D6,D1
                lsr.w   #2,D1
                add.w   D1,D0           ;D0=5*Y+Int(X/4)
                moveq   #3,D1
                and.w   D6,D1           ;D1=X and %11
                move.b  edt_put_tab(PC,D1.w),D1 ;Byte holen
                move.b  0(A4,D0.w),D0   ;eines der 4 Felder isolieren
                lsr.b   D1,D0           ;in die unteren 2 Bit schieben
                and.w   #3,D0
                rts

;*******************************************************************************
;* Die restlichen F-Tasten                                                     *
;*******************************************************************************
load_levels:    move.l  #'load',D0
                bsr     do_dialog       ;Wirklich laden?
                beq     restart_clr     ;NEIN! =>
do_load_levels: move.w  #$2F,-(SP)
                trap    #1              ;Fgetdta()
                addq.l  #2,SP
                movea.l D0,A4           ;DTA-Buffer-Adr merken
                move.w  #7,-(SP)
                pea     fname(A6)
                move.w  #$4E,-(SP)
                trap    #1              ;Fsfirst(path,satt)
                addq.l  #8,SP
                tst.l   D0
                bmi.s   load_levelerr   ;nicht gefunden
                move.l  26(A4),D7       ;Länge der Datei
                moveq   #$3F,D0
                and.w   D7,D0
                bne.s   load_levelerr   ;Dateilänge nicht durch 64 teilbar
                clr.w   -(SP)
                pea     fname(A6)
                move.w  #$3D,-(SP)
                trap    #1              ;Fopen(path,attr)
                addq.l  #8,SP
                move.w  D0,D6           ;fhandle merken
                bmi.s   load_levelerr   ;??? TOS-Fehler
                pea     all_level_data(A6) ;Adresse des Buffers
                move.l  D7,-(SP)        ;Dateilänge
                move.w  D6,-(SP)        ;fhandle
                move.w  #$3F,-(SP)
                trap    #1              ;Fread(hndl,byts,buff)
                lea     12(SP),SP
                move.l  D0,D5
                move.w  D6,-(SP)
                move.w  #$3E,-(SP)
                trap    #1              ;Fclose(hndl)
                addq.l  #4,SP
                cmp.l   D5,D7
                bne.s   load_levelerr   ;Datei nicht komplett gelesen
                lsr.l   #6,D7
                move.w  D7,level_anz(A6) ;Anzahl der geladenen Level setzen
                moveq   #0,D0
                jmp     (A5)
load_levelerr:  lea     all_level_data(A6),A4
                bsr     edt_clr_all     ;1.Level löschen
                clr.w   level_anz(A6)   ;keine Level da!
                moveq   #-1,D0
                jmp     (A5)

save_levels:    move.l  #'save',D0
                bsr     do_dialog       ;Wirklich speichern?
                beq     restart_clr     ;NEIN! =>
                moveq   #0,D7
                move.w  level_anz(A6),D7
                beq.s   save_levelerr   ;keine Levels zum Speichern
                lsl.l   #6,D7           ;mal 64 (Levelgröße) = Dateilänge
                pea     fname(A6)
                move.w  #$41,-(SP)
                trap    #1              ;Fdelete(path)
                addq.l  #6,SP
                clr.w   -(SP)
                pea     fname(A6)
                move.w  #$3C,-(SP)
                trap    #1              ;Fcreate(path,attr)
                addq.l  #8,SP
                move.w  D0,D6           ;fhandle merken
                bmi.s   save_levelerr   ;??? TOS-Fehler
                pea     all_level_data(A6) ;Adresse des Buffers
                move.l  D7,-(SP)        ;Dateilänge
                move.w  D6,-(SP)        ;fhandle
                move.w  #$40,-(SP)
                trap    #1              ;Fwrite(hndl,byts,buff)
                lea     12(SP),SP
                move.l  D0,D5
                move.w  D6,-(SP)
                move.w  #$3E,-(SP)
                trap    #1              ;Fclose(hndl)
                addq.l  #4,SP
                cmp.l   D5,D7
                bne.s   save_levelerr   ;Datei nicht komplett geschrieben
                moveq   #0,D0
                jmp     (A5)
save_levelerr:  moveq   #-1,D0
                jmp     (A5)

inc_level:      addq.w  #1,akt_level(A6)
                cmp.w   akt_level(A6),D1
                bhi.s   inc_level1
                clr.w   akt_level(A6)
inc_level1:     jmp     (A5)

dec_level:      subq.w  #1,akt_level(A6)
                bpl.s   dec_level1
                tst.w   D1
                beq.s   dec_level2
                subq.w  #1,D1
dec_level2:     move.w  D1,akt_level(A6)
dec_level1:     jmp     (A5)

joykey:         lea     joy_text(A6),A0
                lea     joy1_txt(A6),A1 ;Joystick
                cmpi.b  #'K',(A0)
                beq.s   joykey1
                lea     joy2_txt(A6),A1 ;Keyboard
joykey1:        move.b  (A1)+,(A0)+
                bne.s   joykey1
                move.b  #' ',-(A0)
                jmp     (A5)

autorep:        bchg    #1,$0484.w
                bsr     set_autorep_txt
                jmp     (A5)

;*******************************************************************************
;* Ein kleines Demo für den Titel & Level 1                                    *
;*******************************************************************************
demo:           move.w  akt_level(A6),save_level(A6) ;akt.Level merken
                move.w  demo_level(A6),D0
                addq.w  #1,D0
                and.w   #1,D0
                move.w  D0,demo_level(A6)
                subq.w  #1,D0           ;Die Demo-Level zählen ab 0
                move.w  D0,akt_level(A6) ;Die normalen Level zählen ab -1
                st      demo_mode(A6)   ;Demo-Mode an
                st      demo_count2(A6) ;gespeicherte Schritte löschen
                clr.w   demo_count(A6)  ;Demo-Position auf den Anfang setzen
demo1:

;*******************************************************************************
;* Die Hauptroutine: Das Game                                                  *
;*******************************************************************************
play_game:      sf      out_of_game(A6) ;Spiel nun aktiv
                bsr     init_level      ;Levelarray aufbauen, Zeit löschen, etc.
                st      timer_stop(A6)  ;Uhr starten
play_game1:     bsr     plot_level      ;Level zeichnen
                bsr     update_line     ;untere Textzeile neu ausgeben
                bsr     switch_screen   ;Seite anzeigen
                bsr     get_key         ;Tastatur auswerten, Joystick auch
                move.w  key(A6),D0
                tst.b   demo_mode(A6)   ;Demo an?
                beq.s   play_game11     ;Dann geht's hinten weiter ...
                tst.w   D0              ;Taste gedrückt?
                bne     restart_clr     ;dann Ende
play_game11:    cmpi.w  #$44,D0         ;F10 - Abbruch
                beq     restart_clr
                cmpi.w  #$61,D0         ;UNDO - Level neu starten
                beq.s   play_game       ;Level-Neustart
                cmpi.w  #$39,D0         ;Pause
                bne.s   play_game5      ;Nein! =>
                clr.b   timer_stop(A6)  ;Uhr stoppen
                movea.l logbase(A6),A0  ;2. Seite ist am Anfang "logbase"
                move.w  #7999,D0
                moveq   #-1,D1
play_game2:     move.l  D1,(A0)+        ;logbase löschen
                dbra    D0,play_game2
                bsr     switch_screen   ;und schwarze Seite anzeigen
                clr.w   key(A6)
                clr.b   dir(A6)
play_game3:     bsr     get_key
                tst.w   key(A6)         ;weiter, wenn Taste gedrückt
                bne.s   play_game4
                tst.b   dir(A6)         ;oder Joystick bewegt
                beq.s   play_game3
play_game4:     st      timer_stop(A6)  ;Uhr wieder starten
play_game5:     bsr     movement        ;Spieler bewegen
                bsr.s   check_win       ;Alle Steine ok?
                beq.s   play_game1      ;Nein, noch nicht.

                tst.b   demo_mode(A6)
                bne     restart_clr     ;Ende des Demos

                move.w  akt_score(A6),D0 ;erreichtes Levelergebnis holen
                cmp.w   hiscore(A6),D0  ;> der Hiscore?
                bls.s   play_game6      ;NEIN! => kein neuer Hiscore ...
                move.w  D0,hiscore(A6)  ;wird allerdings gleich gelöscht
                lea     all_level_data(A6),A0 ;Hier steht der originale Level
                move.w  akt_level(A6),D1
                muls    #64,D1          ;Da auch "-1" möglich ist!!!
                move.w  D0,62(A0,D1.w)  ;Hiscore auch im Level setzen
play_game6:     add.w   D0,big_score(A6) ;und zum Gesamtscore addieren

                move.w  akt_level(A6),D0
                move.w  level_anz(A6),D1
                beq     restart_clr     ;Ende, wenn nur ein Level vorhanden
                addq.w  #1,D0           ;nächster Level
                move.w  D0,akt_level(A6)
                cmp.w   D1,D0           ;höchster Level erreicht
                bcs     play_game       ;Nächsten Level
                subq.w  #1,akt_level(A6)
                bra     restart_clr     ;und Ende

;*******************************************************************************
;* Sind alle Steine am richtigen Ort? (Z=1, wenn nicht)                        *
;*******************************************************************************
check_win:      movem.l D0-D1/A0,-(SP)
                lea     level_dat(A6),A0 ;Hier steht der Level
                move.w  #22*15-1,D0     ;Feldgröße
                moveq   #0,D1
check_win1:     cmpi.b  #8|6,(A0)       ;Spieler auf Exit?
                bne.s   check_win2
                moveq   #-1,D1          ;Flag dafür setzen
check_win2:     cmpi.b  #3,(A0)+
                dbeq    D0,check_win1   ;Nein, noch nicht
                beq.s   check_win3
                clr.b   timer_stop(A6)  ;Uhr anhalten
                tst.b   D1              ;Spieler auf dem Ausgang? Z=1, wenn nicht
check_win3:     movem.l (SP)+,D0-D1/A0
                rts

;*******************************************************************************
;* Score um einen Punkt erniedrigen                                            *
;*******************************************************************************
score_down:     tst.w   demo_mode(A6)   ;Demo-Mode an?
                bne.s   score_down0     ;dann Score löschen
                subq.w  #1,akt_score(A6) ;sonst ein Punkt abziehen
                bpl.s   score_down1     ;Ende, wenn noch positiv
score_down0:    clr.w   akt_score(A6)   ;sonst Score löschen
score_down1:    rts

;*******************************************************************************
;* Bewegung des Spieler und Verschieben der Objekte                            *
;*******************************************************************************
movement:       move.w  akt_koord_y(A6),D0
                move.w  akt_koord_x(A6),D1
                bsr     get_char

                moveq   #0,D6
                moveq   #0,D7
                btst    #3,dir(A6)      ;Cursor right?
                beq.s   movement1
                moveq   #1,D6
                bsr     move_check
                ble.s   movement4       ;Feld ist gültig
                moveq   #0,D6
                bra.s   movement4

movement1:      lsr.w   #8,D3
                btst    #2,dir(A6)      ;Cursor left?
                beq.s   movement2
                moveq   #-1,D6
                bsr.s   move_check
                ble.s   movement4       ;Feld ist gültig
                moveq   #0,D6
                bra.s   movement4

movement2:      swap    D3
                btst    #1,dir(A6)      ;Cursor down?
                beq.s   movement3
                moveq   #1,D7
                bsr.s   move_check
                ble.s   movement4       ;Feld ist gültig
                moveq   #0,D7
                bra.s   movement4

movement3:      lsr.w   #8,D3
                btst    #0,dir(A6)      ;Cursor up?
                beq.s   movement4
                moveq   #-1,D7
                bsr.s   move_check
                ble.s   movement4       ;Feld ist gültig
                moveq   #0,D7
movement4:      bsr     clear_object    ;Object an (D0;D1) entfernen
                add.w   D7,D0
                add.w   D6,D1
                move.w  D0,akt_koord_y(A6)
                move.w  D1,akt_koord_x(A6)
                moveq   #-1,D2          ;Spieler setzen
                bsr     set_object      ;Object an (D0;D1) setzen
                tst.w   D7
                bne.s   movement5
                tst.w   D6
                beq.s   move_end
movement5:      addq.w  #1,moves(A6)
                bsr     verzögerung
move_end:       btst    #1,$0484.w
                bne.s   move_end2
                btst    #7,dir(A6)      ;Feuertaste?
                bne.s   move_end2       ;dann Autorepeat
                clr.b   dir(A6)         ;Joystick-Daten löschen
move_end2:      rts

move_tab:       DC.B 1,0,0,-1,-1,0
move_check:     ext.w   D3
                move.b  move_tab(PC,D3.w),D5
                bpl.s   move_checke     ;Mauer oder nix => Fertig
                movem.l D0-A6,-(SP)     ;Block bewegen
                add.w   D7,D0           ;Position des Blockes errechnen
                add.w   D6,D1
                bsr     get_char        ;Umgebung des Blocks holen
                moveq   #0,D2
                cmp.b   #1,D6           ;nach rechts?
                beq.s   move_check1
                moveq   #8,D2
                cmp.b   #-1,D6
                beq.s   move_check1
                moveq   #16,D2
                cmp.b   #1,D7
                beq.s   move_check1
                moveq   #24,D2
                cmp.b   #-1,D7
                bne.s   move_checke1    ;Keine gültige Bewegung
move_check1:    lsr.l   D2,D3
                subq.b  #1,D3           ;Hintergrund ist möglich
                beq.s   move_check2
                subq.b  #1,D3           ;Zielfeld ist möglich
                bne.s   move_checke1
move_check2:    bsr.s   clear_object
                add.w   D7,D0           ;Zielposition des Blockes errechnen
                add.w   D6,D1
                moveq   #0,D2
                bsr.s   set_object
                addq.w  #1,pushes(A6)   ;Verschiebungen+1
                bsr     score_down
                bsr     score_down
                bsr     score_down
                bsr     score_down      ;fünf Punkte weniger
                moveq   #0,D0           ;Bewegung ist möglich
                movem.l (SP)+,D0-A6
                rts
move_checke1:   moveq   #1,D0           ;Bewegung nicht möglich
                movem.l (SP)+,D0-A6
move_checke:    rts

;*******************************************************************************
;* Objekt an (D1;D0) löschen                                                   *
;*******************************************************************************
clear_object:   movem.l D0-D1,-(SP)
                mulu    #22,D0          ;Y*22
                add.w   D1,D0           ;+X
                lea     level_dat(A6),A0 ;Hier steht der Level
                lea     0(A0,D0.w),A0   ;Adresse des Feldes
                move.b  (A0),D1         ;Object holen
                cmp.b   #4,D1           ;Diamand an?
                bne.s   clear_object1   ;Weg, wenn nicht
                move.b  #2,(A0)         ;Zielfeld setzen
                bra.s   clear_objecte
clear_object1:  lsr.b   #3,D1
                moveq   #1,D0           ;freies Feld
                tst.b   D1
                beq.s   clear_object2   ;Nix los!
                moveq   #5,D0           ;Exit
                subq.b  #1,D1
                beq.s   clear_object2
                moveq   #2,D0           ;Zielfeld
clear_object2:  move.b  D0,(A0)         ;Hintergrund setzen
clear_objecte:  movem.l (SP)+,D0-D1
                rts

;*******************************************************************************
;* Mauer oder Spieler an (D1;D0) setzen (D2<0: Spieler)                        *
;*******************************************************************************
set_object:     movem.l D0-D1,-(SP)
                mulu    #22,D0          ;Y*22
                add.w   D1,D0           ;+X
                lea     level_dat(A6),A0 ;Hier steht der Level
                lea     0(A0,D0.w),A0   ;Adresse der Position
                move.b  (A0),D1         ;Object holen
                tst.b   D2              ;Block oder Spieler
                bmi.s   set_object1     ;der Spieler =>
                moveq   #3,D0           ;Diamant aus
                subq.b  #1,D1           ;Leeres Feld
                beq.s   set_object2
                moveq   #4,D0           ;Diamant an
                subq.b  #1,D1           ;Zielfeld
                bne.s   set_objecte
                bra.s   set_object2

set_object1:    moveq   #6,D0           ;Spieler auf freiem Feld
                subq.b  #1,D1
                beq.s   set_object2
                moveq   #6|16,D0        ;Spieler auf Zielfeld
                subq.b  #1,D1
                beq.s   set_object2
                moveq   #6|8,D0         ;Spieler auf Exit
                subq.b  #3,D1
                bne.s   set_objecte
set_object2:    move.b  D0,(A0)
set_objecte:    movem.l (SP)+,D0-D1
                rts

;*******************************************************************************
;* Level nach level_dat umkopieren und in ein erweitertes Format wandeln       *
;*******************************************************************************
init_level:     movem.l D0-A6,-(SP)
                clr.b   timer_stop(A6)  ;Uhr anhalten
                clr.w   moves(A6)       ;Bewegungen löschen
                clr.w   pushes(A6)      ;Verschiebungen löschen
                moveq   #'0',D0
                move.b  D0,time_std_txt(A6)
                move.b  D0,time_min_txt(A6) ;Uhrzeit löschen
                move.b  D0,time_min_txt+1(A6)
                move.b  D0,time_sek_txt(A6)
                move.b  D0,time_sek_txt+1(A6)
                lea     all_level_data(A6),A0 ;Hier steht der originale Level
                move.w  akt_level(A6),D0
                muls    #64,D0          ;Da auch "-1" möglich ist!!!
                adda.w  D0,A0
                lea     level_dat+23(A6),A1 ;und hier soll er hin
                lea     init_leveltab(PC),A2
                move.l  #$01020001,(A2)
                tst.b   out_of_game(A6)
                bne.s   init_level0
                move.l  #$01020003,(A2)
init_level0:    moveq   #11,D1          ;12 Zeilen kopieren
init_level1:    moveq   #4,D0           ;5 Byte pro Zeile
init_level2:    move.b  (A0)+,D2
                moveq   #3,D4           ;4 Felder pro Byte
init_level3:    rol.b   #2,D2
                moveq   #3,D3
                and.b   D2,D3
                move.b  0(A2,D3.w),(A1)+
                dbra    D4,init_level3
                dbra    D0,init_level2
                addq.l  #2,A1           ;rechten & linken Rand überspringen
                dbra    D1,init_level1
                moveq   #0,D0
                move.b  (A0)+,D0
                lsr.b   #1,D0           ;X-Koordinate des Starts holen
                addq.w  #1,D0
                move.w  D0,D1
                swap    D0
                move.b  (A0)+,D0
                and.b   #$7F,D0         ;oberstes Bit löschen
                lsr.b   #1,D0           ;Y-Koordinate des Starts holen
                addq.w  #1,D0
                move.l  D0,akt_koord_x(A6) ;aktuelle Spielerkoordinate
                mulu    #22,D0          ;Y*22
                add.w   D1,D0           ;+X
                move.w  (A0),hiscore(A6) ;Highscore setzen
                lea     level_dat(A6),A0 ;Hier steht der originale Level
                move.b  #6|8,0(A0,D0.w) ;Player auf Exit setzen
                clr.w   akt_score(A6)
                move.w  #22*15-1,D0
                moveq   #0,D1
init_level4:    cmpi.b  #2,(A0)+        ;Anzahl der Ziel-Felder ermitteln
                beq.s   init_level5
                cmpi.b  #4,-1(A0)       ;Block auf Zielfeld
                bne.s   init_level6
init_level5:    addi.w  #100,akt_score(A6) ;pro Feld 100 Punkte mehr
init_level6:    dbra    D0,init_level4
                movem.l (SP)+,D0-A6
                rts
init_leveltab:  DC.B 1,2,0,3

;*******************************************************************************
;* Level nach logbase zeichnen                                                 *
;*******************************************************************************
plot_level:     tst.b   rez(A6)         ;Farbmonitor?
                bmi     plotc_level     ;Level in Farbe zeichnen
                movem.l D0-A6,-(SP)
                lea     level_dat(A6),A0 ;Level-Daten
                movea.l logbase(A6),A1  ;Bildschirmadresse
                lea     mono_grafik(A6),A2 ;Grafik-Daten
                moveq   #1,D0           ;Zeile=1
plot_level1:    moveq   #1,D1           ;Spalte=1
plot_level2:    bsr     get_char        ;Zeichen an der Position ermitteln
                and.w   #7,D2           ;Zusatzsbits ausmaskieren
                beq.s   plot_level6     ;Die Wand bekommt eine Extra-Behandlung
                lsl.w   #7,D2
                lea     0(A2,D2.w),A3
                movea.l A1,A4
                moveq   #31,D4          ;32 Pixelzeilen kopieren
plot_level3:    move.l  (A3)+,(A4)      ;Zeichen kopieren
                lea     80(A4),A4
                dbra    D4,plot_level3
plot_level4:    addq.l  #4,A1           ;Nächste Bildschirmposition
                addq.w  #1,D1
                cmp.w   #21,D1          ;Zeile voll?
                bne.s   plot_level2     ;nein, noch nicht
                lea     31*80(A1),A1    ;Nächstes Bildschirmposition errechnen
                addq.w  #1,D0
                cmp.w   #13,D0          ;letzte Zeile bereits ausgegeben?
                bne.s   plot_level1     ;Nein! Oben geht's weiter ^^^
                lea     -80(A1),A1
                moveq   #19,D0
                moveq   #-1,D1
plot_level5:    move.l  D1,(A1)+        ;untere Linie ziehen
                dbra    D0,plot_level5
                movem.l (SP)+,D0-A6
                rts

;Wände erfahren eine Sonderbehandlung, da evtl.Rahmen gezeichnet werden müssen
plot_level6:    movea.l A2,A3
                movea.l A1,A4
                moveq   #30,D4          ;31 Pixelzeilen kopieren
plot_level7:    move.l  (A3)+,D5
                cmp.w   #30,D4          ;1.Zeile?
                bne.s   plot_level71    ;Weiter, wenn nicht
                move.l  #$FF000000,D2
                and.l   D3,D2           ;Oben eine Mauer?
                beq.s   plot_level71    ;Ende, wenn ja
                moveq   #-1,D5          ;sonst oben eine Linie ziehen
                bra.s   plot_level91
plot_level71:   tst.b   D3              ;Ist rechts eine Wand?
                beq.s   plot_level8     ;Nein! =>
                or.b    #1,D5           ;Dann Wand rechts abschließen
plot_level8:    ror.w   #8,D3
                tst.b   D3              ;Ist links auch eine Wand?
                beq.s   plot_level9     ;Nein! =>
                bset    #31,D5          ;Dann Wand links abschließen
plot_level9:    rol.w   #8,D3           ;Wert wieder rekonstruieren
plot_level91:   move.l  D5,(A4)         ;Pixelzeile schreiben
                lea     80(A4),A4       ;Nächste Pixelzeile
                dbra    D4,plot_level7

;Nun kommt die Abhandlung der 31. & letzten Pixelzeile einer Wand:
                move.l  (A3)+,D5        ;letzte Pixelzeile holen
                swap    D3
                tst.b   D3              ;Ist unten auch eine Wand?
                beq.s   plot_level11    ;Ja! =>
                moveq   #-1,D5          ;sonst eine Linie ziehen

;Ab hier werden evtl. zwei einzelne Punkte gesetzt, welche einen sonst
;erkennbaren Schönheitsfehler korrigieren.
;z.B:   Wand1 Wand2
;       Leer  Wand3
;Es wird bei Wand2 ein Pixel in der linken unteren Ecke gesetzt (Eckstück)
;2. Möglichkeit: Wand1 Wand2
;                Wand3 Leer
;Hier wird bei Wand1 ein Pixel in der rechten unteren Ecke gesetzt
plot_level11:   addq.w  #1,D0           ;Zeile+1 (dewegen unten zwei Wandzeilen!)
                bsr     get_char        ;umgebende Zeichen der nächsten Zeile holen
                subq.w  #1,D0           ;Zeile wieder zurück
                tst.b   D3              ;rechts auch eine Wand?
                beq.s   plot_level12    ;dann nichts tun =>
                or.b    #1,D5           ;rechts einen Punkt setzen
plot_level12:   ror.w   #8,D3
                tst.b   D3              ;links auch eine Wand?
                beq.s   plot_level13    ;dann nichts tun =>
                bset    #31,D5          ;links einen Punkt setzen
plot_level13:   move.l  D5,(A4)         ;letzte Pixelzeile schreiben
                bra     plot_level4     ;Nächstes Zeichen ausgeben

;*******************************************************************************
;* Level in Farbe nach logbase zeichnen                                        *
;*******************************************************************************
plotc_level:    tst.b   demo_mode(A6)   ;Im Demo keine Verzögerung
                bne.s   plotc_level0
                bsr.s   plotc_level0    ;3 mal zeichnen => etwa die monochrom
                bsr     plotc_level0    ;Geschwindigkeit
plotc_level0:   movem.l D0-A6,-(SP)
                lea     level_dat(A6),A0 ;Level-Daten
                movea.l logbase(A6),A1  ;Bildschirmadresse
                lea     farb_grafik(A6),A2 ;Grafik-Daten
                moveq   #1,D0           ;Zeile=1
plotc_level1:   moveq   #1,D1           ;Spalte=1
plotc_level2:   bsr.s   get_char        ;Zeichen an der Position ermitteln
                and.w   #7,D2           ;Zusatzsbits ausmaskieren
                lsl.w   #7,D2
                lea     0(A2,D2.w),A3   ;Adresse der Grafiken
                movea.l A1,A4
                moveq   #15,D4          ;16 Pixelzeilen kopieren
plotc_level3:   move.l  (A3)+,(A4)+     ;Zeichen kopieren
                move.l  (A3)+,(A4)
                lea     156(A4),A4
                dbra    D4,plotc_level3
                addq.l  #8,A1           ;Nächste Bildschirmposition
                addq.w  #1,D1
                cmp.w   #21,D1          ;Zeile voll?
                bne.s   plotc_level2    ;nein, noch nicht
                lea     15*160(A1),A1   ;Nächstes Bildschirmposition errechnen
                addq.w  #1,D0
                cmp.w   #13,D0          ;letzte Zeile bereits ausgegeben?
                bne.s   plotc_level1    ;Nein! Oben geht's weiter ^^^
                lea     -160(A1),A1
                moveq   #39,D0
                moveq   #-1,D1
plotc_level5:   move.w  D1,(A1)+        ;untere Linie ziehen
                clr.w   (A1)+
                dbra    D0,plotc_level5
                movem.l (SP)+,D0-A6
                rts

;*******************************************************************************
;* Zeichen um bzw. an einer bestimmten Position ermitteln                      *
;* => D0.W:Zeile (1-12)                                                        *
;*    D1.W:Spalte (1-20)                                                       *
;* <= D2.W:aktuelles Zeichen (5, siehe Tabelle unten)                          *
;*    D3.L:Byte 3 - oberes Zeichen (2)                                         *
;*         Byte 2 - unteres Zeichen (8)                                        *
;*         Byte 1 - linkes Zeichen (4)                                         *
;*         Byte 0 - rechtes Zeichen (6)                                        *
;*    1 2 3                                                                    *
;*    4 5 6                                                                    *
;*    7 8 9                                                                    *
;*******************************************************************************
get_char:       lea     level_dat(A6),A0 ;Level-Daten
                move.w  D0,D2
                mulu    #22,D2          ;Zeile*22
                add.w   D1,D2           ;+Spalte
                lea     0(A0,D2.w),A0   ;Adresse des aktuellen Zeichens
                movep.w -22(A0),D3      ;oberes Zeichen holen
                move.b  22(A0),D3       ;unteres Zeichen holen
                swap    D3
                movep.w -1(A0),D3       ;linkes & rechtes Zeichen holen
                move.b  (A0),D2         ;aktuelles Zeichen holen
                ext.w   D2
                rts

;*******************************************************************************
;* Verzögerung bei Bewegung des Joysticks (Spiel sich besser)                  *
;*******************************************************************************
verzögerung:    move.w  #20000,D0
verzögerung1:   dbra    D0,verzögerung1
                rts

;*******************************************************************************
;* Taste holen (D0=0, wenn keine gedrückt, sonst D0.W=Scancode der Taste)      *
;*******************************************************************************
get_key:        movem.l D0-D2/A0-A2,-(SP)
                move.w  #$0B,-(SP)
                trap    #1              ;Cconis()
                addq.l  #2,SP
                tst.l   D0              ;Taste gedrückt?
                beq.s   get_key1        ;Nein, dann weiter
                move.w  #7,-(SP)
                trap    #1              ;Crawin()
                addq.l  #2,SP
                swap    D0              ;Scancode nach unten
get_key1:       move.w  D0,key(A6)      ;Tastencode merken
                tst.b   demo_mode(A6)   ;Demo-Mode aktiv?
                bne.s   get_demo        ;Richtungen für's Demo holen
                cmpi.b  #'J',joy_text(A6)
                beq.s   get_key3        ;Joystick-Steuerung
                clr.b   dir(A6)         ;Richtungen löschen
                lea     key_tab-1(PC),A0
get_key2:       tst.b   (A0)+
                bmi.s   get_key3
                cmp.b   (A0)+,D0
                bne.s   get_key2
                move.b  (A0),dir(A6)
get_key3:       movem.l (SP)+,D0-D2/A0-A2
                rts
                DC.B 0
key_tab:        DC.B $4D,8,$4B,4,$50,2,$48,1,-1,-1 ;Joystick-Richtungen
                EVEN

get_demo:       tst.b   demo_count2(A6)
                bpl.s   get_demo1
                move.w  demo_level(A6),D0
                add.w   D0,D0           ;und mal 2
                lea     demo_level_tab(PC),A0
                adda.w  0(A0,D0.w),A0   ;Adresse der Demo-Daten des Levels
                move.w  demo_count(A6),D0
                move.b  0(A0,D0.w),D0
                beq.s   get_demo2       ;Ende des Demos (nix mehr tun)
                addq.w  #1,demo_count(A6)
                move.b  D0,D1
                lsr.b   #4,D1           ;Anzahl isolieren
                move.b  D1,demo_count2(A6)
                and.b   #$0F,D0
                move.b  D0,demo_dir(A6) ;Richtung merken
get_demo1:      subq.b  #1,demo_count2(A6)
                move.b  demo_dir(A6),D0
get_demo2:      move.b  D0,dir(A6)
                movem.l (SP)+,D0-D2/A0-A2
                rts
                BASE DC.W,demo_level_tab
demo_level_tab: DC.W demo_titel,demo_level1
demo_titel:     DC.B $41,$68,$02,$08,$01,$08,$01,$F4,$14,$02,$04,$31
                DC.B $22,$C8,$11,$04,$02,$08,$02,$B4,$02,$04,$21
                DC.B $12,$58,$11,$04,$02,$08,$02,$44,$02,$04,$11
                DC.B $12,$18,$12,$08,$11,$08,$01,$24,$02,$04,$01
                DC.B $02,$98,$42
demo_level1:    DC.B $44,$42,$14,$12,$08,$12,$04,$31,$08,$01,$14,$01,$04,$42
                DC.B $31,$18,$22,$28,$02,$18,$01,$34,$02,$04,$21,$08,$01,$14
                DC.B $01,$04,$32
                DC.B $21,$18,$22,$38,$11,$28,$12,$54,$02,$04,$21,$08,$01,$14
                DC.B $01,$04,$22
                DC.B $11,$18,$22,$38,$11,$08,$02,$01,$18,$12,$54,$02,$04,$21
                DC.B $08,$01,$14,$01,$04,$12
                DC.B $11,$34,$01,$34,$11,$08,$02,$04,$02,$08,$38,$01,$08,$12
                DC.B $04,$02,$18,$01,$08,$42
                DC.B $31,$44,$02,$14,$01,$58,$01,$08,$32
                DC.B $21,$64,$32,$18,$21,$04,$01,$48,$01,$08,$22
                DC.B $11,$44,$32,$28,$11,$04,$02,$08,$02,$24,$02,$14,$01,$08
                DC.B $02,$08,$31,$04,$01,$48,$01,$08,$22
                DC.B $01,$48,$31,$48
                EVEN

;*******************************************************************************
;* Autorepeat-Ausgabe setzen                                                   *
;*******************************************************************************
set_autorep_txt:lea     autorep_text(A6),A0
                btst    #1,$0484.w
                bne.s   set_autorep1
                move.b  #'f',(A0)+      ;'off'
                move.b  #'f',(A0)
                rts
set_autorep1:   move.b  #'n',(A0)+      ;'on '
                move.b  #' ',(A0)+
                rts

;*******************************************************************************
;* switch_screen: Physbase & Logbase vertauschen                               *
;*******************************************************************************
switch_screen:  movem.l D0-D2/A0-A2,-(SP)
                move.l  physbase(A6),D0
                move.l  logbase(A6),D1
                move.l  D1,physbase(A6)
                move.l  D0,logbase(A6)
                move.w  #-1,-(SP)
                move.l  D1,-(SP)
                move.l  D0,-(SP)
                move.w  #5,-(SP)
                trap    #14
                lea     12(SP),SP
                movem.l (SP)+,D0-D2/A0-A2
                rts

;*******************************************************************************
;* Untere Zeile neu ausgeben                                                   *
;*******************************************************************************
update_line:    movem.l D0-A6,-(SP)
                moveq   #0,D0
                move.w  akt_level(A6),D0
                addq.w  #1,D0
                divu    #10,D0
                or.l    #$300030,D0
                move.b  D0,level_txt(A6) ;Levelnummer einsetzen
                move.b  D0,level_text(A6)
                swap    D0
                move.b  D0,level_txt+1(A6)
                move.b  D0,level_text+1(A6)
                moveq   #0,D0
                move.w  moves(A6),D0
                moveq   #3,D4
                lea     moves_txt(A6),A0
                bsr     dez_out

                move.w  pushes(A6),D0
                moveq   #3,D4
                lea     pushes_txt(A6),A0
                bsr     dez_out

                move.w  akt_score(A6),D0
                add.w   big_score(A6),D0
                moveq   #3,D4
                lea     score_txt(A6),A0
                bsr     dez_out

                move.w  hiscore(A6),D0
                lea     hiscore_txt(A6),A0
                bsr     dez_out

                lea     text(A6),A0     ;und ausgeben
                bsr.s   print
                movem.l (SP)+,D0-A6
                rts

;*******************************************************************************
;* Frage in D0 stellen, auf "Y" oder "Z" bzw. "N" warten                       *
;*******************************************************************************
do_dialog:      moveq   #3,D1
                lea     dialog_string(A6),A0
do_dialog0:     rol.l   #8,D0           ;die vier Zeichen übertragen
                move.b  D0,(A0)+
                dbra    D1,do_dialog0
                lea     dialog_txt(A6),A0
                bsr.s   print_text      ;String ausgeben
do_dialog1:     bsr     get_key         ;Tastatur auswerten
                move.w  key(A6),D0      ;Taste holen
                cmp.w   #$31,D0
                beq.s   do_dialog2
                cmp.w   #$15,D0         ;Z
                beq.s   do_dialog2
                cmp.w   #$2C,D0         ;Y
                bne.s   do_dialog1
do_dialog2:     sub.w   #$31,D0         ;0= NEIN, <>0= JA
                rts

;*******************************************************************************
;* Text ab A0 ausgeben, aber ganze Seite auch noch neu ausgeben                *
;*******************************************************************************
print_text:     bsr     init_level      ;Level intern entpacken
                bsr     plot_level      ;Level zeichnen
                bsr     update_line     ;untere Textzeile neu ausgeben
                bsr.s   print           ;Text ausgeben
                bra     switch_screen   ;Bildschirmseiten umschalten

;*******************************************************************************
;* Text ab A0 ausgeben                                                         *
;*******************************************************************************
print:          movem.l D0-D4/A0-A3,-(SP)
                tst.b   rez(A6)         ;Farbmonitor?
                bmi.s   print_color0    ;ja! =>
print0:         moveq   #0,D1
                move.b  (A0)+,D1        ;Spalte holen
                moveq   #0,D0
                move.b  (A0)+,D0        ;Zeile holen
                mulu    #16*80,D0       ;Spaltenoffset errechnen
                add.w   D1,D0           ;Spalte dazu
                movea.l logbase(A6),A1
                adda.w  D0,A1           ;Adresse auf dem Schirm
                movea.l font_adr(A6),A2 ;Adresse des 16x16-Fonts
print1:         moveq   #0,D0
                move.b  (A0)+,D0        ;Zeichen holen
                beq.s   print3          ;Ende des Strings
                cmp.b   #$FF,D0         ;Noch ein String
                beq.s   print0          ;nächste Koordinate holen
                moveq   #0,D2           ;nur einmal ausgeben
                cmp.b   #1,D0           ;Zeichen mehrfach ausgeben?
                bne.s   print4
                move.b  (A0)+,D2        ;Anzahl holen
                moveq   #' ',D0         ;Space ausgeben
print4:         lea     0(A2,D0.w),A3   ;Adresse des Zeichens
                moveq   #15,D1          ;16 Pixelzeilen ausgeben
print2:         move.b  (A3),D4
                not.b   D4              ;Zeichen invertieren
                move.b  D4,(A1)
                lea     256(A3),A3
                lea     80(A1),A1
                dbra    D1,print2
                lea     -16*80+1(A1),A1 ;Pointer auf das nächste Zeichen
                dbra    D2,print4       ;Zeichen mehrfach ausgeben?
                bra.s   print1
print3:         movem.l (SP)+,D0-D4/A0-A3
                rts
print_color0:   moveq   #0,D1
                move.b  (A0)+,D1        ;Spalte holen
                moveq   #1,D2
                and.w   D1,D2
                and.w   #-2,D1
                add.w   D1,D1
                add.w   D2,D1
                moveq   #0,D0
                move.b  (A0)+,D0        ;Zeile holen
                mulu    #8*160,D0       ;Spaltenoffset errechnen
                add.w   D1,D0           ;Spalte dazu
                movea.l logbase(A6),A1
                adda.w  D0,A1           ;Adresse auf dem Schirm
                movea.l font_adr+4(A6),A2 ;Adresse des 16x16-Fonts
print_color1:   moveq   #0,D0
                move.b  (A0)+,D0        ;Zeichen holen
                beq.s   print3          ;Ende des Strings
                cmp.b   #$FF,D0         ;Noch ein String
                beq.s   print_color0    ;nächste Koordinate holen
                moveq   #0,D2           ;nur einmal ausgeben
                cmp.b   #1,D0           ;Zeichen mehrfach ausgeben?
                bne.s   print_color4
                move.b  (A0)+,D2        ;Anzahl holen
                moveq   #' ',D0         ;Space ausgeben
print_color4:   lea     0(A2,D0.w),A3   ;Adresse des Zeichens
                moveq   #7,D1           ;8 Pixelzeilen ausgeben
print_color2:   move.b  (A3),D4
                not.b   D4              ;Zeichen invertieren
                move.b  D4,(A1)
                clr.b   2(A1)
                lea     256(A3),A3
                lea     160(A1),A1
                dbra    D1,print_color2
                lea     -8*160+1(A1),A1 ;Pointer auf das nächste Zeichen
                move.w  A1,D4
                btst    #0,D4
                bne.s   print_color5
                addq.l  #2,A1
print_color5:   dbra    D2,print_color4
                bra.s   print_color1

;************************************************************************
;* Dezimal-Zahl in D0 nach A0 ausgeben                                  *
;* Anzahl der Stellen in D4                                             *
;************************************************************************
dez_out:        movem.l D0-D5/A3,-(SP)
                lea     dez_out_tab(PC),A3
                move.w  D4,D5
                lsl.w   #2,D5
                lea     4(A3,D5.w),A3
                moveq   #'0',D5
dez_out1:       move.l  -(A3),D3
                moveq   #$D0,D2
dez_out2:       sub.l   D3,D0
                dbcs    D2,dez_out2
                neg.b   D2
                move.b  D2,D1
                cmp.b   #'0',D1
                beq.s   dez_out4
                moveq   #'0',D5
dez_out3:       move.b  D1,(A0)+        ;Zahl in den Buffer
                add.l   D3,D0
                dbra    D4,dez_out1
                movem.l (SP)+,D0-D5/A3
                rts
dez_out4:       move.w  D5,D1
                tst.w   D4
                bne.s   dez_out3
                moveq   #'0',D1
                bra.s   dez_out3

dez_out_tab:    DC.L 1,10,100,1000,10000,100000
                DC.L 1000000,10000000,100000000,1000000000

;*******************************************************************************
;* Allgemeines Init                                                            *
;*******************************************************************************
init:           move.w  #2,-(SP)        ;Bildschirmadressen merken
                trap    #14
                addq.l  #2,SP
                move.l  D0,old_physbase(A6)
                move.w  #3,-(SP)
                trap    #14
                addq.l  #2,SP
                move.l  D0,old_logbase(A6)

                movem.l $FFFF8240.w,D0-D7
                movem.l D0-D7,old_pal(A6) ;Farben retten
                moveq   #3,D0
                and.b   $FFFF8260.w,D0
                move.b  D0,rez(A6)      ;die akt.Auflösung
                subq.b  #2,rez(A6)
                move.w  D0,old_rez(A6)  ;und die Auflösung merken
                btst    #1,D0
                bne.s   init4
                pea     farb_pal(A6)
                move.w  #6,-(SP)
                trap    #14             ;Farbpalette setzen
                addq.l  #6,SP
                moveq   #-1,D0
                move.w  #1,-(SP)
                move.l  D0,-(SP)
                move.l  D0,-(SP)
                move.w  #5,-(SP)
                trap    #14             ;geringe Auflösung an
                lea     12(SP),SP
init4:          lea     screens+255(A6),A0
                move.l  A0,D0
                clr.b   D0              ;Bildschirmadresse durch 256 teilbar
                movea.l D0,A0           ;machen
                move.l  A0,physbase(A6) ;1.Seite ist am Anfang "physbase"
                lea     32000(A0),A0
                move.l  A0,logbase(A6)  ;2. Seite ist am Anfang "logbase"

                pea     own_timer(PC)
                move.l  #$050045,-(SP)
                trap    #13             ;eigener 200Hz-Timer
                addq.l  #8,SP
                lea     org_timer(PC),A0
                move.l  D0,2(A0)        ;originalen 200Hz-Timer auch noch nutzen

                move.w  #$22,-(SP)
                trap    #14
                addq.l  #2,SP
                movea.l D0,A0
                lea     24(A0),A0
                move.l  A0,old_joyadr(A6)
                move.l  (A0),old_joyvec(A6)
                lea     own_joyvec(PC),A1
                move.l  A1,(A0)         ;eigene Joystickroutine

                pea     init_joy(PC)
                move.l  #$190001,-(SP)
                trap    #14             ;Joystick an, Maus aus
                addq.l  #8,SP

                linea   #10 [ Hidem ]
                linea   #0 [ Init ]
                movea.l 4(A1),A0        ;Adresse des 8x8-Fonts
                move.l  76(A0),font_adr+4(A6)
                movea.l 8(A1),A0        ;Adresse des 16x16-Fonts
                move.l  76(A0),font_adr(A6)

init1:          bsr     get_key         ;Keyboard-Buffer löschen
                tst.w   key(A6)
                bne.s   init1

                lea     titel_level(A6),A0
                lea     all_level_data-64(A6),A1
                moveq   #15,D0
init3:          move.l  (A0)+,(A1)+     ;Titel-Level kopieren
                dbra    D0,init3

                move.b  $0484.w,old_conterm(A6)
                andi.b  #$F6,$0484.w    ;Tastaturklick aus / Kbshift-Bits aus
                bsr     set_autorep_txt ;Autorepeat-Text setzen

                lea     init2(PC),A5
                bra     do_load_levels  ;Level laden

init2:          rts

;*******************************************************************************
;* Allgemeines Exit                                                            *
;*******************************************************************************
exit:           move.l  #'quit',D0
                bsr     do_dialog
                beq     restart_clr     ;Nicht beenden

                movea.l old_joyadr(A6),A0
                move.l  old_joyvec(A6),(A0) ;Joystick-Vektor zurück

                move.b  old_conterm(A6),$0484.w

                movem.l old_pal(A6),D0-D7 ;Farben zurück
                movem.l D0-D7,$FFFF8240.w
                move.w  old_rez(A6),-(SP) ;Bildschirmadressen zurücksetzen
                move.l  old_physbase(A6),-(SP)
                move.l  old_logbase(A6),-(SP)
                move.w  #5,-(SP)
                trap    #14
                lea     12(SP),SP

                move.l  org_timer+2(PC),-(SP)
                move.l  #$050045,-(SP)
                trap    #13             ;alter 200Hz-Timer
                addq.l  #8,SP

                pea     exit_joy(PC)
                move.l  #$190000,-(SP)
                trap    #14             ;Maus wieder an
                addq.l  #8,SP
                rts

;*******************************************************************************
;* Eigene Joystick-IRQ-Routine                                                 *
;*******************************************************************************
own_joyvec:     move.l  A6,-(SP)
                lea     varbase(PC),A6
                tst.b   demo_mode(A6)   ;Demo aktiv?
                bne.s   own_joyvec1     ;dann nix tun
                cmpi.b  #'J',joy_text(A6)
                bne.s   own_joyvec1     ;Joystick-Steuerung
                move.b  2(A0),dir(A6)
own_joyvec1:    movea.l (SP)+,A6
                rts

;*******************************************************************************
;* Eigene 200Hz-IRQ-Timer-Routine                                              *
;*******************************************************************************
own_timer:      movem.l A0/A6,-(SP)
                lea     varbase(PC),A6
                addq.w  #1,timer2(A6)
                subq.w  #1,timer(A6)    ;200Hz-Zähler
                bpl.s   own_timer_e
                move.w  #199,timer(A6)

                tst.b   timer_stop(A6)
                beq.s   own_timer_e

                bsr     score_down      ;pro Sekunde einen Punkt weniger

                lea     time_sek_txt+1(A6),A0
                addq.b  #1,(A0)
                cmpi.b  #$3A,(A0)
                bne.s   own_timer_e     ;Sekunden hochzählen
                move.b  #'0',(A0)
                subq.l  #1,A0
                addq.b  #1,(A0)
                cmpi.b  #'6',(A0)
                bne.s   own_timer_e
                move.b  #'0',(A0)

                lea     time_min_txt+1(A6),A0
                addq.b  #1,(A0)
                cmpi.b  #$3A,(A0)
                bne.s   own_timer_e     ;Minuten hochzählen
                move.b  #'0',(A0)
                subq.l  #1,A0
                addq.b  #1,(A0)
                cmpi.b  #'6',(A0)
                bne.s   own_timer_e
                move.b  #'0',(A0)

                addq.b  #1,time_std_txt(A6) ;Stunden erhöhen

own_timer_e:    movem.l (SP)+,A0/A6
org_timer:      jmp     $12345678

;*******************************************************************************
;* Hier beginnt nun der DATA-Bereich                                           *
;*******************************************************************************
                DATA
                BASE DC.W,*
                DC.L 'MRF!'
                DC.L 0      ;Offset zum Anfang
                DC.W mono_grafik
                DC.W 8      ;8 mal
                DC.W 32     ;Zeilenanzahl
                DC.W 4      ;je 4 Byte
                DC.W 80     ;Zeilenoffset

                DC.L 32034  ;Offset zum Anfang
                DC.W farb_grafik
                DC.W 8      ;8 mal
                DC.W 16     ;Zeilenanzahl
                DC.W 8      ;je 8 Byte
                DC.W 160    ;Zeilenoffset

                DC.L 32002  ;Offset zum Anfang
                DC.W farb_pal
                DC.W 16     ;1 mal
                DC.W 1      ;eine Zeile
                DC.W 2      ;je ein Wort
                DC.W 0      ;keine 2.Zeile

                DC.W -1

mono_grafik:    DC.W 8*32*4 ;Platz für die Daten
                DS.W 8*32*4-2
farb_grafik:    DC.W 8*16*8
                DS.W 8*16*8-2
farb_pal:       DC.W 16*1*2
                DS.W 16*1*2-2

titel_level:    DC.B $A8,$82,$22,$08,$82,$60,$82,$22,$88,$88,$60,$AA,$22,$28,$A0,$60
                DC.B $82,$22,$08,$88,$60,$B2,$22,$C8,$82,$00,$00,$00,$00,$00,$00,$00
                DC.B $00,$00,$0C,$83,$8A,$A2,$A8,$82,$80,$88,$22,$08,$88,$88,$88,$22
                DC.B $A8,$A0,$A2,$88,$22,$20,$88,$80,$8A,$A2,$08,$82,$14,$96,$00,$00

text:           DC.B 0,24   ;Bildschirmposition
                DC.B 1,2,'Level:'
level_txt:      DC.B '00'
                DC.B '  Moves:'
moves_txt:      DC.B '0000'
                DC.B '  Pushes:'
pushes_txt:     DC.B '0000'
                DC.B '  Time:'
time_std_txt:   DC.B '0'
                DC.B ':'
time_min_txt:   DC.B '00'
                DC.B ':'
time_sek_txt:   DC.B '00'
                DC.B '  Score:'
score_txt:      DC.B '0000'
                DC.B '  Hiscore:'
hiscore_txt:    DC.B '0000'
                DC.B 1,3,0

text2:          DC.B 30,5,1,21,-1
                DC.B 30,6,1,3,'THINK and WORK',1,3,-1
                DC.B 30,7,1,2,'©1989  by ∑-soft',1,2,-1
                DC.B 30,8,1,21,-1
                DC.B 30,9,' F1  - Start level '
level_text:     DC.B '00 ',-1
                DC.B 30,10,' F2  - Edit level',1,4,-1
                DC.B 30,11,' F3  - Level - 1',1,5,-1
                DC.B 30,12,' F4  - Level + 1',1,5,-1
                DC.B 30,13,' F5  - '
joy_text:       DC.B 'Joystick       ',-1
                DC.B 30,14,' F6  - Autorepeat o'
autorep_text:   DC.B 'ff ',-1
                DC.B 30,15,' F7  - Copyrights',1,4,-1
                DC.B 30,16,' F8  - Load levels',1,3,-1
                DC.B 30,17,' F9  - Save levels',1,3,-1
                DC.B 30,18,' F10 - Quit',1,10,-1
                DC.B 30,19,1,21,0

joy1_txt:       DC.B 'Joystick',0
joy2_txt:       DC.B 'Keyboard',0
dialog_txt:     DC.B 28,11,1,23,-1
                DC.B 28,12,' Sure you want to '
dialog_string:  DC.B 'xxxx? ',-1
                DC.B 28,13,1,23,0

copyright_txt:  DC.B 30,6,1,21,-1
                DC.B 30,7,1,3,'THINK and WORK',1,3,-1
                DC.B 30,8,1,2,'©1989  by ∑-soft',1,2,-1
                DC.B 30,9,1,21,-1
                DC.B 30,10,' ATARI ST Version by  ',-1
                DC.B 30,11,1,2,'Markus Fritze',1,5,-1
                DC.B 30,12,' Written with the',1,4,-1
                DC.B 30,13,1,2,'OMIKRON.Assembler  ',-1
                DC.B 30,14,1,21,-1
                DC.B 30,15,' ATARI XL-Version',1,4,-1
                DC.B 30,16,' and Levels by',1,7,-1
                DC.B 30,17,1,2,'Johann Schilcher',1,2,-1
                DC.B 30,18,1,21,0

text3:          DC.B 0,24
                DC.B ' F1-Wall F2-Target F3-Block F4-Exit'
                DC.B 1,2,'Clr/Home-Clr level',1,2,'UNDO-Undo',1,2,'F10:Quit ',0

fname:          DC.B 'LEVEL.DAT',0
init_joy:       DC.B $12,$14
exit_joy:       DC.B $08

;*******************************************************************************
;* Hier beginnt nun der BSS-Bereich                                            *
;*******************************************************************************
varbase:        BSS         ;Die Globale Offset-Variable steht stets in A6
rez:            DS.B 1      ;0=Monochrom, <>0=Farbe
                EVEN
physbase:       DS.L 1      ;angezeigte Bildschirmseite
logbase:        DS.L 1      ;aktuell zu bearbeitende Bildschirmseite
font_adr:       DS.L 2      ;Adressen der Fonts
akt_level:      DS.W 1      ;aktueller Level (0-24)
save_level:     DS.W 1      ;gemerkter Level (da das Demo den Level ändert)
akt_koord_x:    DS.W 1      ;X-Koordinate des Spielers
akt_koord_y:    DS.W 1      ;Y           -"-
level_anz:      DS.W 1      ;Levelanzahl (hier 25)
demo_count:     DS.W 1      ;Position des Demos
demo_level:     DS.W 1      ;akt.Level des Demos
demo_count2:    DS.B 1      ;Anzahl der Schritte in die gespeicherte Richtung
demo_dir:       DS.B 1      ;gespeicherte Richtung
demo_mode:      DS.B 1      ;<>0 => Demo-Mode aktiv
out_of_game:    DS.B 1      ;=0 => Spiel läuft gerade (Blockanzeige ...)
dir:            DS.B 1      ;Joystick-Daten (Bits 0-3)
timer_stop:     DS.B 1      ;=0 => Zeit steht
timer:          DS.W 1      ;200Hz Zähler mit Werten von 0-199
timer2:         DS.W 1      ;200Hz Zähler
key:            DS.W 1      ;akt.Tastencode
moves:          DS.W 1      ;Anzahl der Bewegungen des Spielers
pushes:         DS.W 1      ;Anzahl der Verschiebungen durch den Spieler
akt_score:      DS.W 1      ;Die erreichten Punkte
big_score:      DS.W 1      ;gesamter Score
hiscore:        DS.W 1      ;Hiscore des akt.Levels
old_joyvec:     DS.L 1      ;alter Joystick-Vektor
old_joyadr:     DS.L 1      ;alte Joystick-Vektor-Adresse
old_physbase:   DS.L 1      ;Bildschirmadresse beim Start
old_logbase:    DS.L 1
old_rez:        DS.W 1      ;Bildschirmauflösung beim Start
old_pal:        DS.W 16     ;alte Farbpalette
old_conterm:    DS.B 1      ;gemerkte conterm-Variable
button_pressed: DS.B 1      ;<>0, wenn Feuertaste im Editor gedrückt
level_buffer:   DS.L 16     ;für UNDO im Editor (64 Byte Levelbuffer)
level_dat:      DS.B 22*15  ;Platz für den aktuellen Level
                DS.B 64     ;Level "-1" = Titel
all_level_data: DS.B 64*max_level ;Platz für max.100 Level
                DS.L 256    ;eigener Stack
own_stack:      DS.L 0
screens:        DS.B 32000*2+255 ;die 2 Bildschirmseiten
                END