Futura Aquariencomputer Firmware

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

Photo vom Futura 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..."
Previous post:
Next post: